🏡 index : cold-air-networks/mirror-webgpu-cts.git

export const description = `
Note: render pass 'occlusionQuerySet' validation is tested in queries/general.spec.ts

TODO: Check that depth-stencil attachment views must encompass all aspects.

TODO: check for duplication (render_pass/, etc.), plan, and implement.
Note possibly a lot of this should be operation tests instead.
Notes:
> - color attachments {zero, one, multiple}
>     - many different formats (some are non-renderable)
>     - is a view on a texture with multiple mip levels or array layers
>     - two attachments use the same view, or views of {intersecting, disjoint} ranges
>     - {without, with} resolve target
>         - resolve format compatibility with multisampled format
>     - {all possible load ops, load color {in range, negative, too large}}
>     - all possible store ops
> - depth/stencil attachment
>     - {unset, all possible formats}
>     - {all possible {depth, stencil} load ops, load values {in range, negative, too large}}
>     - all possible {depth, stencil} store ops
>     - depthReadOnly {t,f}, stencilReadOnly {t,f}
`;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import * as vtu from '../validation_test_utils.js';

export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

g.test('color_attachments,device_mismatch')
  .desc(
    `
    Tests beginRenderPass cannot be called with color attachments whose texture view or resolve target is created from another device
    The 'view' and 'resolveTarget' are:
    - created from same device in ColorAttachment0 and ColorAttachment1
    - created from different device in ColorAttachment0 and ColorAttachment1
    - created from same device in ColorAttachment0, but from different device in ColorAttachment1
    `
  )
  .paramsSubcasesOnly([
    {
      view0Mismatched: false,
      target0Mismatched: false,
      view1Mismatched: false,
      target1Mismatched: false,
    }, // control case
    {
      view0Mismatched: false,
      target0Mismatched: true,
      view1Mismatched: false,
      target1Mismatched: true,
    },
    {
      view0Mismatched: true,
      target0Mismatched: false,
      view1Mismatched: true,
      target1Mismatched: false,
    },
    {
      view0Mismatched: false,
      target0Mismatched: false,
      view1Mismatched: false,
      target1Mismatched: true,
    },
  ])
  .beforeAllSubcases(t => t.usesMismatchedDevice())
  .fn(t => {
    const { view0Mismatched, target0Mismatched, view1Mismatched, target1Mismatched } = t.params;
    const mismatched = view0Mismatched || target0Mismatched || view1Mismatched || target1Mismatched;

    const view0Texture = view0Mismatched
      ? vtu.getDeviceMismatchedRenderTexture(t, 4)
      : vtu.getRenderTexture(t, 4);
    const target0Texture = target0Mismatched
      ? vtu.getDeviceMismatchedRenderTexture(t)
      : vtu.getRenderTexture(t);
    const view1Texture = view1Mismatched
      ? vtu.getDeviceMismatchedRenderTexture(t, 4)
      : vtu.getRenderTexture(t, 4);
    const target1Texture = target1Mismatched
      ? vtu.getDeviceMismatchedRenderTexture(t)
      : vtu.getRenderTexture(t);

    const encoder = t.createEncoder('non-pass');
    const pass = encoder.encoder.beginRenderPass({
      colorAttachments: [
        {
          view: view0Texture.createView(),
          clearValue: { r: 1.0, g: 0.0, b: 0.0, a: 1.0 },
          loadOp: 'clear',
          storeOp: 'store',
          resolveTarget: target0Texture.createView(),
        },
        {
          view: view1Texture.createView(),
          clearValue: { r: 1.0, g: 0.0, b: 0.0, a: 1.0 },
          loadOp: 'clear',
          storeOp: 'store',
          resolveTarget: target1Texture.createView(),
        },
      ],
    });
    pass.end();

    encoder.validateFinish(!mismatched);
  });

g.test('depth_stencil_attachment,device_mismatch')
  .desc(
    'Tests beginRenderPass cannot be called with a depth stencil attachment whose texture view is created from another device'
  )
  .paramsSubcasesOnly(u => u.combine('mismatched', [true, false]))
  .beforeAllSubcases(t => t.usesMismatchedDevice())
  .fn(t => {
    const { mismatched } = t.params;

    const descriptor: GPUTextureDescriptor = {
      size: { width: 4, height: 4, depthOrArrayLayers: 1 },
      format: 'depth24plus-stencil8',
      usage: GPUTextureUsage.RENDER_ATTACHMENT,
    };

    const depthStencilTexture = mismatched
      ? vtu.getDeviceMismatchedTexture(t, descriptor)
      : t.createTextureTracked(descriptor);

    const encoder = t.createEncoder('non-pass');
    const pass = encoder.encoder.beginRenderPass({
      colorAttachments: [],
      depthStencilAttachment: {
        view: depthStencilTexture.createView(),
        depthClearValue: 0,
        depthLoadOp: 'clear',
        depthStoreOp: 'store',
        stencilClearValue: 0,
        stencilLoadOp: 'clear',
        stencilStoreOp: 'store',
      },
    });
    pass.end();

    encoder.validateFinish(!mismatched);
  });

g.test('occlusion_query_set,device_mismatch')
  .desc(
    'Tests beginRenderPass cannot be called with an occlusion query set created from another device'
  )
  .paramsSubcasesOnly(u => u.combine('mismatched', [true, false]))
  .beforeAllSubcases(t => t.usesMismatchedDevice())
  .fn(t => {
    const { mismatched } = t.params;
    const sourceDevice = mismatched ? t.mismatchedDevice : t.device;

    const occlusionQuerySet = t.trackForCleanup(
      sourceDevice.createQuerySet({
        type: 'occlusion',
        count: 1,
      })
    );

    const encoder = t.createEncoder('render pass', { occlusionQuerySet });
    encoder.validateFinish(!mismatched);
  });

g.test('timestamp_query_set,device_mismatch')
  .desc(
    `
  Tests beginRenderPass cannot be called with a timestamp query set created from another device.
  `
  )
  .paramsSubcasesOnly(u => u.combine('mismatched', [true, false]))
  .beforeAllSubcases(t => t.usesMismatchedDevice())
  .fn(t => {
    t.skipIfDeviceDoesNotSupportQueryType('timestamp');
    const { mismatched } = t.params;
    const sourceDevice = mismatched ? t.mismatchedDevice : t.device;

    const timestampQuerySet = t.trackForCleanup(
      sourceDevice.createQuerySet({
        type: 'timestamp',
        count: 1,
      })
    );

    const timestampWrites = {
      querySet: timestampQuerySet,
      beginningOfPassWriteIndex: 0,
    };

    const colorTexture = t.createTextureTracked({
      format: 'rgba8unorm',
      size: { width: 4, height: 4, depthOrArrayLayers: 1 },
      usage: GPUTextureUsage.RENDER_ATTACHMENT,
    });

    const encoder = t.createEncoder('non-pass');
    const pass = encoder.encoder.beginRenderPass({
      colorAttachments: [
        {
          view: colorTexture.createView(),
          loadOp: 'load',
          storeOp: 'store',
        },
      ],
      timestampWrites,
    });
    pass.end();

    encoder.validateFinish(!mismatched);
  });