From 873305ab6a2eae37dc899856ccd55bebe8e94f8a Mon Sep 17 00:00:00 2001 From: Hao Li Date: Wed, 10 Jan 2024 09:22:49 +0800 Subject: [PATCH] val: Add depthSlice tests to render_pass_descriptor:color_attachments (#3238) * val: Add depthSlice tests to render_pass_descriptor:color_attachments Test depthSlice defined/undefined with special value, OOB, overlaps with mip levels in render pass's color attachments. * Add timing metadata * Rename test Co-authored-by: Corentin Wallez * Separate bound check test * Use kArrayLayerCount instead * Update texture size for bounds checking at more mip levels --------- Co-authored-by: Corentin Wallez --- .../render_pass_descriptor.spec.ts | 182 ++++++++++++++++++ src/webgpu/listing_meta.json | 4 + 2 files changed, 186 insertions(+) diff --git a/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts b/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts index 82e376567894..0b471c5f6d2c 100644 --- a/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts +++ b/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts @@ -20,6 +20,7 @@ class F extends ValidationTest { createTexture( options: { format?: GPUTextureFormat; + dimension?: GPUTextureDimension; width?: number; height?: number; arrayLayerCount?: number; @@ -30,6 +31,7 @@ class F extends ValidationTest { ): GPUTexture { const { format = 'rgba8unorm', + dimension = '2d', width = 16, height = 16, arrayLayerCount = 1, @@ -41,6 +43,7 @@ class F extends ValidationTest { return this.device.createTexture({ size: { width, height, depthOrArrayLayers: arrayLayerCount }, format, + dimension, mipLevelCount, sampleCount, usage, @@ -90,6 +93,7 @@ class F extends ValidationTest { } export const g = makeTestGroup(F); +const kArrayLayerCount = 10; g.test('attachments,one_color_attachment') .desc(`Test that a render pass works with only one color attachment.`) @@ -278,6 +282,184 @@ g.test('color_attachments,limits,maxColorAttachmentBytesPerSample,unaligned') t.tryRenderPass(success, { colorAttachments }); }); +g.test('color_attachments,depthSlice,definedness') + .desc( + ` + Test that depthSlice must be undefined for 2d color attachments and defined for 3d color attachments." + - The special value '0xFFFFFFFF' is not treated as 'undefined'. + ` + ) + .params(u => + u + .combine('dimension', ['2d', '3d'] as GPUTextureDimension[]) + .beginSubcases() + .combine('depthSlice', [undefined, 0, 0xffffffff]) + ) + .fn(t => { + const { dimension, depthSlice } = t.params; + const texture = t.createTexture({ dimension }); + + const colorAttachment = t.getColorAttachment(texture); + if (depthSlice !== undefined) { + colorAttachment.depthSlice = depthSlice; + } + + const descriptor: GPURenderPassDescriptor = { + colorAttachments: [colorAttachment], + }; + + const success = + (dimension === '2d' && depthSlice === undefined) || (dimension === '3d' && depthSlice === 0); + + t.tryRenderPass(success, descriptor); + }); + +g.test('color_attachments,depthSlice,bound_check') + .desc( + ` + Test that depthSlice must be less than the depthOrArrayLayers of 3d texture's subresource at mip levels. + - Check depth bounds with 3d texture size [16, 1, 10], which has 5 mip levels with depth [10, 5, 2, 1, 1] + for testing more mip level size computation. + - Failed if depthSlice >= the depth of each mip level. + ` + ) + .params(u => + u + .combine('mipLevel', [0, 1, 2, 3, 4]) + .beginSubcases() + .expand('depthSlice', ({ mipLevel }) => { + const depthAtMipLevel = Math.max(kArrayLayerCount >> mipLevel, 1); + // Use Set() to exclude duplicates when the depthAtMipLevel is 1 and 2 + return [...new Set([0, 1, depthAtMipLevel - 1, depthAtMipLevel])]; + }) + ) + .fn(t => { + const { mipLevel, depthSlice } = t.params; + + const texture = t.createTexture({ + dimension: '3d', + width: 16, + height: 1, + arrayLayerCount: kArrayLayerCount, + mipLevelCount: mipLevel + 1, + }); + + const viewDescriptor: GPUTextureViewDescriptor = { + baseMipLevel: mipLevel, + mipLevelCount: 1, + baseArrayLayer: 0, + arrayLayerCount: 1, + }; + + const colorAttachment = t.getColorAttachment(texture, viewDescriptor); + colorAttachment.depthSlice = depthSlice; + + const passDescriptor: GPURenderPassDescriptor = { + colorAttachments: [colorAttachment], + }; + + const success = depthSlice < Math.max(kArrayLayerCount >> mipLevel, 1); + + t.tryRenderPass(success, passDescriptor); + }); + +g.test('color_attachments,depthSlice,overlaps,same_miplevel') + .desc( + ` + Test that the depth slices of 3d color attachments have no overlaps for same texture in a render + pass. + - Succeed if the depth slices are different, or from different textures, or on different render + passes. + - Fail if same depth slice from same texture's same mip level is overwritten in a render pass. + ` + ) + .params(u => + u + .combine('sameDepthSlice', [true, false]) + .beginSubcases() + .combine('sameTexture', [true, false]) + .combine('samePass', [true, false]) + ) + .fn(t => { + const { sameDepthSlice, sameTexture, samePass } = t.params; + const arrayLayerCount = 4; + + const texDescriptor = { + dimension: '3d' as GPUTextureDimension, + arrayLayerCount, + }; + const texture = t.createTexture(texDescriptor); + + const colorAttachments = []; + for (let i = 0; i < arrayLayerCount; i++) { + const colorAttachment = t.getColorAttachment( + sameTexture ? texture : t.createTexture(texDescriptor) + ); + colorAttachment.depthSlice = sameDepthSlice ? 0 : i; + colorAttachments.push(colorAttachment); + } + + const encoder = t.createEncoder('non-pass'); + if (samePass) { + const pass = encoder.encoder.beginRenderPass({ colorAttachments }); + pass.end(); + } else { + for (let i = 0; i < arrayLayerCount; i++) { + const pass = encoder.encoder.beginRenderPass({ colorAttachments: [colorAttachments[i]] }); + pass.end(); + } + } + + const success = !sameDepthSlice || !sameTexture || !samePass; + + encoder.validateFinish(success); + }); + +g.test('color_attachments,depthSlice,overlaps,diff_miplevel') + .desc( + ` + Test that the same depth slice from different mip levels of a 3d texture with size [1, 1, N] can + be set in a render pass's color attachments. + ` + ) + .params(u => u.combine('sameMipLevel', [true, false])) + .fn(t => { + const { sameMipLevel } = t.params; + const mipLevelCount = 4; + + const texDescriptor = { + dimension: '3d' as GPUTextureDimension, + width: 1, + height: 1, + arrayLayerCount: 1 << mipLevelCount, + mipLevelCount, + }; + const texture = t.createTexture(texDescriptor); + + const viewDescriptor: GPUTextureViewDescriptor = { + baseMipLevel: 0, + mipLevelCount: 1, + baseArrayLayer: 0, + arrayLayerCount: 1, + }; + + const colorAttachments = []; + for (let i = 0; i < mipLevelCount; i++) { + if (!sameMipLevel) { + viewDescriptor.baseMipLevel = i; + } + const colorAttachment = t.getColorAttachment(texture, viewDescriptor); + colorAttachment.depthSlice = 0; + colorAttachments.push(colorAttachment); + } + + const encoder = t.createEncoder('non-pass'); + const pass = encoder.encoder.beginRenderPass({ colorAttachments }); + pass.end(); + + encoder.validateFinish(!sameMipLevel); + }); + g.test('attachments,same_size') .desc( ` diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 9a495602e194..c173b329732d 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -670,6 +670,10 @@ "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,empty:*": { "subcaseMS": 0.400 }, "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,limits,maxColorAttachmentBytesPerSample,aligned:*": { "subcaseMS": 1.825 }, "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,limits,maxColorAttachmentBytesPerSample,unaligned:*": { "subcaseMS": 17.151 }, + "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,depthSlice,definedness:*": { "subcaseMS": 5.601 }, + "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,depthSlice,bound_check:*": { "subcaseMS": 9.400 }, + "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,depthSlice,overlaps,same_miplevel:*": { "subcaseMS": 6.400 }, + "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,depthSlice,overlaps,diff_miplevel:*": { "subcaseMS": 3.901 }, "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,limits,maxColorAttachments:*": { "subcaseMS": 0.950 }, "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,non_multisampled:*": { "subcaseMS": 32.601 }, "webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,sample_count:*": { "subcaseMS": 33.600 },