From 24315d4517c3d8103d38695b6e91d1db9dd7ee3a Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Sun, 26 Apr 2026 02:07:30 -0700 Subject: [PATCH] Test createView and resolveTarget with TRANSIENT_ATTACHMENT This restriction is missing from the spec, but textures with TRANSIENT_ATTACHMENT must not be used as resolve targets. This must apply regardless of any usage subsetting in createView(). For simplicity (to better avoid bugs in validation elsewhere), it's proposed that createView() simply cannot subset a TRANSIENT_ATTACHMENT texture. This test implements that proposal. --- src/webgpu/api/validation/createView.spec.ts | 46 ++++++++++++++++++- .../validation/render_pass/resolve.spec.ts | 14 ++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/webgpu/api/validation/createView.spec.ts b/src/webgpu/api/validation/createView.spec.ts index 981a24a6138e..18506f9799d5 100644 --- a/src/webgpu/api/validation/createView.spec.ts +++ b/src/webgpu/api/validation/createView.spec.ts @@ -5,6 +5,7 @@ import { kUnitCaseParamsBuilder } from '../../../common/framework/params_builder import { makeTestGroup } from '../../../common/framework/test_group.js'; import { unreachable } from '../../../common/util/util.js'; import { + isValidTextureUsageCombination, kTextureAspects, kTextureDimensions, kTextureUsages, @@ -356,7 +357,7 @@ g.test('texture_view_usage') ); }) .beginSubcases() - .combine('textureViewUsage', [0, ...kTextureUsages]) + .combine('textureViewUsage', kTextureUsages) .unless(({ textureUsage, textureViewUsage }) => { // TRANSIENT_ATTACHMENT is only valid when combined with RENDER_ATTACHMENT. return ( @@ -391,9 +392,50 @@ g.test('texture_view_usage') }, !success); }); +g.test('texture_view_usage_of_multiple_usages') + .desc( + `For a single format (rgba8unorm), check that createView: + - allows 0 usages + - disallows subsetting usages of TRANSIENT_ATTACHMENT textures + ` + ) + .params(u => + u + .combine('usage1', kTextureUsages) + .combine('usage2', kTextureUsages) + .filter(p => p.usage1 <= p.usage2) + .filter(p => isValidTextureUsageCombination(p.usage1 | p.usage2)) + .beginSubcases() + .expand('viewUsage', p => new Set([0, p.usage1, p.usage2, p.usage1 | p.usage2])) + ) + .fn(t => { + const { usage1, usage2, viewUsage } = t.params; + const usage = usage1 | usage2; + + // MAINTENANCE_TODO(#4509): Remove this after all implementations have TRANSIENT_ATTACHMENT. + if ((usage & GPUConst.TextureUsage.TRANSIENT_ATTACHMENT) !== 0) { + t.skipIfTransientAttachmentNotSupported(); + } + + let isValid = true; + if (usage & GPUTextureUsage.TRANSIENT_ATTACHMENT) { + isValid &&= viewUsage === usage; + } + + const texture = t.createTextureTracked({ format: 'rgba8unorm', size: [1, 1], usage }); + t.expectGPUError( + 'validation', + () => { + texture.createView({ usage: viewUsage }); + }, + !isValid + ); + }); + g.test('texture_view_usage_with_view_format') .desc( - `Test that the texture view usage must be supported by the view's format. Checks for every view format possible, and every usage supported by the texture's format` + `Test that the texture view usage must be supported by the view's format. Checks for every view + format possible, and every usage supported by the texture's format` ) .params(u => u diff --git a/src/webgpu/api/validation/render_pass/resolve.spec.ts b/src/webgpu/api/validation/render_pass/resolve.spec.ts index 1ef96643e374..d4578aace2d5 100644 --- a/src/webgpu/api/validation/render_pass/resolve.spec.ts +++ b/src/webgpu/api/validation/render_pass/resolve.spec.ts @@ -20,6 +20,7 @@ Test various validation behaviors when a resolveTarget is provided. - resolve source is not multisampled. - resolve target is not single sampled. - resolve target missing RENDER_ATTACHMENT usage. +- resolve target has TRANSIENT_ATTACHMENT usage. - resolve target must have exactly one subresource: - base mip level {0, >0}, mip level count {1, >1}. - base array layer {0, >0}, array layer count {1, >1}. @@ -35,12 +36,20 @@ Test various validation behaviors when a resolveTarget is provided. // control cases should be valid { _valid: true }, { bindTextureResource: true, _valid: true }, + { resolveTargetUsage: GPUConst.TextureUsage.RENDER_ATTACHMENT, _valid: true }, // a single sampled resolve source should cause a validation error. { colorAttachmentSamples: 1, _valid: false }, // a multisampled resolve target should cause a validation error. { resolveTargetSamples: 4, _valid: false }, // resolveTargetUsage without RENDER_ATTACHMENT usage should cause a validation error. { resolveTargetUsage: GPUConst.TextureUsage.COPY_SRC, _valid: false }, + // resolveTargetUsage with TRANSIENT_ATTACHMENT | RENDER_ATTACHMENT usage should cause a + // validation error. + { + resolveTargetUsage: + GPUConst.TextureUsage.RENDER_ATTACHMENT | GPUConst.TextureUsage.TRANSIENT_ATTACHMENT, + _valid: false, + }, // non-zero resolve target base mip level should be valid. { resolveTargetViewBaseMipLevel: 1, @@ -98,6 +107,11 @@ Test various validation behaviors when a resolveTarget is provided. _valid, } = t.params; + // MAINTENANCE_TODO(#4509): Remove this after all implementations have TRANSIENT_ATTACHMENT. + if ((resolveTargetUsage & GPUConst.TextureUsage.TRANSIENT_ATTACHMENT) !== 0) { + t.skipIfTransientAttachmentNotSupported(); + } + // Run the test in a nested loop such that the configured color attachment with resolve target // is tested while occupying each individual colorAttachment slot. for (let resolveSlot = 0; resolveSlot < kNumColorAttachments; resolveSlot++) {