diff options
author | crowlKats <13135287+crowlKats@users.noreply.github.com> | 2021-03-01 11:31:13 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-01 11:31:13 +0100 |
commit | 7cd14f97c9300357475e3e461fa57cbb7ec5bfec (patch) | |
tree | 39eb11e8a9c53001ffe814f5aac3ec5e37de6357 /op_crates/webgpu/01_webgpu.js | |
parent | dbdbe7a1cf0d56df85305eb3638bc177d8a0216f (diff) |
feat: WebGPU API (#7977)
Co-authored-by: Luca Casonato <lucacasonato@yahoo.com>
Diffstat (limited to 'op_crates/webgpu/01_webgpu.js')
-rw-r--r-- | op_crates/webgpu/01_webgpu.js | 5048 |
1 files changed, 5048 insertions, 0 deletions
diff --git a/op_crates/webgpu/01_webgpu.js b/op_crates/webgpu/01_webgpu.js new file mode 100644 index 000000000..7a078619e --- /dev/null +++ b/op_crates/webgpu/01_webgpu.js @@ -0,0 +1,5048 @@ +// 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; + + /** + * @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 (Array.isArray(data)) { + return { + width: data[0], + height: data[1], + depth: data[2], + }; + } else { + return data; + } + } + + /** + * @param {number[] | GPUOrigin3DDict} data + * @returns {GPUOrigin3DDict} + */ + function normalizeGPUOrigin3D(data) { + if (Array.isArray(data)) { + return { + x: data[0], + y: data[1], + z: data[2], + }; + } else { + return data; + } + } + + /** + * @param {number[] | GPUColor} data + * @returns {GPUColor} + */ + function normalizeGPUColor(data) { + if (Array.isArray(data)) { + return { + r: data[0], + g: data[1], + b: data[2], + a: data[3], + }; + } else { + return data; + } + } + + class GPUOutOfMemoryError extends Error { + constructor() { + super(); + } + } + + class GPUValidationError extends Error { + /** @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.jsonOpAsync( + "op_webgpu_request_adapter", + { ...options }, + ); + + if (err) { + return null; + } else { + return createGPUAdapter(data.name, data); + } + } + + [Symbol.for("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${inspect({})}`; + } + } + + const _name = Symbol("[[name]]"); + const _adapter = Symbol("[[adapter]]"); + const _cleanup = Symbol("[[cleanup]]"); + + /** + * @typedef InnerGPUAdapter + * @property {number} rid + * @property {GPUAdapterFeatures} features + * @property {GPUAdapterLimits} limits + */ + + /** + * @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: createGPUAdapterFeatures(inner.features), + limits: createGPUAdapterLimits(inner.limits), + }; + return adapter; + } + + class GPUAdapter { + /** @type {string} */ + [_name]; + /** @type {InnerGPUAdapter} */ + [_adapter]; + + /** @returns {string} */ + get name() { + webidl.assertBranded(this, GPUAdapter); + return this[_name]; + } + /** @returns {GPUAdapterFeatures} */ + get features() { + webidl.assertBranded(this, GPUAdapter); + return this[_adapter].features; + } + /** @returns {GPUAdapterLimits} */ + get limits() { + webidl.assertBranded(this, GPUAdapter); + return this[_adapter].limits; + } + + 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 nonGuaranteedFeatures = descriptor.nonGuaranteedFeatures ?? []; + for (const feature of nonGuaranteedFeatures) { + if (!this[_adapter].features.has(feature)) { + throw new TypeError( + `${prefix}: nonGuaranteedFeatures must be a subset of the adapter features.`, + ); + } + } + const nonGuaranteedLimits = descriptor.nonGuaranteedLimits ?? []; + // TODO(lucacasonato): validate nonGuaranteedLimits + + const { rid, features, limits } = await core.jsonOpAsync( + "op_webgpu_request_device", + { + adapterRid: this[_adapter].rid, + labe: descriptor.label, + nonGuaranteedFeatures, + nonGuaranteedLimits, + }, + ); + + const inner = new InnerGPUDevice({ + rid, + adapter: this, + features: Object.freeze(features), + limits: Object.freeze(limits), + }); + return createGPUDevice( + descriptor.label ?? null, + inner, + createGPUQueue(descriptor.label ?? null, inner), + ); + } + + [Symbol.for("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${ + inspect({ + name: this.name, + features: this.features, + limits: this.limits, + }) + }`; + } + } + + const _limits = Symbol("[[limits]]"); + + function createGPUAdapterLimits(features) { + /** @type {GPUAdapterLimits} */ + const adapterFeatures = webidl.createBranded(GPUAdapterLimits); + 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} maxVertexBuffers + * @property {number} maxVertexAttributes + * @property {number} maxVertexBufferArrayStride + */ + + class GPUAdapterLimits { + /** @type {InnerAdapterLimits} */ + [_limits]; + constructor() { + webidl.illegalConstructor(); + } + + get maxTextureDimension1D() { + throw new TypeError("Not yet implemented"); + } + get maxTextureDimension2D() { + throw new TypeError("Not yet implemented"); + } + get maxTextureDimension3D() { + throw new TypeError("Not yet implemented"); + } + get maxTextureArrayLayers() { + throw new TypeError("Not yet implemented"); + } + get maxBindGroups() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxBindGroups; + } + get maxDynamicUniformBuffersPerPipelineLayout() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxDynamicUniformBuffersPerPipelineLayout; + } + get maxDynamicStorageBuffersPerPipelineLayout() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxDynamicStorageBuffersPerPipelineLayout; + } + get maxSampledTexturesPerShaderStage() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxSampledTexturesPerShaderStage; + } + get maxSamplersPerShaderStage() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxSamplersPerShaderStage; + } + get maxStorageBuffersPerShaderStage() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxStorageBuffersPerShaderStage; + } + get maxStorageTexturesPerShaderStage() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxStorageTexturesPerShaderStage; + } + get maxUniformBuffersPerShaderStage() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxUniformBuffersPerShaderStage; + } + get maxUniformBufferBindingSize() { + webidl.assertBranded(this, GPUAdapterLimits); + return this[_limits].maxUniformBufferBindingSize; + } + get maxStorageBufferBindingSize() { + throw new TypeError("Not yet implemented"); + } + get maxVertexBuffers() { + throw new TypeError("Not yet implemented"); + } + get maxVertexAttributes() { + throw new TypeError("Not yet implemented"); + } + get maxVertexBufferArrayStride() { + throw new TypeError("Not yet implemented"); + } + + [Symbol.for("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${inspect(this[_limits])}`; + } + } + + const _features = Symbol("[[features]]"); + + function createGPUAdapterFeatures(features) { + /** @type {GPUAdapterFeatures} */ + const adapterFeatures = webidl.createBranded(GPUAdapterFeatures); + adapterFeatures[_features] = new Set(features); + return adapterFeatures; + } + + class GPUAdapterFeatures { + /** @type {Set<string>} */ + [_features]; + + constructor() { + webidl.illegalConstructor(); + } + + /** @return {IterableIterator<[string, string]>} */ + entries() { + webidl.assertBranded(this, GPUAdapterFeatures); + return this[_features].entries(); + } + + /** @return {void} */ + forEach(callbackfn, thisArg) { + webidl.assertBranded(this, GPUAdapterFeatures); + this[_features].forEach(callbackfn, thisArg); + } + + /** @return {boolean} */ + has(value) { + webidl.assertBranded(this, GPUAdapterFeatures); + return this[_features].has(value); + } + + /** @return {IterableIterator<string>} */ + keys() { + webidl.assertBranded(this, GPUAdapterFeatures); + return this[_features].keys(); + } + + /** @return {IterableIterator<string>} */ + values() { + webidl.assertBranded(this, GPUAdapterFeatures); + return this[_features].values(); + } + + /** @return {number} */ + get size() { + webidl.assertBranded(this, GPUAdapterFeatures); + return this[_features].size; + } + + [Symbol.iterator]() { + webidl.assertBranded(this, GPUAdapterFeatures); + return this[_features][Symbol.iterator](); + } + + [Symbol.for("Deno.customInspect")](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]; + } + + [Symbol.for("Deno.customInspect")](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; + Object.defineProperty(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 {GPUError | undefined} error + */ + + /** + * @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) { + this.resources.push(new WeakRef(resource)); + } + + /** @param {{ type: string, value: string | null } | undefined} err */ + pushError(err) { + if (err) { + switch (err.type) { + case "lost": + this.isLost = true; + this.resolveLost( + createGPUDeviceLostInfo(undefined, "device was lost"), + ); + break; + case "validation": + case "out-of-memory": + for ( + let i = this.errorScopeStack.length - 1; + i >= 0; + i-- + ) { + const scope = this.errorScopeStack[i]; + if (scope.filter == err.type) { + if (!scope.error) { + switch (err.type) { + case "validation": + scope.error = new GPUValidationError( + err.value ?? "validation error", + ); + break; + case "out-of-memory": + scope.error = new GPUOutOfMemoryError(); + break; + } + } + return; + } + } + // TODO(lucacasonato): emit a UncapturedErrorEvent + break; + } + } + } + } + + /** + * @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 = resources.pop()?.deref(); + if (resource) { + resource[_cleanup](); + } + } + const rid = device.rid; + if (rid !== undefined) { + core.close(rid); + /** @type {number | undefined} */ + device.rid = undefined; + } + } + + get adapter() { + webidl.assertBranded(this, GPUDevice); + return this[_device].adapter; + } + 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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync( + "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 = descriptor.bindGroupLayouts.map( + (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.jsonOpSync("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 = descriptor.entries.map((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.jsonOpSync("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.jsonOpSync( + "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.jsonOpSync( + "op_webgpu_create_compute_pipeline", + { + deviceRid: device.rid, + label: descriptor.label, + layout, + compute: { + module, + entryPoint: descriptor.compute.entryPoint, + }, + }, + ); + 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.jsonOpSync("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 Promise.resolve(this.createComputePipeline(descriptor)); + } + + createRenderPipelineAsync(descriptor) { + // TODO(lucacasonato): this should be real async + return Promise.resolve(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.jsonOpSync("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.jsonOpSync( + "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.jsonOpSync("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 Promise.resolve(true); + } + if (device.rid === undefined) { + return Promise.resolve(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" }); + device.errorScopeStack.push({ filter, error: undefined }); + } + + /** + * @returns {Promise<GPUError | null>} + */ + // deno-lint-ignore require-await + async popErrorScope() { + webidl.assertBranded(this, GPUDevice); + const prefix = "Failed to execute 'pushErrorScope' on 'GPUDevice'"; + const device = assertDevice(this, { prefix, context: "this" }); + if (device.isLost) { + throw new DOMException("Device has been lost.", "OperationError"); + } + const scope = device.errorScopeStack.pop(); + if (!scope) { + throw new DOMException( + "There are no error scopes on that stack.", + "OperationError", + ); + } + return scope.error ?? null; + } + + [Symbol.for("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${ + inspect({ + adapter: this.adapter, + 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 = commandBuffers.map((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.jsonOpSync("op_webgpu_queue_submit", { + queueRid: device.rid, + commandBuffers: commandBufferRids, + }); + device.pushError(err); + } + + onSubmittedWorkDone() { + webidl.assertBranded(this, GPUQueue); + return Promise.resolve(); + } + + /** + * @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.jsonOpSync( + "op_webgpu_write_buffer", + { + queueRid: device.rid, + buffer: bufferRid, + bufferOffset, + dataOffset, + size, + }, + new Uint8Array(ArrayBuffer.isView(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.jsonOpSync( + "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(ArrayBuffer.isView(data) ? data.buffer : data), + ); + device.pushError(err); + } + + copyImageBitmapToTexture(_source, _destination, _copySize) { + throw new Error("Not yet implemented"); + } + + [Symbol.for("Deno.customInspect")](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 = mappedRanges.pop(); + 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 = Math.max(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 { err } = await core.jsonOpAsync( + "op_webgpu_buffer_get_map_async", + { + bufferRid, + deviceRid: device.rid, + mode, + offset, + size: rangeSize, + }, + ); + device.pushError(err); + 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", + }); + size = size === undefined + ? undefined + : 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 = Math.max(0, this[_size] - offset); + } else { + rangeSize = this[_size]; + } + if (this[_state] !== "mapped" && this[_state] !== "mapped at creation") { + throw new DOMException( + `${prefix}: buffer is not mapped.`, + "OperationError", + ); + } + 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", + ); + } + const mappingRange = this[_mappingRange]; + if (!mappingRange) { + throw new DOMException(`${prefix}: invalid state.`, "OperationError"); + } + if (offset < mappingRange[0]) { + throw new DOMException( + `${prefix}: offset is out of bounds.`, + "OperationError", + ); + } + if ((offset + rangeSize) > mappingRange[1]) { + throw new DOMException( + `${prefix}: offset is out of bounds.`, + "OperationError", + ); + } + 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.jsonOpSync( + "op_webgpu_buffer_get_mapped_range", + { + bufferRid, + offset: offset - mappingRange[0], + size: rangeSize, + }, + new Uint8Array(buffer), + ); + + mappedRanges.push([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.jsonOpSync("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](); + } + + [Symbol.for("Deno.customInspect")](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 = views.pop()?.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.jsonOpSync("op_webgpu_create_texture_view", { + textureRid, + ...descriptor, + }); + device.pushError(err); + + const textureView = createGPUTextureView( + descriptor.label ?? null, + this, + rid, + ); + this[_views].push(new WeakRef(textureView)); + return textureView; + } + + destroy() { + webidl.assertBranded(this, GPUTexture); + this[_cleanup](); + } + + [Symbol.for("Deno.customInspect")](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(); + } + + [Symbol.for("Deno.customInspect")](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(); + } + + [Symbol.for("Deno.customInspect")](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(); + } + + [Symbol.for("Deno.customInspect")](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(); + } + + [Symbol.for("Deno.customInspect")](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(); + } + + [Symbol.for("Deno.customInspect")](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"); + } + + [Symbol.for("Deno.customInspect")](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, GPURenderPipeline); + 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.jsonOpSync( + "op_webgpu_compute_pipeline_get_bind_group_layout", + { computePipelineRid, index }, + ); + device.pushError(err); + + const bindGroupLayout = createGPUBindGroupLayout( + label, + device, + rid, + ); + device.trackResource((bindGroupLayout)); + return bindGroupLayout; + } + + [Symbol.for("Deno.customInspect")](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.jsonOpSync( + "op_webgpu_render_pipeline_get_bind_group_layout", + { renderPipelineRid, index }, + ); + device.pushError(err); + + const bindGroupLayout = createGPUBindGroupLayout( + label, + device, + rid, + ); + device.trackResource((bindGroupLayout)); + return bindGroupLayout; + } + + [Symbol.for("Deno.customInspect")](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 = encoders.pop()?.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 = descriptor.colorAttachments.map( + (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.jsonOpSync( + "op_webgpu_command_encoder_begin_render_pass", + { + commandEncoderRid, + ...descriptor, + colorAttachments, + depthStencilAttachment, + }, + ); + + const renderPassEncoder = createGPURenderPassEncoder( + descriptor.label ?? null, + this, + rid, + ); + this[_encoders].push(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.jsonOpSync( + "op_webgpu_command_encoder_begin_compute_pass", + { + commandEncoderRid, + ...descriptor, + }, + ); + + const computePassEncoder = createGPUComputePassEncoder( + descriptor.label ?? null, + this, + rid, + ); + this[_encoders].push(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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync( + "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.jsonOpSync("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; + } + + [Symbol.for("Deno.customInspect")](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.jsonOpSync("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.jsonOpSync("op_webgpu_render_pass_set_scissor_rect", { + renderPassRid, + x, + y, + width, + height, + }); + } + + /** + * @param {GPUColor} color + */ + setBlendColor(color) { + webidl.assertBranded(this, GPURenderPassEncoder); + const prefix = + "Failed to execute 'setBlendColor' 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.jsonOpSync("op_webgpu_render_pass_set_blend_color", { + 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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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 = bundles.map((bundle, i) => { + const context = `bundle ${i + 1}`; + const rid = assertResource(bundle, { prefix, context }); + assertDeviceMatch(device, bundle, { + prefix, + resourceContext: context, + selfContext: "this", + }); + return rid; + }); + core.jsonOpSync("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.jsonOpSync("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.jsonOpSync( + "op_webgpu_render_pass_set_bind_group", + { + renderPassRid, + index, + bindGroup: bindGroupRid, + dynamicOffsetsDataStart, + dynamicOffsetsDataLength, + }, + dynamicOffsetsData, + ); + } else { + dynamicOffsetsData ??= []; + core.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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 = 0) { + 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", + }); + 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.jsonOpSync("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 = 0) { + 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", + }); + 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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("op_webgpu_render_pass_draw_indexed_indirect", { + renderPassRid, + indirectBuffer: indirectBufferRid, + indirectOffset, + }); + } + + [Symbol.for("Deno.customInspect")](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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync( + "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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync( + "op_webgpu_compute_pass_set_bind_group", + { + computePassRid, + index, + bindGroup: bindGroupRid, + dynamicOffsetsDataStart, + dynamicOffsetsDataLength, + }, + dynamicOffsetsData, + ); + } else { + dynamicOffsetsData ??= []; + core.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("op_webgpu_compute_pass_insert_debug_marker", { + computePassRid, + markerLabel, + }); + } + + [Symbol.for("Deno.customInspect")](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"); + } + + [Symbol.for("Deno.customInspect")](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.jsonOpSync( + "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.jsonOpSync( + "op_webgpu_render_bundle_encoder_set_bind_group", + { + renderBundleEncoderRid, + index, + bindGroup: bindGroupRid, + dynamicOffsetsDataStart, + dynamicOffsetsDataLength, + }, + dynamicOffsetsData, + ); + } else { + dynamicOffsetsData ??= []; + core.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("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.jsonOpSync("op_webgpu_render_bundle_encoder_draw_indirect", { + renderBundleEncoderRid, + indirectBuffer: indirectBufferRid, + indirectOffset, + }); + } + + drawIndexedIndirect(_indirectBuffer, _indirectOffset) { + throw new Error("Not yet implemented"); + } + + [Symbol.for("Deno.customInspect")](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(); + } + + [Symbol.for("Deno.customInspect")](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](); + } + + [Symbol.for("Deno.customInspect")](inspect) { + return `${this.constructor.name} ${ + inspect({ + label: this.label, + }) + }`; + } + } + GPUObjectBaseMixin("GPUQuerySet", GPUQuerySet); + + window.__bootstrap.webgpu = { + gpu: webidl.createBranded(GPU), + GPU, + GPUAdapter, + GPUAdapterLimits, + GPUAdapterFeatures, + 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); |