summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/resolve.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/resolve.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/resolve.spec.ts183
1 files changed, 183 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/resolve.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/resolve.spec.ts
new file mode 100644
index 0000000000..46b03e7b39
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/render_pass/resolve.spec.ts
@@ -0,0 +1,183 @@
+export const description = `API Operation Tests for RenderPass StoreOp.
+Tests a render pass with a resolveTarget resolves correctly for many combinations of:
+ - number of color attachments, some with and some without a resolveTarget
+ - renderPass storeOp set to {'store', 'discard'}
+ - resolveTarget mip level {0, >0} (TODO?: different mip level from colorAttachment)
+ - resolveTarget {2d array layer, TODO: 3d slice} {0, >0} with {2d, TODO: 3d} resolveTarget
+ (TODO?: different z from colorAttachment)
+ - TODO: test all renderable color formats
+ - TODO: test that any not-resolved attachments are rendered to correctly.
+ - TODO: test different loadOps
+ - TODO?: resolveTarget mip level {0, >0} (TODO?: different mip level from colorAttachment)
+ - TODO?: resolveTarget {2d array layer, TODO: 3d slice} {0, >0} with {2d, TODO: 3d} resolveTarget
+ (different z from colorAttachment)
+`;
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { GPUTest, TextureTestMixin } from '../../../gpu_test.js';
+
+const kSlotsToResolve = [
+ [0, 2],
+ [1, 3],
+ [0, 1, 2, 3],
+];
+
+const kSize = 4;
+const kFormat: GPUTextureFormat = 'rgba8unorm';
+
+export const g = makeTestGroup(TextureTestMixin(GPUTest));
+
+g.test('render_pass_resolve')
+ .params(u =>
+ u
+ .combine('storeOperation', ['discard', 'store'] as const)
+ .beginSubcases()
+ .combine('numColorAttachments', [2, 4] as const)
+ .combine('slotsToResolve', kSlotsToResolve)
+ .combine('resolveTargetBaseMipLevel', [0, 1] as const)
+ .combine('resolveTargetBaseArrayLayer', [0, 1] as const)
+ )
+ .fn(t => {
+ const targets: GPUColorTargetState[] = [];
+ for (let i = 0; i < t.params.numColorAttachments; i++) {
+ targets.push({ format: kFormat });
+ }
+
+ // These shaders will draw a white triangle into a texture. After draw, the top left
+ // half of the texture will be white, and the bottom right half will be unchanged. When this
+ // texture is resolved, there will be two distinct colors in each portion of the texture, as
+ // well as a line between the portions that contain the midpoint color due to the multisample
+ // resolve.
+ const pipeline = t.device.createRenderPipeline({
+ layout: 'auto',
+ vertex: {
+ module: t.device.createShaderModule({
+ code: `
+ @vertex fn main(
+ @builtin(vertex_index) VertexIndex : u32
+ ) -> @builtin(position) vec4<f32> {
+ var pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+ vec2<f32>(-1.0, -1.0),
+ vec2<f32>(-1.0, 1.0),
+ vec2<f32>( 1.0, 1.0));
+ return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+ }`,
+ }),
+ entryPoint: 'main',
+ },
+ fragment: {
+ module: t.device.createShaderModule({
+ code: `
+ struct Output {
+ @location(0) fragColor0 : vec4<f32>,
+ @location(1) fragColor1 : vec4<f32>,
+ @location(2) fragColor2 : vec4<f32>,
+ @location(3) fragColor3 : vec4<f32>,
+ };
+
+ @fragment fn main() -> Output {
+ return Output(
+ vec4<f32>(1.0, 1.0, 1.0, 1.0),
+ vec4<f32>(1.0, 1.0, 1.0, 1.0),
+ vec4<f32>(1.0, 1.0, 1.0, 1.0),
+ vec4<f32>(1.0, 1.0, 1.0, 1.0)
+ );
+ }`,
+ }),
+ entryPoint: 'main',
+ targets,
+ },
+ primitive: { topology: 'triangle-list' },
+ multisample: { count: 4 },
+ });
+
+ const resolveTargets: GPUTexture[] = [];
+ const renderPassColorAttachments: GPURenderPassColorAttachment[] = [];
+
+ // The resolve target must be the same size as the color attachment. If we're resolving to mip
+ // level 1, the resolve target base mip level should be 2x the color attachment size.
+ const kResolveTargetSize = kSize << t.params.resolveTargetBaseMipLevel;
+
+ for (let i = 0; i < t.params.numColorAttachments; i++) {
+ const colorAttachment = t.device.createTexture({
+ format: kFormat,
+ size: { width: kSize, height: kSize, depthOrArrayLayers: 1 },
+ sampleCount: 4,
+ mipLevelCount: 1,
+ usage:
+ GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ if (t.params.slotsToResolve.includes(i)) {
+ const colorAttachment = t.device.createTexture({
+ format: kFormat,
+ size: { width: kSize, height: kSize, depthOrArrayLayers: 1 },
+ sampleCount: 4,
+ mipLevelCount: 1,
+ usage:
+ GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const resolveTarget = t.device.createTexture({
+ format: kFormat,
+ size: {
+ width: kResolveTargetSize,
+ height: kResolveTargetSize,
+ depthOrArrayLayers: t.params.resolveTargetBaseArrayLayer + 1,
+ },
+ sampleCount: 1,
+ mipLevelCount: t.params.resolveTargetBaseMipLevel + 1,
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ // Clear to black for the load operation. After the draw, the top left half of the attachment
+ // will be white and the bottom right half will be black.
+ renderPassColorAttachments.push({
+ view: colorAttachment.createView(),
+ clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
+ loadOp: 'clear',
+ storeOp: t.params.storeOperation,
+ resolveTarget: resolveTarget.createView({
+ baseMipLevel: t.params.resolveTargetBaseMipLevel,
+ baseArrayLayer: t.params.resolveTargetBaseArrayLayer,
+ }),
+ });
+
+ resolveTargets.push(resolveTarget);
+ } else {
+ renderPassColorAttachments.push({
+ view: colorAttachment.createView(),
+ clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
+ loadOp: 'clear',
+ storeOp: t.params.storeOperation,
+ });
+ }
+ }
+
+ const encoder = t.device.createCommandEncoder();
+
+ const pass = encoder.beginRenderPass({
+ colorAttachments: renderPassColorAttachments,
+ });
+ pass.setPipeline(pipeline);
+ pass.draw(3);
+ pass.end();
+ t.device.queue.submit([encoder.finish()]);
+
+ // Verify the resolve targets contain the correct values. Note that we use z to specify the
+ // array layer from which to pull the pixels for testing.
+ const z = t.params.resolveTargetBaseArrayLayer;
+ for (const resolveTarget of resolveTargets) {
+ t.expectSinglePixelComparisonsAreOkInTexture(
+ { texture: resolveTarget, mipLevel: t.params.resolveTargetBaseMipLevel },
+ [
+ // Top left pixel should be {1.0, 1.0, 1.0, 1.0}.
+ { coord: { x: 0, y: 0, z }, exp: { R: 1.0, G: 1.0, B: 1.0, A: 1.0 } },
+ // Bottom right pixel should be {0, 0, 0, 0}.
+ { coord: { x: kSize - 1, y: kSize - 1, z }, exp: { R: 0, G: 0, B: 0, A: 0 } },
+ // Top right pixel should be {0.5, 0.5, 0.5, 0.5} due to the multisampled resolve.
+ { coord: { x: kSize - 1, y: 0, z }, exp: { R: 0.5, G: 0.5, B: 0.5, A: 0.5 } },
+ ]
+ );
+ }
+ });