summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/storeOp.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/storeOp.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/storeOp.spec.ts354
1 files changed, 354 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/storeOp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/storeOp.spec.ts
new file mode 100644
index 0000000000..75fa927728
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/storeOp.spec.ts
@@ -0,0 +1,354 @@
+export const description = `API Operation Tests for RenderPass StoreOp.
+
+ Test Coverage:
+
+ - Tests that color and depth-stencil store operations {'discard', 'store'} work correctly for a
+ render pass with both a color attachment and depth-stencil attachment.
+ TODO: use depth24plus-stencil8
+
+ - Tests that store operations {'discard', 'store'} work correctly for a render pass with multiple
+ color attachments.
+ TODO: test with more interesting loadOp values
+
+ - Tests that store operations {'discard', 'store'} work correctly for a render pass with a color
+ attachment for:
+ - All renderable color formats
+ - mip level set to {'0', mip > '0'}
+ - array layer set to {'0', layer > '1'} for 2D textures
+ TODO: depth slice set to {'0', slice > '0'} for 3D textures
+
+ - Tests that store operations {'discard', 'store'} work correctly for a render pass with a
+ depth-stencil attachment for:
+ - All renderable depth-stencil formats
+ - mip level set to {'0', mip > '0'}
+ - array layer set to {'0', layer > '1'} for 2D textures
+ TODO: test depth24plus and depth24plus-stencil8 formats
+ TODO: test that depth and stencil aspects are set separately
+ TODO: depth slice set to {'0', slice > '0'} for 3D textures
+ TODO: test with more interesting loadOp values`;
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import {
+ kTextureFormatInfo,
+ kEncodableTextureFormats,
+ kSizedDepthStencilFormats,
+} from '../../../capability_info.js';
+import { GPUTest } from '../../../gpu_test.js';
+import { PerTexelComponent } from '../../../util/texture/texel_data.js';
+
+// Test with a zero and non-zero mip.
+const kMipLevel: number[] = [0, 1];
+const kMipLevelCount = 2;
+
+// Test with different numbers of color attachments.
+type NumColorAttachments = 1 | 2 | 3 | 4;
+const kNumColorAttachments: NumColorAttachments[] = [1, 2, 3, 4];
+
+// Test with a zero and non-zero array layer.
+const kArrayLayers: number[] = [0, 1];
+
+const kStoreOps: GPUStoreOp[] = ['discard', 'store'];
+
+const kHeight = 2;
+const kWidth = 2;
+
+export const g = makeTestGroup(GPUTest);
+
+// Tests a render pass with both a color and depth stencil attachment to ensure store operations are
+// set independently.
+g.test('render_pass_store_op,color_attachment_with_depth_stencil_attachment')
+ .params(u =>
+ u //
+ .combine('colorStoreOperation', kStoreOps)
+ .combine('depthStencilStoreOperation', kStoreOps)
+ )
+ .fn(t => {
+ // Create a basic color attachment.
+ const kColorFormat: GPUTextureFormat = 'rgba8unorm';
+ const colorAttachment = t.device.createTexture({
+ format: kColorFormat,
+ size: { width: kWidth, height: kHeight, depthOrArrayLayers: 1 },
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const colorAttachmentView = colorAttachment.createView();
+
+ // Create a basic depth/stencil attachment.
+ const kDepthStencilFormat: GPUTextureFormat = 'depth32float';
+ const depthStencilAttachment = t.device.createTexture({
+ format: kDepthStencilFormat,
+ size: { width: kWidth, height: kHeight, depthOrArrayLayers: 1 },
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ // Color load operation will clear to {1.0, 1.0, 1.0, 1.0}.
+ // Depth operation will clear to 1.0.
+ // Store operations are determined by test the params.
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: colorAttachmentView,
+ clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: t.params.colorStoreOperation,
+ },
+ ],
+ depthStencilAttachment: {
+ view: depthStencilAttachment.createView(),
+ depthClearValue: 1.0,
+ depthLoadOp: 'clear',
+ depthStoreOp: t.params.depthStencilStoreOperation,
+ },
+ });
+ pass.end();
+
+ t.device.queue.submit([encoder.finish()]);
+
+ // Check that the correct store operation occurred.
+ let expectedColorValue: PerTexelComponent<number> = {};
+ if (t.params.colorStoreOperation === 'discard') {
+ // If colorStoreOp was clear, the texture should now contain {0.0, 0.0, 0.0, 0.0}.
+ expectedColorValue = { R: 0.0, G: 0.0, B: 0.0, A: 0.0 };
+ } else if (t.params.colorStoreOperation === 'store') {
+ // If colorStoreOP was store, the texture should still contain {1.0, 1.0, 1.0, 1.0}.
+ expectedColorValue = { R: 1.0, G: 1.0, B: 1.0, A: 1.0 };
+ }
+ t.expectSingleColor(colorAttachment, kColorFormat, {
+ size: [kHeight, kWidth, 1],
+ exp: expectedColorValue,
+ });
+
+ // Check that the correct store operation occurred.
+ let expectedDepthValue: PerTexelComponent<number> = {};
+ if (t.params.depthStencilStoreOperation === 'discard') {
+ // If depthStencilStoreOperation was clear, the texture's depth component should be 0.0, and
+ // the stencil component should be 0.0.
+ expectedDepthValue = { Depth: 0.0 };
+ } else if (t.params.depthStencilStoreOperation === 'store') {
+ // If depthStencilStoreOperation was store, the texture's depth component should be 1.0, and
+ // the stencil component should be 1.0.
+ expectedDepthValue = { Depth: 1.0 };
+ }
+ t.expectSingleColor(depthStencilAttachment, kDepthStencilFormat, {
+ size: [kHeight, kWidth, 1],
+ exp: expectedDepthValue,
+ layout: { mipLevel: 0, aspect: 'depth-only' },
+ });
+ });
+
+// Tests that render pass color attachment store operations work correctly for all renderable color
+// formats, mip levels and array layers.
+g.test('render_pass_store_op,color_attachment_only')
+ .params(u =>
+ u
+ .combine('colorFormat', kEncodableTextureFormats)
+ // Filter out any non-renderable formats
+ .filter(({ colorFormat }) => {
+ const info = kTextureFormatInfo[colorFormat];
+ return info.color && info.renderable;
+ })
+ .combine('storeOperation', kStoreOps)
+ .beginSubcases()
+ .combine('mipLevel', kMipLevel)
+ .combine('arrayLayer', kArrayLayers)
+ )
+ .fn(t => {
+ const colorAttachment = t.device.createTexture({
+ format: t.params.colorFormat,
+ size: { width: kWidth, height: kHeight, depthOrArrayLayers: t.params.arrayLayer + 1 },
+ mipLevelCount: kMipLevelCount,
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const colorViewDesc: GPUTextureViewDescriptor = {
+ baseArrayLayer: t.params.arrayLayer,
+ baseMipLevel: t.params.mipLevel,
+ mipLevelCount: 1,
+ arrayLayerCount: 1,
+ };
+
+ const colorAttachmentView = colorAttachment.createView(colorViewDesc);
+
+ // Color load operation will clear to {1.0, 0.0, 0.0, 1.0}.
+ // Color store operation is determined by the test params.
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: colorAttachmentView,
+ clearValue: { r: 1.0, g: 0.0, b: 0.0, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: t.params.storeOperation,
+ },
+ ],
+ });
+ pass.end();
+ t.device.queue.submit([encoder.finish()]);
+
+ // Check that the correct store operation occurred.
+ let expectedValue: PerTexelComponent<number> = {};
+ if (t.params.storeOperation === 'discard') {
+ // If colorStoreOp was clear, the texture should now contain {0.0, 0.0, 0.0, 0.0}.
+ expectedValue = { R: 0.0, G: 0.0, B: 0.0, A: 0.0 };
+ } else if (t.params.storeOperation === 'store') {
+ // If colorStoreOP was store, the texture should still contain {1.0, 0.0, 0.0, 1.0}.
+ expectedValue = { R: 1.0, G: 0.0, B: 0.0, A: 1.0 };
+ }
+
+ t.expectSingleColor(colorAttachment, t.params.colorFormat, {
+ size: [kHeight, kWidth, 1],
+ slice: t.params.arrayLayer,
+ exp: expectedValue,
+ layout: { mipLevel: t.params.mipLevel },
+ });
+ });
+
+// Test with multiple color attachments to ensure each attachment's storeOp is set independently.
+g.test('render_pass_store_op,multiple_color_attachments')
+ .params(u =>
+ u
+ .combine('storeOperation1', kStoreOps)
+ .combine('storeOperation2', kStoreOps)
+ .beginSubcases()
+ .combine('colorAttachments', kNumColorAttachments)
+ )
+ .fn(t => {
+ const kColorFormat: GPUTextureFormat = 'rgba8unorm';
+ const colorAttachments: GPUTexture[] = [];
+
+ for (let i = 0; i < t.params.colorAttachments; i++) {
+ colorAttachments.push(
+ t.device.createTexture({
+ format: kColorFormat,
+ size: { width: kWidth, height: kHeight, depthOrArrayLayers: 1 },
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ })
+ );
+ }
+
+ // Color load operation will clear to {1.0, 1.0, 1.0, 1.0}
+ // Color store operation is determined by test params. Use storeOperation1 for even numbered
+ // attachments and storeOperation2 for odd numbered attachments.
+ const renderPassColorAttachments: GPURenderPassColorAttachment[] = [];
+ for (let i = 0; i < t.params.colorAttachments; i++) {
+ renderPassColorAttachments.push({
+ view: colorAttachments[i].createView(),
+ clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: i % 2 === 0 ? t.params.storeOperation1 : t.params.storeOperation2,
+ });
+ }
+
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginRenderPass({
+ colorAttachments: renderPassColorAttachments,
+ });
+ pass.end();
+ t.device.queue.submit([encoder.finish()]);
+
+ // Check that the correct store operation occurred.
+ let expectedValue: PerTexelComponent<number> = {};
+ for (let i = 0; i < t.params.colorAttachments; i++) {
+ if (renderPassColorAttachments[i].storeOp === 'discard') {
+ // If colorStoreOp was clear, the texture should now contain {0.0, 0.0, 0.0, 0.0}.
+ expectedValue = { R: 0.0, G: 0.0, B: 0.0, A: 0.0 };
+ } else if (renderPassColorAttachments[i].storeOp === 'store') {
+ // If colorStoreOP was store, the texture should still contain {1.0, 1.0, 1.0, 1.0}.
+ expectedValue = { R: 1.0, G: 1.0, B: 1.0, A: 1.0 };
+ }
+ t.expectSingleColor(colorAttachments[i], kColorFormat, {
+ size: [kHeight, kWidth, 1],
+ exp: expectedValue,
+ });
+ }
+ });
+
+g.test('render_pass_store_op,depth_stencil_attachment_only')
+ .desc(
+ `
+Tests that render pass depth stencil store operations work correctly for all renderable color
+formats, mip levels and array layers.
+
+- x= all (sized) depth stencil formats, all store ops, multiple mip levels, multiple array layers
+
+TODO: Also test unsized depth/stencil formats [1]
+ `
+ )
+ .params(u =>
+ u
+ .combine('depthStencilFormat', kSizedDepthStencilFormats) // [1]
+ .combine('storeOperation', kStoreOps)
+ .beginSubcases()
+ .combine('mipLevel', kMipLevel)
+ .combine('arrayLayer', kArrayLayers)
+ )
+ .fn(t => {
+ const depthStencilTexture = t.device.createTexture({
+ format: t.params.depthStencilFormat,
+ size: { width: kWidth, height: kHeight, depthOrArrayLayers: t.params.arrayLayer + 1 },
+ mipLevelCount: kMipLevelCount,
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const depthStencilViewDesc: GPUTextureViewDescriptor = {
+ baseArrayLayer: t.params.arrayLayer,
+ baseMipLevel: t.params.mipLevel,
+ mipLevelCount: 1,
+ arrayLayerCount: 1,
+ };
+
+ const depthStencilAttachmentView = depthStencilTexture.createView(depthStencilViewDesc);
+
+ // Depth-stencil load operation will clear to depth = 1.0, stencil = 1.0.
+ // Depth-stencil store operate is determined by test params.
+ const encoder = t.device.createCommandEncoder();
+ const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
+ view: depthStencilAttachmentView,
+ };
+ if (kTextureFormatInfo[t.params.depthStencilFormat].depth) {
+ depthStencilAttachment.depthClearValue = 1.0;
+ depthStencilAttachment.depthLoadOp = 'clear';
+ depthStencilAttachment.depthStoreOp = t.params.storeOperation;
+ }
+ if (kTextureFormatInfo[t.params.depthStencilFormat].stencil) {
+ depthStencilAttachment.stencilClearValue = 1;
+ depthStencilAttachment.stencilLoadOp = 'clear';
+ depthStencilAttachment.stencilStoreOp = t.params.storeOperation;
+ }
+ const pass = encoder.beginRenderPass({
+ colorAttachments: [],
+ depthStencilAttachment,
+ });
+ pass.end();
+ t.device.queue.submit([encoder.finish()]);
+
+ let expectedDepthValue: PerTexelComponent<number> = {};
+ let expectedStencilValue: PerTexelComponent<number> = {};
+ if (t.params.storeOperation === 'discard') {
+ // If depthStencilStoreOperation was clear, the texture's depth/stencil component should be 0,
+ expectedDepthValue = { Depth: 0.0 };
+ expectedStencilValue = { Stencil: 0 };
+ } else if (t.params.storeOperation === 'store') {
+ // If depthStencilStoreOperation was store, the texture's depth/stencil components should be 1,
+ expectedDepthValue = { Depth: 1.0 };
+ expectedStencilValue = { Stencil: 1 };
+ }
+
+ if (kTextureFormatInfo[t.params.depthStencilFormat].depth) {
+ t.expectSingleColor(depthStencilTexture, t.params.depthStencilFormat, {
+ size: [kHeight, kWidth, 1],
+ slice: t.params.arrayLayer,
+ exp: expectedDepthValue,
+ layout: { mipLevel: t.params.mipLevel, aspect: 'depth-only' },
+ });
+ }
+ if (kTextureFormatInfo[t.params.depthStencilFormat].stencil) {
+ t.expectSingleColor(depthStencilTexture, t.params.depthStencilFormat, {
+ size: [kHeight, kWidth, 1],
+ slice: t.params.arrayLayer,
+ exp: expectedStencilValue,
+ layout: { mipLevel: t.params.mipLevel, aspect: 'stencil-only' },
+ });
+ }
+ });