diff --git a/src/webgpu/api/validation/createTexture.spec.ts b/src/webgpu/api/validation/createTexture.spec.ts index 568678c41e2d..30ab5155e253 100644 --- a/src/webgpu/api/validation/createTexture.spec.ts +++ b/src/webgpu/api/validation/createTexture.spec.ts @@ -343,7 +343,11 @@ g.test('sampleCount,valid_sampleCount_with_other_parameter_varies') dimension !== '2d')) || ((usage & GPUConst.TextureUsage.STORAGE_BINDING) !== 0 && !isTextureFormatPossiblyStorageReadable(format)) || - (mipLevelCount !== 1 && dimension === '1d') + (mipLevelCount !== 1 && dimension === '1d') || + ((usage & GPUConst.TextureUsage.TRANSIENT_ATTACHMENT) !== 0 && + usage !== + (GPUConst.TextureUsage.RENDER_ATTACHMENT | + GPUConst.TextureUsage.TRANSIENT_ATTACHMENT)) ); }) ) @@ -1037,6 +1041,11 @@ g.test('texture_usage') if (isColorTextureFormat(format) && !isTextureFormatColorRenderable(t.device, format)) success = false; } + if (usage & GPUTextureUsage.TRANSIENT_ATTACHMENT) { + if (usage !== (GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT)) { + success = false; + } + } t.expectValidationError(() => { t.createTextureTracked(descriptor); 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 2aaa755a801e..9c2a95de0e85 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 @@ -718,6 +718,48 @@ g.test('attachments,mip_level_count') } }); +g.test('color_attachments,loadOp_storeOp') + .desc( + ` + Test GPURenderPassColorAttachment Usage: + - if usage includes TRANSIENT_ATTACHMENT + - loadOp must be clear + - storeOp must be discard + ` + ) + .params(u => + u + .combine('format', kPossibleColorRenderableTextureFormats) + .beginSubcases() + .combine('transientTexture', [true, false]) + .combine('loadOp', ['clear', 'load'] as GPULoadOp[]) + .combine('storeOp', ['discard', 'store'] as GPUStoreOp[]) + ) + .fn(t => { + const { format, transientTexture, loadOp, storeOp } = t.params; + + t.skipIfTextureFormatNotSupported(format); + t.skipIfTextureFormatNotUsableAsRenderAttachment(format); + + const usage = transientTexture + ? GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT + : GPUTextureUsage.RENDER_ATTACHMENT; + + const texture = t.createTestTexture({ usage }); + + const colorAttachment = t.getColorAttachment(texture); + colorAttachment.loadOp = loadOp; + colorAttachment.storeOp = storeOp; + + const passDescriptor: GPURenderPassDescriptor = { + colorAttachments: [colorAttachment], + }; + + const success = !transientTexture || (loadOp === 'clear' && storeOp === 'discard'); + + t.tryRenderPass(success, passDescriptor); + }); + g.test('color_attachments,non_multisampled') .desc( ` @@ -1039,22 +1081,28 @@ g.test('depth_stencil_attachment,loadOp_storeOp_match_depthReadOnly_stencilReadO .desc( ` Test GPURenderPassDepthStencilAttachment Usage: - - if the format has a depth aspect: - - if depthReadOnly is true - - depthLoadOp and depthStoreOp must not be provided - - else: - - depthLoadOp and depthStoreOp must be provided - - if the format has a stencil aspect: - - if stencilReadOnly is true - - stencilLoadOp and stencilStoreOp must not be provided - - else: - - stencilLoadOp and stencilStoreOp must be provided + - if the format has a depth aspect and depthReadOnly is false + - depthLoadOp and depthStoreOp must be provided + - else: + - depthLoadOp and depthStoreOp must not be provided + - if the format has a stencil aspect and stencilReadOnly is false + - stencilLoadOp and stencilStoreOp must be provided + - else: + - stencilLoadOp and stencilStoreOp must not be provided + - if usage includes TRANSIENT_ATTACHMENT + - if the format has a depth aspect: + - depthLoadOp must be clear + - depthStoreOp must be discard + - if the format has a stencil aspect: + - stencilLoadOp must be clear + - stencilStoreOp must be discard ` ) .params(u => u .combine('format', kDepthStencilFormats) .beginSubcases() // Note: It's easier to debug if you comment this line out as you can then run an individual case. + .combine('transientTexture', [true, false]) .combine('depthReadOnly', [undefined, true, false]) .combine('depthLoadOp', [undefined, 'clear', 'load'] as GPULoadOp[]) .combine('depthStoreOp', [undefined, 'discard', 'store'] as GPUStoreOp[]) @@ -1065,6 +1113,7 @@ g.test('depth_stencil_attachment,loadOp_storeOp_match_depthReadOnly_stencilReadO .fn(t => { const { format, + transientTexture, depthReadOnly, depthLoadOp, depthStoreOp, @@ -1075,10 +1124,13 @@ g.test('depth_stencil_attachment,loadOp_storeOp_match_depthReadOnly_stencilReadO t.skipIfTextureFormatNotSupported(format); + const usage = transientTexture + ? GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT + : GPUTextureUsage.RENDER_ATTACHMENT; const depthAttachment = t.createTextureTracked({ format, size: { width: 1, height: 1, depthOrArrayLayers: 1 }, - usage: GPUTextureUsage.RENDER_ATTACHMENT, + usage, }); const depthAttachmentView = depthAttachment.createView(); @@ -1120,7 +1172,13 @@ g.test('depth_stencil_attachment,loadOp_storeOp_match_depthReadOnly_stencilReadO const goodStencilCombo = hasStencil && !stencilReadOnly ? hasBothStencilOps : hasNeitherStencilOps; - const shouldError = !goodAspectSettingsPresent || !goodDepthCombo || !goodStencilCombo; + const goodTransient = + !transientTexture || + ((!hasDepth || (depthLoadOp === 'clear' && depthStoreOp === 'discard')) && + (!hasStencil || (stencilLoadOp === 'clear' && stencilStoreOp === 'discard'))); + + const shouldError = + !goodAspectSettingsPresent || !goodDepthCombo || !goodStencilCombo || !goodTransient; t.expectValidationError(() => { encoder.finish(); diff --git a/src/webgpu/capability_info.ts b/src/webgpu/capability_info.ts index d9c669fc96be..87c45a45a112 100644 --- a/src/webgpu/capability_info.ts +++ b/src/webgpu/capability_info.ts @@ -210,6 +210,7 @@ export const kTextureUsageInfo: { [GPUConst.TextureUsage.TEXTURE_BINDING]: {}, [GPUConst.TextureUsage.STORAGE_BINDING]: {}, [GPUConst.TextureUsage.RENDER_ATTACHMENT]: {}, + [GPUConst.TextureUsage.TRANSIENT_ATTACHMENT]: {}, }; /** List of all GPUTextureUsage values. */ export const kTextureUsages = numericKeysOf(kTextureUsageInfo); diff --git a/src/webgpu/constants.ts b/src/webgpu/constants.ts index b44b23144c77..1aa692b1b806 100644 --- a/src/webgpu/constants.ts +++ b/src/webgpu/constants.ts @@ -17,6 +17,13 @@ const BufferUsage = { } as const; checkType(BufferUsage); +declare global { + // MAINTENANCE_TODO: Remove this once TRANSIENT_ATTACHMENT is added to @webgpu/types + interface GPUTextureUsage { + readonly TRANSIENT_ATTACHMENT: GPUFlagsConstant; + } +} + const TextureUsage = { COPY_SRC: 0x01, COPY_DST: 0x02, @@ -25,6 +32,7 @@ const TextureUsage = { STORAGE_BINDING: 0x08, STORAGE: 0x08, RENDER_ATTACHMENT: 0x10, + TRANSIENT_ATTACHMENT: 0x20, } as const; checkType(TextureUsage); diff --git a/src/webgpu/idl/constants/flags.spec.ts b/src/webgpu/idl/constants/flags.spec.ts index ca78892fb4d7..1103afe89b86 100644 --- a/src/webgpu/idl/constants/flags.spec.ts +++ b/src/webgpu/idl/constants/flags.spec.ts @@ -35,6 +35,7 @@ const kTextureUsageExp = { TEXTURE_BINDING: 0x04, STORAGE_BINDING: 0x08, RENDER_ATTACHMENT: 0x10, + TRANSIENT_ATTACHMENT: 0x20, }; g.test('TextureUsage,count').fn(t => { t.assertMemberCount(GPUTextureUsage, kTextureUsageExp);