summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/util/command_buffer_maker.ts
blob: f52c8bb0591262795846b3a4c7f8a69156890692 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { ResourceState, GPUTest } from '../gpu_test.js';

export const kRenderEncodeTypes = ['render pass', 'render bundle'] as const;
export type RenderEncodeType = typeof kRenderEncodeTypes[number];
export const kProgrammableEncoderTypes = ['compute pass', ...kRenderEncodeTypes] as const;
export type ProgrammableEncoderType = typeof kProgrammableEncoderTypes[number];
export const kEncoderTypes = ['non-pass', ...kProgrammableEncoderTypes] as const;
export type EncoderType = typeof kEncoderTypes[number];

// Look up the type of the encoder based on `T`. If `T` is a union, this will be too!
type EncoderByEncoderType<T extends EncoderType> = {
  'non-pass': GPUCommandEncoder;
  'compute pass': GPUComputePassEncoder;
  'render pass': GPURenderPassEncoder;
  'render bundle': GPURenderBundleEncoder;
}[T];

/** See {@link webgpu/api/validation/validation_test.ValidationTest.createEncoder |
 * GPUTest.createEncoder()}. */
export class CommandBufferMaker<T extends EncoderType> {
  /** `GPU___Encoder` for recording commands into. */
  // Look up the type of the encoder based on `T`. If `T` is a union, this will be too!
  readonly encoder: EncoderByEncoderType<T>;

  /**
   * Finish any passes, finish and record any bundles, and finish/return the command buffer. Any
   * errors are ignored and the GPUCommandBuffer (which may be an error buffer) is returned.
   */
  readonly finish: () => GPUCommandBuffer;

  /**
   * Finish any passes, finish and record any bundles, and finish/return the command buffer.
   * Checks for validation errors in (only) the appropriate finish call.
   */
  readonly validateFinish: (shouldSucceed: boolean) => GPUCommandBuffer;

  /**
   * Finish the command buffer and submit it. Checks for validation errors in either the submit or
   * the appropriate finish call, depending on the state of a resource used in the encoding.
   */
  readonly validateFinishAndSubmit: (
    shouldBeValid: boolean,
    submitShouldSucceedIfValid: boolean
  ) => void;

  /**
   * `validateFinishAndSubmit()` based on the state of a resource in the command encoder.
   * - `finish()` should fail if the resource is 'invalid'.
   * - Only `submit()` should fail if the resource is 'destroyed'.
   */
  readonly validateFinishAndSubmitGivenState: (resourceState: ResourceState) => void;

  constructor(
    t: GPUTest,
    encoder: EncoderByEncoderType<EncoderType>,
    finish: () => GPUCommandBuffer
  ) {
    // TypeScript introduces an intersection type here where we don't want one.
    this.encoder = encoder as EncoderByEncoderType<T>;
    this.finish = finish;

    // Define extra methods like this, otherwise they get unbound when destructured, e.g.:
    //   const { encoder, validateFinishAndSubmit } = t.createEncoder(type);
    // Alternatively, do not destructure, and call member functions, e.g.:
    //   const encoder = t.createEncoder(type);
    //   encoder.validateFinish(true);
    this.validateFinish = (shouldSucceed: boolean) => {
      return t.expectGPUError('validation', this.finish, !shouldSucceed);
    };

    this.validateFinishAndSubmit = (
      shouldBeValid: boolean,
      submitShouldSucceedIfValid: boolean
    ) => {
      const commandBuffer = this.validateFinish(shouldBeValid);
      if (shouldBeValid) {
        t.expectValidationError(() => t.queue.submit([commandBuffer]), !submitShouldSucceedIfValid);
      }
    };

    this.validateFinishAndSubmitGivenState = (resourceState: ResourceState) => {
      this.validateFinishAndSubmit(resourceState !== 'invalid', resourceState !== 'destroyed');
    };
  }
}