diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index c7d10a4c0f2..f0038ea46bc 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1611,8 +1611,6 @@ "webgpu:shader,execution,expression,call,builtin,textureSampleCompareLevel:3d_coords:*": { "subcaseMS": 10.301 }, "webgpu:shader,execution,expression,call,builtin,textureSampleCompareLevel:arrayed_2d_coords:*": { "subcaseMS": 705.100 }, "webgpu:shader,execution,expression,call,builtin,textureSampleCompareLevel:arrayed_3d_coords:*": { "subcaseMS": 622.700 }, - "webgpu:shader,execution,expression,call,builtin,textureSampleCompareLevel:control_flow:*": { "subcaseMS": 2.202 }, - "webgpu:shader,execution,expression,call,builtin,textureSampleCompareLevel:stage:*": { "subcaseMS": 7.901 }, "webgpu:shader,execution,expression,call,builtin,textureSampleGrad:sampled_2d_coords:*": { "subcaseMS": 82.401 }, "webgpu:shader,execution,expression,call,builtin,textureSampleGrad:sampled_3d_coords:*": { "subcaseMS": 309.101 }, "webgpu:shader,execution,expression,call,builtin,textureSampleGrad:sampled_array_2d_coords:*": { "subcaseMS": 352.900 }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts index 89891738b0f..cad0e85c662 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts @@ -33,6 +33,7 @@ import { generateTextureBuiltinInputs2D, kCubeSamplePointMethods, kSamplePointMethods, + makeRandomDepthComparisonTexelGenerator, TextureCall, vec2, vec3, @@ -87,15 +88,17 @@ Parameters: const { format, samplePoints, A, addressModeU, addressModeV, minFilter, compare, offset } = t.params; - const [width, height] = chooseTextureSize({ minSize: 8, minBlocks: 4, format }); - const depthOrArrayLayers = 4; + const viewDimension = '2d-array'; + const size = chooseTextureSize({ minSize: 8, minBlocks: 4, format, viewDimension }); const descriptor: GPUTextureDescriptor = { format, - size: { width, height, depthOrArrayLayers }, + size, usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, }; - const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor); + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); const sampler: GPUSamplerDescriptor = { addressModeU, addressModeV, @@ -184,7 +187,9 @@ Parameters: size, usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, }; - const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor); + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); const sampler: GPUSamplerDescriptor = { addressModeU: addressMode, addressModeV: addressMode, @@ -267,13 +272,15 @@ Parameters: .fn(async t => { const { format, C, samplePoints, addressMode, compare, minFilter, offset } = t.params; - const [width, height] = chooseTextureSize({ minSize: 8, minBlocks: 4, format }); + const size = chooseTextureSize({ minSize: 8, minBlocks: 4, format }); const descriptor: GPUTextureDescriptor = { format, - size: { width, height }, + size, usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, }; - const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor); + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); const sampler: GPUSamplerDescriptor = { addressModeU: addressMode, addressModeV: addressMode, @@ -344,16 +351,17 @@ Parameters: const { format, samplePoints, addressMode, minFilter, compare } = t.params; const viewDimension: GPUTextureViewDimension = 'cube'; - const [width, height] = chooseTextureSize({ minSize: 8, minBlocks: 2, format, viewDimension }); - const depthOrArrayLayers = 6; + const size = chooseTextureSize({ minSize: 8, minBlocks: 2, format, viewDimension }); const descriptor: GPUTextureDescriptor = { format, ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), - size: { width, height, depthOrArrayLayers }, + size, usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, }; - const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor); + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); const sampler: GPUSamplerDescriptor = { addressModeU: addressMode, addressModeV: addressMode, diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts index eae5098257e..bb268a64595 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts @@ -1,13 +1,35 @@ export const description = ` Samples a depth texture and compares the sampled depth values against a reference value. + +- TODO: test cube maps with more than 1 mip level. +- TODO: test un-encodable formats. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { GPUTest } from '../../../../../gpu_test.js'; +import { kCompareFunctions } from '../../../../../capability_info.js'; +import { + isDepthTextureFormat, + isEncodableTextureFormat, + kDepthStencilFormats, +} from '../../../../../format_info.js'; -import { generateCoordBoundaries, generateOffsets } from './utils.js'; +import { + checkCallResults, + chooseTextureSize, + createTextureWithRandomDataAndGetTexels, + doTextureCalls, + generateSamplePointsCube, + generateTextureBuiltinInputs2D, + kCubeSamplePointMethods, + kSamplePointMethods, + makeRandomDepthComparisonTexelGenerator, + TextureCall, + vec2, + vec3, + WGSLTextureSampleTest, +} from './texture_utils.js'; -export const g = makeTestGroup(GPUTest); +export const g = makeTestGroup(WGSLTextureSampleTest); g.test('2d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') @@ -18,7 +40,7 @@ fn textureSampleCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2 Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * depth_ref The reference value to compare the sampled depth value against. * offset @@ -29,14 +51,77 @@ Parameters: Values outside of this range will result in a shader-creation error. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(2)) - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) - .combine('offset', generateOffsets(2)) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kSamplePointMethods) + .combine('addressModeU', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('addressModeV', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) + .combine('offset', [false, true] as const) ) - .unimplemented(); + .fn(async t => { + const { format, samplePoints, addressModeU, addressModeV, minFilter, compare, offset } = + t.params; + + const size = chooseTextureSize({ minSize: 16, minBlocks: 4, format }); + + const descriptor: GPUTextureDescriptor = { + format, + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + mipLevelCount: 3, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU, + addressModeV, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateTextureBuiltinInputs2D(50, { + method: samplePoints, + textureBuiltin: 'textureSampleCompare', + sampler, + descriptor, + derivatives: true, + depthRef: true, + offset, + hashInputs: [format, samplePoints, addressModeU, addressModeV, minFilter, offset], + }).map(({ coords, derivativeMult, arrayIndex, depthRef, offset }) => { + return { + builtin: 'textureSampleCompare', + coordType: 'f', + coords, + derivativeMult, + depthRef, + offset, + }; + }); + const textureType = 'texture_depth_2d'; + const viewDescriptor = {}; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); g.test('3d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') @@ -46,31 +131,96 @@ fn textureSampleCompare(t: texture_depth_cube, s: sampler_comparison, coords: ve Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * depth_ref The reference value to compare the sampled depth value against. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(3)) - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kCubeSamplePointMethods) + .combine('addressMode', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) ) - .unimplemented(); + .fn(async t => { + const { format, samplePoints, addressMode, minFilter, compare } = t.params; + + const viewDimension: GPUTextureViewDimension = 'cube'; + const size = chooseTextureSize({ minSize: 16, minBlocks: 2, format, viewDimension }); + + const descriptor: GPUTextureDescriptor = { + format, + ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + // MAINTENANCE_TODO: change to 3 once derivatives with cube maps are supported + mipLevelCount: 1, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU: addressMode, + addressModeV: addressMode, + addressModeW: addressMode, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateSamplePointsCube(50, { + method: samplePoints, + sampler, + descriptor, + derivatives: true, + depthRef: true, + textureBuiltin: 'textureSampleCompare', + hashInputs: [format, samplePoints, addressMode, minFilter, compare], + }).map(({ coords, derivativeMult, depthRef }) => { + return { + builtin: 'textureSampleCompare', + coordType: 'f', + coords, + derivativeMult, + depthRef, + }; + }); + const viewDescriptor = { + dimension: viewDimension, + }; + const textureType = 'texture_depth_cube'; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); g.test('arrayed_2d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') .desc( ` -C is i32 or u32 +A is i32 or u32 -fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: C, depth_ref: f32) -> f32 -fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: C, depth_ref: f32, offset: vec2) -> f32 +fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: A, depth_ref: f32) -> f32 +fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: A, depth_ref: f32, offset: vec2) -> f32 Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * array_index: The 0-based texture array index to sample. * depth_ref The reference value to compare the sampled depth value against. @@ -82,41 +232,178 @@ Parameters: Values outside of this range will result in a shader-creation error. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(2)) - .combine('C', ['i32', 'u32'] as const) - .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) - /* array_index not param'd as out-of-bounds is implementation specific */ - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) - .combine('offset', generateOffsets(2)) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kSamplePointMethods) + .combine('A', ['i32', 'u32'] as const) + .combine('addressModeU', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('addressModeV', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) + .combine('offset', [false, true] as const) ) - .unimplemented(); + .beforeAllSubcases(t => { + t.skipIfTextureFormatNotSupported(t.params.format); + }) + .fn(async t => { + const { format, samplePoints, A, addressModeU, addressModeV, minFilter, compare, offset } = + t.params; + + const viewDimension = '2d-array'; + const size = chooseTextureSize({ minSize: 16, minBlocks: 4, format, viewDimension }); + + const descriptor: GPUTextureDescriptor = { + format, + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + mipLevelCount: 3, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU, + addressModeV, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateTextureBuiltinInputs2D(50, { + method: samplePoints, + textureBuiltin: 'textureSampleCompare', + sampler, + descriptor, + derivatives: true, + arrayIndex: { num: texture.depthOrArrayLayers, type: A }, + depthRef: true, + offset, + hashInputs: [format, samplePoints, A, addressModeU, addressModeV, minFilter, offset], + }).map(({ coords, derivativeMult, arrayIndex, depthRef, offset }) => { + return { + builtin: 'textureSampleCompare', + coordType: 'f', + coords, + derivativeMult, + arrayIndex, + arrayIndexType: A === 'i32' ? 'i' : 'u', + depthRef, + offset, + }; + }); + const textureType = 'texture_depth_2d_array'; + const viewDescriptor = {}; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); g.test('arrayed_3d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') .desc( ` -C is i32 or u32 +A is i32 or u32 -fn textureSampleCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3, array_index: C, depth_ref: f32) -> f32 +fn textureSampleCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3, array_index: A, depth_ref: f32) -> f32 Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * array_index: The 0-based texture array index to sample. * depth_ref The reference value to compare the sampled depth value against. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(3)) - .combine('C', ['i32', 'u32'] as const) - .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) - /* array_index not param'd as out-of-bounds is implementation specific */ - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kCubeSamplePointMethods) + .combine('A', ['i32', 'u32'] as const) + .combine('addressMode', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) ) - .unimplemented(); + .beforeAllSubcases(t => { + t.skipIfTextureViewDimensionNotSupported('cube-array'); + }) + .fn(async t => { + const { format, A, samplePoints, addressMode, minFilter, compare } = t.params; + + const viewDimension: GPUTextureViewDimension = 'cube-array'; + const size = chooseTextureSize({ minSize: 8, minBlocks: 2, format, viewDimension }); + + const descriptor: GPUTextureDescriptor = { + format, + ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + // MAINTENANCE_TODO: change to 3 once derivatives with cube maps are supported + mipLevelCount: 1, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU: addressMode, + addressModeV: addressMode, + addressModeW: addressMode, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateSamplePointsCube(50, { + method: samplePoints, + sampler, + descriptor, + derivatives: true, + textureBuiltin: 'textureSampleCompare', + arrayIndex: { num: texture.depthOrArrayLayers / 6, type: A }, + depthRef: true, + hashInputs: [format, samplePoints, addressMode, minFilter], + }).map(({ coords, derivativeMult, depthRef, arrayIndex }) => { + return { + builtin: 'textureSampleCompare', + arrayIndex, + arrayIndexType: A === 'i32' ? 'i' : 'u', + coordType: 'f', + coords, + derivativeMult, + depthRef, + }; + }); + const viewDescriptor = { + dimension: viewDimension, + }; + const textureType = 'texture_depth_cube_array'; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts index 500df8a6eca..75077b5afa7 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts @@ -7,34 +7,35 @@ The textureSampleCompareLevel function is the same as textureSampleCompare, exce * The function does not compute derivatives. * There is no requirement for textureSampleCompareLevel to be invoked in uniform control flow. * textureSampleCompareLevel may be invoked in any shader stage. + +- TODO: test un-encodable formats. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { GPUTest } from '../../../../../gpu_test.js'; - -import { generateCoordBoundaries, generateOffsets } from './utils.js'; +import { kCompareFunctions } from '../../../../../capability_info.js'; +import { + isDepthTextureFormat, + isEncodableTextureFormat, + kDepthStencilFormats, +} from '../../../../../format_info.js'; -export const g = makeTestGroup(GPUTest); +import { + checkCallResults, + chooseTextureSize, + createTextureWithRandomDataAndGetTexels, + doTextureCalls, + generateSamplePointsCube, + generateTextureBuiltinInputs2D, + kCubeSamplePointMethods, + kSamplePointMethods, + makeRandomDepthComparisonTexelGenerator, + TextureCall, + vec2, + vec3, + WGSLTextureSampleTest, +} from './texture_utils.js'; -g.test('stage') - .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') - .desc( - ` -Tests that 'textureSampleCompareLevel' maybe called in any shader stage. -` - ) - .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) - .unimplemented(); - -g.test('control_flow') - .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') - .desc( - ` -Tests that 'textureSampleCompareLevel' maybe called in non-uniform control flow. -` - ) - .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) - .unimplemented(); +export const g = makeTestGroup(WGSLTextureSampleTest); g.test('2d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') @@ -45,7 +46,7 @@ fn textureSampleCompareLevel(t: texture_depth_2d, s: sampler_comparison, coords: Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * depth_ref The reference value to compare the sampled depth value against. * offset @@ -56,14 +57,77 @@ Parameters: Values outside of this range will result in a shader-creation error. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(2)) - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) - .combine('offset', generateOffsets(2)) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kSamplePointMethods) + .combine('addressModeU', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('addressModeV', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) + .combine('offset', [false, true] as const) ) - .unimplemented(); + .fn(async t => { + const { format, samplePoints, addressModeU, addressModeV, minFilter, compare, offset } = + t.params; + + const size = chooseTextureSize({ minSize: 16, minBlocks: 4, format }); + + const descriptor: GPUTextureDescriptor = { + format, + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + mipLevelCount: 3, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU, + addressModeV, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateTextureBuiltinInputs2D(50, { + method: samplePoints, + textureBuiltin: 'textureSampleCompareLevel', + sampler, + descriptor, + derivatives: true, + depthRef: true, + offset, + hashInputs: [format, samplePoints, addressModeU, addressModeV, minFilter, offset], + }).map(({ coords, derivativeMult, arrayIndex, depthRef, offset }) => { + return { + builtin: 'textureSampleCompareLevel', + coordType: 'f', + coords, + derivativeMult, + depthRef, + offset, + }; + }); + const textureType = 'texture_depth_2d'; + const viewDescriptor = {}; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); g.test('3d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') @@ -73,31 +137,96 @@ fn textureSampleCompareLevel(t: texture_depth_cube, s: sampler_comparison, coord Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * depth_ref The reference value to compare the sampled depth value against. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(3)) - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kCubeSamplePointMethods) + .combine('addressMode', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) ) - .unimplemented(); + .fn(async t => { + const { format, samplePoints, addressMode, minFilter, compare } = t.params; + + const viewDimension: GPUTextureViewDimension = 'cube'; + const size = chooseTextureSize({ minSize: 16, minBlocks: 2, format, viewDimension }); + + const descriptor: GPUTextureDescriptor = { + format, + ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + // MAINTENANCE_TODO: change to 3 + mipLevelCount: 1, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU: addressMode, + addressModeV: addressMode, + addressModeW: addressMode, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateSamplePointsCube(50, { + method: samplePoints, + sampler, + descriptor, + derivatives: true, + depthRef: true, + textureBuiltin: 'textureSampleCompareLevel', + hashInputs: [format, samplePoints, addressMode, minFilter, compare], + }).map(({ coords, derivativeMult, depthRef }) => { + return { + builtin: 'textureSampleCompareLevel', + coordType: 'f', + coords, + derivativeMult, + depthRef, + }; + }); + const viewDescriptor = { + dimension: viewDimension, + }; + const textureType = 'texture_depth_cube'; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); g.test('arrayed_2d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') .desc( ` -C is i32 or u32 +A is i32 or u32 -fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: C, depth_ref: f32) -> f32 -fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: C, depth_ref: f32, offset: vec2) -> f32 +fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: A, depth_ref: f32) -> f32 +fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2, array_index: A, depth_ref: f32, offset: vec2) -> f32 Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * array_index: The 0-based texture array index to sample. * depth_ref The reference value to compare the sampled depth value against. @@ -109,41 +238,176 @@ Parameters: Values outside of this range will result in a shader-creation error. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(2)) - .combine('C', ['i32', 'u32'] as const) - .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) - /* array_index not param'd as out-of-bounds is implementation specific */ - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) - .combine('offset', generateOffsets(2)) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kSamplePointMethods) + .combine('A', ['i32', 'u32'] as const) + .combine('addressModeU', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('addressModeV', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) + .combine('offset', [false, true] as const) ) - .unimplemented(); + .beforeAllSubcases(t => { + t.skipIfTextureFormatNotSupported(t.params.format); + }) + .fn(async t => { + const { format, samplePoints, A, addressModeU, addressModeV, minFilter, compare, offset } = + t.params; + + const viewDimension = '2d-array'; + const size = chooseTextureSize({ minSize: 16, minBlocks: 4, format, viewDimension }); + + const descriptor: GPUTextureDescriptor = { + format, + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + mipLevelCount: 3, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU, + addressModeV, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateTextureBuiltinInputs2D(50, { + method: samplePoints, + textureBuiltin: 'textureSampleCompareLevel', + sampler, + descriptor, + derivatives: true, + arrayIndex: { num: texture.depthOrArrayLayers, type: A }, + depthRef: true, + offset, + hashInputs: [format, samplePoints, A, addressModeU, addressModeV, minFilter, offset], + }).map(({ coords, derivativeMult, arrayIndex, depthRef, offset }) => { + return { + builtin: 'textureSampleCompareLevel', + coordType: 'f', + coords, + derivativeMult, + arrayIndex, + arrayIndexType: A === 'i32' ? 'i' : 'u', + depthRef, + offset, + }; + }); + const textureType = 'texture_depth_2d_array'; + const viewDescriptor = {}; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); g.test('arrayed_3d_coords') .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') .desc( ` -C is i32 or u32 +A is i32 or u32 -fn textureSampleCompareLevel(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3, array_index: C, depth_ref: f32) -> f32 +fn textureSampleCompareLevel(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3, array_index: A, depth_ref: f32) -> f32 Parameters: * t The depth texture to sample. - * s The sampler_comparision type. + * s The sampler_comparison type. * coords The texture coordinates used for sampling. * array_index: The 0-based texture array index to sample. * depth_ref The reference value to compare the sampled depth value against. ` ) - .paramsSubcasesOnly(u => + .params(u => u - .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) - .combine('coords', generateCoordBoundaries(3)) - .combine('C', ['i32', 'u32'] as const) - .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) - /* array_index not param'd as out-of-bounds is implementation specific */ - .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('format', kDepthStencilFormats) + // filter out stencil only formats + .filter(t => isDepthTextureFormat(t.format)) + // MAINTENANCE_TODO: Remove when support for depth24plus, depth24plus-stencil8, and depth32float-stencil8 is added. + .filter(t => isEncodableTextureFormat(t.format)) + .combine('minFilter', ['nearest', 'linear'] as const) + .beginSubcases() + .combine('samplePoints', kCubeSamplePointMethods) + .combine('A', ['i32', 'u32'] as const) + .combine('addressMode', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('compare', kCompareFunctions) ) - .unimplemented(); + .beforeAllSubcases(t => { + t.skipIfTextureViewDimensionNotSupported('cube-array'); + }) + .fn(async t => { + const { format, A, samplePoints, addressMode, minFilter, compare } = t.params; + + const viewDimension: GPUTextureViewDimension = 'cube-array'; + const size = chooseTextureSize({ minSize: 8, minBlocks: 2, format, viewDimension }); + + const descriptor: GPUTextureDescriptor = { + format, + ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), + size, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + }; + const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare), + }); + const sampler: GPUSamplerDescriptor = { + addressModeU: addressMode, + addressModeV: addressMode, + addressModeW: addressMode, + compare, + minFilter, + magFilter: minFilter, + mipmapFilter: minFilter, + }; + + const calls: TextureCall[] = generateSamplePointsCube(50, { + method: samplePoints, + sampler, + descriptor, + derivatives: true, + textureBuiltin: 'textureSampleCompareLevel', + arrayIndex: { num: texture.depthOrArrayLayers / 6, type: A }, + depthRef: true, + hashInputs: [format, samplePoints, addressMode, minFilter], + }).map(({ coords, derivativeMult, depthRef, arrayIndex }) => { + return { + builtin: 'textureSampleCompareLevel', + arrayIndex, + arrayIndexType: A === 'i32' ? 'i' : 'u', + coordType: 'f', + coords, + derivativeMult, + depthRef, + }; + }); + const viewDescriptor = { + dimension: viewDimension, + }; + const textureType = 'texture_depth_cube_array'; + const results = await doTextureCalls(t, texture, viewDescriptor, textureType, sampler, calls); + const res = await checkCallResults( + t, + { texels, descriptor, viewDescriptor }, + textureType, + sampler, + calls, + results + ); + t.expectOK(res); + }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts index 1f2b38d1e29..2f64739c86c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts @@ -37,7 +37,7 @@ import { TexelComponent, TexelRepresentationInfo, } from '../../../../../util/texture/texel_data.js'; -import { TexelView } from '../../../../../util/texture/texel_view.js'; +import { PerPixelAtLevel, TexelView } from '../../../../../util/texture/texel_view.js'; import { createTextureFromTexelViews } from '../../../../../util/texture.js'; import { reifyExtent3D } from '../../../../../util/unions.js'; @@ -732,11 +732,64 @@ export function appendComponentTypeForFormatToTextureType(base: string, format: : `${base}<${getTextureFormatTypeInfo(format).componentType}>`; } -function createRandomTexelViewViaColors(info: { - format: GPUTextureFormat; - size: GPUExtent3D; - mipLevel: number; -}): TexelView { +type RandomTextureOptions = { + generator: PerPixelAtLevel>; +}; + +/** + * Make a generator for texels for depth comparison tests. + */ +export function makeRandomDepthComparisonTexelGenerator( + info: { + format: GPUTextureFormat; + size: GPUExtent3D; + }, + comparison: GPUCompareFunction +) { + const rep = kTexelRepresentationInfo[info.format as EncodableTextureFormat]; + const size = reifyExtent3D(info.size); + + const comparisonIsEqualOrNotEqual = comparison === 'equal' || comparison === 'not-equal'; + + // for equal and not-equal we just want to test 0, 0.6, and 1 + // for everything else we want 0 to 1 + // Note: 0.6 is chosen because we'll never choose 0.6 as our depth reference + // value. (see generateTextureBuiltinInputsImpl and generateSamplePointsCube) + // The problem with comparing equal is other than 0.0 and 1.0, no other + // values are guaranteed to be equal. + const fixedValues = [0, 0.6, 1, 1]; + const format = comparisonIsEqualOrNotEqual + ? (norm: number) => fixedValues[(norm * (fixedValues.length - 1)) | 0] + : (norm: number) => norm; + + return (coords: SampleCoord): Readonly> => { + const texel: PerTexelComponent = {}; + for (const component of rep.componentOrder) { + const rnd = hashU32( + coords.x, + coords.y, + coords.z, + coords.sampleIndex ?? 0, + component.charCodeAt(0), + size.width, + size.height, + size.depthOrArrayLayers + ); + const normalized = clamp(rnd / 0xffffffff, { min: 0, max: 1 }); + texel[component] = format(normalized); + } + return quantize(texel, rep); + }; +} + +function createRandomTexelViewViaColors( + info: { + format: GPUTextureFormat; + size: GPUExtent3D; + mipLevel: number; + }, + options?: RandomTextureOptions | undefined +): TexelView { const rep = kTexelRepresentationInfo[info.format as EncodableTextureFormat]; const size = reifyExtent3D(info.size); const minMax = Object.fromEntries( @@ -765,7 +818,10 @@ function createRandomTexelViewViaColors(info: { } return quantize(texel, rep); }; - return TexelView.fromTexelsAsColors(info.format as EncodableTextureFormat, generator); + return TexelView.fromTexelsAsColors( + info.format as EncodableTextureFormat, + options?.generator ?? generator + ); } function createRandomTexelViewViaBytes(info: { @@ -825,16 +881,20 @@ function createRandomTexelViewViaBytes(info: { /** * Creates a TexelView filled with random values. */ -function createRandomTexelView(info: { - format: GPUTextureFormat; - size: GPUExtent3D; - mipLevel: number; - sampleCount: number; -}): TexelView { +function createRandomTexelView( + info: { + format: GPUTextureFormat; + size: GPUExtent3D; + mipLevel: number; + sampleCount: number; + }, + options?: RandomTextureOptions | undefined +): TexelView { assert(!isCompressedTextureFormat(info.format)); const formatInfo = kTextureFormatInfo[info.format]; const type = formatInfo.color?.type ?? formatInfo.depth?.type ?? formatInfo.stencil?.type; const canFillWithRandomTypedData = + !options && isEncodableTextureFormat(info.format) && ((info.format.includes('norm') && type !== 'depth') || info.format.includes('16float') || @@ -844,28 +904,34 @@ function createRandomTexelView(info: { return canFillWithRandomTypedData ? createRandomTexelViewViaBytes(info) - : createRandomTexelViewViaColors(info); + : createRandomTexelViewViaColors(info, options); } /** * Creates a mip chain of TexelViews filled with random values */ -function createRandomTexelViewMipmap(info: { - format: GPUTextureFormat; - size: GPUExtent3D; - mipLevelCount?: number; - dimension?: GPUTextureDimension; - sampleCount?: number; -}): TexelView[] { +function createRandomTexelViewMipmap( + info: { + format: GPUTextureFormat; + size: GPUExtent3D; + mipLevelCount?: number; + dimension?: GPUTextureDimension; + sampleCount?: number; + }, + options?: RandomTextureOptions | undefined +): TexelView[] { const mipLevelCount = info.mipLevelCount ?? 1; const dimension = info.dimension ?? '2d'; return range(mipLevelCount, i => - createRandomTexelView({ - format: info.format, - size: virtualMipSize(dimension, info.size, i), - mipLevel: i, - sampleCount: info.sampleCount ?? 1, - }) + createRandomTexelView( + { + format: info.format, + size: virtualMipSize(dimension, info.size, i), + mipLevel: i, + sampleCount: info.sampleCount ?? 1, + }, + options + ) ); } @@ -911,6 +977,8 @@ export type TextureBuiltin = | 'textureSample' | 'textureSampleBaseClampToEdge' | 'textureSampleBias' + | 'textureSampleCompare' + | 'textureSampleCompareLevel' | 'textureSampleGrad' | 'textureSampleLevel'; @@ -923,13 +991,18 @@ export interface TextureCall extends TextureCallArgs builtin === 'textureGatherCompare'; +const isBuiltinComparison = (builtin: TextureBuiltin) => + builtin === 'textureGatherCompare' || + builtin === 'textureSampleCompare' || + builtin === 'textureSampleCompareLevel'; const isBuiltinGather = (builtin: TextureBuiltin | undefined) => builtin === 'textureGather' || builtin === 'textureGatherCompare'; const builtinNeedsSampler = (builtin: TextureBuiltin) => builtin.startsWith('textureSample') || builtin.startsWith('textureGather'); const builtinNeedsDerivatives = (builtin: TextureBuiltin) => - builtin === 'textureSample' || builtin === 'textureSampleBias'; + builtin === 'textureSample' || + builtin === 'textureSampleBias' || + builtin === 'textureSampleCompare'; const isCubeViewDimension = (viewDescriptor?: GPUTextureViewDescriptor) => viewDescriptor?.dimension === 'cube' || viewDescriptor?.dimension === 'cube-array'; @@ -1173,6 +1246,8 @@ function softwareTextureReadMipLevel( case 'textureSample': case 'textureSampleBias': case 'textureSampleBaseClampToEdge': + case 'textureSampleCompare': + case 'textureSampleCompareLevel': case 'textureSampleGrad': case 'textureSampleLevel': { let coords = toArray(call.coords!); @@ -1924,6 +1999,11 @@ export async function checkCallResults( errs.push(layoutTwoColumns(expectedSamplePoints, gotSamplePoints).join('\n')); errs.push('', ''); } + + // this is not an else because it's common to comment out the previous `if` for running on a CQ. + if (!t.rec.debugging) { + errs.push('### turn on debugging to see sample points ###'); + } } // if (sampler) // Don't report the other errors. There 50 sample points per subcase and @@ -2330,9 +2410,11 @@ function createTextureFromTexelViewsLocal( */ export async function createTextureWithRandomDataAndGetTexels( t: GPUTest, - descriptor: GPUTextureDescriptor + descriptor: GPUTextureDescriptor, + options?: RandomTextureOptions ) { if (isCompressedTextureFormat(descriptor.format)) { + assert(!options, 'options not supported for compressed textures'); const texture = t.createTextureTracked(descriptor); fillTextureWithRandomData(t.device, texture); @@ -2344,7 +2426,7 @@ export async function createTextureWithRandomDataAndGetTexels( ); return { texture, texels }; } else { - const texels = createRandomTexelViewMipmap(descriptor); + const texels = createRandomTexelViewMipmap(descriptor, options); const texture = createTextureFromTexelViewsLocal(t, texels, descriptor); return { texture, texels }; } @@ -3031,7 +3113,12 @@ function generateTextureBuiltinInputsImpl( mipLevel, sampleIndex: args.sampleIndex ? makeRangeValue(args.sampleIndex, i, 1) : undefined, arrayIndex: args.arrayIndex ? makeRangeValue(args.arrayIndex, i, 2) : undefined, - depthRef: args.depthRef ? makeRangeValue({ num: 1, type: 'f32' }, i, 5) : undefined, + // use 0.0, 0.5, or 1.0 for depthRef. We can't test for equality except for values 0 and 1 + // The texture will be filled with random values unless our comparison is 'equal' or 'not-equal' + // in which case the texture will be filled with only 0, 0.6, 1. Choosing 0.0, 0.5, 1.0 here + // means we can test 'equal' and 'not-equal'. For other comparisons, the fact that the texture's + // contents is random seems enough to test all the comparison modes. + depthRef: args.depthRef ? makeRandValue({ num: 3, type: 'u32' }, i, 5) / 2 : undefined, ddx: args.grad ? makeGradient(7) : undefined, ddy: args.grad ? makeGradient(8) : undefined, bias, @@ -3562,7 +3649,12 @@ export function generateSamplePointsCube( mipLevel, arrayIndex: args.arrayIndex ? makeRangeValue(args.arrayIndex, i, 2) : undefined, bias, - depthRef: args.depthRef ? makeRangeValue({ num: 1, type: 'f32' }, i, 5) : undefined, + // use 0.0, 0.5, or 1.0 for depthRef. We can't test for equality except for values 0 and 1 + // The texture will be filled with random values unless our comparison is 'equal' or 'not-equal' + // in which case the texture will be filled with only 0, 0.6, 1. Choosing 0.0, 0.5, 1.0 here + // means we can test 'equal' and 'not-equal'. For other comparisons, the fact that the texture's + // contents is random seems enough to test all the comparison modes. + depthRef: args.depthRef ? makeRandValue({ num: 3, type: 'u32' }, i, 5) / 2 : undefined, component: args.component ? makeIntHashValue(0, 4, i, 4) : undefined, }; });