Skip to content

Commit 75aeefe

Browse files
committed
Test swizzle with softwareTextureRead
This is just a basic test. Other tests that would be good. * test with sampler and linear filtering * test cube maps * test gather All of these take different paths through softwareTextureRead. It's unlikely they are broken. I just wanted to check the code takes swizzle into account as that path is currently unused anywhere else. For now, it just seemed to be good to try
1 parent 5a1dcdc commit 75aeefe

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

src/webgpu/shader/execution/expression/call/builtin/texture_utils.spec.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ import {
2626
makeRandomDepthComparisonTexelGenerator,
2727
queryMipLevelMixWeightsForDevice,
2828
readTextureToTexelViews,
29+
SoftwareTexture,
30+
softwareTextureRead,
31+
swizzleTexel,
2932
texelsApproximatelyEqual,
33+
TextureCall,
34+
vec2,
3035
} from './texture_utils.js';
3136

3237
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
@@ -325,3 +330,106 @@ are supposed to work around this issue by poly-filling on devices that fail this
325330
validateWeights(t, stage, 'textureSampleLevel', weights.sampleLevelWeights);
326331
validateWeights(t, stage, 'textureSampleGrad', weights.softwareMixToGPUMixGradWeights);
327332
});
333+
334+
g.test('softwareSamplerSwizzle')
335+
.desc('test the software sampler swizzles')
336+
.params(u =>
337+
u
338+
.combine('srcFormat', ['rgba8unorm', 'bgra8unorm', 'depth24plus', 'stencil8'] as const)
339+
.beginSubcases()
340+
.combine('swizzle', ['rgba', 'abgr', '10gr'] as const)
341+
.expand('textureType', function* (t) {
342+
const type = getTextureFormatType(t.srcFormat, 'all');
343+
switch (type) {
344+
case 'depth':
345+
yield 'texture_2d<f32>';
346+
yield 'texture_depth_2d';
347+
break;
348+
case 'uint':
349+
yield 'texture_2d<u32>';
350+
break;
351+
case 'sint':
352+
yield 'texture_2d<i32>';
353+
break;
354+
default:
355+
yield 'texture_2d<f32>';
356+
}
357+
})
358+
)
359+
.fn(async t => {
360+
t.skipIfDeviceDoesNotHaveFeature('texture-component-swizzle');
361+
362+
const { srcFormat, swizzle, textureType } = t.params;
363+
const size = chooseTextureSize({ minSize: 9, minBlocks: 4, format: srcFormat });
364+
t.debug(`size: ${size.map(v => v.toString()).join(', ')}`);
365+
const descriptor: GPUTextureDescriptor = {
366+
format: srcFormat,
367+
size,
368+
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
369+
};
370+
const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor);
371+
372+
const viewDescriptor = { swizzle };
373+
const softwareTexture: SoftwareTexture = {
374+
texels,
375+
descriptor,
376+
viewDescriptor,
377+
};
378+
const call: TextureCall<vec2> = {
379+
builtin: 'textureLoad',
380+
coordType: 'u',
381+
mipLevel: 0,
382+
coords: [0, 0],
383+
};
384+
385+
const type = getTextureFormatType(srcFormat, 'all');
386+
const resultFormat =
387+
type === 'uint' ? 'rgba32uint' : type === 'sint' ? 'rgba32sint' : 'rgba32float';
388+
389+
const aspect = srcFormat === 'stencil8' ? 'stencil-only' : 'all';
390+
391+
// For each pixel in the texelView, convert it to RGBA and swizzle.
392+
// Then, call softwareTextureRead for that texel. The results should
393+
// be the same.
394+
const stage = 'compute';
395+
const errors = [];
396+
for (let mipLevel = 0; mipLevel < texels.length; ++mipLevel) {
397+
const expectedMipLevelTexelView = texels[mipLevel];
398+
const mipLevelSize = virtualMipSize(texture.dimension, size, mipLevel);
399+
const rep = kTexelRepresentationInfo[expectedMipLevelTexelView.format];
400+
401+
for (let y = 0; y < mipLevelSize[1]; ++y) {
402+
for (let x = 0; x < mipLevelSize[0]; ++x) {
403+
const expectedRaw = expectedMipLevelTexelView.color({ x, y, z: 0 });
404+
const expectedRGBA = convertPerTexelComponentToResultFormat(
405+
expectedRaw,
406+
resultFormat,
407+
aspect
408+
);
409+
const expected = swizzleTexel(expectedRGBA, swizzle);
410+
call.coords = [x, y];
411+
call.mipLevel = mipLevel;
412+
const actual = softwareTextureRead<vec2>(t, stage, call, softwareTexture);
413+
const maxFractionalDiff = 0;
414+
if (
415+
!texelsApproximatelyEqual(
416+
t.device,
417+
call.builtin,
418+
textureType,
419+
actual,
420+
resultFormat,
421+
expected,
422+
resultFormat,
423+
maxFractionalDiff
424+
)
425+
) {
426+
const actualStr = texelFormat(actual, rep);
427+
const expectedStr = texelFormat(expected, rep);
428+
errors.push(`texel at ${x}, ${y}, expected: ${expectedStr}, actual: ${actualStr}`);
429+
}
430+
}
431+
}
432+
433+
assert(errors.length === 0, errors.join('\n'));
434+
}
435+
});

src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2137,7 +2137,11 @@ function derivativeForCall<T extends Dimensionality>(
21372137
return dd.map((v, i) => v * (call.derivativeMult?.[i] ?? 1)) as T;
21382138
}
21392139

2140-
function softwareTextureRead<T extends Dimensionality>(
2140+
/**
2141+
* Implements a software texture reader. The software version of
2142+
* textureSample, textureGather, textureLoad, etc...
2143+
*/
2144+
export function softwareTextureRead<T extends Dimensionality>(
21412145
t: GPUTest,
21422146
stage: ShaderStage,
21432147
call: TextureCall<T>,

0 commit comments

Comments
 (0)