Skip to content

Commit

Permalink
Print out bias info for various texture sizes
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman committed Nov 12, 2024
1 parent b9f32fd commit 70ab87a
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Samples a texture with a bias to the mip level.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { isFilterableAsTextureF32, kAllTextureFormats } from '../../../../../format_info.js';
import { TextureTestMixin } from '../../../../../gpu_test.js';
import { clamp } from '../../../../../util/math.js';

import {
vec2,
Expand Down Expand Up @@ -37,6 +38,230 @@ import {

export const g = makeTestGroup(TextureTestMixin(WGSLTextureSampleTest));

function makeGraph(width: number, height: number) {
const data = new Uint8Array(width * height);

return {
plot(norm: number, x: number, c: number) {
const y = clamp(Math.floor(norm * height), { min: 0, max: height - 1 });
const offset = (height - y - 1) * width + x;
data[offset] = c;
},
plotValues(values: Iterable<number>, c: number) {
let i = 0;
for (const v of values) {
this.plot(v, i, c);
++i;
}
},
toString(conversion = ['.', 'e', 'A']) {
const lines = [];
for (let y = 0; y < height; ++y) {
const offset = y * width;
lines.push([...data.subarray(offset, offset + width)].map(v => conversion[v]).join(''));
}
return lines.join('\n');
},
};
}

function safeStr(v?: string | number) {
return v === undefined ? 'undefined' : v.toString();
}

function pad(format: string, len: number, v: string | number) {
switch (format) {
case '>': // move to right
case 'r': // pad right
case 's': // pad start
return safeStr(v).padStart(len);
default:
return safeStr(v).padEnd(len);
}
}

function padColumns(rows: (string | number)[][], formats = '') {
const columnLengths: number[] = [];

// get size of each column
for (const row of rows) {
row.forEach((v, i) => {
columnLengths[i] = Math.max(columnLengths[i] || 0, safeStr(v).length);
});
}

return rows
.map(row => row.map((v, i) => pad(formats[i], columnLengths[i], v)).join(''))
.join('\n');
}

g.test('info')
.desc(
`
test various bias settings for a given mip level with different texture sizes
`
)
.fn(async t => {
const { device } = t;
const biases = [
-16, -15.9, -15.8, -15, 8, 9, 10, 11, 12, 12.125, 12.25, 12.5, 12.75, 13, 14, 15, 15.99,
];

const module = device.createShaderModule({
code: `
struct VOut {
@builtin(position) pos: vec4f,
@location(0) @interpolate(flat, either) ndx: u32,
@location(1) @interpolate(flat, either) result: vec4<f32>,
};
struct Data {
derivativeMult: f32,
bias: f32,
pad0: f32,
pad1: f32,
};
@group(0) @binding(0) var T : texture_2d<f32>;
@group(0) @binding(1) var S : sampler;
@group(0) @binding(2) var<uniform> data : array<Data, ${biases.length}>;
fn getResult(idx: u32, derivativeBase: vec2f) -> vec4<f32> {
let args = data[idx];
return textureSampleBias(T, S, vec2f(0.5) + derivativeBase * vec2f(args.derivativeMult, 0), args.bias);
}
// --------------------------- fragment stage shaders --------------------------------
@vertex fn vsFragment(
@builtin(vertex_index) vertex_index : u32,
@builtin(instance_index) instance_index : u32) -> VOut {
let positions = array(vec2f(-1, 3), vec2f(3, -1), vec2f(-1, -1));
return VOut(vec4f(positions[vertex_index], 0, 1), instance_index, vec4<f32>(0));
}
@fragment fn fsFragment(v: VOut) -> @location(0) vec4u {
let derivativeBase = (v.pos.xy - 0.5 - vec2f(f32(v.ndx), 0)) / vec2f(textureDimensions(T));
return bitcast<vec4u>(getResult(v.ndx, derivativeBase));
//return bitcast<vec4u>(vec4f(data[v.ndx].bias));
}
`,
});

const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module },
fragment: { module, targets: [{ format: 'rgba32uint' }] },
});

const sampler = device.createSampler({
minFilter: 'linear',
magFilter: 'linear',
mipmapFilter: 'linear',
});

const data = new Float32Array(biases.length * 4);
biases.forEach((bias, i) => {
const mipLevel = 0.5;
const derivativeBasedMipLevel = mipLevel - bias;
const derivativeMult = Math.pow(2, derivativeBasedMipLevel);
const offset = i * 4;
data[offset + 0] = derivativeMult;
data[offset + 1] = bias;
});

const dataBuffer = t.createBufferTracked({
size: data.byteLength,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(dataBuffer, 0, data);

const sizes = [2, 4, 8, 12, 16, 24, 32, 40, 128];
const buffers: GPUBuffer[] = [];
await Promise.all(
sizes.map(size => {
const texture = t.createTextureTracked({
size: [size, size],
format: 'r8unorm',
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
});

// fill mip level 1 with ones
const ones = new Uint8Array((texture.width / 2) * (texture.height / 2)).fill(255);
device.queue.writeTexture({ texture, mipLevel: 1 }, ones, { bytesPerRow: size / 2 }, [
size / 2,
size / 2,
]);

const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: texture.createView() },
{ binding: 1, resource: sampler },
{ binding: 2, resource: { buffer: dataBuffer } },
],
});

const resultTexture = t.createTextureTracked({
size: [biases.length, 1],
format: 'rgba32uint',
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
});

const resultBuffer = t.createBufferTracked({
size: resultTexture.width * 4 * 4,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
view: resultTexture.createView(),
loadOp: 'clear',
storeOp: 'store',
},
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
for (let i = 0; i < biases.length; ++i) {
pass.setViewport(i, 0, 1, 1, 0, 1);
pass.draw(3, 1, 0, i);
}
pass.end();
encoder.copyTextureToBuffer(
{ texture: resultTexture },
{
buffer: resultBuffer,
},
[biases.length, 1]
);
device.queue.submit([encoder.finish()]);
buffers.push(resultBuffer);
return resultBuffer.mapAsync(GPUMapMode.READ);
})
);

const graph = makeGraph(biases.length, 20);

const rows: (number | string)[][] = [['bias->', ...biases.map(v => `|${v}`)]];
rows.push(rows[0].map(v => '|-------'));
sizes.forEach((size, i) => {
const results = new Float32Array(buffers[i].getMappedRange());
const row: (number | string)[] = [`size:${size}`];
for (let j = 0; j < results.length; j += 4) {
graph.plot((1 - results[j] - 0.4) / 0.2, j / 4, i + 1);
row.push(`|${(1 - results[j]).toFixed(5)}`);
}
rows.push(row);
});

t.info(`\n${padColumns(rows)}`);
t.info(`\n${graph.toString(['.', ...biases.map((v, i) => String.fromCodePoint(97 + i))])}`);
t.expectOK(new Error('info'));
});

g.test('sampled_2d_coords')
.specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
.desc(
Expand Down Expand Up @@ -111,6 +336,7 @@ Parameters:
offset,
};
});

const viewDescriptor = {};
const textureType = 'texture_2d<f32>';
const results = await doTextureCalls(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2190,7 +2190,7 @@ export async function checkCallResults<T extends Dimensionality>(
maxFractionalDiff
)
) {
continue;
// continue;
}

if (!sampler && okBecauseOutOfBounds(texture, call, gotRGBA, maxFractionalDiff)) {
Expand All @@ -2208,6 +2208,7 @@ export async function checkCallResults<T extends Dimensionality>(
: kRComponent;

let bad = false;
bad = true;
const diffs = rgbaComponentsToCheck.map(component => {
const g = gotRGBA[component]!;
const e = expectRGBA[component]!;
Expand Down

0 comments on commit 70ab87a

Please sign in to comment.