// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

// @ts-check
/// <reference path="../../core/lib.deno_core.d.ts" />
/// <reference path="../web/internal.d.ts" />
/// <reference path="../web/lib.deno_web.d.ts" />
/// <reference path="./lib.deno_webgpu.d.ts" />

"use strict";

((window) => {
  const core = window.Deno.core;
  const webidl = window.__bootstrap.webidl;
  const eventTarget = window.__bootstrap.eventTarget;
  const { DOMException } = window.__bootstrap.domException;
  const {
    ArrayBuffer,
    ArrayBufferIsView,
    ArrayIsArray,
    ArrayPrototypeFilter,
    ArrayPrototypeMap,
    ArrayPrototypePop,
    ArrayPrototypePush,
    Error,
    MathMax,
    ObjectDefineProperty,
    ObjectFreeze,
    Promise,
    PromiseAll,
    PromisePrototypeCatch,
    PromisePrototypeThen,
    PromiseReject,
    PromiseResolve,
    Set,
    SetPrototypeEntries,
    SetPrototypeForEach,
    SetPrototypeHas,
    SetPrototypeKeys,
    SetPrototypeValues,
    Symbol,
    SymbolFor,
    SymbolIterator,
    TypeError,
    Uint32Array,
    Uint8Array,
  } = window.__bootstrap.primordials;

  /**
   * @param {any} self
   * @param {{prefix: string, context: string}} opts
   * @returns {InnerGPUDevice & {rid: number}}
   */
  function assertDevice(self, { prefix, context }) {
    const device = self[_device];
    const deviceRid = device?.rid;
    if (deviceRid === undefined) {
      throw new DOMException(
        `${prefix}: ${context} references an invalid or destroyed device.`,
        "OperationError",
      );
    }
    return device;
  }

  /**
   * @param {InnerGPUDevice} self
   * @param {any} resource
   * @param {{prefix: string, resourceContext: string, selfContext: string}} opts
   * @returns {InnerGPUDevice & {rid: number}}
   */
  function assertDeviceMatch(
    self,
    resource,
    { prefix, resourceContext, selfContext },
  ) {
    const resourceDevice = assertDevice(resource, {
      prefix,
      context: resourceContext,
    });
    if (resourceDevice.rid !== self.rid) {
      throw new DOMException(
        `${prefix}: ${resourceContext} belongs to a diffent device than ${selfContext}.`,
        "OperationError",
      );
    }
    return { ...resourceDevice, rid: resourceDevice.rid };
  }

  /**
   * @param {any} self
   * @param {{prefix: string, context: string}} opts
   * @returns {number}
   */
  function assertResource(self, { prefix, context }) {
    const rid = self[_rid];
    if (rid === undefined) {
      throw new DOMException(
        `${prefix}: ${context} an invalid or destroyed resource.`,
        "OperationError",
      );
    }
    return rid;
  }

  /**
   * @param {number[] | GPUExtent3DDict} data
   * @returns {GPUExtent3DDict}
   */
  function normalizeGPUExtent3D(data) {
    if (ArrayIsArray(data)) {
      return {
        width: data[0],
        height: data[1],
        depthOrArrayLayers: data[2],
      };
    } else {
      return data;
    }
  }

  /**
   * @param {number[] | GPUOrigin3DDict} data
   * @returns {GPUOrigin3DDict}
   */
  function normalizeGPUOrigin3D(data) {
    if (ArrayIsArray(data)) {
      return {
        x: data[0],
        y: data[1],
        z: data[2],
      };
    } else {
      return data;
    }
  }

  /**
   * @param {number[] | GPUColor} data
   * @returns {GPUColor}
   */
  function normalizeGPUColor(data) {
    if (ArrayIsArray(data)) {
      return {
        r: data[0],
        g: data[1],
        b: data[2],
        a: data[3],
      };
    } else {
      return data;
    }
  }

  class GPUOutOfMemoryError extends Error {
    name = "GPUOutOfMemoryError";
    constructor() {
      super("device out of memory");
    }
  }

  class GPUValidationError extends Error {
    name = "GPUValidationError";
    /** @param {string} message */
    constructor(message) {
      const prefix = "Failed to construct 'GPUValidationError'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      message = webidl.converters.DOMString(message, {
        prefix,
        context: "Argument 1",
      });
      super(message);
    }
  }

  class GPU {
    [webidl.brand] = webidl.brand;

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPURequestAdapterOptions} options
     */
    async requestAdapter(options = {}) {
      webidl.assertBranded(this, GPU);
      options = webidl.converters.GPURequestAdapterOptions(options, {
        prefix: "Failed to execute 'requestAdapter' on 'GPU'",
        context: "Argument 1",
      });

      const { err, ...data } = await core.opAsync(
        "op_webgpu_request_adapter",
        { ...options },
      );

      if (err) {
        return null;
      } else {
        return createGPUAdapter(data.name, data);
      }
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${inspect({})}`;
    }
  }

  const _name = Symbol("[[name]]");
  const _adapter = Symbol("[[adapter]]");
  const _cleanup = Symbol("[[cleanup]]");

  /**
   * @typedef InnerGPUAdapter
   * @property {number} rid
   * @property {GPUSupportedFeatures} features
   * @property {GPUSupportedLimits} limits
   * @property {boolean} isSoftware
   */

  /**
    * @param {string} name
    * @param {InnerGPUAdapter} inner
    * @returns {GPUAdapter}
    */
  function createGPUAdapter(name, inner) {
    /** @type {GPUAdapter} */
    const adapter = webidl.createBranded(GPUAdapter);
    adapter[_name] = name;
    adapter[_adapter] = {
      ...inner,
      features: createGPUSupportedFeatures(inner.features),
      limits: createGPUSupportedLimits(inner.limits),
    };
    return adapter;
  }

  class GPUAdapter {
    /** @type {string} */
    [_name];
    /** @type {InnerGPUAdapter} */
    [_adapter];

    /** @returns {string} */
    get name() {
      webidl.assertBranded(this, GPUAdapter);
      return this[_name];
    }
    /** @returns {GPUSupportedFeatures} */
    get features() {
      webidl.assertBranded(this, GPUAdapter);
      return this[_adapter].features;
    }
    /** @returns {GPUSupportedLimits} */
    get limits() {
      webidl.assertBranded(this, GPUAdapter);
      return this[_adapter].limits;
    }
    /** @returns {boolean} */
    get isSoftware() {
      return this[_adapter].isSoftware;
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPUDeviceDescriptor} descriptor
     * @returns {Promise<GPUDevice>}
     */
    async requestDevice(descriptor = {}) {
      webidl.assertBranded(this, GPUAdapter);
      const prefix = "Failed to execute 'requestDevice' on 'GPUAdapter'";
      descriptor = webidl.converters.GPUDeviceDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const requiredFeatures = descriptor.requiredFeatures ?? [];
      for (const feature of requiredFeatures) {
        if (!SetPrototypeHas(this[_adapter].features[_features], feature)) {
          throw new TypeError(
            `${prefix}: nonGuaranteedFeatures must be a subset of the adapter features.`,
          );
        }
      }
      const requiredLimits = descriptor.requiredLimits;
      // TODO(lucacasonato): validate requiredLimits

      const { rid, features, limits } = await core.opAsync(
        "op_webgpu_request_device",
        {
          adapterRid: this[_adapter].rid,
          labe: descriptor.label,
          requiredFeatures,
          requiredLimits,
        },
      );

      const inner = new InnerGPUDevice({
        rid,
        adapter: this,
        features: ObjectFreeze(features),
        limits: ObjectFreeze(limits),
      });
      return createGPUDevice(
        descriptor.label ?? null,
        inner,
        createGPUQueue(descriptor.label ?? null, inner),
      );
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          name: this.name,
          features: this.features,
          limits: this.limits,
        })
      }`;
    }
  }

  const _limits = Symbol("[[limits]]");

  function createGPUSupportedLimits(features) {
    /** @type {GPUSupportedLimits} */
    const adapterFeatures = webidl.createBranded(GPUSupportedLimits);
    adapterFeatures[_limits] = features;
    return adapterFeatures;
  }

  /**
   * @typedef InnerAdapterLimits
   * @property {number} maxTextureDimension1D
   * @property {number} maxTextureDimension2D
   * @property {number} maxTextureDimension3D
   * @property {number} maxTextureArrayLayers
   * @property {number} maxBindGroups
   * @property {number} maxDynamicUniformBuffersPerPipelineLayout
   * @property {number} maxDynamicStorageBuffersPerPipelineLayout
   * @property {number} maxSampledTexturesPerShaderStage
   * @property {number} maxSamplersPerShaderStage
   * @property {number} maxStorageBuffersPerShaderStage
   * @property {number} maxStorageTexturesPerShaderStage
   * @property {number} maxUniformBuffersPerShaderStage
   * @property {number} maxUniformBufferBindingSize
   * @property {number} maxStorageBufferBindingSize
   * @property {number} minUniformBufferOffsetAlignment
   * @property {number} minStorageBufferOffsetAlignment
   * @property {number} maxVertexBuffers
   * @property {number} maxVertexAttributes
   * @property {number} maxVertexBufferArrayStride
   * @property {number} maxInterStageShaderComponents
   * @property {number} maxComputeWorkgroupStorageSize
   * @property {number} maxComputeWorkgroupInvocations
   * @property {number} maxComputePerDimensionDispatchSize
   */

  class GPUSupportedLimits {
    /** @type {InnerAdapterLimits} */
    [_limits];
    constructor() {
      webidl.illegalConstructor();
    }

    get maxTextureDimension1D() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxTextureDimension1D;
    }
    get maxTextureDimension2D() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxTextureDimension2D;
    }
    get maxTextureDimension3D() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxTextureDimension3D;
    }
    get maxTextureArrayLayers() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxTextureArrayLayers;
    }
    get maxBindGroups() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxBindGroups;
    }
    get maxDynamicUniformBuffersPerPipelineLayout() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxDynamicUniformBuffersPerPipelineLayout;
    }
    get maxDynamicStorageBuffersPerPipelineLayout() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxDynamicStorageBuffersPerPipelineLayout;
    }
    get maxSampledTexturesPerShaderStage() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxSampledTexturesPerShaderStage;
    }
    get maxSamplersPerShaderStage() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxSamplersPerShaderStage;
    }
    get maxStorageBuffersPerShaderStage() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxStorageBuffersPerShaderStage;
    }
    get maxStorageTexturesPerShaderStage() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxStorageTexturesPerShaderStage;
    }
    get maxUniformBuffersPerShaderStage() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxUniformBuffersPerShaderStage;
    }
    get maxUniformBufferBindingSize() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxUniformBufferBindingSize;
    }
    get maxStorageBufferBindingSize() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxStorageBufferBindingSize;
    }
    get minUniformBufferOffsetAlignment() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].minUniformBufferOffsetAlignment;
    }
    get minStorageBufferOffsetAlignment() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].minStorageBufferOffsetAlignment;
    }
    get maxVertexBuffers() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxVertexBuffers;
    }
    get maxVertexAttributes() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxVertexAttributes;
    }
    get maxVertexBufferArrayStride() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxVertexBufferArrayStride;
    }
    get maxInterStageShaderComponents() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxInterStageShaderComponents;
    }
    get maxComputeWorkgroupStorageSize() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxComputeWorkgroupStorageSize;
    }
    get maxComputeWorkgroupInvocations() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxComputeWorkgroupInvocations;
    }
    get maxComputePerDimensionDispatchSize() {
      webidl.assertBranded(this, GPUSupportedLimits);
      return this[_limits].maxComputePerDimensionDispatchSize;
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${inspect(this[_limits])}`;
    }
  }

  const _features = Symbol("[[features]]");

  function createGPUSupportedFeatures(features) {
    /** @type {GPUSupportedFeatures} */
    const adapterFeatures = webidl.createBranded(GPUSupportedFeatures);
    adapterFeatures[_features] = new Set(features);
    return adapterFeatures;
  }

  class GPUSupportedFeatures {
    /** @type {Set<string>} */
    [_features];

    constructor() {
      webidl.illegalConstructor();
    }

    /** @return {IterableIterator<[string, string]>} */
    entries() {
      webidl.assertBranded(this, GPUSupportedFeatures);
      return SetPrototypeEntries(this[_features]);
    }

    /** @return {void} */
    forEach(callbackfn, thisArg) {
      webidl.assertBranded(this, GPUSupportedFeatures);
      SetPrototypeForEach(this[_features], callbackfn, thisArg);
    }

    /** @return {boolean} */
    has(value) {
      webidl.assertBranded(this, GPUSupportedFeatures);
      return SetPrototypeHas(this[_features], value);
    }

    /** @return {IterableIterator<string>} */
    keys() {
      webidl.assertBranded(this, GPUSupportedFeatures);
      return SetPrototypeKeys(this[_features]);
    }

    /** @return {IterableIterator<string>} */
    values() {
      webidl.assertBranded(this, GPUSupportedFeatures);
      return SetPrototypeValues(this[_features]);
    }

    /** @return {number} */
    get size() {
      webidl.assertBranded(this, GPUSupportedFeatures);
      return this[_features].size;
    }

    [SymbolIterator]() {
      webidl.assertBranded(this, GPUSupportedFeatures);
      return this[_features][SymbolIterator]();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${inspect([...this.values()])}`;
    }
  }

  const _reason = Symbol("[[reason]]");
  const _message = Symbol("[[message]]");

  /**
   *
   * @param {string | undefined} reason
   * @param {string} message
   * @returns {GPUDeviceLostInfo}
   */
  function createGPUDeviceLostInfo(reason, message) {
    /** @type {GPUDeviceLostInfo} */
    const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo);
    deviceLostInfo[_reason] = reason;
    deviceLostInfo[_message] = message;
    return deviceLostInfo;
  }

  class GPUDeviceLostInfo {
    /** @type {string | undefined} */
    [_reason];
    /** @type {string} */
    [_message];

    constructor() {
      webidl.illegalConstructor();
    }

    get reason() {
      webidl.assertBranded(this, GPUDeviceLostInfo);
      return this[_reason];
    }
    get message() {
      webidl.assertBranded(this, GPUDeviceLostInfo);
      return this[_message];
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({ reason: this[_reason], message: this[_message] })
      }`;
    }
  }

  const _label = Symbol("[[label]]");

  /**
   * @param {string} name
   * @param {any} type
   */
  function GPUObjectBaseMixin(name, type) {
    type.prototype[_label] = null;
    ObjectDefineProperty(type.prototype, "label", {
      /**
       * @return {string | null}
       */
      get() {
        webidl.assertBranded(this, type);
        return this[_label];
      },
      /**
       * @param {string | null} label
       */
      set(label) {
        webidl.assertBranded(this, type);
        label = webidl.converters["UVString?"](label, {
          prefix: `Failed to set 'label' on '${name}'`,
          context: "Argument 1",
        });
        this[_label] = label;
      },
    });
  }

  const _device = Symbol("[[device]]");
  const _queue = Symbol("[[queue]]");

  /**
   * @typedef ErrorScope
   * @property {string} filter
   * @property {Promise<void>[]} operations
   */

  /**
   * @typedef InnerGPUDeviceOptions
   * @property {GPUAdapter} adapter
   * @property {number | undefined} rid
   * @property {GPUFeatureName[]} features
   * @property {object} limits
   */

  class InnerGPUDevice {
    /** @type {GPUAdapter} */
    adapter;
    /** @type {number | undefined} */
    rid;
    /** @type {GPUFeatureName[]} */
    features;
    /** @type {object} */
    limits;
    /** @type {WeakRef<any>[]} */
    resources;
    /** @type {boolean} */
    isLost;
    /** @type {Promise<GPUDeviceLostInfo>} */
    lost;
    /** @type {(info: GPUDeviceLostInfo) => void} */
    resolveLost;
    /** @type {ErrorScope[]} */
    errorScopeStack;

    /**
     * @param {InnerGPUDeviceOptions} options
     */
    constructor(options) {
      this.adapter = options.adapter;
      this.rid = options.rid;
      this.features = options.features;
      this.limits = options.limits;
      this.resources = [];
      this.isLost = false;
      this.resolveLost = () => {};
      this.lost = new Promise((resolve) => {
        this.resolveLost = resolve;
      });
      this.errorScopeStack = [];
    }

    /** @param {any} resource */
    trackResource(resource) {
      ArrayPrototypePush(this.resources, new WeakRef(resource));
    }

    /** @param {{ type: string, value: string | null } | undefined} err */
    pushError(err) {
      this.pushErrorPromise(PromiseResolve(err));
    }

    /** @param {Promise<{ type: string, value: string | null } | undefined>} promise */
    pushErrorPromise(promise) {
      const operation = PromisePrototypeThen(promise, (err) => {
        if (err) {
          switch (err.type) {
            case "lost":
              this.isLost = true;
              this.resolveLost(
                createGPUDeviceLostInfo(undefined, "device was lost"),
              );
              break;
            case "validation":
              return PromiseReject(
                new GPUValidationError(err.value ?? "validation error"),
              );
            case "out-of-memory":
              return PromiseReject(new GPUOutOfMemoryError());
          }
        }
      });

      const validationStack = ArrayPrototypeFilter(
        this.errorScopeStack,
        ({ filter }) => filter == "validation",
      );
      const validationScope = validationStack[validationStack.length - 1];
      const validationFilteredPromise = PromisePrototypeCatch(
        operation,
        (err) => {
          if (err instanceof GPUValidationError) return PromiseReject(err);
          return PromiseResolve();
        },
      );
      if (validationScope) {
        ArrayPrototypePush(
          validationScope.operations,
          validationFilteredPromise,
        );
      } else {
        PromisePrototypeCatch(validationFilteredPromise, () => {
          // TODO(lucacasonato): emit an UncapturedErrorEvent
        });
      }
      // prevent uncaptured promise rejections
      PromisePrototypeCatch(validationFilteredPromise, (_err) => {});

      const oomStack = ArrayPrototypeFilter(
        this.errorScopeStack,
        ({ filter }) => filter == "out-of-memory",
      );
      const oomScope = oomStack[oomStack.length - 1];
      const oomFilteredPromise = PromisePrototypeCatch(operation, (err) => {
        if (err instanceof GPUOutOfMemoryError) return PromiseReject(err);
        return PromiseResolve();
      });
      if (oomScope) {
        ArrayPrototypePush(oomScope.operations, oomFilteredPromise);
      } else {
        PromisePrototypeCatch(oomFilteredPromise, () => {
          // TODO(lucacasonato): emit an UncapturedErrorEvent
        });
      }
      // prevent uncaptured promise rejections
      PromisePrototypeCatch(oomFilteredPromise, (_err) => {});
    }
  }

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} inner
   * @param {GPUQueue} queue
   * @returns {GPUDevice}
   */
  function createGPUDevice(label, inner, queue) {
    /** @type {GPUDevice} */
    const device = webidl.createBranded(GPUDevice);
    device[_label] = label;
    device[_device] = inner;
    device[_queue] = queue;
    return device;
  }

  // TODO(@crowlKats): https://gpuweb.github.io/gpuweb/#errors-and-debugging
  class GPUDevice extends eventTarget.EventTarget {
    /** @type {InnerGPUDevice} */
    [_device];

    /** @type {GPUQueue} */
    [_queue];

    [_cleanup]() {
      const device = this[_device];
      const resources = device.resources;
      while (resources.length > 0) {
        const resource = ArrayPrototypePop(resources)?.deref();
        if (resource) {
          resource[_cleanup]();
        }
      }
      const rid = device.rid;
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        device.rid = undefined;
      }
    }

    get features() {
      webidl.assertBranded(this, GPUDevice);
      return this[_device].features;
    }
    get limits() {
      webidl.assertBranded(this, GPUDevice);
      return this[_device].limits;
    }
    get queue() {
      webidl.assertBranded(this, GPUDevice);
      return this[_queue];
    }

    constructor() {
      webidl.illegalConstructor();
      super();
    }

    destroy() {
      webidl.assertBranded(this, GPUDevice);
      this[_cleanup]();
    }

    /**
     * @param {GPUBufferDescriptor} descriptor
     * @returns {GPUBuffer}
     */
    createBuffer(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createBuffer' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUBufferDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync("op_webgpu_create_buffer", {
        deviceRid: device.rid,
        ...descriptor,
      });
      device.pushError(err);
      /** @type {CreateGPUBufferOptions} */
      let options;
      if (descriptor.mappedAtCreation) {
        options = {
          mapping: new ArrayBuffer(descriptor.size),
          mappingRange: [0, descriptor.size],
          mappedRanges: [],
          state: "mapped at creation",
        };
      } else {
        options = {
          mapping: null,
          mappedRanges: null,
          mappingRange: null,
          state: "unmapped",
        };
      }
      const buffer = createGPUBuffer(
        descriptor.label ?? null,
        device,
        rid,
        descriptor.size,
        descriptor.usage,
        options,
      );
      device.trackResource((buffer));
      return buffer;
    }

    /**
     * @param {GPUTextureDescriptor} descriptor
     * @returns {GPUTexture}
     */
    createTexture(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createTexture' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUTextureDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync("op_webgpu_create_texture", {
        deviceRid: device.rid,
        ...descriptor,
        size: normalizeGPUExtent3D(descriptor.size),
      });
      device.pushError(err);

      const texture = createGPUTexture(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((texture));
      return texture;
    }

    /**
     * @param {GPUSamplerDescriptor} descriptor
     * @returns {GPUSampler}
     */
    createSampler(descriptor = {}) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createSampler' on 'GPUDevice'";
      descriptor = webidl.converters.GPUSamplerDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync("op_webgpu_create_sampler", {
        deviceRid: device.rid,
        ...descriptor,
      });
      device.pushError(err);

      const sampler = createGPUSampler(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((sampler));
      return sampler;
    }

    /**
     * @param {GPUBindGroupLayoutDescriptor} descriptor
     * @returns {GPUBindGroupLayout}
     */
    createBindGroupLayout(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createBindGroupLayout' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUBindGroupLayoutDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      for (const entry of descriptor.entries) {
        let i = 0;
        if (entry.buffer) i++;
        if (entry.sampler) i++;
        if (entry.texture) i++;
        if (entry.storageTexture) i++;

        if (i !== 1) {
          throw new Error(); // TODO(@crowlKats): correct error
        }
      }

      const { rid, err } = core.opSync(
        "op_webgpu_create_bind_group_layout",
        {
          deviceRid: device.rid,
          ...descriptor,
        },
      );
      device.pushError(err);

      const bindGroupLayout = createGPUBindGroupLayout(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((bindGroupLayout));
      return bindGroupLayout;
    }

    /**
     * @param {GPUPipelineLayoutDescriptor} descriptor
     * @returns {GPUPipelineLayout}
     */
    createPipelineLayout(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createPipelineLayout' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUPipelineLayoutDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const bindGroupLayouts = ArrayPrototypeMap(
        descriptor.bindGroupLayouts,
        (layout, i) => {
          const context = `bind group layout ${i + 1}`;
          const rid = assertResource(layout, { prefix, context });
          assertDeviceMatch(device, layout, {
            prefix,
            selfContext: "this",
            resourceContext: context,
          });
          return rid;
        },
      );
      const { rid, err } = core.opSync("op_webgpu_create_pipeline_layout", {
        deviceRid: device.rid,
        label: descriptor.label,
        bindGroupLayouts,
      });
      device.pushError(err);

      const pipelineLayout = createGPUPipelineLayout(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((pipelineLayout));
      return pipelineLayout;
    }

    /**
     * @param {GPUBindGroupDescriptor} descriptor
     * @returns {GPUBindGroup}
     */
    createBindGroup(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createBindGroup' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUBindGroupDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const layout = assertResource(descriptor.layout, {
        prefix,
        context: "layout",
      });
      assertDeviceMatch(device, descriptor.layout, {
        prefix,
        resourceContext: "layout",
        selfContext: "this",
      });
      const entries = ArrayPrototypeMap(descriptor.entries, (entry, i) => {
        const context = `entry ${i + 1}`;
        const resource = entry.resource;
        if (resource instanceof GPUSampler) {
          const rid = assertResource(resource, {
            prefix,
            context,
          });
          assertDeviceMatch(device, resource, {
            prefix,
            resourceContext: context,
            selfContext: "this",
          });
          return {
            binding: entry.binding,
            kind: "GPUSampler",
            resource: rid,
          };
        } else if (resource instanceof GPUTextureView) {
          const rid = assertResource(resource, {
            prefix,
            context,
          });
          assertResource(resource[_texture], {
            prefix,
            context,
          });
          assertDeviceMatch(device, resource[_texture], {
            prefix,
            resourceContext: context,
            selfContext: "this",
          });
          return {
            binding: entry.binding,
            kind: "GPUTextureView",
            resource: rid,
          };
        } else {
          const rid = assertResource(resource.buffer, { prefix, context });
          assertDeviceMatch(device, resource.buffer, {
            prefix,
            resourceContext: context,
            selfContext: "this",
          });
          return {
            binding: entry.binding,
            kind: "GPUBufferBinding",
            resource: rid,
            offset: entry.resource.offset,
            size: entry.resource.size,
          };
        }
      });

      const { rid, err } = core.opSync("op_webgpu_create_bind_group", {
        deviceRid: device.rid,
        label: descriptor.label,
        layout,
        entries,
      });
      device.pushError(err);

      const bindGroup = createGPUBindGroup(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((bindGroup));
      return bindGroup;
    }

    /**
     * @param {GPUShaderModuleDescriptor} descriptor
     */
    createShaderModule(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createShaderModule' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUShaderModuleDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync(
        "op_webgpu_create_shader_module",
        {
          deviceRid: device.rid,
          label: descriptor.label,
          code: (typeof descriptor.code === "string")
            ? descriptor.code
            : undefined,
          sourceMap: descriptor.sourceMap,
        },
        ...(descriptor.code instanceof Uint32Array
          ? [new Uint8Array(descriptor.code.buffer)]
          : []),
      );
      device.pushError(err);

      const shaderModule = createGPUShaderModule(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((shaderModule));
      return shaderModule;
    }

    /**
     * @param {GPUComputePipelineDescriptor} descriptor
     * @returns {GPUComputePipeline}
     */
    createComputePipeline(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createComputePipeline' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUComputePipelineDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      let layout = undefined;
      if (descriptor.layout) {
        const context = "layout";
        layout = assertResource(descriptor.layout, { prefix, context });
        assertDeviceMatch(device, descriptor.layout, {
          prefix,
          resourceContext: context,
          selfContext: "this",
        });
      }
      const module = assertResource(descriptor.compute.module, {
        prefix,
        context: "compute shader module",
      });
      assertDeviceMatch(device, descriptor.compute.module, {
        prefix,
        resourceContext: "compute shader module",
        selfContext: "this",
      });

      const { rid, err } = core.opSync(
        "op_webgpu_create_compute_pipeline",
        {
          deviceRid: device.rid,
          label: descriptor.label,
          layout,
          compute: {
            module,
            entryPoint: descriptor.compute.entryPoint,
            constants: descriptor.compute.constants,
          },
        },
      );
      device.pushError(err);

      const computePipeline = createGPUComputePipeline(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((computePipeline));
      return computePipeline;
    }

    /**
     * @param {GPURenderPipelineDescriptor} descriptor
     * @returns {GPURenderPipeline}
     */
    createRenderPipeline(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createRenderPipeline' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPURenderPipelineDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      let layout = undefined;
      if (descriptor.layout) {
        const context = "layout";
        layout = assertResource(descriptor.layout, { prefix, context });
        assertDeviceMatch(device, descriptor.layout, {
          prefix,
          resourceContext: context,
          selfContext: "this",
        });
      }
      const module = assertResource(descriptor.vertex.module, {
        prefix,
        context: "vertex shader module",
      });
      assertDeviceMatch(device, descriptor.vertex.module, {
        prefix,
        resourceContext: "vertex shader module",
        selfContext: "this",
      });
      let fragment = undefined;
      if (descriptor.fragment) {
        const module = assertResource(descriptor.fragment.module, {
          prefix,
          context: "fragment shader module",
        });
        assertDeviceMatch(device, descriptor.fragment.module, {
          prefix,
          resourceContext: "fragment shader module",
          selfContext: "this",
        });
        fragment = {
          module,
          entryPoint: descriptor.fragment.entryPoint,
          targets: descriptor.fragment.targets,
        };
      }

      const { rid, err } = core.opSync("op_webgpu_create_render_pipeline", {
        deviceRid: device.rid,
        label: descriptor.label,
        layout,
        vertex: {
          module,
          entryPoint: descriptor.vertex.entryPoint,
          buffers: descriptor.vertex.buffers,
        },
        primitive: descriptor.primitive,
        depthStencil: descriptor.depthStencil,
        multisample: descriptor.multisample,
        fragment,
      });
      device.pushError(err);

      const renderPipeline = createGPURenderPipeline(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((renderPipeline));
      return renderPipeline;
    }

    createComputePipelineAsync(descriptor) {
      // TODO(lucacasonato): this should be real async
      return PromiseResolve(this.createComputePipeline(descriptor));
    }

    createRenderPipelineAsync(descriptor) {
      // TODO(lucacasonato): this should be real async
      return PromiseResolve(this.createRenderPipeline(descriptor));
    }

    /**
     * @param {GPUCommandEncoderDescriptor} descriptor
     * @returns {GPUCommandEncoder}
     */
    createCommandEncoder(descriptor = {}) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createCommandEncoder' on 'GPUDevice'";
      descriptor = webidl.converters.GPUCommandEncoderDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync("op_webgpu_create_command_encoder", {
        deviceRid: device.rid,
        ...descriptor,
      });
      device.pushError(err);

      const commandEncoder = createGPUCommandEncoder(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((commandEncoder));
      return commandEncoder;
    }

    /**
     * @param {GPURenderBundleEncoderDescriptor} descriptor
     * @returns {GPURenderBundleEncoder}
     */
    createRenderBundleEncoder(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix =
        "Failed to execute 'createRenderBundleEncoder' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPURenderBundleEncoderDescriptor(
        descriptor,
        {
          prefix,
          context: "Argument 1",
        },
      );
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync(
        "op_webgpu_create_render_bundle_encoder",
        {
          deviceRid: device.rid,
          ...descriptor,
        },
      );
      device.pushError(err);

      const renderBundleEncoder = createGPURenderBundleEncoder(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((renderBundleEncoder));
      return renderBundleEncoder;
    }

    /**
     * @param {GPUQuerySetDescriptor} descriptor
     * @returns {GPUQuerySet}
     */
    createQuerySet(descriptor) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'createQuerySet' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPUQuerySetDescriptor(
        descriptor,
        {
          prefix,
          context: "Argument 1",
        },
      );
      const device = assertDevice(this, { prefix, context: "this" });
      const { rid, err } = core.opSync("op_webgpu_create_query_set", {
        deviceRid: device.rid,
        ...descriptor,
      });
      device.pushError(err);

      const querySet = createGPUQuerySet(
        descriptor.label ?? null,
        device,
        rid,
        descriptor,
      );
      device.trackResource((querySet));
      return querySet;
    }

    get lost() {
      webidl.assertBranded(this, GPUDevice);
      const device = this[_device];
      if (!device) {
        return PromiseResolve(true);
      }
      if (device.rid === undefined) {
        return PromiseResolve(true);
      }
      return device.lost;
    }

    /**
     * @param {GPUErrorFilter} filter
     */
    pushErrorScope(filter) {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'pushErrorScope' on 'GPUDevice'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      filter = webidl.converters.GPUErrorFilter(filter, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      ArrayPrototypePush(device.errorScopeStack, { filter, operations: [] });
    }

    /**
     * @returns {Promise<GPUError | null>}
     */
    // deno-lint-ignore require-await
    async popErrorScope() {
      webidl.assertBranded(this, GPUDevice);
      const prefix = "Failed to execute 'popErrorScope' on 'GPUDevice'";
      const device = assertDevice(this, { prefix, context: "this" });
      if (device.isLost) {
        throw new DOMException("Device has been lost.", "OperationError");
      }
      const scope = ArrayPrototypePop(device.errorScopeStack);
      if (!scope) {
        throw new DOMException(
          "There are no error scopes on the error scope stack.",
          "OperationError",
        );
      }
      const operations = PromiseAll(scope.operations);
      return PromisePrototypeThen(
        operations,
        () => PromiseResolve(null),
        (err) => PromiseResolve(err),
      );
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          features: this.features,
          label: this.label,
          limits: this.limits,
          queue: this.queue,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUDevice", GPUDevice);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @returns {GPUQueue}
   */
  function createGPUQueue(label, device) {
    /** @type {GPUQueue} */
    const queue = webidl.createBranded(GPUQueue);
    queue[_label] = label;
    queue[_device] = device;
    return queue;
  }

  class GPUQueue {
    /** @type {InnerGPUDevice} */
    [_device];

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPUCommandBuffer[]} commandBuffers
     */
    submit(commandBuffers) {
      webidl.assertBranded(this, GPUQueue);
      const prefix = "Failed to execute 'submit' on 'GPUQueue'";
      webidl.requiredArguments(arguments.length, 1, {
        prefix,
      });
      commandBuffers = webidl.converters["sequence<GPUCommandBuffer>"](
        commandBuffers,
        { prefix, context: "Argument 1" },
      );
      const device = assertDevice(this, { prefix, context: "this" });
      const commandBufferRids = ArrayPrototypeMap(
        commandBuffers,
        (buffer, i) => {
          const context = `command buffer ${i + 1}`;
          const rid = assertResource(buffer, { prefix, context });
          assertDeviceMatch(device, buffer, {
            prefix,
            selfContext: "this",
            resourceContext: context,
          });
          return rid;
        },
      );
      const { err } = core.opSync("op_webgpu_queue_submit", {
        queueRid: device.rid,
        commandBuffers: commandBufferRids,
      });
      device.pushError(err);
    }

    onSubmittedWorkDone() {
      webidl.assertBranded(this, GPUQueue);
      return PromiseResolve();
    }

    /**
     * @param {GPUBuffer} buffer
     * @param {number} bufferOffset
     * @param {BufferSource} data
     * @param {number} [dataOffset]
     * @param {number} [size]
     */
    writeBuffer(buffer, bufferOffset, data, dataOffset = 0, size) {
      webidl.assertBranded(this, GPUQueue);
      const prefix = "Failed to execute 'writeBuffer' on 'GPUQueue'";
      webidl.requiredArguments(arguments.length, 3, { prefix });
      buffer = webidl.converters["GPUBuffer"](buffer, {
        prefix,
        context: "Argument 1",
      });
      bufferOffset = webidl.converters["GPUSize64"](bufferOffset, {
        prefix,
        context: "Argument 2",
      });
      data = webidl.converters.BufferSource(data, {
        prefix,
        context: "Argument 3",
      });
      dataOffset = webidl.converters["GPUSize64"](dataOffset, {
        prefix,
        context: "Argument 4",
      });
      size = size === undefined
        ? undefined
        : webidl.converters["GPUSize64"](size, {
          prefix,
          context: "Argument 5",
        });
      const device = assertDevice(this, { prefix, context: "this" });
      const bufferRid = assertResource(buffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, buffer, {
        prefix,
        selfContext: "this",
        resourceContext: "Argument 1",
      });
      const { err } = core.opSync(
        "op_webgpu_write_buffer",
        {
          queueRid: device.rid,
          buffer: bufferRid,
          bufferOffset,
          dataOffset,
          size,
        },
        new Uint8Array(ArrayBufferIsView(data) ? data.buffer : data),
      );
      device.pushError(err);
    }

    /**
     * @param {GPUImageCopyTexture} destination
     * @param {BufferSource} data
     * @param {GPUImageDataLayout} dataLayout
     * @param {GPUExtent3D} size
     */
    writeTexture(destination, data, dataLayout, size) {
      webidl.assertBranded(this, GPUQueue);
      const prefix = "Failed to execute 'writeTexture' on 'GPUQueue'";
      webidl.requiredArguments(arguments.length, 4, { prefix });
      destination = webidl.converters.GPUImageCopyTexture(destination, {
        prefix,
        context: "Argument 1",
      });
      data = webidl.converters.BufferSource(data, {
        prefix,
        context: "Argument 2",
      });
      dataLayout = webidl.converters.GPUImageDataLayout(dataLayout, {
        prefix,
        context: "Argument 3",
      });
      size = webidl.converters.GPUExtent3D(size, {
        prefix,
        context: "Argument 4",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const textureRid = assertResource(destination.texture, {
        prefix,
        context: "texture",
      });
      assertDeviceMatch(device, destination.texture, {
        prefix,
        selfContext: "this",
        resourceContext: "texture",
      });
      const { err } = core.opSync(
        "op_webgpu_write_texture",
        {
          queueRid: device.rid,
          destination: {
            texture: textureRid,
            mipLevel: destination.mipLevel,
            origin: destination.origin
              ? normalizeGPUOrigin3D(destination.origin)
              : undefined,
          },
          dataLayout,
          size: normalizeGPUExtent3D(size),
        },
        new Uint8Array(ArrayBufferIsView(data) ? data.buffer : data),
      );
      device.pushError(err);
    }

    copyImageBitmapToTexture(_source, _destination, _copySize) {
      throw new Error("Not yet implemented");
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUQueue", GPUQueue);

  const _rid = Symbol("[[rid]]");

  const _size = Symbol("[[size]]");
  const _usage = Symbol("[[usage]]");
  const _state = Symbol("[[state]]");
  const _mappingRange = Symbol("[[mapping_range]]");
  const _mappedRanges = Symbol("[[mapped_ranges]]");
  const _mapMode = Symbol("[[map_mode]]");

  /**
   * @typedef CreateGPUBufferOptions
   * @property {ArrayBuffer | null} mapping
   * @property {number[] | null} mappingRange
   * @property {[ArrayBuffer, number, number][] | null} mappedRanges
   * @property {"mapped" | "mapped at creation" | "mapped pending" | "unmapped" | "destroy" } state
   */

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @param {number} size
   * @param {number} usage
   * @param {CreateGPUBufferOptions} options
   * @returns {GPUBuffer}
   */
  function createGPUBuffer(label, device, rid, size, usage, options) {
    /** @type {GPUBuffer} */
    const buffer = webidl.createBranded(GPUBuffer);
    buffer[_label] = label;
    buffer[_device] = device;
    buffer[_rid] = rid;
    buffer[_size] = size;
    buffer[_usage] = usage;
    buffer[_mappingRange] = options.mappingRange;
    buffer[_mappedRanges] = options.mappedRanges;
    buffer[_state] = options.state;
    return buffer;
  }

  class GPUBuffer {
    /** @type {InnerGPUDevice} */
    [_device];

    /** @type {number} */
    [_rid];

    /** @type {number} */
    [_size];

    /** @type {number} */
    [_usage];

    /** @type {"mapped" | "mapped at creation" | "mapped pending" | "unmapped" | "destroy"} */
    [_state];

    /** @type {[number, number] | null} */
    [_mappingRange];

    /** @type {[ArrayBuffer, number, number][] | null} */
    [_mappedRanges];

    /** @type {number} */
    [_mapMode];

    [_cleanup]() {
      const mappedRanges = this[_mappedRanges];
      if (mappedRanges) {
        while (mappedRanges.length > 0) {
          const mappedRange = ArrayPrototypePop(mappedRanges);
          if (mappedRange !== undefined) {
            core.close(mappedRange[1]);
          }
        }
      }
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
      this[_state] = "destroy";
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {number} mode
     * @param {number} offset
     * @param {number} [size]
     */
    async mapAsync(mode, offset = 0, size) {
      webidl.assertBranded(this, GPUBuffer);
      const prefix = "Failed to execute 'mapAsync' on 'GPUBuffer'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      mode = webidl.converters.GPUMapModeFlags(mode, {
        prefix,
        context: "Argument 1",
      });
      offset = webidl.converters.GPUSize64(offset, {
        prefix,
        context: "Argument 2",
      });
      size = size === undefined
        ? undefined
        : webidl.converters.GPUSize64(size, {
          prefix,
          context: "Argument 3",
        });
      const device = assertDevice(this, { prefix, context: "this" });
      const bufferRid = assertResource(this, { prefix, context: "this" });
      /** @type {number} */
      let rangeSize;
      if (size === undefined) {
        rangeSize = MathMax(0, this[_size] - offset);
      } else {
        rangeSize = this[_size];
      }
      if ((offset % 8) !== 0) {
        throw new DOMException(
          `${prefix}: offset must be a multiple of 8.`,
          "OperationError",
        );
      }
      if ((rangeSize % 4) !== 0) {
        throw new DOMException(
          `${prefix}: rangeSize must be a multiple of 4.`,
          "OperationError",
        );
      }
      if ((offset + rangeSize) > this[_size]) {
        throw new DOMException(
          `${prefix}: offset + rangeSize must be less than or equal to buffer size.`,
          "OperationError",
        );
      }
      if (this[_state] !== "unmapped") {
        throw new DOMException(
          `${prefix}: GPUBuffer is not currently unmapped.`,
          "OperationError",
        );
      }
      const readMode = (mode & 0x0001) === 0x0001;
      const writeMode = (mode & 0x0002) === 0x0002;
      if ((readMode && writeMode) || (!readMode && !writeMode)) {
        throw new DOMException(
          `${prefix}: exactly one of READ or WRITE map mode must be set.`,
          "OperationError",
        );
      }
      if (readMode && !((this[_usage] && 0x0001) === 0x0001)) {
        throw new DOMException(
          `${prefix}: READ map mode not valid because buffer does not have MAP_READ usage.`,
          "OperationError",
        );
      }
      if (writeMode && !((this[_usage] && 0x0002) === 0x0002)) {
        throw new DOMException(
          `${prefix}: WRITE map mode not valid because buffer does not have MAP_WRITE usage.`,
          "OperationError",
        );
      }

      this[_mapMode] = mode;
      this[_state] = "mapping pending";
      const promise = PromisePrototypeThen(
        core.opAsync(
          "op_webgpu_buffer_get_map_async",
          {
            bufferRid,
            deviceRid: device.rid,
            mode,
            offset,
            size: rangeSize,
          },
        ),
        ({ err }) => err,
      );
      device.pushErrorPromise(promise);
      const err = await promise;
      if (err) {
        throw new DOMException("validation error occured", "OperationError");
      }
      this[_state] = "mapped";
      this[_mappingRange] = [offset, offset + rangeSize];
      /** @type {[ArrayBuffer, number, number][] | null} */
      this[_mappedRanges] = [];
    }

    /**
     * @param {number} offset
     * @param {number} size
     */
    getMappedRange(offset = 0, size) {
      webidl.assertBranded(this, GPUBuffer);
      const prefix = "Failed to execute 'getMappedRange' on 'GPUBuffer'";
      offset = webidl.converters.GPUSize64(offset, {
        prefix,
        context: "Argument 1",
      });
      if (size !== undefined) {
        size = webidl.converters.GPUSize64(size, {
          prefix,
          context: "Argument 2",
        });
      }
      assertDevice(this, { prefix, context: "this" });
      const bufferRid = assertResource(this, { prefix, context: "this" });
      /** @type {number} */
      let rangeSize;
      if (size === undefined) {
        rangeSize = MathMax(0, this[_size] - offset);
      } else {
        rangeSize = size;
      }

      const mappedRanges = this[_mappedRanges];
      if (!mappedRanges) {
        throw new DOMException(`${prefix}: invalid state.`, "OperationError");
      }
      for (const [buffer, _rid, start] of mappedRanges) {
        // TODO(lucacasonato): is this logic correct?
        const end = start + buffer.byteLength;
        if (
          (start >= offset && start < (offset + rangeSize)) ||
          (end >= offset && end < (offset + rangeSize))
        ) {
          throw new DOMException(
            `${prefix}: requested buffer overlaps with another mapped range.`,
            "OperationError",
          );
        }
      }

      const buffer = new ArrayBuffer(rangeSize);
      const { rid } = core.opSync(
        "op_webgpu_buffer_get_mapped_range",
        {
          bufferRid,
          offset,
          size,
        },
        new Uint8Array(buffer),
      );

      ArrayPrototypePush(mappedRanges, [buffer, rid, offset]);

      return buffer;
    }

    unmap() {
      webidl.assertBranded(this, GPUBuffer);
      const prefix = "Failed to execute 'unmap' on 'GPUBuffer'";
      const device = assertDevice(this, { prefix, context: "this" });
      const bufferRid = assertResource(this, { prefix, context: "this" });
      if (this[_state] === "unmapped" || this[_state] === "destroyed") {
        throw new DOMException(
          `${prefix}: buffer is not ready to be unmapped.`,
          "OperationError",
        );
      }
      if (this[_state] === "mapping pending") {
        // TODO(lucacasonato): this is not spec compliant.
        throw new DOMException(
          `${prefix}: can not unmap while mapping. This is a Deno limitation.`,
          "OperationError",
        );
      } else if (
        this[_state] === "mapped" || this[_state] === "mapped at creation"
      ) {
        /** @type {boolean} */
        let write = false;
        if (this[_state] === "mapped at creation") {
          write = true;
        } else if (this[_state] === "mapped") {
          const mapMode = this[_mapMode];
          if (mapMode === undefined) {
            throw new DOMException(
              `${prefix}: invalid state.`,
              "OperationError",
            );
          }
          if ((mapMode & 0x0002) === 0x0002) {
            write = true;
          }
        }

        const mappedRanges = this[_mappedRanges];
        if (!mappedRanges) {
          throw new DOMException(`${prefix}: invalid state.`, "OperationError");
        }
        for (const [buffer, mappedRid] of mappedRanges) {
          const { err } = core.opSync("op_webgpu_buffer_unmap", {
            bufferRid,
            mappedRid,
          }, ...(write ? [new Uint8Array(buffer)] : []));
          device.pushError(err);
          if (err) return;
        }
        this[_mappingRange] = null;
        this[_mappedRanges] = null;
      }

      this[_state] = "unmapped";
    }

    destroy() {
      webidl.assertBranded(this, GPUBuffer);
      this[_cleanup]();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUBuffer", GPUBuffer);

  class GPUBufferUsage {
    constructor() {
      webidl.illegalConstructor();
    }

    static get MAP_READ() {
      return 0x0001;
    }
    static get MAP_WRITE() {
      return 0x0002;
    }
    static get COPY_SRC() {
      return 0x0004;
    }
    static get COPY_DST() {
      return 0x0008;
    }
    static get INDEX() {
      return 0x0010;
    }
    static get VERTEX() {
      return 0x0020;
    }
    static get UNIFORM() {
      return 0x0040;
    }
    static get STORAGE() {
      return 0x0080;
    }
    static get INDIRECT() {
      return 0x0100;
    }
    static get QUERY_RESOLVE() {
      return 0x0200;
    }
  }

  class GPUMapMode {
    constructor() {
      webidl.illegalConstructor();
    }

    static get READ() {
      return 0x0001;
    }
    static get WRITE() {
      return 0x0002;
    }
  }

  const _views = Symbol("[[views]]");

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUTexture}
   */
  function createGPUTexture(label, device, rid) {
    /** @type {GPUTexture} */
    const texture = webidl.createBranded(GPUTexture);
    texture[_label] = label;
    texture[_device] = device;
    texture[_rid] = rid;
    texture[_views] = [];
    return texture;
  }

  class GPUTexture {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];
    /** @type {WeakRef<GPUTextureView>[]} */
    [_views];

    [_cleanup]() {
      const views = this[_views];
      while (views.length > 0) {
        const view = ArrayPrototypePop(views)?.deref();
        if (view) {
          view[_cleanup]();
        }
      }
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPUTextureViewDescriptor} descriptor
     */
    createView(descriptor = {}) {
      webidl.assertBranded(this, GPUTexture);
      const prefix = "Failed to execute 'createView' on 'GPUTexture'";
      webidl.requiredArguments(arguments.length, 0, { prefix });
      descriptor = webidl.converters.GPUTextureViewDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const textureRid = assertResource(this, { prefix, context: "this" });
      const { rid, err } = core.opSync("op_webgpu_create_texture_view", {
        textureRid,
        ...descriptor,
      });
      device.pushError(err);

      const textureView = createGPUTextureView(
        descriptor.label ?? null,
        this,
        rid,
      );
      ArrayPrototypePush(this[_views], new WeakRef(textureView));
      return textureView;
    }

    destroy() {
      webidl.assertBranded(this, GPUTexture);
      this[_cleanup]();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUTexture", GPUTexture);

  class GPUTextureUsage {
    constructor() {
      webidl.illegalConstructor();
    }

    static get COPY_SRC() {
      return 0x01;
    }
    static get COPY_DST() {
      return 0x02;
    }
    static get SAMPLED() {
      return 0x04;
    }
    static get STORAGE() {
      return 0x08;
    }
    static get RENDER_ATTACHMENT() {
      return 0x10;
    }
  }

  const _texture = Symbol("[[texture]]");

  /**
   * @param {string | null} label
   * @param {GPUTexture} texture
   * @param {number} rid
   * @returns {GPUTextureView}
   */
  function createGPUTextureView(label, texture, rid) {
    /** @type {GPUTextureView} */
    const textureView = webidl.createBranded(GPUTextureView);
    textureView[_label] = label;
    textureView[_texture] = texture;
    textureView[_rid] = rid;
    return textureView;
  }
  class GPUTextureView {
    /** @type {GPUTexture} */
    [_texture];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUTextureView", GPUTextureView);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUSampler}
   */
  function createGPUSampler(label, device, rid) {
    /** @type {GPUSampler} */
    const sampler = webidl.createBranded(GPUSampler);
    sampler[_label] = label;
    sampler[_device] = device;
    sampler[_rid] = rid;
    return sampler;
  }
  class GPUSampler {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUSampler", GPUSampler);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUBindGroupLayout}
   */
  function createGPUBindGroupLayout(label, device, rid) {
    /** @type {GPUBindGroupLayout} */
    const bindGroupLayout = webidl.createBranded(GPUBindGroupLayout);
    bindGroupLayout[_label] = label;
    bindGroupLayout[_device] = device;
    bindGroupLayout[_rid] = rid;
    return bindGroupLayout;
  }
  class GPUBindGroupLayout {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUBindGroupLayout", GPUBindGroupLayout);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUPipelineLayout}
   */
  function createGPUPipelineLayout(label, device, rid) {
    /** @type {GPUPipelineLayout} */
    const pipelineLayout = webidl.createBranded(GPUPipelineLayout);
    pipelineLayout[_label] = label;
    pipelineLayout[_device] = device;
    pipelineLayout[_rid] = rid;
    return pipelineLayout;
  }
  class GPUPipelineLayout {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUPipelineLayout", GPUPipelineLayout);

  /**
   * @param {string | null} label
    * @param {InnerGPUDevice} device
  * @param {number} rid
   * @returns {GPUBindGroup}
   */
  function createGPUBindGroup(label, device, rid) {
    /** @type {GPUBindGroup} */
    const bindGroup = webidl.createBranded(GPUBindGroup);
    bindGroup[_label] = label;
    bindGroup[_device] = device;
    bindGroup[_rid] = rid;
    return bindGroup;
  }
  class GPUBindGroup {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUBindGroup", GPUBindGroup);

  /**
   * @param {string | null} label
    * @param {InnerGPUDevice} device
  * @param {number} rid
   * @returns {GPUShaderModule}
   */
  function createGPUShaderModule(label, device, rid) {
    /** @type {GPUShaderModule} */
    const bindGroup = webidl.createBranded(GPUShaderModule);
    bindGroup[_label] = label;
    bindGroup[_device] = device;
    bindGroup[_rid] = rid;
    return bindGroup;
  }
  class GPUShaderModule {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    compilationInfo() {
      throw new Error("Not yet implemented");
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUShaderModule", GPUShaderModule);

  class GPUShaderStage {
    constructor() {
      webidl.illegalConstructor();
    }

    static get VERTEX() {
      return 0x1;
    }

    static get FRAGMENT() {
      return 0x2;
    }

    static get COMPUTE() {
      return 0x4;
    }
  }

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUComputePipeline}
   */
  function createGPUComputePipeline(label, device, rid) {
    /** @type {GPUComputePipeline} */
    const pipeline = webidl.createBranded(GPUComputePipeline);
    pipeline[_label] = label;
    pipeline[_device] = device;
    pipeline[_rid] = rid;
    return pipeline;
  }
  class GPUComputePipeline {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {number} index
     * @returns {GPUBindGroupLayout}
     */
    getBindGroupLayout(index) {
      webidl.assertBranded(this, GPUComputePipeline);
      const prefix =
        "Failed to execute 'getBindGroupLayout' on 'GPUComputePipeline'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      index = webidl.converters["unsigned long"](index, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const computePipelineRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { rid, label, err } = core.opSync(
        "op_webgpu_compute_pipeline_get_bind_group_layout",
        { computePipelineRid, index },
      );
      device.pushError(err);

      const bindGroupLayout = createGPUBindGroupLayout(
        label,
        device,
        rid,
      );
      device.trackResource((bindGroupLayout));
      return bindGroupLayout;
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUComputePipeline", GPUComputePipeline);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPURenderPipeline}
   */
  function createGPURenderPipeline(label, device, rid) {
    /** @type {GPURenderPipeline} */
    const pipeline = webidl.createBranded(GPURenderPipeline);
    pipeline[_label] = label;
    pipeline[_device] = device;
    pipeline[_rid] = rid;
    return pipeline;
  }
  class GPURenderPipeline {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {number} index
     */
    getBindGroupLayout(index) {
      webidl.assertBranded(this, GPURenderPipeline);
      const prefix =
        "Failed to execute 'getBindGroupLayout' on 'GPURenderPipeline'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      index = webidl.converters["unsigned long"](index, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const renderPipelineRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { rid, label, err } = core.opSync(
        "op_webgpu_render_pipeline_get_bind_group_layout",
        { renderPipelineRid, index },
      );
      device.pushError(err);

      const bindGroupLayout = createGPUBindGroupLayout(
        label,
        device,
        rid,
      );
      device.trackResource((bindGroupLayout));
      return bindGroupLayout;
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPURenderPipeline", GPURenderPipeline);

  class GPUColorWrite {
    constructor() {
      webidl.illegalConstructor();
    }

    static get RED() {
      return 0x1;
    }
    static get GREEN() {
      return 0x2;
    }
    static get BLUE() {
      return 0x4;
    }
    static get ALPHA() {
      return 0x8;
    }
    static get ALL() {
      return 0xF;
    }
  }

  const _encoders = Symbol("[[encoders]]");

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUCommandEncoder}
   */
  function createGPUCommandEncoder(label, device, rid) {
    /** @type {GPUCommandEncoder} */
    const encoder = webidl.createBranded(GPUCommandEncoder);
    encoder[_label] = label;
    encoder[_device] = device;
    encoder[_rid] = rid;
    encoder[_encoders] = [];
    return encoder;
  }
  class GPUCommandEncoder {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];
    /** @type {WeakRef<GPURenderPassEncoder | GPUComputePassEncoder>[]} */
    [_encoders];

    [_cleanup]() {
      const encoders = this[_encoders];
      while (encoders.length > 0) {
        const encoder = ArrayPrototypePop(encoders)?.deref();
        if (encoder) {
          encoder[_cleanup]();
        }
      }
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPURenderPassDescriptor} descriptor
     * @return {GPURenderPassEncoder}
     */
    beginRenderPass(descriptor) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      descriptor = webidl.converters.GPURenderPassDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });

      if (this[_rid] === undefined) {
        throw new DOMException(
          "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder': already consumed",
          "OperationError",
        );
      }

      let depthStencilAttachment;
      if (descriptor.depthStencilAttachment) {
        const view = assertResource(descriptor.depthStencilAttachment.view, {
          prefix,
          context: "texture view for depth stencil attachment",
        });
        assertDeviceMatch(
          device,
          descriptor.depthStencilAttachment.view[_texture],
          {
            prefix,
            resourceContext: "texture view for depth stencil attachment",
            selfContext: "this",
          },
        );

        depthStencilAttachment = {
          ...descriptor.depthStencilAttachment,
          view,
        };

        if (
          typeof descriptor.depthStencilAttachment.depthLoadValue === "string"
        ) {
          depthStencilAttachment.depthLoadOp =
            descriptor.depthStencilAttachment.depthLoadValue;
        } else {
          depthStencilAttachment.depthLoadOp = "clear";
          depthStencilAttachment.depthLoadValue =
            descriptor.depthStencilAttachment.depthLoadValue;
        }

        if (
          typeof descriptor.depthStencilAttachment.stencilLoadValue === "string"
        ) {
          depthStencilAttachment.stencilLoadOp =
            descriptor.depthStencilAttachment.stencilLoadValue;
          depthStencilAttachment.stencilLoadValue = undefined;
        } else {
          depthStencilAttachment.stencilLoadOp = "clear";
          depthStencilAttachment.stencilLoadValue =
            descriptor.depthStencilAttachment.stencilLoadValue;
        }
      }
      const colorAttachments = ArrayPrototypeMap(
        descriptor.colorAttachments,
        (colorAttachment, i) => {
          const context = `color attachment ${i + 1}`;
          const view = assertResource(colorAttachment.view, {
            prefix,
            context: `texture view for ${context}`,
          });
          assertResource(colorAttachment.view[_texture], {
            prefix,
            context: `texture backing texture view for ${context}`,
          });
          assertDeviceMatch(
            device,
            colorAttachment.view[_texture],
            {
              prefix,
              resourceContext: `texture view for ${context}`,
              selfContext: "this",
            },
          );
          let resolveTarget;
          if (colorAttachment.resolveTarget) {
            resolveTarget = assertResource(
              colorAttachment.resolveTarget,
              {
                prefix,
                context: `resolve target texture view for ${context}`,
              },
            );
            assertResource(colorAttachment.resolveTarget[_texture], {
              prefix,
              context:
                `texture backing resolve target texture view for ${context}`,
            });
            assertDeviceMatch(
              device,
              colorAttachment.resolveTarget[_texture],
              {
                prefix,
                resourceContext: `resolve target texture view for ${context}`,
                selfContext: "this",
              },
            );
          }
          const attachment = {
            view: view,
            resolveTarget,
            storeOp: colorAttachment.storeOp,
          };

          if (typeof colorAttachment.loadValue === "string") {
            attachment.loadOp = colorAttachment.loadValue;
          } else {
            attachment.loadOp = "clear";
            attachment.loadValue = normalizeGPUColor(
              colorAttachment.loadValue,
            );
          }

          return attachment;
        },
      );

      const { rid } = core.opSync(
        "op_webgpu_command_encoder_begin_render_pass",
        {
          commandEncoderRid,
          ...descriptor,
          colorAttachments,
          depthStencilAttachment,
        },
      );

      const renderPassEncoder = createGPURenderPassEncoder(
        descriptor.label ?? null,
        this,
        rid,
      );
      ArrayPrototypePush(this[_encoders], new WeakRef(renderPassEncoder));
      return renderPassEncoder;
    }

    /**
     * @param {GPUComputePassDescriptor} descriptor
     */
    beginComputePass(descriptor = {}) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'beginComputePass' on 'GPUCommandEncoder'";
      descriptor = webidl.converters.GPUComputePassDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });

      assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });

      const { rid } = core.opSync(
        "op_webgpu_command_encoder_begin_compute_pass",
        {
          commandEncoderRid,
          ...descriptor,
        },
      );

      const computePassEncoder = createGPUComputePassEncoder(
        descriptor.label ?? null,
        this,
        rid,
      );
      ArrayPrototypePush(this[_encoders], new WeakRef(computePassEncoder));
      return computePassEncoder;
    }

    /**
     * @param {GPUBuffer} source
     * @param {number} sourceOffset
     * @param {GPUBuffer} destination
     * @param {number} destinationOffset
     * @param {number} size
     */
    copyBufferToBuffer(
      source,
      sourceOffset,
      destination,
      destinationOffset,
      size,
    ) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'copyBufferToBuffer' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 5, { prefix });
      source = webidl.converters.GPUBuffer(source, {
        prefix,
        context: "Argument 1",
      });
      sourceOffset = webidl.converters.GPUSize64(sourceOffset, {
        prefix,
        context: "Argument 2",
      });
      destination = webidl.converters.GPUBuffer(destination, {
        prefix,
        context: "Argument 3",
      });
      destinationOffset = webidl.converters.GPUSize64(destinationOffset, {
        prefix,
        context: "Argument 4",
      });
      size = webidl.converters.GPUSize64(size, {
        prefix,
        context: "Argument 5",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const sourceRid = assertResource(source, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, source, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      const destinationRid = assertResource(destination, {
        prefix,
        context: "Argument 3",
      });
      assertDeviceMatch(device, destination, {
        prefix,
        resourceContext: "Argument 3",
        selfContext: "this",
      });

      const { err } = core.opSync(
        "op_webgpu_command_encoder_copy_buffer_to_buffer",
        {
          commandEncoderRid,
          source: sourceRid,
          sourceOffset,
          destination: destinationRid,
          destinationOffset,
          size,
        },
      );
      device.pushError(err);
    }

    /**
     * @param {GPUImageCopyBuffer} source
     * @param {GPUImageCopyTexture} destination
     * @param {GPUExtent3D} copySize
     */
    copyBufferToTexture(source, destination, copySize) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'copyBufferToTexture' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 3, { prefix });
      source = webidl.converters.GPUImageCopyBuffer(source, {
        prefix,
        context: "Argument 1",
      });
      destination = webidl.converters.GPUImageCopyTexture(destination, {
        prefix,
        context: "Argument 2",
      });
      copySize = webidl.converters.GPUExtent3D(copySize, {
        prefix,
        context: "Argument 3",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const sourceBufferRid = assertResource(source.buffer, {
        prefix,
        context: "source in Argument 1",
      });
      assertDeviceMatch(device, source.buffer, {
        prefix,
        resourceContext: "source in Argument 1",
        selfContext: "this",
      });
      const destinationTextureRid = assertResource(destination.texture, {
        prefix,
        context: "texture in Argument 2",
      });
      assertDeviceMatch(device, destination.texture, {
        prefix,
        resourceContext: "texture in Argument 2",
        selfContext: "this",
      });

      const { err } = core.opSync(
        "op_webgpu_command_encoder_copy_buffer_to_texture",
        {
          commandEncoderRid,
          source: {
            ...source,
            buffer: sourceBufferRid,
          },
          destination: {
            texture: destinationTextureRid,
            mipLevel: destination.mipLevel,
            origin: destination.origin
              ? normalizeGPUOrigin3D(destination.origin)
              : undefined,
          },
          copySize: normalizeGPUExtent3D(copySize),
        },
      );
      device.pushError(err);
    }

    /**
     * @param {GPUImageCopyTexture} source
     * @param {GPUImageCopyBuffer} destination
     * @param {GPUExtent3D} copySize
     */
    copyTextureToBuffer(source, destination, copySize) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'copyTextureToBuffer' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 3, { prefix });
      source = webidl.converters.GPUImageCopyTexture(source, {
        prefix,
        context: "Argument 1",
      });
      destination = webidl.converters.GPUImageCopyBuffer(destination, {
        prefix,
        context: "Argument 2",
      });
      copySize = webidl.converters.GPUExtent3D(copySize, {
        prefix,
        context: "Argument 3",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const sourceTextureRid = assertResource(source.texture, {
        prefix,
        context: "texture in Argument 1",
      });
      assertDeviceMatch(device, source.texture, {
        prefix,
        resourceContext: "texture in Argument 1",
        selfContext: "this",
      });
      const destinationBufferRid = assertResource(destination.buffer, {
        prefix,
        context: "buffer in Argument 2",
      });
      assertDeviceMatch(device, destination.buffer, {
        prefix,
        resourceContext: "buffer in Argument 2",
        selfContext: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_copy_texture_to_buffer",
        {
          commandEncoderRid,
          source: {
            texture: sourceTextureRid,
            mipLevel: source.mipLevel,
            origin: source.origin
              ? normalizeGPUOrigin3D(source.origin)
              : undefined,
          },
          destination: {
            ...destination,
            buffer: destinationBufferRid,
          },
          copySize: normalizeGPUExtent3D(copySize),
        },
      );
      device.pushError(err);
    }

    /**
     * @param {GPUImageCopyTexture} source
     * @param {GPUImageCopyTexture} destination
     * @param {GPUExtent3D} copySize
     */
    copyTextureToTexture(source, destination, copySize) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'copyTextureToTexture' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 3, { prefix });
      source = webidl.converters.GPUImageCopyTexture(source, {
        prefix,
        context: "Argument 1",
      });
      destination = webidl.converters.GPUImageCopyTexture(destination, {
        prefix,
        context: "Argument 2",
      });
      copySize = webidl.converters.GPUExtent3D(copySize, {
        prefix,
        context: "Argument 3",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const sourceTextureRid = assertResource(source.texture, {
        prefix,
        context: "texture in Argument 1",
      });
      assertDeviceMatch(device, source.texture, {
        prefix,
        resourceContext: "texture in Argument 1",
        selfContext: "this",
      });
      const destinationTextureRid = assertResource(destination.texture, {
        prefix,
        context: "texture in Argument 2",
      });
      assertDeviceMatch(device, destination.texture, {
        prefix,
        resourceContext: "texture in Argument 2",
        selfContext: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_copy_texture_to_texture",
        {
          commandEncoderRid,
          source: {
            texture: sourceTextureRid,
            mipLevel: source.mipLevel,
            origin: source.origin
              ? normalizeGPUOrigin3D(source.origin)
              : undefined,
          },
          destination: {
            texture: destinationTextureRid,
            mipLevel: destination.mipLevel,
            origin: destination.origin
              ? normalizeGPUOrigin3D(destination.origin)
              : undefined,
          },
          copySize: normalizeGPUExtent3D(copySize),
        },
      );
      device.pushError(err);
    }

    /**
     * @param {string} groupLabel
     */
    pushDebugGroup(groupLabel) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'pushDebugGroup' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      groupLabel = webidl.converters.USVString(groupLabel, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_push_debug_group",
        {
          commandEncoderRid,
          groupLabel,
        },
      );
      device.pushError(err);
    }

    popDebugGroup() {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix = "Failed to execute 'popDebugGroup' on 'GPUCommandEncoder'";
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_pop_debug_group",
        {
          commandEncoderRid,
        },
      );
      device.pushError(err);
    }

    /**
     * @param {string} markerLabel
     */
    insertDebugMarker(markerLabel) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'insertDebugMarker' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      markerLabel = webidl.converters.USVString(markerLabel, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_insert_debug_marker",
        {
          commandEncoderRid,
          markerLabel,
        },
      );
      device.pushError(err);
    }

    /**
     * @param {GPUQuerySet} querySet
     * @param {number} queryIndex
     */
    writeTimestamp(querySet, queryIndex) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'writeTimestamp' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      querySet = webidl.converters.GPUQuerySet(querySet, {
        prefix,
        context: "Argument 1",
      });
      queryIndex = webidl.converters.GPUSize32(queryIndex, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const querySetRid = assertResource(querySet, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, querySet, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_write_timestamp",
        {
          commandEncoderRid,
          querySet: querySetRid,
          queryIndex,
        },
      );
      device.pushError(err);
    }

    /**
     * @param {GPUQuerySet} querySet
     * @param {number} firstQuery
     * @param {number} queryCount
     * @param {GPUBuffer} destination
     * @param {number} destinationOffset
     */
    resolveQuerySet(
      querySet,
      firstQuery,
      queryCount,
      destination,
      destinationOffset,
    ) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix =
        "Failed to execute 'resolveQuerySet' on 'GPUCommandEncoder'";
      webidl.requiredArguments(arguments.length, 5, { prefix });
      querySet = webidl.converters.GPUQuerySet(querySet, {
        prefix,
        context: "Argument 1",
      });
      firstQuery = webidl.converters.GPUSize32(firstQuery, {
        prefix,
        context: "Argument 2",
      });
      queryCount = webidl.converters.GPUSize32(queryCount, {
        prefix,
        context: "Argument 3",
      });
      destination = webidl.converters.GPUQuerySet(destination, {
        prefix,
        context: "Argument 4",
      });
      destinationOffset = webidl.converters.GPUSize64(destinationOffset, {
        prefix,
        context: "Argument 5",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const querySetRid = assertResource(querySet, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, querySet, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      const destinationRid = assertResource(destination, {
        prefix,
        context: "Argument 3",
      });
      assertDeviceMatch(device, destination, {
        prefix,
        resourceContext: "Argument 3",
        selfContext: "this",
      });
      const { err } = core.opSync(
        "op_webgpu_command_encoder_resolve_query_set",
        {
          commandEncoderRid,
          querySet: querySetRid,
          firstQuery,
          queryCount,
          destination: destinationRid,
          destinationOffset,
        },
      );
      device.pushError(err);
    }

    /**
     * @param {GPUCommandBufferDescriptor} descriptor
     * @returns {GPUCommandBuffer}
     */
    finish(descriptor = {}) {
      webidl.assertBranded(this, GPUCommandEncoder);
      const prefix = "Failed to execute 'finish' on 'GPUCommandEncoder'";
      descriptor = webidl.converters.GPUCommandBufferDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const commandEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { rid, err } = core.opSync("op_webgpu_command_encoder_finish", {
        commandEncoderRid,
        ...descriptor,
      });
      device.pushError(err);
      /** @type {number | undefined} */
      this[_rid] = undefined;

      const commandBuffer = createGPUCommandBuffer(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((commandBuffer));
      return commandBuffer;
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUCommandEncoder", GPUCommandEncoder);

  const _encoder = Symbol("[[encoder]]");

  /**
   * @param {string | null} label
   * @param {GPUCommandEncoder} encoder
   * @param {number} rid
   * @returns {GPURenderPassEncoder}
   */
  function createGPURenderPassEncoder(label, encoder, rid) {
    /** @type {GPURenderPassEncoder} */
    const passEncoder = webidl.createBranded(GPURenderPassEncoder);
    passEncoder[_label] = label;
    passEncoder[_encoder] = encoder;
    passEncoder[_rid] = rid;
    return passEncoder;
  }

  class GPURenderPassEncoder {
    /** @type {GPUCommandEncoder} */
    [_encoder];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {number} x
     * @param {number} y
     * @param {number} width
     * @param {number} height
     * @param {number} minDepth
     * @param {number} maxDepth
     */
    setViewport(x, y, width, height, minDepth, maxDepth) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setViewport' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 6, { prefix });
      x = webidl.converters.float(x, { prefix, context: "Argument 1" });
      y = webidl.converters.float(y, { prefix, context: "Argument 2" });
      width = webidl.converters.float(width, { prefix, context: "Argument 3" });
      height = webidl.converters.float(height, {
        prefix,
        context: "Argument 4",
      });
      minDepth = webidl.converters.float(minDepth, {
        prefix,
        context: "Argument 5",
      });
      maxDepth = webidl.converters.float(maxDepth, {
        prefix,
        context: "Argument 6",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_set_viewport", {
        renderPassRid,
        x,
        y,
        width,
        height,
        minDepth,
        maxDepth,
      });
    }

    /**
     *
     * @param {number} x
     * @param {number} y
     * @param {number} width
     * @param {number} height
     */
    setScissorRect(x, y, width, height) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setScissorRect' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 4, { prefix });
      x = webidl.converters.GPUIntegerCoordinate(x, {
        prefix,
        context: "Argument 1",
      });
      y = webidl.converters.GPUIntegerCoordinate(y, {
        prefix,
        context: "Argument 2",
      });
      width = webidl.converters.GPUIntegerCoordinate(width, {
        prefix,
        context: "Argument 3",
      });
      height = webidl.converters.GPUIntegerCoordinate(height, {
        prefix,
        context: "Argument 4",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_set_scissor_rect", {
        renderPassRid,
        x,
        y,
        width,
        height,
      });
    }

    /**
     * @param {GPUColor} color
     */
    setBlendConstant(color) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setBlendConstant' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      color = webidl.converters.GPUColor(color, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_set_blend_constant", {
        renderPassRid,
        color: normalizeGPUColor(color),
      });
    }

    /**
     * @param {number} reference
     */
    setStencilReference(reference) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setStencilReference' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      reference = webidl.converters.GPUStencilValue(reference, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_set_stencil_reference", {
        renderPassRid,
        reference,
      });
    }

    beginOcclusionQuery(_queryIndex) {
      throw new Error("Not yet implemented");
    }

    endOcclusionQuery() {
      throw new Error("Not yet implemented");
    }

    /**
     * @param {GPUQuerySet} querySet
     * @param {number} queryIndex
     */
    beginPipelineStatisticsQuery(querySet, queryIndex) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'beginPipelineStatisticsQuery' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      querySet = webidl.converters.GPUQuerySet(querySet, {
        prefix,
        context: "Argument 1",
      });
      queryIndex = webidl.converters.GPUSize32(queryIndex, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const querySetRid = assertResource(querySet, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, querySet, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_begin_pipeline_statistics_query", {
        renderPassRid,
        querySet: querySetRid,
        queryIndex,
      });
    }

    endPipelineStatisticsQuery() {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'endPipelineStatisticsQuery' on 'GPURenderPassEncoder'";
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_end_pipeline_statistics_query", {
        renderPassRid,
      });
    }

    /**
     * @param {GPUQuerySet} querySet
     * @param {number} queryIndex
     */
    writeTimestamp(querySet, queryIndex) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'writeTimestamp' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      querySet = webidl.converters.GPUQuerySet(querySet, {
        prefix,
        context: "Argument 1",
      });
      queryIndex = webidl.converters.GPUSize32(queryIndex, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const querySetRid = assertResource(querySet, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, querySet, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_write_timestamp", {
        renderPassRid,
        querySet: querySetRid,
        queryIndex,
      });
    }

    /**
     * @param {GPURenderBundle[]} bundles
     */
    executeBundles(bundles) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'executeBundles' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      bundles = webidl.converters["sequence<GPURenderBundle>"](bundles, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const bundleRids = ArrayPrototypeMap(bundles, (bundle, i) => {
        const context = `bundle ${i + 1}`;
        const rid = assertResource(bundle, { prefix, context });
        assertDeviceMatch(device, bundle, {
          prefix,
          resourceContext: context,
          selfContext: "this",
        });
        return rid;
      });
      core.opSync("op_webgpu_render_pass_execute_bundles", {
        renderPassRid,
        bundles: bundleRids,
      });
    }

    endPass() {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix = "Failed to execute 'endPass' on 'GPURenderPassEncoder'";
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const commandEncoderRid = assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const { err } = core.opSync("op_webgpu_render_pass_end_pass", {
        commandEncoderRid,
        renderPassRid,
      });
      device.pushError(err);
      this[_rid] = undefined;
    }

    // TODO(lucacasonato): has an overload
    setBindGroup(
      index,
      bindGroup,
      dynamicOffsetsData,
      dynamicOffsetsDataStart,
      dynamicOffsetsDataLength,
    ) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setBindGroup' on 'GPURenderPassEncoder'";
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const bindGroupRid = assertResource(bindGroup, {
        prefix,
        context: "Argument 2",
      });
      assertDeviceMatch(device, bindGroup, {
        prefix,
        resourceContext: "Argument 2",
        selfContext: "this",
      });
      if (dynamicOffsetsData instanceof Uint32Array) {
        core.opSync(
          "op_webgpu_render_pass_set_bind_group",
          {
            renderPassRid,
            index,
            bindGroup: bindGroupRid,
            dynamicOffsetsDataStart,
            dynamicOffsetsDataLength,
          },
          dynamicOffsetsData,
        );
      } else {
        dynamicOffsetsData ??= [];
        core.opSync("op_webgpu_render_pass_set_bind_group", {
          renderPassRid,
          index,
          bindGroup: bindGroupRid,
          dynamicOffsetsData,
          dynamicOffsetsDataStart: 0,
          dynamicOffsetsDataLength: dynamicOffsetsData.length,
        });
      }
    }

    /**
     * @param {string} groupLabel
     */
    pushDebugGroup(groupLabel) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'pushDebugGroup' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      groupLabel = webidl.converters.USVString(groupLabel, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_push_debug_group", {
        renderPassRid,
        groupLabel,
      });
    }

    popDebugGroup() {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'popDebugGroup' on 'GPURenderPassEncoder'";
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_pop_debug_group", {
        renderPassRid,
      });
    }

    /**
     * @param {string} markerLabel
     */
    insertDebugMarker(markerLabel) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'insertDebugMarker' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      markerLabel = webidl.converters.USVString(markerLabel, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_insert_debug_marker", {
        renderPassRid,
        markerLabel,
      });
    }

    /**
     * @param {GPURenderPipeline} pipeline
     */
    setPipeline(pipeline) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setPipeline' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      pipeline = webidl.converters.GPURenderPipeline(pipeline, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const pipelineRid = assertResource(pipeline, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, pipeline, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_set_pipeline", {
        renderPassRid,
        pipeline: pipelineRid,
      });
    }

    /**
     * @param {GPUBuffer} buffer
     * @param {GPUIndexFormat} indexFormat
     * @param {number} offset
     * @param {number} size
     */
    setIndexBuffer(buffer, indexFormat, offset = 0, size) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setIndexBuffer' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      buffer = webidl.converters.GPUBuffer(buffer, {
        prefix,
        context: "Argument 1",
      });
      indexFormat = webidl.converters.GPUIndexFormat(indexFormat, {
        prefix,
        context: "Argument 2",
      });
      offset = webidl.converters.GPUSize64(offset, {
        prefix,
        context: "Argument 3",
      });
      if (size !== undefined) {
        size = webidl.converters.GPUSize64(size, {
          prefix,
          context: "Argument 4",
        });
      }
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const bufferRid = assertResource(buffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, buffer, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_set_index_buffer", {
        renderPassRid,
        buffer: bufferRid,
        indexFormat,
        offset,
        size,
      });
    }

    /**
     * @param {number} slot
     * @param {GPUBuffer} buffer
     * @param {number} offset
     * @param {number} size
     */
    setVertexBuffer(slot, buffer, offset = 0, size) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'setVertexBuffer' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      slot = webidl.converters.GPUSize32(slot, {
        prefix,
        context: "Argument 2",
      });
      buffer = webidl.converters.GPUBuffer(buffer, {
        prefix,
        context: "Argument 2",
      });
      offset = webidl.converters.GPUSize64(offset, {
        prefix,
        context: "Argument 3",
      });
      if (size !== undefined) {
        size = webidl.converters.GPUSize64(size, {
          prefix,
          context: "Argument 4",
        });
      }
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const bufferRid = assertResource(buffer, {
        prefix,
        context: "Argument 2",
      });
      assertDeviceMatch(device, buffer, {
        prefix,
        resourceContext: "Argument 2",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_set_vertex_buffer", {
        renderPassRid,
        slot,
        buffer: bufferRid,
        offset,
        size,
      });
    }

    /**
     * @param {number} vertexCount
     * @param {number} instanceCount
     * @param {number} firstVertex
     * @param {number} firstInstance
     */
    draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix = "Failed to execute 'draw' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      vertexCount = webidl.converters.GPUSize32(vertexCount, {
        prefix,
        context: "Argument 1",
      });
      instanceCount = webidl.converters.GPUSize32(instanceCount, {
        prefix,
        context: "Argument 2",
      });
      firstVertex = webidl.converters.GPUSize32(firstVertex, {
        prefix,
        context: "Argument 3",
      });
      firstInstance = webidl.converters.GPUSize32(firstInstance, {
        prefix,
        context: "Argument 4",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_draw", {
        renderPassRid,
        vertexCount,
        instanceCount,
        firstVertex,
        firstInstance,
      });
    }

    /**
     * @param {number} indexCount
     * @param {number} instanceCount
     * @param {number} firstIndex
     * @param {number} baseVertex
     * @param {number} firstInstance
     */
    drawIndexed(
      indexCount,
      instanceCount = 1,
      firstIndex = 0,
      baseVertex = 0,
      firstInstance = 0,
    ) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'drawIndexed' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      indexCount = webidl.converters.GPUSize32(indexCount, {
        prefix,
        context: "Argument 1",
      });
      instanceCount = webidl.converters.GPUSize32(instanceCount, {
        prefix,
        context: "Argument 2",
      });
      firstIndex = webidl.converters.GPUSize32(firstIndex, {
        prefix,
        context: "Argument 3",
      });
      baseVertex = webidl.converters.GPUSignedOffset32(baseVertex, {
        prefix,
        context: "Argument 4",
      });
      firstInstance = webidl.converters.GPUSize32(firstInstance, {
        prefix,
        context: "Argument 5",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_render_pass_draw_indexed", {
        renderPassRid,
        indexCount,
        instanceCount,
        firstIndex,
        baseVertex,
        firstInstance,
      });
    }

    /**
     * @param {GPUBuffer} indirectBuffer
     * @param {number} indirectOffset
     */
    drawIndirect(indirectBuffer, indirectOffset) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'drawIndirect' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const indirectBufferRid = assertResource(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, indirectBuffer, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_draw_indirect", {
        renderPassRid,
        indirectBuffer: indirectBufferRid,
        indirectOffset,
      });
    }

    /**
     * @param {GPUBuffer} indirectBuffer
     * @param {number} indirectOffset
     */
    drawIndexedIndirect(indirectBuffer, indirectOffset) {
      webidl.assertBranded(this, GPURenderPassEncoder);
      const prefix =
        "Failed to execute 'drawIndirect' on 'GPURenderPassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const renderPassRid = assertResource(this, { prefix, context: "this" });
      const indirectBufferRid = assertResource(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, indirectBuffer, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_pass_draw_indexed_indirect", {
        renderPassRid,
        indirectBuffer: indirectBufferRid,
        indirectOffset,
      });
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPURenderPassEncoder", GPURenderPassEncoder);

  /**
   * @param {string | null} label
   * @param {GPUCommandEncoder} encoder
   * @param {number} rid
   * @returns {GPUComputePassEncoder}
   */
  function createGPUComputePassEncoder(label, encoder, rid) {
    /** @type {GPUComputePassEncoder} */
    const computePassEncoder = webidl.createBranded(GPUComputePassEncoder);
    computePassEncoder[_label] = label;
    computePassEncoder[_encoder] = encoder;
    computePassEncoder[_rid] = rid;
    return computePassEncoder;
  }

  class GPUComputePassEncoder {
    /** @type {GPUCommandEncoder} */
    [_encoder];

    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPUComputePipeline} pipeline
     */
    setPipeline(pipeline) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'setPipeline' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      pipeline = webidl.converters.GPUComputePipeline(pipeline, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      const pipelineRid = assertResource(pipeline, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, pipeline, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_compute_pass_set_pipeline", {
        computePassRid,
        pipeline: pipelineRid,
      });
    }

    /**
     * @param {number} x
     * @param {number} y
     * @param {number} z
     */
    dispatch(x, y = 1, z = 1) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix = "Failed to execute 'dispatch' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      x = webidl.converters.GPUSize32(x, { prefix, context: "Argument 1" });
      y = webidl.converters.GPUSize32(y, { prefix, context: "Argument 2" });
      z = webidl.converters.GPUSize32(z, { prefix, context: "Argument 3" });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_compute_pass_dispatch", {
        computePassRid,
        x,
        y,
        z,
      });
    }

    /**
     * @param {GPUBuffer} indirectBuffer
     * @param {number} indirectOffset
     */
    dispatchIndirect(indirectBuffer, indirectOffset) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'dispatchIndirect' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      const indirectBufferRid = assertResource(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, indirectBuffer, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_compute_pass_dispatch_indirect", {
        computePassRid: computePassRid,
        indirectBuffer: indirectBufferRid,
        indirectOffset,
      });
    }

    /**
     * @param {GPUQuerySet} querySet
     * @param {number} queryIndex
     */
    beginPipelineStatisticsQuery(querySet, queryIndex) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'beginPipelineStatisticsQuery' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      querySet = webidl.converters.GPUQuerySet(querySet, {
        prefix,
        context: "Argument 1",
      });
      queryIndex = webidl.converters.GPUSize32(queryIndex, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      const querySetRid = assertResource(querySet, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, querySet, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync(
        "op_webgpu_compute_pass_begin_pipeline_statistics_query",
        {
          computePassRid,
          querySet: querySetRid,
          queryIndex,
        },
      );
    }

    endPipelineStatisticsQuery() {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'endPipelineStatisticsQuery' on 'GPUComputePassEncoder'";
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_compute_pass_end_pipeline_statistics_query", {
        computePassRid,
      });
    }

    /**
     * @param {GPUQuerySet} querySet
     * @param {number} queryIndex
     */
    writeTimestamp(querySet, queryIndex) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'writeTimestamp' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      querySet = webidl.converters.GPUQuerySet(querySet, {
        prefix,
        context: "Argument 1",
      });
      queryIndex = webidl.converters.GPUSize32(queryIndex, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      const querySetRid = assertResource(querySet, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, querySet, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_compute_pass_write_timestamp", {
        computePassRid,
        querySet: querySetRid,
        queryIndex,
      });
    }

    endPass() {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix = "Failed to execute 'endPass' on 'GPUComputePassEncoder'";
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const commandEncoderRid = assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      const { err } = core.opSync("op_webgpu_compute_pass_end_pass", {
        commandEncoderRid,
        computePassRid,
      });
      device.pushError(err);
      this[_rid] = undefined;
    }

    // TODO(lucacasonato): has an overload
    setBindGroup(
      index,
      bindGroup,
      dynamicOffsetsData,
      dynamicOffsetsDataStart,
      dynamicOffsetsDataLength,
    ) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
      const device = assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      const bindGroupRid = assertResource(bindGroup, {
        prefix,
        context: "Argument 2",
      });
      assertDeviceMatch(device, bindGroup, {
        prefix,
        resourceContext: "Argument 2",
        selfContext: "this",
      });
      if (dynamicOffsetsData instanceof Uint32Array) {
        core.opSync(
          "op_webgpu_compute_pass_set_bind_group",
          {
            computePassRid,
            index,
            bindGroup: bindGroupRid,
            dynamicOffsetsDataStart,
            dynamicOffsetsDataLength,
          },
          dynamicOffsetsData,
        );
      } else {
        dynamicOffsetsData ??= [];
        core.opSync("op_webgpu_compute_pass_set_bind_group", {
          computePassRid,
          index,
          bindGroup: bindGroupRid,
          dynamicOffsetsData,
          dynamicOffsetsDataStart: 0,
          dynamicOffsetsDataLength: dynamicOffsetsData.length,
        });
      }
    }

    /**
     * @param {string} groupLabel
     */
    pushDebugGroup(groupLabel) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'pushDebugGroup' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      groupLabel = webidl.converters.USVString(groupLabel, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_compute_pass_push_debug_group", {
        computePassRid,
        groupLabel,
      });
    }

    popDebugGroup() {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'popDebugGroup' on 'GPUComputePassEncoder'";
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_compute_pass_pop_debug_group", {
        computePassRid,
      });
    }

    /**
     * @param {string} markerLabel
     */
    insertDebugMarker(markerLabel) {
      webidl.assertBranded(this, GPUComputePassEncoder);
      const prefix =
        "Failed to execute 'insertDebugMarker' on 'GPUComputePassEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      markerLabel = webidl.converters.USVString(markerLabel, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      assertResource(this[_encoder], {
        prefix,
        context: "encoder referenced by this",
      });
      const computePassRid = assertResource(this, { prefix, context: "this" });
      core.opSync("op_webgpu_compute_pass_insert_debug_marker", {
        computePassRid,
        markerLabel,
      });
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUComputePassEncoder", GPUComputePassEncoder);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUCommandBuffer}
   */
  function createGPUCommandBuffer(label, device, rid) {
    /** @type {GPUCommandBuffer} */
    const commandBuffer = webidl.createBranded(GPUCommandBuffer);
    commandBuffer[_label] = label;
    commandBuffer[_device] = device;
    commandBuffer[_rid] = rid;
    return commandBuffer;
  }

  class GPUCommandBuffer {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    get executionTime() {
      throw new Error("Not yet implemented");
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
          // TODO(crowlKats): executionTime
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUCommandBuffer", GPUCommandBuffer);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPURenderBundleEncoder}
   */
  function createGPURenderBundleEncoder(label, device, rid) {
    /** @type {GPURenderBundleEncoder} */
    const bundleEncoder = webidl.createBranded(GPURenderBundleEncoder);
    bundleEncoder[_label] = label;
    bundleEncoder[_device] = device;
    bundleEncoder[_rid] = rid;
    return bundleEncoder;
  }

  class GPURenderBundleEncoder {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    /**
     * @param {GPURenderBundleDescriptor} descriptor
     */
    finish(descriptor = {}) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix = "Failed to execute 'finish' on 'GPURenderBundleEncoder'";
      descriptor = webidl.converters.GPURenderBundleDescriptor(descriptor, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const { rid, err } = core.opSync(
        "op_webgpu_render_bundle_encoder_finish",
        {
          renderBundleEncoderRid,
          ...descriptor,
        },
      );
      device.pushError(err);
      this[_rid] = undefined;

      const renderBundle = createGPURenderBundle(
        descriptor.label ?? null,
        device,
        rid,
      );
      device.trackResource((renderBundle));
      return renderBundle;
    }

    // TODO(lucacasonato): has an overload
    setBindGroup(
      index,
      bindGroup,
      dynamicOffsetsData,
      dynamicOffsetsDataStart,
      dynamicOffsetsDataLength,
    ) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'setBindGroup' on 'GPURenderBundleEncoder'";
      const device = assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const bindGroupRid = assertResource(bindGroup, {
        prefix,
        context: "Argument 2",
      });
      assertDeviceMatch(device, bindGroup, {
        prefix,
        resourceContext: "Argument 2",
        selfContext: "this",
      });
      if (dynamicOffsetsData instanceof Uint32Array) {
        core.opSync(
          "op_webgpu_render_bundle_encoder_set_bind_group",
          {
            renderBundleEncoderRid,
            index,
            bindGroup: bindGroupRid,
            dynamicOffsetsDataStart,
            dynamicOffsetsDataLength,
          },
          dynamicOffsetsData,
        );
      } else {
        dynamicOffsetsData ??= [];
        core.opSync("op_webgpu_render_bundle_encoder_set_bind_group", {
          renderBundleEncoderRid,
          index,
          bindGroup: bindGroupRid,
          dynamicOffsetsData,
          dynamicOffsetsDataStart: 0,
          dynamicOffsetsDataLength: dynamicOffsetsData.length,
        });
      }
    }

    /**
     * @param {string} groupLabel
     */
    pushDebugGroup(groupLabel) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'pushDebugGroup' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      groupLabel = webidl.converters.USVString(groupLabel, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_push_debug_group", {
        renderBundleEncoderRid,
        groupLabel,
      });
    }

    popDebugGroup() {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'popDebugGroup' on 'GPURenderBundleEncoder'";
      assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_pop_debug_group", {
        renderBundleEncoderRid,
      });
    }

    /**
     * @param {string} markerLabel
     */
    insertDebugMarker(markerLabel) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'insertDebugMarker' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      markerLabel = webidl.converters.USVString(markerLabel, {
        prefix,
        context: "Argument 1",
      });
      assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_push_debug_group", {
        renderBundleEncoderRid,
        markerLabel,
      });
    }

    /**
     * @param {GPURenderPipeline} pipeline
     */
    setPipeline(pipeline) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'setPipeline' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      pipeline = webidl.converters.GPURenderPipeline(pipeline, {
        prefix,
        context: "Argument 1",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const pipelineRid = assertResource(pipeline, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, pipeline, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_set_pipeline", {
        renderBundleEncoderRid,
        pipeline: pipelineRid,
      });
    }

    /**
     * @param {GPUBuffer} buffer
     * @param {GPUIndexFormat} indexFormat
     * @param {number} offset
     * @param {number} size
     */
    setIndexBuffer(buffer, indexFormat, offset = 0, size = 0) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'setIndexBuffer' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      buffer = webidl.converters.GPUBuffer(buffer, {
        prefix,
        context: "Argument 1",
      });
      indexFormat = webidl.converters.GPUIndexFormat(indexFormat, {
        prefix,
        context: "Argument 2",
      });
      offset = webidl.converters.GPUSize64(offset, {
        prefix,
        context: "Argument 3",
      });
      size = webidl.converters.GPUSize64(size, {
        prefix,
        context: "Argument 4",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const bufferRid = assertResource(buffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, buffer, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_set_index_buffer", {
        renderBundleEncoderRid,
        buffer: bufferRid,
        indexFormat,
        offset,
        size,
      });
    }

    /**
     * @param {number} slot
     * @param {GPUBuffer} buffer
     * @param {number} offset
     * @param {number} size
     */
    setVertexBuffer(slot, buffer, offset = 0, size = 0) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'setVertexBuffer' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      slot = webidl.converters.GPUSize32(slot, {
        prefix,
        context: "Argument 2",
      });
      buffer = webidl.converters.GPUBuffer(buffer, {
        prefix,
        context: "Argument 2",
      });
      offset = webidl.converters.GPUSize64(offset, {
        prefix,
        context: "Argument 3",
      });
      size = webidl.converters.GPUSize64(size, {
        prefix,
        context: "Argument 4",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const bufferRid = assertResource(buffer, {
        prefix,
        context: "Argument 2",
      });
      assertDeviceMatch(device, buffer, {
        prefix,
        resourceContext: "Argument 2",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_set_vertex_buffer", {
        renderBundleEncoderRid,
        slot,
        buffer: bufferRid,
        offset,
        size,
      });
    }

    /**
     * @param {number} vertexCount
     * @param {number} instanceCount
     * @param {number} firstVertex
     * @param {number} firstInstance
     */
    draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix = "Failed to execute 'draw' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      vertexCount = webidl.converters.GPUSize32(vertexCount, {
        prefix,
        context: "Argument 1",
      });
      instanceCount = webidl.converters.GPUSize32(instanceCount, {
        prefix,
        context: "Argument 2",
      });
      firstVertex = webidl.converters.GPUSize32(firstVertex, {
        prefix,
        context: "Argument 3",
      });
      firstInstance = webidl.converters.GPUSize32(firstInstance, {
        prefix,
        context: "Argument 4",
      });
      assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_draw", {
        renderBundleEncoderRid,
        vertexCount,
        instanceCount,
        firstVertex,
        firstInstance,
      });
    }

    /**
     * @param {number} indexCount
     * @param {number} instanceCount
     * @param {number} firstIndex
     * @param {number} baseVertex
     * @param {number} firstInstance
     */
    drawIndexed(
      indexCount,
      instanceCount = 1,
      firstIndex = 0,
      baseVertex = 0,
      firstInstance = 0,
    ) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'drawIndexed' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 1, { prefix });
      indexCount = webidl.converters.GPUSize32(indexCount, {
        prefix,
        context: "Argument 1",
      });
      instanceCount = webidl.converters.GPUSize32(instanceCount, {
        prefix,
        context: "Argument 2",
      });
      firstIndex = webidl.converters.GPUSize32(firstIndex, {
        prefix,
        context: "Argument 3",
      });
      baseVertex = webidl.converters.GPUSignedOffset32(baseVertex, {
        prefix,
        context: "Argument 4",
      });
      firstInstance = webidl.converters.GPUSize32(firstInstance, {
        prefix,
        context: "Argument 5",
      });
      assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_draw_indexed", {
        renderBundleEncoderRid,
        indexCount,
        instanceCount,
        firstIndex,
        baseVertex,
        firstInstance,
      });
    }

    /**
     * @param {GPUBuffer} indirectBuffer
     * @param {number} indirectOffset
     */
    drawIndirect(indirectBuffer, indirectOffset) {
      webidl.assertBranded(this, GPURenderBundleEncoder);
      const prefix =
        "Failed to execute 'drawIndirect' on 'GPURenderBundleEncoder'";
      webidl.requiredArguments(arguments.length, 2, { prefix });
      indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
        prefix,
        context: "Argument 2",
      });
      const device = assertDevice(this, { prefix, context: "this" });
      const renderBundleEncoderRid = assertResource(this, {
        prefix,
        context: "this",
      });
      const indirectBufferRid = assertResource(indirectBuffer, {
        prefix,
        context: "Argument 1",
      });
      assertDeviceMatch(device, indirectBuffer, {
        prefix,
        resourceContext: "Argument 1",
        selfContext: "this",
      });
      core.opSync("op_webgpu_render_bundle_encoder_draw_indirect", {
        renderBundleEncoderRid,
        indirectBuffer: indirectBufferRid,
        indirectOffset,
      });
    }

    drawIndexedIndirect(_indirectBuffer, _indirectOffset) {
      throw new Error("Not yet implemented");
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPURenderBundleEncoder", GPURenderBundleEncoder);

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPURenderBundle}
   */
  function createGPURenderBundle(label, device, rid) {
    /** @type {GPURenderBundle} */
    const bundle = webidl.createBranded(GPURenderBundle);
    bundle[_label] = label;
    bundle[_device] = device;
    bundle[_rid] = rid;
    return bundle;
  }

  class GPURenderBundle {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPURenderBundle", GPURenderBundle);

  const _descriptor = Symbol("[[descriptor]]");

  /**
   * @param {string | null} label
   * @param {InnerGPUDevice} device
   * @param {number} rid
   * @returns {GPUQuerySet}
   */
  function createGPUQuerySet(label, device, rid, descriptor) {
    /** @type {GPUQuerySet} */
    const queue = webidl.createBranded(GPUQuerySet);
    queue[_label] = label;
    queue[_device] = device;
    queue[_rid] = rid;
    queue[_descriptor] = descriptor;
    return queue;
  }

  class GPUQuerySet {
    /** @type {InnerGPUDevice} */
    [_device];
    /** @type {number | undefined} */
    [_rid];
    /** @type {GPUQuerySetDescriptor} */
    [_descriptor];

    [_cleanup]() {
      const rid = this[_rid];
      if (rid !== undefined) {
        core.close(rid);
        /** @type {number | undefined} */
        this[_rid] = undefined;
      }
    }

    constructor() {
      webidl.illegalConstructor();
    }

    destroy() {
      webidl.assertBranded(this, GPUQuerySet);
      this[_cleanup]();
    }

    [SymbolFor("Deno.privateCustomInspect")](inspect) {
      return `${this.constructor.name} ${
        inspect({
          label: this.label,
        })
      }`;
    }
  }
  GPUObjectBaseMixin("GPUQuerySet", GPUQuerySet);

  window.__bootstrap.webgpu = {
    gpu: webidl.createBranded(GPU),
    GPU,
    GPUAdapter,
    GPUSupportedLimits,
    GPUSupportedFeatures,
    GPUDevice,
    GPUQueue,
    GPUBuffer,
    GPUBufferUsage,
    GPUMapMode,
    GPUTextureUsage,
    GPUTexture,
    GPUTextureView,
    GPUSampler,
    GPUBindGroupLayout,
    GPUPipelineLayout,
    GPUBindGroup,
    GPUShaderModule,
    GPUShaderStage,
    GPUComputePipeline,
    GPURenderPipeline,
    GPUColorWrite,
    GPUCommandEncoder,
    GPURenderPassEncoder,
    GPUComputePassEncoder,
    GPUCommandBuffer,
    GPURenderBundleEncoder,
    GPURenderBundle,
    GPUQuerySet,
    GPUOutOfMemoryError,
    GPUValidationError,
  };
})(this);