1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

BREAKING(unstable): remove WebGPU (#18094)

This PR _**temporarily**_ removes WebGPU (which has behind the
`--unstable` flag in Deno), due to performance complications due to its
presence.

It will be brought back in the future; as a point of reference, Chrome
will ship WebGPU to stable on 26/04/2023.

---------

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Leo Kettmeir 2023-03-16 19:29:32 -04:00 committed by GitHub
parent 3f031ad9af
commit 35196eab27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 145 additions and 15304 deletions

648
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,6 @@ members = [
"ext/node",
"ext/url",
"ext/web",
"ext/webgpu",
"ext/webidl",
"ext/websocket",
"ext/webstorage",
@ -71,7 +70,6 @@ deno_node = { version = "0.30.0", path = "./ext/node" }
deno_tls = { version = "0.80.0", path = "./ext/tls" }
deno_url = { version = "0.93.0", path = "./ext/url" }
deno_web = { version = "0.124.0", path = "./ext/web" }
deno_webgpu = { version = "0.94.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.93.0", path = "./ext/webidl" }
deno_websocket = { version = "0.98.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.88.0", path = "./ext/webstorage" }
@ -136,11 +134,6 @@ zstd = "=0.11.2"
# crypto
rsa = { version = "0.7.0", default-features = false, features = ["std", "pem"] }
# webgpu
raw-window-handle = "0.5.0"
wgpu-core = "0.15"
wgpu-types = "0.15"
# macros
proc-macro2 = "1"
quote = "1"

View file

@ -602,7 +602,6 @@ static ENV_VARIABLES_HELP: &str = r#"ENVIRONMENT VARIABLES:
DENO_NO_UPDATE_CHECK Set to disable checking if a newer Deno version is
available
DENO_V8_FLAGS Set V8 command line options
DENO_WEBGPU_TRACE Directory to use for wgpu traces
DENO_JOBS Number of parallel workers used for the --parallel
flag with the test subcommand. Defaults to number
of available CPUs.

View file

@ -1,7 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use std::env;
use std::path::Path;
use std::path::PathBuf;
use deno_core::include_js_files;
@ -15,7 +14,6 @@ use deno_runtime::*;
mod ts {
use super::*;
use crate::deno_webgpu_get_declaration;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use deno_core::op;
@ -43,7 +41,6 @@ mod ts {
op_crate_libs.insert("deno.url", deno_url::get_declaration());
op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
op_crate_libs.insert("deno.webgpu", deno_webgpu_get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
@ -323,7 +320,6 @@ fn create_cli_snapshot(snapshot_path: PathBuf) {
deno_websocket::init_ops::<PermissionsContainer>("".to_owned(), None, None),
deno_webstorage::init_ops(None),
deno_crypto::init_ops(None),
deno_webgpu::init_ops(false),
deno_broadcast_channel::init_ops(
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
false, // No --unstable.
@ -489,11 +485,3 @@ fn main() {
res.compile().unwrap();
}
}
fn deno_webgpu_get_declaration() -> PathBuf {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
manifest_dir
.join("tsc")
.join("dts")
.join("lib.deno_webgpu.d.ts")
}

View file

@ -124,9 +124,6 @@ const OP_DETAILS = {
"op_tls_start": ["start a TLS connection", "awaiting a `Deno.startTls` call"],
"op_truncate_async": ["truncate a file", "awaiting the result of a `Deno.truncate` call"],
"op_utime_async": ["change file timestamps", "awaiting the result of a `Deno.utime` call"],
"op_webgpu_buffer_get_map_async": ["map a WebGPU buffer", "awaiting the result of a `GPUBuffer#mapAsync` call"],
"op_webgpu_request_adapter": ["request a WebGPU adapter", "awaiting the result of a `navigator.gpu.requestAdapter` call"],
"op_webgpu_request_device": ["request a WebGPU device", "awaiting the result of a `GPUAdapter#requestDevice` call"],
"op_worker_recv_message": ["receive a message from a web worker", "terminating a `Worker`"],
"op_ws_close": ["close a WebSocket", "awaiting until the `close` event is emitted on a `WebSocket`, or the `WebSocketStream#closed` promise resolves"],
"op_ws_create": ["create a WebSocket", "awaiting until the `open` event is emitted on a `WebSocket`, or the result of a `WebSocketStream#connection` promise"],

View file

@ -1,38 +0,0 @@
@group(0)
@binding(0)
var<storage, read_write> v_indices: array<u32>; // this is used as both input and output for convenience
// The Collatz Conjecture states that for any integer n:
// If n is even, n = n/2
// If n is odd, n = 3n+1
// And repeat this process for each new n, you will always eventually reach 1.
// Though the conjecture has not been proven, no counterexample has ever been found.
// This function returns how many times this recurrence needs to be applied to reach 1.
fn collatz_iterations(n_base: u32) -> u32{
var n: u32 = n_base;
var i: u32 = 0u;
loop {
if (n <= 1u) {
break;
}
if (n % 2u == 0u) {
n = n / 2u;
}
else {
// Overflow? (i.e. 3*n + 1 > 0xffffffffu?)
if (n >= 1431655765u) { // 0x55555555u
return 4294967295u; // 0xffffffffu
}
n = 3u * n + 1u;
}
i = i + 1u;
}
return i;
}
@compute
@workgroup_size(1)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
v_indices[global_id.x] = collatz_iterations(v_indices[global_id.x]);
}

Binary file not shown.

View file

@ -1,11 +0,0 @@
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(i32(in_vertex_index) - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return vec4<f32>(x, y, 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}

View file

@ -1,242 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "./test_util.ts";
let isCI: boolean;
try {
isCI = (Deno.env.get("CI")?.length ?? 0) > 0;
} catch {
isCI = true;
}
// Skip these tests on linux CI, because the vulkan emulator is not good enough
// yet, and skip on macOS CI because these do not have virtual GPUs.
const isLinuxOrMacCI =
(Deno.build.os === "linux" || Deno.build.os === "darwin") && isCI;
// Skip these tests in WSL because it doesn't have good GPU support.
const isWsl = await checkIsWsl();
Deno.test({
permissions: { read: true, env: true },
ignore: isWsl || isLinuxOrMacCI,
}, async function webgpuComputePass() {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter);
const numbers = [1, 4, 3, 295];
const device = await adapter.requestDevice();
assert(device);
const shaderCode = await Deno.readTextFile(
"cli/tests/testdata/webgpu/computepass_shader.wgsl",
);
const shaderModule = device.createShaderModule({
code: shaderCode,
});
const size = new Uint32Array(numbers).byteLength;
const stagingBuffer = device.createBuffer({
size: size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
const storageBuffer = device.createBuffer({
label: "Storage Buffer",
size: size,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST |
GPUBufferUsage.COPY_SRC,
mappedAtCreation: true,
});
const buf = new Uint32Array(storageBuffer.getMappedRange());
buf.set(numbers);
storageBuffer.unmap();
const computePipeline = device.createComputePipeline({
layout: "auto",
compute: {
module: shaderModule,
entryPoint: "main",
},
});
const bindGroupLayout = computePipeline.getBindGroupLayout(0);
const bindGroup = device.createBindGroup({
layout: bindGroupLayout,
entries: [
{
binding: 0,
resource: {
buffer: storageBuffer,
},
},
],
});
const encoder = device.createCommandEncoder();
const computePass = encoder.beginComputePass();
computePass.setPipeline(computePipeline);
computePass.setBindGroup(0, bindGroup);
computePass.insertDebugMarker("compute collatz iterations");
computePass.dispatchWorkgroups(numbers.length);
computePass.end();
encoder.copyBufferToBuffer(storageBuffer, 0, stagingBuffer, 0, size);
device.queue.submit([encoder.finish()]);
await stagingBuffer.mapAsync(1);
const data = stagingBuffer.getMappedRange();
assertEquals(new Uint32Array(data), new Uint32Array([0, 2, 7, 55]));
stagingBuffer.unmap();
device.destroy();
// TODO(lucacasonato): webgpu spec should add a explicit destroy method for
// adapters.
const resources = Object.keys(Deno.resources());
Deno.close(Number(resources[resources.length - 1]));
});
Deno.test({
permissions: { read: true, env: true },
ignore: isWsl || isLinuxOrMacCI,
}, async function webgpuHelloTriangle() {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter);
const device = await adapter.requestDevice();
assert(device);
const shaderCode = await Deno.readTextFile(
"cli/tests/testdata/webgpu/hellotriangle_shader.wgsl",
);
const shaderModule = device.createShaderModule({
code: shaderCode,
});
const pipelineLayout = device.createPipelineLayout({
bindGroupLayouts: [],
});
const renderPipeline = device.createRenderPipeline({
layout: pipelineLayout,
vertex: {
module: shaderModule,
entryPoint: "vs_main",
},
fragment: {
module: shaderModule,
entryPoint: "fs_main",
targets: [
{
format: "rgba8unorm-srgb",
},
],
},
});
const dimensions = {
width: 200,
height: 200,
};
const unpaddedBytesPerRow = dimensions.width * 4;
const align = 256;
const paddedBytesPerRowPadding = (align - unpaddedBytesPerRow % align) %
align;
const paddedBytesPerRow = unpaddedBytesPerRow + paddedBytesPerRowPadding;
const outputBuffer = device.createBuffer({
label: "Capture",
size: paddedBytesPerRow * dimensions.height,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
const texture = device.createTexture({
label: "Capture",
size: dimensions,
format: "rgba8unorm-srgb",
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
});
const encoder = device.createCommandEncoder();
const view = texture.createView();
const renderPass = encoder.beginRenderPass({
colorAttachments: [
{
view,
storeOp: "store",
loadOp: "clear",
clearValue: [0, 1, 0, 1],
},
],
});
renderPass.setPipeline(renderPipeline);
renderPass.draw(3, 1);
renderPass.end();
encoder.copyTextureToBuffer(
{
texture,
},
{
buffer: outputBuffer,
bytesPerRow: paddedBytesPerRow,
rowsPerImage: 0,
},
dimensions,
);
const bundle = encoder.finish();
device.queue.submit([bundle]);
await outputBuffer.mapAsync(1);
const data = new Uint8Array(outputBuffer.getMappedRange());
assertEquals(
data,
await Deno.readFile("cli/tests/testdata/webgpu/hellotriangle.out"),
);
outputBuffer.unmap();
device.destroy();
// TODO(lucacasonato): webgpu spec should add a explicit destroy method for
// adapters.
const resources = Object.keys(Deno.resources());
Deno.close(Number(resources[resources.length - 1]));
});
Deno.test({
ignore: isWsl || isLinuxOrMacCI,
}, async function webgpuAdapterHasFeatures() {
const adapter = await navigator.gpu.requestAdapter();
assert(adapter);
assert(adapter.features);
const resources = Object.keys(Deno.resources());
Deno.close(Number(resources[resources.length - 1]));
});
async function checkIsWsl() {
return Deno.build.os === "linux" && await hasMicrosoftProcVersion();
async function hasMicrosoftProcVersion() {
// https://github.com/microsoft/WSL/issues/423#issuecomment-221627364
try {
const procVersion = await Deno.readTextFile("/proc/version");
return /microsoft/i.test(procVersion);
} catch {
return false;
}
}
}

View file

@ -3,7 +3,6 @@
/// <reference no-default-lib="true" />
/// <reference lib="deno.ns" />
/// <reference lib="deno.shared_globals" />
/// <reference lib="deno.webgpu" />
/// <reference lib="deno.webstorage" />
/// <reference lib="esnext" />
/// <reference lib="deno.cache" />
@ -94,7 +93,6 @@ declare var caches: CacheStorage;
/** @category Web APIs */
declare class Navigator {
constructor();
readonly gpu: GPU;
readonly hardwareConcurrency: number;
readonly userAgent: string;
readonly language: string;

View file

@ -3,7 +3,6 @@
/// <reference no-default-lib="true" />
/// <reference lib="deno.ns" />
/// <reference lib="deno.shared_globals" />
/// <reference lib="deno.webgpu" />
/// <reference lib="esnext" />
/// <reference lib="deno.cache" />
@ -58,7 +57,6 @@ declare class WorkerGlobalScope extends EventTarget {
/** @category Web APIs */
declare class WorkerNavigator {
constructor();
readonly gpu: GPU;
readonly hardwareConcurrency: number;
readonly userAgent: string;
readonly language: string;

File diff suppressed because it is too large Load diff

View file

@ -89,7 +89,6 @@ pub fn get_types_declaration_file_text(unstable: bool) -> String {
"deno.url",
"deno.web",
"deno.fetch",
"deno.webgpu",
"deno.websocket",
"deno.webstorage",
"deno.crypto",

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,146 +0,0 @@
// Copyright 2018-2023 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" />
const core = globalThis.Deno.core;
const ops = core.ops;
import * as webidl from "ext:deno_webidl/00_webidl.js";
const primordials = globalThis.__bootstrap.primordials;
const { Symbol } = primordials;
import {
_device,
assertDevice,
createGPUTexture,
} from "ext:deno_webgpu/01_webgpu.js";
const _surfaceRid = Symbol("[[surfaceRid]]");
const _configuration = Symbol("[[configuration]]");
const _canvas = Symbol("[[canvas]]");
const _currentTexture = Symbol("[[currentTexture]]");
class GPUCanvasContext {
/** @type {number} */
[_surfaceRid];
/** @type {InnerGPUDevice} */
[_device];
[_configuration];
[_canvas];
/** @type {GPUTexture | undefined} */
[_currentTexture];
get canvas() {
webidl.assertBranded(this, GPUCanvasContextPrototype);
return this[_canvas];
}
constructor() {
webidl.illegalConstructor();
}
configure(configuration) {
webidl.assertBranded(this, GPUCanvasContextPrototype);
const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
webidl.requiredArguments(arguments.length, 1, { prefix });
configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
prefix,
context: "Argument 1",
});
this[_device] = configuration.device[_device];
this[_configuration] = configuration;
const device = assertDevice(this, {
prefix,
context: "configuration.device",
});
const { err } = ops.op_webgpu_surface_configure({
surfaceRid: this[_surfaceRid],
deviceRid: device.rid,
format: configuration.format,
viewFormats: configuration.viewFormats,
usage: configuration.usage,
width: configuration.width,
height: configuration.height,
alphaMode: configuration.alphaMode,
});
device.pushError(err);
}
unconfigure() {
webidl.assertBranded(this, GPUCanvasContextPrototype);
this[_configuration] = null;
this[_device] = null;
}
getCurrentTexture() {
webidl.assertBranded(this, GPUCanvasContextPrototype);
const prefix =
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
if (this[_configuration] === null) {
throw new DOMException(
"context is not configured.",
"InvalidStateError",
);
}
const device = assertDevice(this, { prefix, context: "this" });
if (this[_currentTexture]) {
return this[_currentTexture];
}
const { rid } = ops.op_webgpu_surface_get_current_texture(
device.rid,
this[_surfaceRid],
);
const texture = createGPUTexture(
{
size: {
width: this[_configuration].width,
height: this[_configuration].height,
depthOrArrayLayers: 1,
},
mipLevelCount: 1,
sampleCount: 1,
dimension: "2d",
format: this[_configuration].format,
usage: this[_configuration].usage,
},
device,
rid,
);
device.trackResource(texture);
this[_currentTexture] = texture;
return texture;
}
// Extended from spec. Required to present the texture; browser don't need this.
present() {
webidl.assertBranded(this, GPUCanvasContextPrototype);
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
const device = assertDevice(this[_currentTexture], {
prefix,
context: "this",
});
ops.op_webgpu_surface_present(device.rid, this[_surfaceRid]);
this[_currentTexture].destroy();
this[_currentTexture] = undefined;
}
}
const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
function createCanvasContext(options) {
const canvasContext = webidl.createBranded(GPUCanvasContext);
canvasContext[_surfaceRid] = options.surfaceRid;
canvasContext[_canvas] = options.canvas;
return canvasContext;
}
export { createCanvasContext, GPUCanvasContext };

View file

@ -1,82 +0,0 @@
// Copyright 2018-2023 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" />
import * as webidl from "ext:deno_webidl/00_webidl.js";
import { GPUTextureUsage } from "ext:deno_webgpu/01_webgpu.js";
// ENUM: GPUCanvasAlphaMode
webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
"GPUCanvasAlphaMode",
[
"opaque",
"premultiplied",
],
);
// NON-SPEC: ENUM: GPUPresentMode
webidl.converters["GPUPresentMode"] = webidl.createEnumConverter(
"GPUPresentMode",
[
"autoVsync",
"autoNoVsync",
"fifo",
"fifoRelaxed",
"immediate",
"mailbox",
],
);
// DICT: GPUCanvasConfiguration
const dictMembersGPUCanvasConfiguration = [
{ key: "device", converter: webidl.converters.GPUDevice, required: true },
{
key: "format",
converter: webidl.converters.GPUTextureFormat,
required: true,
},
{
key: "usage",
converter: webidl.converters["GPUTextureUsageFlags"],
defaultValue: GPUTextureUsage.RENDER_ATTACHMENT,
},
{
key: "alphaMode",
converter: webidl.converters["GPUCanvasAlphaMode"],
defaultValue: "opaque",
},
// Extended from spec
{
key: "presentMode",
converter: webidl.converters["GPUPresentMode"],
},
{
key: "width",
converter: webidl.converters["long"],
required: true,
},
{
key: "height",
converter: webidl.converters["long"],
required: true,
},
{
key: "viewFormats",
converter: webidl.createSequenceConverter(
webidl.converters["GPUTextureFormat"],
),
get defaultValue() {
return [];
},
},
];
webidl.converters["GPUCanvasConfiguration"] = webidl
.createDictionaryConverter(
"GPUCanvasConfiguration",
dictMembersGPUCanvasConfiguration,
);

View file

@ -1,43 +0,0 @@
# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
[package]
name = "deno_webgpu"
version = "0.94.0"
authors = ["the Deno authors"]
edition.workspace = true
license = "MIT"
readme = "README.md"
repository = "https://github.com/gfx-rs/wgpu"
description = "WebGPU implementation for Deno"
[lib]
path = "lib.rs"
[features]
surface = ["wgpu-core/raw-window-handle", "dep:raw-window-handle"]
[dependencies]
deno_core.workspace = true
raw-window-handle = { workspace = true, optional = true }
serde.workspace = true
tokio.workspace = true
wgpu-types = { workspace = true, features = ["trace", "replay", "serde"] }
[dependencies.wgpu-core]
workspace = true
features = ["trace", "replay", "serde", "strict_asserts", "wgsl", "gles"]
# We want the wgpu-core Metal backend on macOS and iOS.
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgpu-core]
workspace = true
features = ["metal"]
# We want the wgpu-core Direct3D backends on Windows.
[target.'cfg(windows)'.dependencies.wgpu-core]
workspace = true
features = ["dx11", "dx12"]
# We want the wgpu-core Vulkan backend on Unix (but not Emscripten) and Windows.
[target.'cfg(any(windows, all(unix, not(target_arch = "emscripten"))))'.dependencies.wgpu-core]
workspace = true
features = ["vulkan"]

View file

@ -1,20 +0,0 @@
MIT License
Copyright 2018-2023 the Deno authors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,35 +0,0 @@
# deno_webgpu
This op crate implements the WebGPU API as defined in
https://gpuweb.github.io/gpuweb/ in Deno. The implementation targets the spec
draft as of February 22, 2021. The spec is still very much in flux. This op
crate tries to stay up to date with the spec, but is constrained by the features
implemented in our GPU backend library [wgpu](https://github.com/gfx-rs/wgpu).
The spec is still very bare bones, and is still missing many details. As the
spec becomes more concrete, we will implement to follow the spec more closely.
In addition, setting the `DENO_WEBGPU_TRACE` environmental variable will output
a
[wgpu trace](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications#tracing-infrastructure)
to the specified directory.
For testing this op crate will make use of the WebGPU conformance tests suite,
running through our WPT runner. This will be used to validate implementation
conformance.
GitHub CI doesn't run with GPUs, so testing relies on software like DX WARP &
Vulkan lavapipe. Currently only using DX WARP works, so tests are only run on
Windows.
## Links
Specification: https://gpuweb.github.io/gpuweb/
Design documents: https://github.com/gpuweb/gpuweb/tree/main/design
Conformance tests suite: https://github.com/gpuweb/cts
WebGPU examples for Deno: https://github.com/crowlKats/webgpu-examples
wgpu-users matrix channel: https://matrix.to/#/#wgpu-users:matrix.org

View file

@ -1,322 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use std::borrow::Cow;
use super::error::WebGpuResult;
pub(crate) struct WebGpuBindGroupLayout(
pub(crate) wgpu_core::id::BindGroupLayoutId,
);
impl Resource for WebGpuBindGroupLayout {
fn name(&self) -> Cow<str> {
"webGPUBindGroupLayout".into()
}
}
pub(crate) struct WebGpuBindGroup(pub(crate) wgpu_core::id::BindGroupId);
impl Resource for WebGpuBindGroup {
fn name(&self) -> Cow<str> {
"webGPUBindGroup".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuBufferBindingLayout {
r#type: GpuBufferBindingType,
has_dynamic_offset: bool,
min_binding_size: u64,
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
enum GpuBufferBindingType {
Uniform,
Storage,
ReadOnlyStorage,
}
impl From<GpuBufferBindingType> for wgpu_types::BufferBindingType {
fn from(binding_type: GpuBufferBindingType) -> Self {
match binding_type {
GpuBufferBindingType::Uniform => wgpu_types::BufferBindingType::Uniform,
GpuBufferBindingType::Storage => {
wgpu_types::BufferBindingType::Storage { read_only: false }
}
GpuBufferBindingType::ReadOnlyStorage => {
wgpu_types::BufferBindingType::Storage { read_only: true }
}
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuSamplerBindingLayout {
r#type: wgpu_types::SamplerBindingType,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuTextureBindingLayout {
sample_type: GpuTextureSampleType,
view_dimension: wgpu_types::TextureViewDimension,
multisampled: bool,
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
enum GpuTextureSampleType {
Float,
UnfilterableFloat,
Depth,
Sint,
Uint,
}
impl From<GpuTextureSampleType> for wgpu_types::TextureSampleType {
fn from(sample_type: GpuTextureSampleType) -> Self {
match sample_type {
GpuTextureSampleType::Float => {
wgpu_types::TextureSampleType::Float { filterable: true }
}
GpuTextureSampleType::UnfilterableFloat => {
wgpu_types::TextureSampleType::Float { filterable: false }
}
GpuTextureSampleType::Depth => wgpu_types::TextureSampleType::Depth,
GpuTextureSampleType::Sint => wgpu_types::TextureSampleType::Sint,
GpuTextureSampleType::Uint => wgpu_types::TextureSampleType::Uint,
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuStorageTextureBindingLayout {
access: GpuStorageTextureAccess,
format: wgpu_types::TextureFormat,
view_dimension: wgpu_types::TextureViewDimension,
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
enum GpuStorageTextureAccess {
WriteOnly,
}
impl From<GpuStorageTextureAccess> for wgpu_types::StorageTextureAccess {
fn from(access: GpuStorageTextureAccess) -> Self {
match access {
GpuStorageTextureAccess::WriteOnly => {
wgpu_types::StorageTextureAccess::WriteOnly
}
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuBindGroupLayoutEntry {
binding: u32,
visibility: u32,
#[serde(flatten)]
binding_type: GpuBindingType,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
enum GpuBindingType {
Buffer(GpuBufferBindingLayout),
Sampler(GpuSamplerBindingLayout),
Texture(GpuTextureBindingLayout),
StorageTexture(GpuStorageTextureBindingLayout),
}
impl From<GpuBindingType> for wgpu_types::BindingType {
fn from(binding_type: GpuBindingType) -> wgpu_types::BindingType {
match binding_type {
GpuBindingType::Buffer(buffer) => wgpu_types::BindingType::Buffer {
ty: buffer.r#type.into(),
has_dynamic_offset: buffer.has_dynamic_offset,
min_binding_size: std::num::NonZeroU64::new(buffer.min_binding_size),
},
GpuBindingType::Sampler(sampler) => {
wgpu_types::BindingType::Sampler(sampler.r#type)
}
GpuBindingType::Texture(texture) => wgpu_types::BindingType::Texture {
sample_type: texture.sample_type.into(),
view_dimension: texture.view_dimension,
multisampled: texture.multisampled,
},
GpuBindingType::StorageTexture(storage_texture) => {
wgpu_types::BindingType::StorageTexture {
access: storage_texture.access.into(),
format: storage_texture.format,
view_dimension: storage_texture.view_dimension,
}
}
}
}
}
#[op]
pub fn op_webgpu_create_bind_group_layout(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
entries: Vec<GpuBindGroupLayoutEntry>,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let entries = entries
.into_iter()
.map(|entry| {
wgpu_types::BindGroupLayoutEntry {
binding: entry.binding,
visibility: wgpu_types::ShaderStages::from_bits(entry.visibility)
.unwrap(),
ty: entry.binding_type.into(),
count: None, // native-only
}
})
.collect::<Vec<_>>();
let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
label: label.map(Cow::from),
entries: Cow::from(entries),
};
gfx_put!(device => instance.device_create_bind_group_layout(
device,
&descriptor,
()
) => state, WebGpuBindGroupLayout)
}
#[op]
pub fn op_webgpu_create_pipeline_layout(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
bind_group_layouts: Vec<u32>,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let bind_group_layouts = bind_group_layouts
.into_iter()
.map(|rid| {
let bind_group_layout =
state.resource_table.get::<WebGpuBindGroupLayout>(rid)?;
Ok(bind_group_layout.0)
})
.collect::<Result<Vec<_>, AnyError>>()?;
let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
label: label.map(Cow::from),
bind_group_layouts: Cow::from(bind_group_layouts),
push_constant_ranges: Default::default(),
};
gfx_put!(device => instance.device_create_pipeline_layout(
device,
&descriptor,
()
) => state, super::pipeline::WebGpuPipelineLayout)
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuBindGroupEntry {
binding: u32,
kind: String,
resource: ResourceId,
offset: Option<u64>,
size: Option<u64>,
}
#[op]
pub fn op_webgpu_create_bind_group(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
layout: ResourceId,
entries: Vec<GpuBindGroupEntry>,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let entries = entries
.into_iter()
.map(|entry| {
Ok(wgpu_core::binding_model::BindGroupEntry {
binding: entry.binding,
resource: match entry.kind.as_str() {
"GPUSampler" => {
let sampler_resource =
state
.resource_table
.get::<super::sampler::WebGpuSampler>(entry.resource)?;
wgpu_core::binding_model::BindingResource::Sampler(
sampler_resource.0,
)
}
"GPUTextureView" => {
let texture_view_resource =
state
.resource_table
.get::<super::texture::WebGpuTextureView>(entry.resource)?;
wgpu_core::binding_model::BindingResource::TextureView(
texture_view_resource.0,
)
}
"GPUBufferBinding" => {
let buffer_resource =
state
.resource_table
.get::<super::buffer::WebGpuBuffer>(entry.resource)?;
wgpu_core::binding_model::BindingResource::Buffer(
wgpu_core::binding_model::BufferBinding {
buffer_id: buffer_resource.0,
offset: entry.offset.unwrap_or(0),
size: std::num::NonZeroU64::new(entry.size.unwrap_or(0)),
},
)
}
_ => unreachable!(),
},
})
})
.collect::<Result<Vec<_>, AnyError>>()?;
let bind_group_layout =
state.resource_table.get::<WebGpuBindGroupLayout>(layout)?;
let descriptor = wgpu_core::binding_model::BindGroupDescriptor {
label: label.map(Cow::from),
layout: bind_group_layout.0,
entries: Cow::from(entries),
};
gfx_put!(device => instance.device_create_bind_group(
device,
&descriptor,
()
) => state, WebGpuBindGroup)
}

View file

@ -1,197 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::futures::channel::oneshot;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use std::borrow::Cow;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::rc::Rc;
use std::time::Duration;
use wgpu_core::resource::BufferAccessResult;
use super::error::DomExceptionOperationError;
use super::error::WebGpuResult;
pub(crate) struct WebGpuBuffer(pub(crate) wgpu_core::id::BufferId);
impl Resource for WebGpuBuffer {
fn name(&self) -> Cow<str> {
"webGPUBuffer".into()
}
}
struct WebGpuBufferMapped(*mut u8, usize);
impl Resource for WebGpuBufferMapped {
fn name(&self) -> Cow<str> {
"webGPUBufferMapped".into()
}
}
#[op]
pub fn op_webgpu_create_buffer(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
size: u64,
usage: u32,
mapped_at_creation: bool,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let descriptor = wgpu_core::resource::BufferDescriptor {
label: label.map(Cow::from),
size,
usage: wgpu_types::BufferUsages::from_bits(usage)
.ok_or_else(|| type_error("usage is not valid"))?,
mapped_at_creation,
};
gfx_put!(device => instance.device_create_buffer(
device,
&descriptor,
()
) => state, WebGpuBuffer)
}
#[op]
pub async fn op_webgpu_buffer_get_map_async(
state: Rc<RefCell<OpState>>,
buffer_rid: ResourceId,
device_rid: ResourceId,
mode: u32,
offset: u64,
size: u64,
) -> Result<WebGpuResult, AnyError> {
let (sender, receiver) = oneshot::channel::<BufferAccessResult>();
let device;
{
let state_ = state.borrow();
let instance = state_.borrow::<super::Instance>();
let buffer_resource =
state_.resource_table.get::<WebGpuBuffer>(buffer_rid)?;
let buffer = buffer_resource.0;
let device_resource = state_
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
device = device_resource.0;
let callback = Box::new(move |status| {
sender.send(status).unwrap();
});
// TODO(lucacasonato): error handling
let maybe_err = gfx_select!(buffer => instance.buffer_map_async(
buffer,
offset..(offset + size),
wgpu_core::resource::BufferMapOperation {
host: match mode {
1 => wgpu_core::device::HostMap::Read,
2 => wgpu_core::device::HostMap::Write,
_ => unreachable!(),
},
callback: wgpu_core::resource::BufferMapCallback::from_rust(callback),
}
))
.err();
if maybe_err.is_some() {
return Ok(WebGpuResult::maybe_err(maybe_err));
}
}
let done = Rc::new(RefCell::new(false));
let done_ = done.clone();
let device_poll_fut = async move {
while !*done.borrow() {
{
let state = state.borrow();
let instance = state.borrow::<super::Instance>();
gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::Wait))
.unwrap();
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
Ok::<(), AnyError>(())
};
let receiver_fut = async move {
receiver.await??;
let mut done = done_.borrow_mut();
*done = true;
Ok::<(), AnyError>(())
};
tokio::try_join!(device_poll_fut, receiver_fut)?;
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_buffer_get_mapped_range(
state: &mut OpState,
buffer_rid: ResourceId,
offset: u64,
size: Option<u64>,
mut buf: ZeroCopyBuf,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let buffer_resource = state.resource_table.get::<WebGpuBuffer>(buffer_rid)?;
let buffer = buffer_resource.0;
let (slice_pointer, range_size) =
gfx_select!(buffer => instance.buffer_get_mapped_range(
buffer,
offset,
size
))
.map_err(|e| DomExceptionOperationError::new(&e.to_string()))?;
// TODO(crowlKats):
#[allow(clippy::undocumented_unsafe_blocks)]
let slice = unsafe {
std::slice::from_raw_parts_mut(slice_pointer, range_size as usize)
};
buf.copy_from_slice(slice);
let rid = state
.resource_table
.add(WebGpuBufferMapped(slice_pointer, range_size as usize));
Ok(WebGpuResult::rid(rid))
}
#[op]
pub fn op_webgpu_buffer_unmap(
state: &mut OpState,
buffer_rid: ResourceId,
mapped_rid: ResourceId,
buf: Option<ZeroCopyBuf>,
) -> Result<WebGpuResult, AnyError> {
let mapped_resource = state
.resource_table
.take::<WebGpuBufferMapped>(mapped_rid)?;
let instance = state.borrow::<super::Instance>();
let buffer_resource = state.resource_table.get::<WebGpuBuffer>(buffer_rid)?;
let buffer = buffer_resource.0;
if let Some(buf) = buf {
// TODO(crowlKats):
#[allow(clippy::undocumented_unsafe_blocks)]
let slice = unsafe {
std::slice::from_raw_parts_mut(mapped_resource.0, mapped_resource.1)
};
slice.copy_from_slice(&buf);
}
gfx_ok!(buffer => instance.buffer_unmap(buffer))
}

View file

@ -1,395 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::rc::Rc;
use super::error::WebGpuResult;
struct WebGpuRenderBundleEncoder(
RefCell<wgpu_core::command::RenderBundleEncoder>,
);
impl Resource for WebGpuRenderBundleEncoder {
fn name(&self) -> Cow<str> {
"webGPURenderBundleEncoder".into()
}
}
pub(crate) struct WebGpuRenderBundle(pub(crate) wgpu_core::id::RenderBundleId);
impl Resource for WebGpuRenderBundle {
fn name(&self) -> Cow<str> {
"webGPURenderBundle".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateRenderBundleEncoderArgs {
device_rid: ResourceId,
label: Option<String>,
color_formats: Vec<Option<wgpu_types::TextureFormat>>,
depth_stencil_format: Option<wgpu_types::TextureFormat>,
sample_count: u32,
depth_read_only: bool,
stencil_read_only: bool,
}
#[op]
pub fn op_webgpu_create_render_bundle_encoder(
state: &mut OpState,
args: CreateRenderBundleEncoderArgs,
) -> Result<WebGpuResult, AnyError> {
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(args.device_rid)?;
let device = device_resource.0;
let depth_stencil = args.depth_stencil_format.map(|format| {
wgpu_types::RenderBundleDepthStencil {
format,
depth_read_only: args.depth_read_only,
stencil_read_only: args.stencil_read_only,
}
});
let descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
label: args.label.map(Cow::from),
color_formats: Cow::from(args.color_formats),
sample_count: args.sample_count,
depth_stencil,
multiview: None,
};
let res =
wgpu_core::command::RenderBundleEncoder::new(&descriptor, device, None);
let (render_bundle_encoder, maybe_err) = match res {
Ok(encoder) => (encoder, None),
Err(e) => (
wgpu_core::command::RenderBundleEncoder::dummy(device),
Some(e),
),
};
let rid = state
.resource_table
.add(WebGpuRenderBundleEncoder(RefCell::new(
render_bundle_encoder,
)));
Ok(WebGpuResult::rid_err(rid, maybe_err))
}
#[op]
pub fn op_webgpu_render_bundle_encoder_finish(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
label: Option<String>,
) -> Result<WebGpuResult, AnyError> {
let render_bundle_encoder_resource =
state
.resource_table
.take::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
let render_bundle_encoder = Rc::try_unwrap(render_bundle_encoder_resource)
.ok()
.expect("unwrapping render_bundle_encoder_resource should succeed")
.0
.into_inner();
let instance = state.borrow::<super::Instance>();
gfx_put!(render_bundle_encoder.parent() => instance.render_bundle_encoder_finish(
render_bundle_encoder,
&wgpu_core::command::RenderBundleDescriptor {
label: label.map(Cow::from),
},
()
) => state, WebGpuRenderBundle)
}
#[op]
pub fn op_webgpu_render_bundle_encoder_set_bind_group(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
index: u32,
bind_group: ResourceId,
dynamic_offsets_data: ZeroCopyBuf,
dynamic_offsets_data_start: usize,
dynamic_offsets_data_length: usize,
) -> Result<WebGpuResult, AnyError> {
let bind_group_resource =
state
.resource_table
.get::<super::binding::WebGpuBindGroup>(bind_group)?;
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
// Align the data
assert!(dynamic_offsets_data.len() % std::mem::size_of::<u32>() == 0);
let (prefix, dynamic_offsets_data, suffix) =
// SAFETY: A u8 to u32 cast is safe because we asserted that the length is a
// multiple of 4.
unsafe { dynamic_offsets_data.align_to::<u32>() };
assert!(prefix.is_empty());
assert!(suffix.is_empty());
let start = dynamic_offsets_data_start;
let len = dynamic_offsets_data_length;
// Assert that length and start are both in bounds
assert!(start <= dynamic_offsets_data.len());
assert!(len <= dynamic_offsets_data.len() - start);
let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
// SAFETY: the raw pointer and length are of the same slice, and that slice
// lives longer than the below function invocation.
unsafe {
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
&mut render_bundle_encoder_resource.0.borrow_mut(),
index,
bind_group_resource.0,
dynamic_offsets_data.as_ptr(),
dynamic_offsets_data.len(),
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_push_debug_group(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
group_label: String,
) -> Result<WebGpuResult, AnyError> {
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
let label = std::ffi::CString::new(group_label).unwrap();
// SAFETY: the string the raw pointer points to lives longer than the below
// function invocation.
unsafe {
wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
&mut render_bundle_encoder_resource.0.borrow_mut(),
label.as_ptr(),
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_pop_debug_group(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(
&mut render_bundle_encoder_resource.0.borrow_mut(),
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_insert_debug_marker(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
marker_label: String,
) -> Result<WebGpuResult, AnyError> {
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
let label = std::ffi::CString::new(marker_label).unwrap();
// SAFETY: the string the raw pointer points to lives longer than the below
// function invocation.
unsafe {
wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
&mut render_bundle_encoder_resource.0.borrow_mut(),
label.as_ptr(),
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_set_pipeline(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
pipeline: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let render_pipeline_resource =
state
.resource_table
.get::<super::pipeline::WebGpuRenderPipeline>(pipeline)?;
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(
&mut render_bundle_encoder_resource.0.borrow_mut(),
render_pipeline_resource.0,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_set_index_buffer(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
buffer: ResourceId,
index_format: wgpu_types::IndexFormat,
offset: u64,
size: u64,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer)?;
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
let size = Some(
std::num::NonZeroU64::new(size)
.ok_or_else(|| type_error("size must be larger than 0"))?,
);
render_bundle_encoder_resource
.0
.borrow_mut()
.set_index_buffer(buffer_resource.0, index_format, offset, size);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
slot: u32,
buffer: ResourceId,
offset: u64,
size: Option<u64>,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer)?;
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
let size = if let Some(size) = size {
Some(
std::num::NonZeroU64::new(size)
.ok_or_else(|| type_error("size must be larger than 0"))?,
)
} else {
None
};
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
&mut render_bundle_encoder_resource.0.borrow_mut(),
slot,
buffer_resource.0,
offset,
size,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_draw(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<WebGpuResult, AnyError> {
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
&mut render_bundle_encoder_resource.0.borrow_mut(),
vertex_count,
instance_count,
first_vertex,
first_instance,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_draw_indexed(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<WebGpuResult, AnyError> {
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
&mut render_bundle_encoder_resource.0.borrow_mut(),
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_bundle_encoder_draw_indirect(
state: &mut OpState,
render_bundle_encoder_rid: ResourceId,
indirect_buffer: ResourceId,
indirect_offset: u64,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
let render_bundle_encoder_resource =
state
.resource_table
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
&mut render_bundle_encoder_resource.0.borrow_mut(),
buffer_resource.0,
indirect_offset,
);
Ok(WebGpuResult::empty())
}

View file

@ -1,540 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::num::NonZeroU32;
use super::error::WebGpuResult;
pub(crate) struct WebGpuCommandEncoder(
pub(crate) wgpu_core::id::CommandEncoderId,
);
impl Resource for WebGpuCommandEncoder {
fn name(&self) -> Cow<str> {
"webGPUCommandEncoder".into()
}
}
pub(crate) struct WebGpuCommandBuffer(
pub(crate) wgpu_core::id::CommandBufferId,
);
impl Resource for WebGpuCommandBuffer {
fn name(&self) -> Cow<str> {
"webGPUCommandBuffer".into()
}
}
#[op]
pub fn op_webgpu_create_command_encoder(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let descriptor = wgpu_types::CommandEncoderDescriptor {
label: label.map(Cow::from),
};
gfx_put!(device => instance.device_create_command_encoder(
device,
&descriptor,
()
) => state, WebGpuCommandEncoder)
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuRenderPassColorAttachment {
view: ResourceId,
resolve_target: Option<ResourceId>,
clear_value: Option<wgpu_types::Color>,
load_op: wgpu_core::command::LoadOp,
store_op: wgpu_core::command::StoreOp,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuRenderPassDepthStencilAttachment {
view: ResourceId,
depth_clear_value: f32,
depth_load_op: Option<wgpu_core::command::LoadOp>,
depth_store_op: Option<wgpu_core::command::StoreOp>,
depth_read_only: bool,
stencil_clear_value: u32,
stencil_load_op: Option<wgpu_core::command::LoadOp>,
stencil_store_op: Option<wgpu_core::command::StoreOp>,
stencil_read_only: bool,
}
#[op]
pub fn op_webgpu_command_encoder_begin_render_pass(
state: &mut OpState,
command_encoder_rid: ResourceId,
label: Option<String>,
color_attachments: Vec<Option<GpuRenderPassColorAttachment>>,
depth_stencil_attachment: Option<GpuRenderPassDepthStencilAttachment>,
) -> Result<WebGpuResult, AnyError> {
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let color_attachments = color_attachments
.into_iter()
.map(|color_attachment| {
let rp_at = if let Some(at) = color_attachment.as_ref() {
let texture_view_resource =
state
.resource_table
.get::<super::texture::WebGpuTextureView>(at.view)?;
let resolve_target = at
.resolve_target
.map(|rid| {
state
.resource_table
.get::<super::texture::WebGpuTextureView>(rid)
})
.transpose()?
.map(|texture| texture.0);
Some(wgpu_core::command::RenderPassColorAttachment {
view: texture_view_resource.0,
resolve_target,
channel: wgpu_core::command::PassChannel {
load_op: at.load_op,
store_op: at.store_op,
clear_value: at.clear_value.unwrap_or_default(),
read_only: false,
},
})
} else {
None
};
Ok(rp_at)
})
.collect::<Result<Vec<_>, AnyError>>()?;
let mut processed_depth_stencil_attachment = None;
if let Some(attachment) = depth_stencil_attachment {
let texture_view_resource =
state
.resource_table
.get::<super::texture::WebGpuTextureView>(attachment.view)?;
processed_depth_stencil_attachment =
Some(wgpu_core::command::RenderPassDepthStencilAttachment {
view: texture_view_resource.0,
depth: wgpu_core::command::PassChannel {
load_op: attachment
.depth_load_op
.unwrap_or(wgpu_core::command::LoadOp::Load),
store_op: attachment
.depth_store_op
.unwrap_or(wgpu_core::command::StoreOp::Store),
clear_value: attachment.depth_clear_value,
read_only: attachment.depth_read_only,
},
stencil: wgpu_core::command::PassChannel {
load_op: attachment
.stencil_load_op
.unwrap_or(wgpu_core::command::LoadOp::Load),
store_op: attachment
.stencil_store_op
.unwrap_or(wgpu_core::command::StoreOp::Store),
clear_value: attachment.stencil_clear_value,
read_only: attachment.stencil_read_only,
},
});
}
let descriptor = wgpu_core::command::RenderPassDescriptor {
label: label.map(Cow::from),
color_attachments: Cow::from(color_attachments),
depth_stencil_attachment: processed_depth_stencil_attachment.as_ref(),
};
let render_pass = wgpu_core::command::RenderPass::new(
command_encoder_resource.0,
&descriptor,
);
let rid = state
.resource_table
.add(super::render_pass::WebGpuRenderPass(RefCell::new(
render_pass,
)));
Ok(WebGpuResult::rid(rid))
}
#[op]
pub fn op_webgpu_command_encoder_begin_compute_pass(
state: &mut OpState,
command_encoder_rid: ResourceId,
label: Option<String>,
) -> Result<WebGpuResult, AnyError> {
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let descriptor = wgpu_core::command::ComputePassDescriptor {
label: label.map(Cow::from),
};
let compute_pass = wgpu_core::command::ComputePass::new(
command_encoder_resource.0,
&descriptor,
);
let rid = state
.resource_table
.add(super::compute_pass::WebGpuComputePass(RefCell::new(
compute_pass,
)));
Ok(WebGpuResult::rid(rid))
}
#[op]
pub fn op_webgpu_command_encoder_copy_buffer_to_buffer(
state: &mut OpState,
command_encoder_rid: ResourceId,
source: ResourceId,
source_offset: u64,
destination: ResourceId,
destination_offset: u64,
size: u64,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let source_buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(source)?;
let source_buffer = source_buffer_resource.0;
let destination_buffer_resource =
state
.resource_table
.get::<super::buffer::WebGpuBuffer>(destination)?;
let destination_buffer = destination_buffer_resource.0;
gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_buffer(
command_encoder,
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size
))
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuImageCopyBuffer {
buffer: ResourceId,
offset: u64,
bytes_per_row: Option<u32>,
rows_per_image: Option<u32>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuImageCopyTexture {
pub texture: ResourceId,
pub mip_level: u32,
pub origin: wgpu_types::Origin3d,
pub aspect: wgpu_types::TextureAspect,
}
#[op]
pub fn op_webgpu_command_encoder_copy_buffer_to_texture(
state: &mut OpState,
command_encoder_rid: ResourceId,
source: GpuImageCopyBuffer,
destination: GpuImageCopyTexture,
copy_size: wgpu_types::Extent3d,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let source_buffer_resource =
state
.resource_table
.get::<super::buffer::WebGpuBuffer>(source.buffer)?;
let destination_texture_resource =
state
.resource_table
.get::<super::texture::WebGpuTexture>(destination.texture)?;
let source = wgpu_core::command::ImageCopyBuffer {
buffer: source_buffer_resource.0,
layout: wgpu_types::ImageDataLayout {
offset: source.offset,
bytes_per_row: NonZeroU32::new(source.bytes_per_row.unwrap_or(0)),
rows_per_image: NonZeroU32::new(source.rows_per_image.unwrap_or(0)),
},
};
let destination = wgpu_core::command::ImageCopyTexture {
texture: destination_texture_resource.0,
mip_level: destination.mip_level,
origin: destination.origin,
aspect: destination.aspect,
};
gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_texture(
command_encoder,
&source,
&destination,
&copy_size
))
}
#[op]
pub fn op_webgpu_command_encoder_copy_texture_to_buffer(
state: &mut OpState,
command_encoder_rid: ResourceId,
source: GpuImageCopyTexture,
destination: GpuImageCopyBuffer,
copy_size: wgpu_types::Extent3d,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let source_texture_resource =
state
.resource_table
.get::<super::texture::WebGpuTexture>(source.texture)?;
let destination_buffer_resource =
state
.resource_table
.get::<super::buffer::WebGpuBuffer>(destination.buffer)?;
let source = wgpu_core::command::ImageCopyTexture {
texture: source_texture_resource.0,
mip_level: source.mip_level,
origin: source.origin,
aspect: source.aspect,
};
let destination = wgpu_core::command::ImageCopyBuffer {
buffer: destination_buffer_resource.0,
layout: wgpu_types::ImageDataLayout {
offset: destination.offset,
bytes_per_row: NonZeroU32::new(destination.bytes_per_row.unwrap_or(0)),
rows_per_image: NonZeroU32::new(destination.rows_per_image.unwrap_or(0)),
},
};
gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_buffer(
command_encoder,
&source,
&destination,
&copy_size
))
}
#[op]
pub fn op_webgpu_command_encoder_copy_texture_to_texture(
state: &mut OpState,
command_encoder_rid: ResourceId,
source: GpuImageCopyTexture,
destination: GpuImageCopyTexture,
copy_size: wgpu_types::Extent3d,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let source_texture_resource =
state
.resource_table
.get::<super::texture::WebGpuTexture>(source.texture)?;
let destination_texture_resource =
state
.resource_table
.get::<super::texture::WebGpuTexture>(destination.texture)?;
let source = wgpu_core::command::ImageCopyTexture {
texture: source_texture_resource.0,
mip_level: source.mip_level,
origin: source.origin,
aspect: source.aspect,
};
let destination = wgpu_core::command::ImageCopyTexture {
texture: destination_texture_resource.0,
mip_level: destination.mip_level,
origin: destination.origin,
aspect: destination.aspect,
};
gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_texture(
command_encoder,
&source,
&destination,
&copy_size
))
}
#[op]
pub fn op_webgpu_command_encoder_clear_buffer(
state: &mut OpState,
command_encoder_rid: u32,
buffer_rid: u32,
offset: u64,
size: u64,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let destination_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer_rid)?;
gfx_ok!(command_encoder => instance.command_encoder_clear_buffer(
command_encoder,
destination_resource.0,
offset,
std::num::NonZeroU64::new(size)
))
}
#[op]
pub fn op_webgpu_command_encoder_push_debug_group(
state: &mut OpState,
command_encoder_rid: ResourceId,
group_label: String,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
gfx_ok!(command_encoder => instance.command_encoder_push_debug_group(command_encoder, &group_label))
}
#[op]
pub fn op_webgpu_command_encoder_pop_debug_group(
state: &mut OpState,
command_encoder_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
gfx_ok!(command_encoder => instance.command_encoder_pop_debug_group(command_encoder))
}
#[op]
pub fn op_webgpu_command_encoder_insert_debug_marker(
state: &mut OpState,
command_encoder_rid: ResourceId,
marker_label: String,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
gfx_ok!(command_encoder => instance.command_encoder_insert_debug_marker(
command_encoder,
&marker_label
))
}
#[op]
pub fn op_webgpu_command_encoder_write_timestamp(
state: &mut OpState,
command_encoder_rid: ResourceId,
query_set: ResourceId,
query_index: u32,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let query_set_resource = state
.resource_table
.get::<super::WebGpuQuerySet>(query_set)?;
gfx_ok!(command_encoder => instance.command_encoder_write_timestamp(
command_encoder,
query_set_resource.0,
query_index
))
}
#[op]
pub fn op_webgpu_command_encoder_resolve_query_set(
state: &mut OpState,
command_encoder_rid: ResourceId,
query_set: ResourceId,
first_query: u32,
query_count: u32,
destination: ResourceId,
destination_offset: u64,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let command_encoder_resource = state
.resource_table
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let query_set_resource = state
.resource_table
.get::<super::WebGpuQuerySet>(query_set)?;
let destination_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(destination)?;
gfx_ok!(command_encoder => instance.command_encoder_resolve_query_set(
command_encoder,
query_set_resource.0,
first_query,
query_count,
destination_resource.0,
destination_offset
))
}
#[op]
pub fn op_webgpu_command_encoder_finish(
state: &mut OpState,
command_encoder_rid: ResourceId,
label: Option<String>,
) -> Result<WebGpuResult, AnyError> {
let command_encoder_resource = state
.resource_table
.take::<WebGpuCommandEncoder>(command_encoder_rid)?;
let command_encoder = command_encoder_resource.0;
let instance = state.borrow::<super::Instance>();
let descriptor = wgpu_types::CommandBufferDescriptor {
label: label.map(Cow::from),
};
gfx_put!(command_encoder => instance.command_encoder_finish(
command_encoder,
&descriptor
) => state, WebGpuCommandBuffer)
}

View file

@ -1,289 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use std::borrow::Cow;
use std::cell::RefCell;
use super::error::WebGpuResult;
pub(crate) struct WebGpuComputePass(
pub(crate) RefCell<wgpu_core::command::ComputePass>,
);
impl Resource for WebGpuComputePass {
fn name(&self) -> Cow<str> {
"webGPUComputePass".into()
}
}
#[op]
pub fn op_webgpu_compute_pass_set_pipeline(
state: &mut OpState,
compute_pass_rid: ResourceId,
pipeline: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let compute_pipeline_resource =
state
.resource_table
.get::<super::pipeline::WebGpuComputePipeline>(pipeline)?;
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_set_pipeline(
&mut compute_pass_resource.0.borrow_mut(),
compute_pipeline_resource.0,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_dispatch_workgroups(
state: &mut OpState,
compute_pass_rid: ResourceId,
x: u32,
y: u32,
z: u32,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_workgroups(
&mut compute_pass_resource.0.borrow_mut(),
x,
y,
z,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_dispatch_workgroups_indirect(
state: &mut OpState,
compute_pass_rid: ResourceId,
indirect_buffer: ResourceId,
indirect_offset: u64,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_workgroups_indirect(
&mut compute_pass_resource.0.borrow_mut(),
buffer_resource.0,
indirect_offset,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_begin_pipeline_statistics_query(
state: &mut OpState,
compute_pass_rid: ResourceId,
query_set: ResourceId,
query_index: u32,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
let query_set_resource = state
.resource_table
.get::<super::WebGpuQuerySet>(query_set)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_begin_pipeline_statistics_query(
&mut compute_pass_resource.0.borrow_mut(),
query_set_resource.0,
query_index,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_end_pipeline_statistics_query(
state: &mut OpState,
compute_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_end_pipeline_statistics_query(
&mut compute_pass_resource.0.borrow_mut(),
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_write_timestamp(
state: &mut OpState,
compute_pass_rid: ResourceId,
query_set: ResourceId,
query_index: u32,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
let query_set_resource = state
.resource_table
.get::<super::WebGpuQuerySet>(query_set)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_write_timestamp(
&mut compute_pass_resource.0.borrow_mut(),
query_set_resource.0,
query_index,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_end(
state: &mut OpState,
command_encoder_rid: ResourceId,
compute_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let command_encoder_resource = state
.resource_table
.get::<super::command_encoder::WebGpuCommandEncoder>(
command_encoder_rid,
)?;
let command_encoder = command_encoder_resource.0;
let compute_pass_resource = state
.resource_table
.take::<WebGpuComputePass>(compute_pass_rid)?;
let compute_pass = &compute_pass_resource.0.borrow();
let instance = state.borrow::<super::Instance>();
gfx_ok!(command_encoder => instance.command_encoder_run_compute_pass(
command_encoder,
compute_pass
))
}
#[op]
pub fn op_webgpu_compute_pass_set_bind_group(
state: &mut OpState,
compute_pass_rid: ResourceId,
index: u32,
bind_group: ResourceId,
dynamic_offsets_data: ZeroCopyBuf,
dynamic_offsets_data_start: usize,
dynamic_offsets_data_length: usize,
) -> Result<WebGpuResult, AnyError> {
let bind_group_resource =
state
.resource_table
.get::<super::binding::WebGpuBindGroup>(bind_group)?;
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
// Align the data
assert!(dynamic_offsets_data_start % std::mem::size_of::<u32>() == 0);
let (prefix, dynamic_offsets_data, suffix) =
// SAFETY: A u8 to u32 cast is safe because we asserted that the length is a
// multiple of 4.
unsafe { dynamic_offsets_data.align_to::<u32>() };
assert!(prefix.is_empty());
assert!(suffix.is_empty());
let start = dynamic_offsets_data_start;
let len = dynamic_offsets_data_length;
// Assert that length and start are both in bounds
assert!(start <= dynamic_offsets_data.len());
assert!(len <= dynamic_offsets_data.len() - start);
let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
// SAFETY: the raw pointer and length are of the same slice, and that slice
// lives longer than the below function invocation.
unsafe {
wgpu_core::command::compute_ffi::wgpu_compute_pass_set_bind_group(
&mut compute_pass_resource.0.borrow_mut(),
index,
bind_group_resource.0,
dynamic_offsets_data.as_ptr(),
dynamic_offsets_data.len(),
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_push_debug_group(
state: &mut OpState,
compute_pass_rid: ResourceId,
group_label: String,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
let label = std::ffi::CString::new(group_label).unwrap();
// SAFETY: the string the raw pointer points to lives longer than the below
// function invocation.
unsafe {
wgpu_core::command::compute_ffi::wgpu_compute_pass_push_debug_group(
&mut compute_pass_resource.0.borrow_mut(),
label.as_ptr(),
0, // wgpu#975
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_pop_debug_group(
state: &mut OpState,
compute_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
wgpu_core::command::compute_ffi::wgpu_compute_pass_pop_debug_group(
&mut compute_pass_resource.0.borrow_mut(),
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_compute_pass_insert_debug_marker(
state: &mut OpState,
compute_pass_rid: ResourceId,
marker_label: String,
) -> Result<WebGpuResult, AnyError> {
let compute_pass_resource = state
.resource_table
.get::<WebGpuComputePass>(compute_pass_rid)?;
let label = std::ffi::CString::new(marker_label).unwrap();
// SAFETY: the string the raw pointer points to lives longer than the below
// function invocation.
unsafe {
wgpu_core::command::compute_ffi::wgpu_compute_pass_insert_debug_marker(
&mut compute_pass_resource.0.borrow_mut(),
label.as_ptr(),
0, // wgpu#975
);
}
Ok(WebGpuResult::empty())
}

View file

@ -1,314 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::ResourceId;
use serde::Serialize;
use std::convert::From;
use std::error::Error;
use std::fmt;
use wgpu_core::binding_model::CreateBindGroupError;
use wgpu_core::binding_model::CreateBindGroupLayoutError;
use wgpu_core::binding_model::CreatePipelineLayoutError;
use wgpu_core::binding_model::GetBindGroupLayoutError;
use wgpu_core::command::ClearError;
use wgpu_core::command::CommandEncoderError;
use wgpu_core::command::ComputePassError;
use wgpu_core::command::CopyError;
use wgpu_core::command::CreateRenderBundleError;
use wgpu_core::command::QueryError;
use wgpu_core::command::RenderBundleError;
use wgpu_core::command::RenderPassError;
use wgpu_core::device::queue::QueueSubmitError;
use wgpu_core::device::queue::QueueWriteError;
use wgpu_core::device::DeviceError;
use wgpu_core::pipeline::CreateComputePipelineError;
use wgpu_core::pipeline::CreateRenderPipelineError;
use wgpu_core::pipeline::CreateShaderModuleError;
#[cfg(feature = "surface")]
use wgpu_core::present::ConfigureSurfaceError;
use wgpu_core::resource::BufferAccessError;
use wgpu_core::resource::CreateBufferError;
use wgpu_core::resource::CreateQuerySetError;
use wgpu_core::resource::CreateSamplerError;
use wgpu_core::resource::CreateTextureError;
use wgpu_core::resource::CreateTextureViewError;
fn fmt_err(err: &(dyn Error + 'static)) -> String {
let mut output = err.to_string();
let mut e = err.source();
while let Some(source) = e {
output.push_str(&format!(": {source}"));
e = source.source();
}
output
}
#[derive(Serialize)]
pub struct WebGpuResult {
pub rid: Option<ResourceId>,
pub err: Option<WebGpuError>,
}
impl WebGpuResult {
pub fn rid(rid: ResourceId) -> Self {
Self {
rid: Some(rid),
err: None,
}
}
pub fn rid_err<T: Into<WebGpuError>>(
rid: ResourceId,
err: Option<T>,
) -> Self {
Self {
rid: Some(rid),
err: err.map(Into::into),
}
}
pub fn maybe_err<T: Into<WebGpuError>>(err: Option<T>) -> Self {
Self {
rid: None,
err: err.map(Into::into),
}
}
pub fn empty() -> Self {
Self {
rid: None,
err: None,
}
}
}
#[derive(Serialize)]
#[serde(tag = "type", content = "value")]
#[serde(rename_all = "kebab-case")]
pub enum WebGpuError {
Lost,
OutOfMemory,
Validation(String),
}
impl From<CreateBufferError> for WebGpuError {
fn from(err: CreateBufferError) -> Self {
match err {
CreateBufferError::Device(err) => err.into(),
CreateBufferError::AccessError(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<DeviceError> for WebGpuError {
fn from(err: DeviceError) -> Self {
match err {
DeviceError::Lost => WebGpuError::Lost,
DeviceError::OutOfMemory => WebGpuError::OutOfMemory,
DeviceError::Invalid => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<BufferAccessError> for WebGpuError {
fn from(err: BufferAccessError) -> Self {
match err {
BufferAccessError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<CreateBindGroupLayoutError> for WebGpuError {
fn from(err: CreateBindGroupLayoutError) -> Self {
match err {
CreateBindGroupLayoutError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<CreatePipelineLayoutError> for WebGpuError {
fn from(err: CreatePipelineLayoutError) -> Self {
match err {
CreatePipelineLayoutError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<CreateBindGroupError> for WebGpuError {
fn from(err: CreateBindGroupError) -> Self {
match err {
CreateBindGroupError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<RenderBundleError> for WebGpuError {
fn from(err: RenderBundleError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CreateRenderBundleError> for WebGpuError {
fn from(err: CreateRenderBundleError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CopyError> for WebGpuError {
fn from(err: CopyError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CommandEncoderError> for WebGpuError {
fn from(err: CommandEncoderError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<QueryError> for WebGpuError {
fn from(err: QueryError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<ComputePassError> for WebGpuError {
fn from(err: ComputePassError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CreateComputePipelineError> for WebGpuError {
fn from(err: CreateComputePipelineError) -> Self {
match err {
CreateComputePipelineError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<GetBindGroupLayoutError> for WebGpuError {
fn from(err: GetBindGroupLayoutError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CreateRenderPipelineError> for WebGpuError {
fn from(err: CreateRenderPipelineError) -> Self {
match err {
CreateRenderPipelineError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<RenderPassError> for WebGpuError {
fn from(err: RenderPassError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CreateSamplerError> for WebGpuError {
fn from(err: CreateSamplerError) -> Self {
match err {
CreateSamplerError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<CreateShaderModuleError> for WebGpuError {
fn from(err: CreateShaderModuleError) -> Self {
match err {
CreateShaderModuleError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<CreateTextureError> for WebGpuError {
fn from(err: CreateTextureError) -> Self {
match err {
CreateTextureError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<CreateTextureViewError> for WebGpuError {
fn from(err: CreateTextureViewError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
impl From<CreateQuerySetError> for WebGpuError {
fn from(err: CreateQuerySetError) -> Self {
match err {
CreateQuerySetError::Device(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<QueueSubmitError> for WebGpuError {
fn from(err: QueueSubmitError) -> Self {
match err {
QueueSubmitError::Queue(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<QueueWriteError> for WebGpuError {
fn from(err: QueueWriteError) -> Self {
match err {
QueueWriteError::Queue(err) => err.into(),
err => WebGpuError::Validation(fmt_err(&err)),
}
}
}
impl From<ClearError> for WebGpuError {
fn from(err: ClearError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
#[cfg(feature = "surface")]
impl From<ConfigureSurfaceError> for WebGpuError {
fn from(err: ConfigureSurfaceError) -> Self {
WebGpuError::Validation(fmt_err(&err))
}
}
#[derive(Debug)]
pub struct DomExceptionOperationError {
pub msg: String,
}
impl DomExceptionOperationError {
pub fn new(msg: &str) -> Self {
DomExceptionOperationError {
msg: msg.to_string(),
}
}
}
impl fmt::Display for DomExceptionOperationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&self.msg)
}
}
impl std::error::Error for DomExceptionOperationError {}
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
e.downcast_ref::<DomExceptionOperationError>()
.map(|_| "DOMExceptionOperationError")
}

View file

@ -1,674 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
#![warn(unsafe_op_in_unsafe_fn)]
use deno_core::error::AnyError;
use deno_core::include_js_files;
use deno_core::op;
use deno_core::Extension;
use deno_core::ExtensionBuilder;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashSet;
use std::convert::TryFrom;
use std::rc::Rc;
pub use wgpu_core;
pub use wgpu_types;
use error::DomExceptionOperationError;
use error::WebGpuResult;
#[macro_use]
mod macros {
macro_rules! gfx_select {
($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
match $id.backend() {
#[cfg(any(
all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")),
feature = "vulkan-portability"
))]
wgpu_types::Backend::Vulkan => $global.$method::<wgpu_core::api::Vulkan>( $($param),* ),
#[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
wgpu_types::Backend::Metal => $global.$method::<wgpu_core::api::Metal>( $($param),* ),
#[cfg(all(not(target_arch = "wasm32"), windows))]
wgpu_types::Backend::Dx12 => $global.$method::<wgpu_core::api::Dx12>( $($param),* ),
#[cfg(all(not(target_arch = "wasm32"), windows))]
wgpu_types::Backend::Dx11 => $global.$method::<wgpu_core::api::Dx11>( $($param),* ),
#[cfg(any(
all(unix, not(target_os = "macos"), not(target_os = "ios")),
feature = "angle",
target_arch = "wasm32"
))]
wgpu_types::Backend::Gl => $global.$method::<wgpu_core::api::Gles>( $($param),+ ),
other => panic!("Unexpected backend {:?}", other),
}
};
}
macro_rules! gfx_put {
($id:expr => $global:ident.$method:ident( $($param:expr),* ) => $state:expr, $rc:expr) => {{
let (val, maybe_err) = gfx_select!($id => $global.$method($($param),*));
let rid = $state.resource_table.add($rc(val));
Ok(WebGpuResult::rid_err(rid, maybe_err))
}};
}
macro_rules! gfx_ok {
($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {{
let maybe_err = gfx_select!($id => $global.$method($($param),*)).err();
Ok(WebGpuResult::maybe_err(maybe_err))
}};
}
}
pub mod binding;
pub mod buffer;
pub mod bundle;
pub mod command_encoder;
pub mod compute_pass;
pub mod error;
pub mod pipeline;
pub mod queue;
pub mod render_pass;
pub mod sampler;
pub mod shader;
#[cfg(feature = "surface")]
pub mod surface;
pub mod texture;
pub struct Unstable(pub bool);
fn check_unstable(state: &OpState, api_name: &str) {
let unstable = state.borrow::<Unstable>();
if !unstable.0 {
eprintln!(
"Unstable API '{api_name}'. The --unstable flag must be provided."
);
std::process::exit(70);
}
}
pub type Instance =
wgpu_core::hub::Global<wgpu_core::hub::IdentityManagerFactory>;
struct WebGpuAdapter(wgpu_core::id::AdapterId);
impl Resource for WebGpuAdapter {
fn name(&self) -> Cow<str> {
"webGPUAdapter".into()
}
}
struct WebGpuDevice(wgpu_core::id::DeviceId);
impl Resource for WebGpuDevice {
fn name(&self) -> Cow<str> {
"webGPUDevice".into()
}
}
struct WebGpuQuerySet(wgpu_core::id::QuerySetId);
impl Resource for WebGpuQuerySet {
fn name(&self) -> Cow<str> {
"webGPUQuerySet".into()
}
}
fn ext() -> ExtensionBuilder {
Extension::builder_with_deps(
env!("CARGO_PKG_NAME"),
&["deno_webidl", "deno_web"],
)
}
fn ops(ext: &mut ExtensionBuilder, unstable: bool) -> &mut ExtensionBuilder {
ext.ops(declare_webgpu_ops()).state(move |state| {
// TODO: check & possibly streamline this
// Unstable might be able to be OpMiddleware
// let unstable_checker = state.borrow::<super::UnstableChecker>();
// let unstable = unstable_checker.unstable;
state.put(Unstable(unstable));
})
}
pub fn init_ops_and_esm(unstable: bool) -> Extension {
ops(&mut ext(), unstable)
.esm(include_js_files!("01_webgpu.js", "02_idl_types.js",))
.build()
}
pub fn init_ops(unstable: bool) -> Extension {
ops(&mut ext(), unstable).build()
}
fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
let mut return_features: Vec<&'static str> = vec![];
if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
return_features.push("depth-clip-control");
}
if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) {
return_features.push("depth32float-stencil8");
}
if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
return_features.push("pipeline-statistics-query");
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
return_features.push("texture-compression-bc");
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
return_features.push("texture-compression-etc2");
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR) {
return_features.push("texture-compression-astc");
}
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
return_features.push("timestamp-query");
}
if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) {
return_features.push("indirect-first-instance");
}
if features.contains(wgpu_types::Features::SHADER_FLOAT16) {
return_features.push("shader-f16")
}
// extended from spec
if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
return_features.push("mappable-primary-buffers");
}
if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) {
return_features.push("texture-binding-array");
}
if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) {
return_features.push("buffer-binding-array");
}
if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
return_features.push("storage-resource-binding-array");
}
if features.contains(
wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
) {
return_features.push("sampled-texture-and-storage-buffer-array-non-uniform-indexing");
}
if features.contains(
wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
) {
return_features.push("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing");
}
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
return_features.push("address-mode-clamp-to-border");
}
if features
.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
{
return_features.push("texture-adapter-specific-format-features");
}
if features.contains(wgpu_types::Features::SHADER_FLOAT64) {
return_features.push("shader-float64");
}
if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
return_features.push("vertex-attribute-64bit");
}
if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) {
return_features.push("vertex-writable-storage");
}
if features.contains(wgpu_types::Features::CLEAR_TEXTURE) {
return_features.push("clear-texture");
}
if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
return_features.push("shader-primitive-index");
}
if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
return_features.push("shader-primitive-index");
}
return_features
}
#[derive(Serialize)]
#[serde(untagged)]
pub enum GpuAdapterDeviceOrErr {
Error { err: String },
Features(GpuAdapterDevice),
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuAdapterDevice {
rid: ResourceId,
limits: wgpu_types::Limits,
features: Vec<&'static str>,
is_software: bool,
}
#[op]
pub async fn op_webgpu_request_adapter(
state: Rc<RefCell<OpState>>,
power_preference: Option<wgpu_types::PowerPreference>,
force_fallback_adapter: bool,
) -> Result<GpuAdapterDeviceOrErr, AnyError> {
let mut state = state.borrow_mut();
check_unstable(&state, "navigator.gpu.requestAdapter");
let backends = std::env::var("DENO_WEBGPU_BACKEND")
.map(|s| wgpu_core::instance::parse_backends_from_comma_list(&s))
.unwrap_or_else(|_| wgpu_types::Backends::all());
let instance = if let Some(instance) = state.try_borrow::<Instance>() {
instance
} else {
state.put(wgpu_core::hub::Global::new(
"webgpu",
wgpu_core::hub::IdentityManagerFactory,
wgpu_types::InstanceDescriptor {
backends,
dx12_shader_compiler: wgpu_types::Dx12Compiler::Fxc,
},
));
state.borrow::<Instance>()
};
let descriptor = wgpu_core::instance::RequestAdapterOptions {
power_preference: power_preference.unwrap_or_default(),
force_fallback_adapter,
compatible_surface: None, // windowless
};
let res = instance.request_adapter(
&descriptor,
wgpu_core::instance::AdapterInputs::Mask(backends, |_| ()),
);
let adapter = match res {
Ok(adapter) => adapter,
Err(err) => {
return Ok(GpuAdapterDeviceOrErr::Error {
err: err.to_string(),
})
}
};
let adapter_features =
gfx_select!(adapter => instance.adapter_features(adapter))?;
let features = deserialize_features(&adapter_features);
let adapter_limits =
gfx_select!(adapter => instance.adapter_limits(adapter))?;
let rid = state.resource_table.add(WebGpuAdapter(adapter));
Ok(GpuAdapterDeviceOrErr::Features(GpuAdapterDevice {
rid,
features,
limits: adapter_limits,
is_software: false,
}))
}
#[derive(Deserialize)]
pub struct GpuRequiredFeatures(HashSet<String>);
impl From<GpuRequiredFeatures> for wgpu_types::Features {
fn from(required_features: GpuRequiredFeatures) -> wgpu_types::Features {
let mut features: wgpu_types::Features = wgpu_types::Features::empty();
features.set(
wgpu_types::Features::DEPTH_CLIP_CONTROL,
required_features.0.contains("depth-clip-control"),
);
features.set(
wgpu_types::Features::DEPTH32FLOAT_STENCIL8,
required_features.0.contains("depth32float-stencil8"),
);
features.set(
wgpu_types::Features::PIPELINE_STATISTICS_QUERY,
required_features.0.contains("pipeline-statistics-query"),
);
features.set(
wgpu_types::Features::TEXTURE_COMPRESSION_BC,
required_features.0.contains("texture-compression-bc"),
);
features.set(
wgpu_types::Features::TEXTURE_COMPRESSION_ETC2,
required_features.0.contains("texture-compression-etc2"),
);
features.set(
wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR,
required_features.0.contains("texture-compression-astc"),
);
features.set(
wgpu_types::Features::TIMESTAMP_QUERY,
required_features.0.contains("timestamp-query"),
);
features.set(
wgpu_types::Features::INDIRECT_FIRST_INSTANCE,
required_features.0.contains("indirect-first-instance"),
);
features.set(
wgpu_types::Features::SHADER_FLOAT16,
required_features.0.contains("shader-f16"),
);
// extended from spec
features.set(
wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS,
required_features.0.contains("mappable-primary-buffers"),
);
features.set(
wgpu_types::Features::TEXTURE_BINDING_ARRAY,
required_features.0.contains("texture-binding-array"),
);
features.set(
wgpu_types::Features::BUFFER_BINDING_ARRAY,
required_features.0.contains("buffer-binding-array"),
);
features.set(
wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY,
required_features
.0
.contains("storage-resource-binding-array"),
);
features.set(
wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
required_features
.0
.contains("sampled-texture-and-storage-buffer-array-non-uniform-indexing"),
);
features.set(
wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
required_features
.0
.contains("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing"),
);
features.set(
wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
required_features.0.contains("address-mode-clamp-to-border"),
);
features.set(
wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
required_features
.0
.contains("texture-adapter-specific-format-features"),
);
features.set(
wgpu_types::Features::SHADER_FLOAT64,
required_features.0.contains("shader-float64"),
);
features.set(
wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT,
required_features.0.contains("vertex-attribute-64bit"),
);
features.set(
wgpu_types::Features::VERTEX_WRITABLE_STORAGE,
required_features.0.contains("vertex-writable-storage"),
);
features.set(
wgpu_types::Features::CLEAR_TEXTURE,
required_features.0.contains("clear-commands"),
);
features.set(
wgpu_types::Features::SHADER_PRIMITIVE_INDEX,
required_features.0.contains("shader-primitive-index"),
);
features.set(
wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY,
required_features
.0
.contains("partially-bound-binding-array"),
);
features
}
}
#[op]
pub async fn op_webgpu_request_device(
state: Rc<RefCell<OpState>>,
adapter_rid: ResourceId,
label: Option<String>,
required_features: GpuRequiredFeatures,
required_limits: Option<wgpu_types::Limits>,
) -> Result<GpuAdapterDevice, AnyError> {
let mut state = state.borrow_mut();
let adapter_resource =
state.resource_table.get::<WebGpuAdapter>(adapter_rid)?;
let adapter = adapter_resource.0;
let instance = state.borrow::<Instance>();
let descriptor = wgpu_types::DeviceDescriptor {
label: label.map(Cow::from),
features: required_features.into(),
limits: required_limits.unwrap_or_default(),
};
let (device, maybe_err) = gfx_select!(adapter => instance.adapter_request_device(
adapter,
&descriptor,
std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new),
()
));
if let Some(err) = maybe_err {
return Err(DomExceptionOperationError::new(&err.to_string()).into());
}
let device_features =
gfx_select!(device => instance.device_features(device))?;
let features = deserialize_features(&device_features);
let limits = gfx_select!(device => instance.device_limits(device))?;
let rid = state.resource_table.add(WebGpuDevice(device));
Ok(GpuAdapterDevice {
rid,
features,
limits,
// TODO(lucacasonato): report correctly from wgpu
is_software: false,
})
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GPUAdapterInfo {
vendor: String,
architecture: String,
device: String,
description: String,
}
#[op]
pub async fn op_webgpu_request_adapter_info(
state: Rc<RefCell<OpState>>,
adapter_rid: ResourceId,
) -> Result<GPUAdapterInfo, AnyError> {
let state = state.borrow_mut();
let adapter_resource =
state.resource_table.get::<WebGpuAdapter>(adapter_rid)?;
let adapter = adapter_resource.0;
let instance = state.borrow::<Instance>();
let info = gfx_select!(adapter => instance.adapter_get_info(adapter))?;
Ok(GPUAdapterInfo {
vendor: info.vendor.to_string(),
architecture: String::new(), // TODO(#2170)
device: info.device.to_string(),
description: info.name,
})
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateQuerySetArgs {
device_rid: ResourceId,
label: Option<String>,
#[serde(flatten)]
r#type: GpuQueryType,
count: u32,
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case", tag = "type")]
enum GpuQueryType {
Occlusion,
#[serde(rename_all = "camelCase")]
PipelineStatistics {
pipeline_statistics: HashSet<String>,
},
Timestamp,
}
impl From<GpuQueryType> for wgpu_types::QueryType {
fn from(query_type: GpuQueryType) -> Self {
match query_type {
GpuQueryType::Occlusion => wgpu_types::QueryType::Occlusion,
GpuQueryType::PipelineStatistics {
pipeline_statistics,
} => {
use wgpu_types::PipelineStatisticsTypes;
let mut types = PipelineStatisticsTypes::empty();
if pipeline_statistics.contains("vertex-shader-invocations") {
types.set(PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS, true);
}
if pipeline_statistics.contains("clipper-invocations") {
types.set(PipelineStatisticsTypes::CLIPPER_INVOCATIONS, true);
}
if pipeline_statistics.contains("clipper-primitives-out") {
types.set(PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT, true);
}
if pipeline_statistics.contains("fragment-shader-invocations") {
types.set(PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS, true);
}
if pipeline_statistics.contains("compute-shader-invocations") {
types.set(PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS, true);
}
wgpu_types::QueryType::PipelineStatistics(types)
}
GpuQueryType::Timestamp => wgpu_types::QueryType::Timestamp,
}
}
}
#[op]
pub fn op_webgpu_create_query_set(
state: &mut OpState,
args: CreateQuerySetArgs,
) -> Result<WebGpuResult, AnyError> {
let device_resource =
state.resource_table.get::<WebGpuDevice>(args.device_rid)?;
let device = device_resource.0;
let instance = &state.borrow::<Instance>();
let descriptor = wgpu_types::QuerySetDescriptor {
label: args.label.map(Cow::from),
ty: args.r#type.into(),
count: args.count,
};
gfx_put!(device => instance.device_create_query_set(
device,
&descriptor,
()
) => state, WebGpuQuerySet)
}
fn declare_webgpu_ops() -> Vec<deno_core::OpDecl> {
vec![
// Request device/adapter
op_webgpu_request_adapter::decl(),
op_webgpu_request_device::decl(),
op_webgpu_request_adapter_info::decl(),
// Query Set
op_webgpu_create_query_set::decl(),
// buffer
buffer::op_webgpu_create_buffer::decl(),
buffer::op_webgpu_buffer_get_mapped_range::decl(),
buffer::op_webgpu_buffer_unmap::decl(),
// buffer async
buffer::op_webgpu_buffer_get_map_async::decl(),
// remaining sync ops
// texture
texture::op_webgpu_create_texture::decl(),
texture::op_webgpu_create_texture_view::decl(),
// sampler
sampler::op_webgpu_create_sampler::decl(),
// binding
binding::op_webgpu_create_bind_group_layout::decl(),
binding::op_webgpu_create_pipeline_layout::decl(),
binding::op_webgpu_create_bind_group::decl(),
// pipeline
pipeline::op_webgpu_create_compute_pipeline::decl(),
pipeline::op_webgpu_compute_pipeline_get_bind_group_layout::decl(),
pipeline::op_webgpu_create_render_pipeline::decl(),
pipeline::op_webgpu_render_pipeline_get_bind_group_layout::decl(),
// command_encoder
command_encoder::op_webgpu_create_command_encoder::decl(),
command_encoder::op_webgpu_command_encoder_begin_render_pass::decl(),
command_encoder::op_webgpu_command_encoder_begin_compute_pass::decl(),
command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer::decl(),
command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture::decl(),
command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer::decl(),
command_encoder::op_webgpu_command_encoder_copy_texture_to_texture::decl(),
command_encoder::op_webgpu_command_encoder_clear_buffer::decl(),
command_encoder::op_webgpu_command_encoder_push_debug_group::decl(),
command_encoder::op_webgpu_command_encoder_pop_debug_group::decl(),
command_encoder::op_webgpu_command_encoder_insert_debug_marker::decl(),
command_encoder::op_webgpu_command_encoder_write_timestamp::decl(),
command_encoder::op_webgpu_command_encoder_resolve_query_set::decl(),
command_encoder::op_webgpu_command_encoder_finish::decl(),
// render_pass
render_pass::op_webgpu_render_pass_set_viewport::decl(),
render_pass::op_webgpu_render_pass_set_scissor_rect::decl(),
render_pass::op_webgpu_render_pass_set_blend_constant::decl(),
render_pass::op_webgpu_render_pass_set_stencil_reference::decl(),
render_pass::op_webgpu_render_pass_begin_pipeline_statistics_query::decl(),
render_pass::op_webgpu_render_pass_end_pipeline_statistics_query::decl(),
render_pass::op_webgpu_render_pass_write_timestamp::decl(),
render_pass::op_webgpu_render_pass_execute_bundles::decl(),
render_pass::op_webgpu_render_pass_end::decl(),
render_pass::op_webgpu_render_pass_set_bind_group::decl(),
render_pass::op_webgpu_render_pass_push_debug_group::decl(),
render_pass::op_webgpu_render_pass_pop_debug_group::decl(),
render_pass::op_webgpu_render_pass_insert_debug_marker::decl(),
render_pass::op_webgpu_render_pass_set_pipeline::decl(),
render_pass::op_webgpu_render_pass_set_index_buffer::decl(),
render_pass::op_webgpu_render_pass_set_vertex_buffer::decl(),
render_pass::op_webgpu_render_pass_draw::decl(),
render_pass::op_webgpu_render_pass_draw_indexed::decl(),
render_pass::op_webgpu_render_pass_draw_indirect::decl(),
render_pass::op_webgpu_render_pass_draw_indexed_indirect::decl(),
// compute_pass
compute_pass::op_webgpu_compute_pass_set_pipeline::decl(),
compute_pass::op_webgpu_compute_pass_dispatch_workgroups::decl(),
compute_pass::op_webgpu_compute_pass_dispatch_workgroups_indirect::decl(),
compute_pass::op_webgpu_compute_pass_begin_pipeline_statistics_query::decl(
),
compute_pass::op_webgpu_compute_pass_end_pipeline_statistics_query::decl(),
compute_pass::op_webgpu_compute_pass_write_timestamp::decl(),
compute_pass::op_webgpu_compute_pass_end::decl(),
compute_pass::op_webgpu_compute_pass_set_bind_group::decl(),
compute_pass::op_webgpu_compute_pass_push_debug_group::decl(),
compute_pass::op_webgpu_compute_pass_pop_debug_group::decl(),
compute_pass::op_webgpu_compute_pass_insert_debug_marker::decl(),
// bundle
bundle::op_webgpu_create_render_bundle_encoder::decl(),
bundle::op_webgpu_render_bundle_encoder_finish::decl(),
bundle::op_webgpu_render_bundle_encoder_set_bind_group::decl(),
bundle::op_webgpu_render_bundle_encoder_push_debug_group::decl(),
bundle::op_webgpu_render_bundle_encoder_pop_debug_group::decl(),
bundle::op_webgpu_render_bundle_encoder_insert_debug_marker::decl(),
bundle::op_webgpu_render_bundle_encoder_set_pipeline::decl(),
bundle::op_webgpu_render_bundle_encoder_set_index_buffer::decl(),
bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer::decl(),
bundle::op_webgpu_render_bundle_encoder_draw::decl(),
bundle::op_webgpu_render_bundle_encoder_draw_indexed::decl(),
bundle::op_webgpu_render_bundle_encoder_draw_indirect::decl(),
// queue
queue::op_webgpu_queue_submit::decl(),
queue::op_webgpu_write_buffer::decl(),
queue::op_webgpu_write_texture::decl(),
// shader
shader::op_webgpu_create_shader_module::decl(),
]
}

View file

@ -1,424 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use super::error::WebGpuError;
use super::error::WebGpuResult;
const MAX_BIND_GROUPS: usize = 8;
pub(crate) struct WebGpuPipelineLayout(
pub(crate) wgpu_core::id::PipelineLayoutId,
);
impl Resource for WebGpuPipelineLayout {
fn name(&self) -> Cow<str> {
"webGPUPipelineLayout".into()
}
}
pub(crate) struct WebGpuComputePipeline(
pub(crate) wgpu_core::id::ComputePipelineId,
);
impl Resource for WebGpuComputePipeline {
fn name(&self) -> Cow<str> {
"webGPUComputePipeline".into()
}
}
pub(crate) struct WebGpuRenderPipeline(
pub(crate) wgpu_core::id::RenderPipelineId,
);
impl Resource for WebGpuRenderPipeline {
fn name(&self) -> Cow<str> {
"webGPURenderPipeline".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum GPUAutoLayoutMode {
Auto,
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum GPUPipelineLayoutOrGPUAutoLayoutMode {
Layout(ResourceId),
Auto(GPUAutoLayoutMode),
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuProgrammableStage {
module: ResourceId,
entry_point: String,
// constants: HashMap<String, GPUPipelineConstantValue>
}
#[op]
pub fn op_webgpu_create_compute_pipeline(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
compute: GpuProgrammableStage,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let pipeline_layout = match layout {
GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => {
let id = state.resource_table.get::<WebGpuPipelineLayout>(rid)?;
Some(id.0)
}
GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None,
};
let compute_shader_module_resource =
state
.resource_table
.get::<super::shader::WebGpuShaderModule>(compute.module)?;
let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
label: label.map(Cow::from),
layout: pipeline_layout,
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
module: compute_shader_module_resource.0,
entry_point: Cow::from(compute.entry_point),
// TODO(lucacasonato): support args.compute.constants
},
};
let implicit_pipelines = match layout {
GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None,
GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => {
Some(wgpu_core::device::ImplicitPipelineIds {
root_id: (),
group_ids: &[(); MAX_BIND_GROUPS],
})
}
};
let (compute_pipeline, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline(
device,
&descriptor,
(),
implicit_pipelines
));
let rid = state
.resource_table
.add(WebGpuComputePipeline(compute_pipeline));
Ok(WebGpuResult::rid_err(rid, maybe_err))
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PipelineLayout {
rid: ResourceId,
label: String,
err: Option<WebGpuError>,
}
#[op]
pub fn op_webgpu_compute_pipeline_get_bind_group_layout(
state: &mut OpState,
compute_pipeline_rid: ResourceId,
index: u32,
) -> Result<PipelineLayout, AnyError> {
let instance = state.borrow::<super::Instance>();
let compute_pipeline_resource = state
.resource_table
.get::<WebGpuComputePipeline>(compute_pipeline_rid)?;
let compute_pipeline = compute_pipeline_resource.0;
let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, index, ()));
let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
let rid = state
.resource_table
.add(super::binding::WebGpuBindGroupLayout(bind_group_layout));
Ok(PipelineLayout {
rid,
label,
err: maybe_err.map(WebGpuError::from),
})
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum GpuCullMode {
None,
Front,
Back,
}
impl From<GpuCullMode> for Option<wgpu_types::Face> {
fn from(value: GpuCullMode) -> Option<wgpu_types::Face> {
match value {
GpuCullMode::None => None,
GpuCullMode::Front => Some(wgpu_types::Face::Front),
GpuCullMode::Back => Some(wgpu_types::Face::Back),
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuPrimitiveState {
topology: wgpu_types::PrimitiveTopology,
strip_index_format: Option<wgpu_types::IndexFormat>,
front_face: wgpu_types::FrontFace,
cull_mode: GpuCullMode,
unclipped_depth: bool,
}
impl From<GpuPrimitiveState> for wgpu_types::PrimitiveState {
fn from(value: GpuPrimitiveState) -> wgpu_types::PrimitiveState {
wgpu_types::PrimitiveState {
topology: value.topology,
strip_index_format: value.strip_index_format,
front_face: value.front_face,
cull_mode: value.cull_mode.into(),
unclipped_depth: value.unclipped_depth,
polygon_mode: Default::default(), // native-only
conservative: false, // native-only
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuDepthStencilState {
format: wgpu_types::TextureFormat,
depth_write_enabled: bool,
depth_compare: wgpu_types::CompareFunction,
stencil_front: wgpu_types::StencilFaceState,
stencil_back: wgpu_types::StencilFaceState,
stencil_read_mask: u32,
stencil_write_mask: u32,
depth_bias: i32,
depth_bias_slope_scale: f32,
depth_bias_clamp: f32,
}
impl From<GpuDepthStencilState> for wgpu_types::DepthStencilState {
fn from(state: GpuDepthStencilState) -> wgpu_types::DepthStencilState {
wgpu_types::DepthStencilState {
format: state.format,
depth_write_enabled: state.depth_write_enabled,
depth_compare: state.depth_compare,
stencil: wgpu_types::StencilState {
front: state.stencil_front,
back: state.stencil_back,
read_mask: state.stencil_read_mask,
write_mask: state.stencil_write_mask,
},
bias: wgpu_types::DepthBiasState {
constant: state.depth_bias,
slope_scale: state.depth_bias_slope_scale,
clamp: state.depth_bias_clamp,
},
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuVertexBufferLayout {
array_stride: u64,
step_mode: wgpu_types::VertexStepMode,
attributes: Vec<wgpu_types::VertexAttribute>,
}
impl<'a> From<GpuVertexBufferLayout>
for wgpu_core::pipeline::VertexBufferLayout<'a>
{
fn from(
layout: GpuVertexBufferLayout,
) -> wgpu_core::pipeline::VertexBufferLayout<'a> {
wgpu_core::pipeline::VertexBufferLayout {
array_stride: layout.array_stride,
step_mode: layout.step_mode,
attributes: Cow::Owned(layout.attributes),
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuVertexState {
module: ResourceId,
entry_point: String,
buffers: Vec<Option<GpuVertexBufferLayout>>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuMultisampleState {
count: u32,
mask: u64,
alpha_to_coverage_enabled: bool,
}
impl From<GpuMultisampleState> for wgpu_types::MultisampleState {
fn from(gms: GpuMultisampleState) -> wgpu_types::MultisampleState {
wgpu_types::MultisampleState {
count: gms.count,
mask: gms.mask,
alpha_to_coverage_enabled: gms.alpha_to_coverage_enabled,
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct GpuFragmentState {
targets: Vec<Option<wgpu_types::ColorTargetState>>,
module: u32,
entry_point: String,
// TODO(lucacasonato): constants
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateRenderPipelineArgs {
device_rid: ResourceId,
label: Option<String>,
layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
vertex: GpuVertexState,
primitive: GpuPrimitiveState,
depth_stencil: Option<GpuDepthStencilState>,
multisample: wgpu_types::MultisampleState,
fragment: Option<GpuFragmentState>,
}
#[op]
pub fn op_webgpu_create_render_pipeline(
state: &mut OpState,
args: CreateRenderPipelineArgs,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(args.device_rid)?;
let device = device_resource.0;
let layout = match args.layout {
GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => {
let pipeline_layout_resource =
state.resource_table.get::<WebGpuPipelineLayout>(rid)?;
Some(pipeline_layout_resource.0)
}
GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None,
};
let vertex_shader_module_resource =
state
.resource_table
.get::<super::shader::WebGpuShaderModule>(args.vertex.module)?;
let fragment = if let Some(fragment) = args.fragment {
let fragment_shader_module_resource =
state
.resource_table
.get::<super::shader::WebGpuShaderModule>(fragment.module)?;
Some(wgpu_core::pipeline::FragmentState {
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
module: fragment_shader_module_resource.0,
entry_point: Cow::from(fragment.entry_point),
},
targets: Cow::from(fragment.targets),
})
} else {
None
};
let vertex_buffers = args
.vertex
.buffers
.into_iter()
.flatten()
.map(Into::into)
.collect();
let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
label: args.label.map(Cow::Owned),
layout,
vertex: wgpu_core::pipeline::VertexState {
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
module: vertex_shader_module_resource.0,
entry_point: Cow::Owned(args.vertex.entry_point),
},
buffers: Cow::Owned(vertex_buffers),
},
primitive: args.primitive.into(),
depth_stencil: args.depth_stencil.map(Into::into),
multisample: args.multisample,
fragment,
multiview: None,
};
let implicit_pipelines = match args.layout {
GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None,
GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => {
Some(wgpu_core::device::ImplicitPipelineIds {
root_id: (),
group_ids: &[(); MAX_BIND_GROUPS],
})
}
};
let (render_pipeline, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline(
device,
&descriptor,
(),
implicit_pipelines
));
let rid = state
.resource_table
.add(WebGpuRenderPipeline(render_pipeline));
Ok(WebGpuResult::rid_err(rid, maybe_err))
}
#[op]
pub fn op_webgpu_render_pipeline_get_bind_group_layout(
state: &mut OpState,
render_pipeline_rid: ResourceId,
index: u32,
) -> Result<PipelineLayout, AnyError> {
let instance = state.borrow::<super::Instance>();
let render_pipeline_resource = state
.resource_table
.get::<WebGpuRenderPipeline>(render_pipeline_rid)?;
let render_pipeline = render_pipeline_resource.0;
let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, index, ()));
let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
let rid = state
.resource_table
.add(super::binding::WebGpuBindGroupLayout(bind_group_layout));
Ok(PipelineLayout {
rid,
label,
err: maybe_err.map(WebGpuError::from),
})
}

View file

@ -1,129 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use std::num::NonZeroU32;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use super::error::WebGpuResult;
type WebGpuQueue = super::WebGpuDevice;
#[op]
pub fn op_webgpu_queue_submit(
state: &mut OpState,
queue_rid: ResourceId,
command_buffers: Vec<ResourceId>,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
let queue = queue_resource.0;
let ids = command_buffers
.iter()
.map(|rid| {
let buffer_resource =
state
.resource_table
.get::<super::command_encoder::WebGpuCommandBuffer>(*rid)?;
Ok(buffer_resource.0)
})
.collect::<Result<Vec<_>, AnyError>>()?;
let maybe_err =
gfx_select!(queue => instance.queue_submit(queue, &ids)).err();
for rid in command_buffers {
state.resource_table.close(rid)?;
}
Ok(WebGpuResult::maybe_err(maybe_err))
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GpuImageDataLayout {
offset: u64,
bytes_per_row: Option<u32>,
rows_per_image: Option<u32>,
}
impl From<GpuImageDataLayout> for wgpu_types::ImageDataLayout {
fn from(layout: GpuImageDataLayout) -> Self {
wgpu_types::ImageDataLayout {
offset: layout.offset,
bytes_per_row: NonZeroU32::new(layout.bytes_per_row.unwrap_or(0)),
rows_per_image: NonZeroU32::new(layout.rows_per_image.unwrap_or(0)),
}
}
}
#[op]
pub fn op_webgpu_write_buffer(
state: &mut OpState,
queue_rid: ResourceId,
buffer: ResourceId,
buffer_offset: u64,
data_offset: usize,
size: Option<usize>,
buf: ZeroCopyBuf,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer)?;
let buffer = buffer_resource.0;
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
let queue = queue_resource.0;
let data = match size {
Some(size) => &buf[data_offset..(data_offset + size)],
None => &buf[data_offset..],
};
let maybe_err = gfx_select!(queue => instance.queue_write_buffer(
queue,
buffer,
buffer_offset,
data
))
.err();
Ok(WebGpuResult::maybe_err(maybe_err))
}
#[op]
pub fn op_webgpu_write_texture(
state: &mut OpState,
queue_rid: ResourceId,
destination: super::command_encoder::GpuImageCopyTexture,
data_layout: GpuImageDataLayout,
size: wgpu_types::Extent3d,
buf: ZeroCopyBuf,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let texture_resource = state
.resource_table
.get::<super::texture::WebGpuTexture>(destination.texture)?;
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
let queue = queue_resource.0;
let destination = wgpu_core::command::ImageCopyTexture {
texture: texture_resource.0,
mip_level: destination.mip_level,
origin: destination.origin,
aspect: destination.aspect,
};
let data_layout = data_layout.into();
gfx_ok!(queue => instance.queue_write_texture(
queue,
&destination,
&*buf,
&data_layout,
&size
))
}

View file

@ -1,538 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use std::borrow::Cow;
use std::cell::RefCell;
use super::error::WebGpuResult;
pub(crate) struct WebGpuRenderPass(
pub(crate) RefCell<wgpu_core::command::RenderPass>,
);
impl Resource for WebGpuRenderPass {
fn name(&self) -> Cow<str> {
"webGPURenderPass".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RenderPassSetViewportArgs {
render_pass_rid: ResourceId,
x: f32,
y: f32,
width: f32,
height: f32,
min_depth: f32,
max_depth: f32,
}
#[op]
pub fn op_webgpu_render_pass_set_viewport(
state: &mut OpState,
args: RenderPassSetViewportArgs,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(args.render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_set_viewport(
&mut render_pass_resource.0.borrow_mut(),
args.x,
args.y,
args.width,
args.height,
args.min_depth,
args.max_depth,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_set_scissor_rect(
state: &mut OpState,
render_pass_rid: ResourceId,
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_set_scissor_rect(
&mut render_pass_resource.0.borrow_mut(),
x,
y,
width,
height,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_set_blend_constant(
state: &mut OpState,
render_pass_rid: ResourceId,
color: wgpu_types::Color,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_set_blend_constant(
&mut render_pass_resource.0.borrow_mut(),
&color,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_set_stencil_reference(
state: &mut OpState,
render_pass_rid: ResourceId,
reference: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_set_stencil_reference(
&mut render_pass_resource.0.borrow_mut(),
reference,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_begin_pipeline_statistics_query(
state: &mut OpState,
render_pass_rid: ResourceId,
query_set: u32,
query_index: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
let query_set_resource = state
.resource_table
.get::<super::WebGpuQuerySet>(query_set)?;
wgpu_core::command::render_ffi::wgpu_render_pass_begin_pipeline_statistics_query(
&mut render_pass_resource.0.borrow_mut(),
query_set_resource.0,
query_index,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_end_pipeline_statistics_query(
state: &mut OpState,
render_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_end_pipeline_statistics_query(
&mut render_pass_resource.0.borrow_mut(),
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_write_timestamp(
state: &mut OpState,
render_pass_rid: ResourceId,
query_set: u32,
query_index: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
let query_set_resource = state
.resource_table
.get::<super::WebGpuQuerySet>(query_set)?;
wgpu_core::command::render_ffi::wgpu_render_pass_write_timestamp(
&mut render_pass_resource.0.borrow_mut(),
query_set_resource.0,
query_index,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_execute_bundles(
state: &mut OpState,
render_pass_rid: ResourceId,
bundles: Vec<u32>,
) -> Result<WebGpuResult, AnyError> {
let bundles = bundles
.iter()
.map(|rid| {
let render_bundle_resource =
state
.resource_table
.get::<super::bundle::WebGpuRenderBundle>(*rid)?;
Ok(render_bundle_resource.0)
})
.collect::<Result<Vec<_>, AnyError>>()?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
// SAFETY: the raw pointer and length are of the same slice, and that slice
// lives longer than the below function invocation.
unsafe {
wgpu_core::command::render_ffi::wgpu_render_pass_execute_bundles(
&mut render_pass_resource.0.borrow_mut(),
bundles.as_ptr(),
bundles.len(),
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_end(
state: &mut OpState,
command_encoder_rid: ResourceId,
render_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let command_encoder_resource = state
.resource_table
.get::<super::command_encoder::WebGpuCommandEncoder>(
command_encoder_rid,
)?;
let command_encoder = command_encoder_resource.0;
let render_pass_resource = state
.resource_table
.take::<WebGpuRenderPass>(render_pass_rid)?;
let render_pass = &render_pass_resource.0.borrow();
let instance = state.borrow::<super::Instance>();
gfx_ok!(command_encoder => instance.command_encoder_run_render_pass(command_encoder, render_pass))
}
#[op]
pub fn op_webgpu_render_pass_set_bind_group(
state: &mut OpState,
render_pass_rid: ResourceId,
index: u32,
bind_group: u32,
dynamic_offsets_data: ZeroCopyBuf,
dynamic_offsets_data_start: usize,
dynamic_offsets_data_length: usize,
) -> Result<WebGpuResult, AnyError> {
let bind_group_resource =
state
.resource_table
.get::<super::binding::WebGpuBindGroup>(bind_group)?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
// Align the data
assert_eq!(dynamic_offsets_data_start % std::mem::size_of::<u32>(), 0);
let (prefix, dynamic_offsets_data, suffix) =
// SAFETY: A u8 to u32 cast is safe because we asserted that the length is a
// multiple of 4.
unsafe { dynamic_offsets_data.align_to::<u32>() };
assert!(prefix.is_empty());
assert!(suffix.is_empty());
let start = dynamic_offsets_data_start;
let len = dynamic_offsets_data_length;
// Assert that length and start are both in bounds
assert!(start <= dynamic_offsets_data.len());
assert!(len <= dynamic_offsets_data.len() - start);
let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
// SAFETY: the raw pointer and length are of the same slice, and that slice
// lives longer than the below function invocation.
unsafe {
wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
&mut render_pass_resource.0.borrow_mut(),
index,
bind_group_resource.0,
dynamic_offsets_data.as_ptr(),
dynamic_offsets_data.len(),
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_push_debug_group(
state: &mut OpState,
render_pass_rid: ResourceId,
group_label: String,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
let label = std::ffi::CString::new(group_label).unwrap();
// SAFETY: the string the raw pointer points to lives longer than the below
// function invocation.
unsafe {
wgpu_core::command::render_ffi::wgpu_render_pass_push_debug_group(
&mut render_pass_resource.0.borrow_mut(),
label.as_ptr(),
0, // wgpu#975
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_pop_debug_group(
state: &mut OpState,
render_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_pop_debug_group(
&mut render_pass_resource.0.borrow_mut(),
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_insert_debug_marker(
state: &mut OpState,
render_pass_rid: ResourceId,
marker_label: String,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
let label = std::ffi::CString::new(marker_label).unwrap();
// SAFETY: the string the raw pointer points to lives longer than the below
// function invocation.
unsafe {
wgpu_core::command::render_ffi::wgpu_render_pass_insert_debug_marker(
&mut render_pass_resource.0.borrow_mut(),
label.as_ptr(),
0, // wgpu#975
);
}
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_set_pipeline(
state: &mut OpState,
render_pass_rid: ResourceId,
pipeline: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pipeline_resource =
state
.resource_table
.get::<super::pipeline::WebGpuRenderPipeline>(pipeline)?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_set_pipeline(
&mut render_pass_resource.0.borrow_mut(),
render_pipeline_resource.0,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_set_index_buffer(
state: &mut OpState,
render_pass_rid: ResourceId,
buffer: u32,
index_format: wgpu_types::IndexFormat,
offset: u64,
size: Option<u64>,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer)?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
let size = if let Some(size) = size {
Some(
std::num::NonZeroU64::new(size)
.ok_or_else(|| type_error("size must be larger than 0"))?,
)
} else {
None
};
render_pass_resource.0.borrow_mut().set_index_buffer(
buffer_resource.0,
index_format,
offset,
size,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_set_vertex_buffer(
state: &mut OpState,
render_pass_rid: ResourceId,
slot: u32,
buffer: u32,
offset: u64,
size: Option<u64>,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(buffer)?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
let size = if let Some(size) = size {
Some(
std::num::NonZeroU64::new(size)
.ok_or_else(|| type_error("size must be larger than 0"))?,
)
} else {
None
};
wgpu_core::command::render_ffi::wgpu_render_pass_set_vertex_buffer(
&mut render_pass_resource.0.borrow_mut(),
slot,
buffer_resource.0,
offset,
size,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_draw(
state: &mut OpState,
render_pass_rid: ResourceId,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_draw(
&mut render_pass_resource.0.borrow_mut(),
vertex_count,
instance_count,
first_vertex,
first_instance,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_draw_indexed(
state: &mut OpState,
render_pass_rid: ResourceId,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed(
&mut render_pass_resource.0.borrow_mut(),
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_draw_indirect(
state: &mut OpState,
render_pass_rid: ResourceId,
indirect_buffer: u32,
indirect_offset: u64,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_draw_indirect(
&mut render_pass_resource.0.borrow_mut(),
buffer_resource.0,
indirect_offset,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_draw_indexed_indirect(
state: &mut OpState,
render_pass_rid: ResourceId,
indirect_buffer: u32,
indirect_offset: u64,
) -> Result<WebGpuResult, AnyError> {
let buffer_resource = state
.resource_table
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed_indirect(
&mut render_pass_resource.0.borrow_mut(),
buffer_resource.0,
indirect_offset,
);
Ok(WebGpuResult::empty())
}

View file

@ -1,70 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use std::borrow::Cow;
use super::error::WebGpuResult;
pub(crate) struct WebGpuSampler(pub(crate) wgpu_core::id::SamplerId);
impl Resource for WebGpuSampler {
fn name(&self) -> Cow<str> {
"webGPUSampler".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateSamplerArgs {
device_rid: ResourceId,
label: Option<String>,
address_mode_u: wgpu_types::AddressMode,
address_mode_v: wgpu_types::AddressMode,
address_mode_w: wgpu_types::AddressMode,
mag_filter: wgpu_types::FilterMode,
min_filter: wgpu_types::FilterMode,
mipmap_filter: wgpu_types::FilterMode, // TODO: GPUMipmapFilterMode
lod_min_clamp: f32,
lod_max_clamp: f32,
compare: Option<wgpu_types::CompareFunction>,
max_anisotropy: u8,
}
#[op]
pub fn op_webgpu_create_sampler(
state: &mut OpState,
args: CreateSamplerArgs,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(args.device_rid)?;
let device = device_resource.0;
let descriptor = wgpu_core::resource::SamplerDescriptor {
label: args.label.map(Cow::from),
address_modes: [
args.address_mode_u,
args.address_mode_v,
args.address_mode_w,
],
mag_filter: args.mag_filter,
min_filter: args.min_filter,
mipmap_filter: args.mipmap_filter,
lod_min_clamp: args.lod_min_clamp,
lod_max_clamp: args.lod_max_clamp,
compare: args.compare,
anisotropy_clamp: std::num::NonZeroU8::new(args.max_anisotropy),
border_color: None, // native-only
};
gfx_put!(device => instance.device_create_sampler(
device,
&descriptor,
()
) => state, WebGpuSampler)
}

View file

@ -1,45 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use std::borrow::Cow;
use super::error::WebGpuResult;
pub(crate) struct WebGpuShaderModule(pub(crate) wgpu_core::id::ShaderModuleId);
impl Resource for WebGpuShaderModule {
fn name(&self) -> Cow<str> {
"webGPUShaderModule".into()
}
}
#[op]
pub fn op_webgpu_create_shader_module(
state: &mut OpState,
device_rid: ResourceId,
label: Option<String>,
code: String,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let source = wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::from(code));
let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
label: label.map(Cow::from),
shader_bound_checks: wgpu_types::ShaderBoundChecks::default(),
};
gfx_put!(device => instance.device_create_shader_module(
device,
&descriptor,
source,
()
) => state, WebGpuShaderModule)
}

View file

@ -1,137 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use super::WebGpuResult;
use deno_core::error::AnyError;
use deno_core::include_js_files;
use deno_core::op;
use deno_core::Extension;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use std::borrow::Cow;
use wgpu_types::SurfaceStatus;
pub fn init_surface(unstable: bool) -> Extension {
Extension::builder_with_deps(
"deno_webgpu_surface",
&["deno_webidl", "deno_web", "deno_webgpu"],
)
.esm(include_js_files!(
"03_surface.js",
"04_surface_idl_types.js",
))
.ops(vec![
op_webgpu_surface_configure::decl(),
op_webgpu_surface_get_current_texture::decl(),
op_webgpu_surface_present::decl(),
])
.state(move |state| {
// TODO: check & possibly streamline this
// Unstable might be able to be OpMiddleware
// let unstable_checker = state.borrow::<super::UnstableChecker>();
// let unstable = unstable_checker.unstable;
state.put(super::Unstable(unstable));
})
.build()
}
pub struct WebGpuSurface(pub wgpu_core::id::SurfaceId);
impl Resource for WebGpuSurface {
fn name(&self) -> Cow<str> {
"webGPUSurface".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceConfigureArgs {
surface_rid: ResourceId,
device_rid: ResourceId,
format: wgpu_types::TextureFormat,
usage: u32,
width: u32,
height: u32,
present_mode: Option<wgpu_types::PresentMode>,
alpha_mode: wgpu_types::CompositeAlphaMode,
view_formats: Vec<wgpu_types::TextureFormat>,
}
#[op]
pub fn op_webgpu_surface_configure(
state: &mut OpState,
args: SurfaceConfigureArgs,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(args.device_rid)?;
let device = device_resource.0;
let surface_resource = state
.resource_table
.get::<WebGpuSurface>(args.surface_rid)?;
let surface = surface_resource.0;
let conf = wgpu_types::SurfaceConfiguration::<Vec<wgpu_types::TextureFormat>> {
usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
format: args.format,
width: args.width,
height: args.height,
present_mode: args.present_mode.unwrap_or_default(),
alpha_mode: args.alpha_mode,
view_formats: args.view_formats,
};
let err =
gfx_select!(device => instance.surface_configure(surface, device, &conf));
Ok(WebGpuResult::maybe_err(err))
}
#[op]
pub fn op_webgpu_surface_get_current_texture(
state: &mut OpState,
device_rid: ResourceId,
surface_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let surface_resource =
state.resource_table.get::<WebGpuSurface>(surface_rid)?;
let surface = surface_resource.0;
let output =
gfx_select!(device => instance.surface_get_current_texture(surface, ()))?;
match output.status {
SurfaceStatus::Good | SurfaceStatus::Suboptimal => {
let id = output.texture_id.unwrap();
let rid = state.resource_table.add(crate::texture::WebGpuTexture(id));
Ok(WebGpuResult::rid(rid))
}
_ => Err(AnyError::msg("Invalid Surface Status")),
}
}
#[op]
pub fn op_webgpu_surface_present(
state: &mut OpState,
device_rid: ResourceId,
surface_rid: ResourceId,
) -> Result<(), AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(device_rid)?;
let device = device_resource.0;
let surface_resource =
state.resource_table.get::<WebGpuSurface>(surface_rid)?;
let surface = surface_resource.0;
let _ = gfx_select!(device => instance.surface_present(surface))?;
Ok(())
}

View file

@ -1,116 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use serde::Deserialize;
use std::borrow::Cow;
use super::error::WebGpuResult;
pub(crate) struct WebGpuTexture(pub(crate) wgpu_core::id::TextureId);
impl Resource for WebGpuTexture {
fn name(&self) -> Cow<str> {
"webGPUTexture".into()
}
}
pub(crate) struct WebGpuTextureView(pub(crate) wgpu_core::id::TextureViewId);
impl Resource for WebGpuTextureView {
fn name(&self) -> Cow<str> {
"webGPUTextureView".into()
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTextureArgs {
device_rid: ResourceId,
label: Option<String>,
size: wgpu_types::Extent3d,
mip_level_count: u32,
sample_count: u32,
dimension: wgpu_types::TextureDimension,
format: wgpu_types::TextureFormat,
usage: u32,
view_formats: Vec<wgpu_types::TextureFormat>,
}
#[op]
pub fn op_webgpu_create_texture(
state: &mut OpState,
args: CreateTextureArgs,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let device_resource = state
.resource_table
.get::<super::WebGpuDevice>(args.device_rid)?;
let device = device_resource.0;
let descriptor = wgpu_core::resource::TextureDescriptor {
label: args.label.map(Cow::from),
size: args.size,
mip_level_count: args.mip_level_count,
sample_count: args.sample_count,
dimension: args.dimension,
format: args.format,
usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
view_formats: args.view_formats,
};
gfx_put!(device => instance.device_create_texture(
device,
&descriptor,
()
) => state, WebGpuTexture)
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTextureViewArgs {
texture_rid: ResourceId,
label: Option<String>,
format: Option<wgpu_types::TextureFormat>,
dimension: Option<wgpu_types::TextureViewDimension>,
aspect: wgpu_types::TextureAspect,
base_mip_level: u32,
mip_level_count: Option<u32>,
base_array_layer: u32,
array_layer_count: Option<u32>,
}
#[op]
pub fn op_webgpu_create_texture_view(
state: &mut OpState,
args: CreateTextureViewArgs,
) -> Result<WebGpuResult, AnyError> {
let instance = state.borrow::<super::Instance>();
let texture_resource = state
.resource_table
.get::<WebGpuTexture>(args.texture_rid)?;
let texture = texture_resource.0;
let descriptor = wgpu_core::resource::TextureViewDescriptor {
label: args.label.map(Cow::from),
format: args.format,
dimension: args.dimension,
range: wgpu_types::ImageSubresourceRange {
aspect: args.aspect,
base_mip_level: args.base_mip_level,
mip_level_count: std::num::NonZeroU32::new(
args.mip_level_count.unwrap_or(0),
),
base_array_layer: args.base_array_layer,
array_layer_count: std::num::NonZeroU32::new(
args.array_layer_count.unwrap_or(0),
),
},
};
gfx_put!(texture => instance.texture_create_view(
texture,
&descriptor,
()
) => state, WebGpuTextureView)
}

File diff suppressed because it is too large Load diff

View file

@ -50,7 +50,6 @@ deno_node.workspace = true
deno_tls.workspace = true
deno_url.workspace = true
deno_web.workspace = true
deno_webgpu.workspace = true
deno_webidl.workspace = true
deno_websocket.workspace = true
deno_webstorage.workspace = true
@ -80,7 +79,6 @@ deno_node.workspace = true
deno_tls.workspace = true
deno_url.workspace = true
deno_web.workspace = true
deno_webgpu.workspace = true
deno_webidl.workspace = true
deno_websocket.workspace = true
deno_webstorage.workspace = true

View file

@ -217,7 +217,6 @@ mod startup_snapshot {
"deno_websocket",
"deno_webstorage",
"deno_crypto",
"deno_webgpu",
"deno_broadcast_channel",
// FIXME(bartlomieju): this should be reenabled
// "deno_node",
@ -269,7 +268,6 @@ mod startup_snapshot {
),
deno_webstorage::init_ops_and_esm(None),
deno_crypto::init_ops_and_esm(None),
deno_webgpu::init_ops_and_esm(false),
deno_broadcast_channel::init_ops_and_esm(
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
false, // No --unstable.

View file

@ -154,7 +154,6 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
.or_else(|| deno_webgpu::error::get_error_class_name(e))
.or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| deno_websocket::get_network_error_class_name(e))

View file

@ -24,7 +24,6 @@ import * as urlPattern from "ext:deno_url/01_urlpattern.js";
import * as headers from "ext:deno_fetch/20_headers.js";
import * as streams from "ext:deno_web/06_streams.js";
import * as fileReader from "ext:deno_web/10_filereader.js";
import * as webgpu from "ext:deno_webgpu/01_webgpu.js";
import * as webSocket from "ext:deno_websocket/01_websocket.js";
import * as webSocketStream from "ext:deno_websocket/02_websocketstream.js";
import * as broadcastChannel from "ext:deno_broadcast_channel/01_broadcast_channel.js";
@ -142,40 +141,6 @@ const windowOrWorkerGlobalScope = {
const unstableWindowOrWorkerGlobalScope = {
BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
WebSocketStream: util.nonEnumerable(webSocketStream.WebSocketStream),
GPU: util.nonEnumerable(webgpu.GPU),
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
GPUAdapterInfo: util.nonEnumerable(webgpu.GPUAdapterInfo),
GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits),
GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures),
GPUDeviceLostInfo: util.nonEnumerable(webgpu.GPUDeviceLostInfo),
GPUDevice: util.nonEnumerable(webgpu.GPUDevice),
GPUQueue: util.nonEnumerable(webgpu.GPUQueue),
GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),
GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),
GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),
GPUTexture: util.nonEnumerable(webgpu.GPUTexture),
GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),
GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),
GPUSampler: util.nonEnumerable(webgpu.GPUSampler),
GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),
GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),
GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),
GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),
GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),
GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),
GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),
GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),
GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),
GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),
GPUError: util.nonEnumerable(webgpu.GPUError),
GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),
GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),
};
class Navigator {
@ -205,14 +170,6 @@ function setLanguage(val) {
}
ObjectDefineProperties(Navigator.prototype, {
gpu: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return webgpu.gpu;
},
},
hardwareConcurrency: {
configurable: true,
enumerable: true,
@ -261,14 +218,6 @@ class WorkerNavigator {
const workerNavigator = webidl.createBranded(WorkerNavigator);
ObjectDefineProperties(WorkerNavigator.prototype, {
gpu: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return webgpu.gpu;
},
},
hardwareConcurrency: {
configurable: true,
enumerable: true,

View file

@ -17,7 +17,6 @@ pub use deno_node;
pub use deno_tls;
pub use deno_url;
pub use deno_web;
pub use deno_webgpu;
pub use deno_webidl;
pub use deno_websocket;
pub use deno_webstorage;

View file

@ -411,7 +411,6 @@ impl WebWorker {
),
deno_webstorage::init_ops(None).disable(),
deno_crypto::init_ops(options.seed),
deno_webgpu::init_ops(unstable),
deno_broadcast_channel::init_ops(
options.broadcast_channel.clone(),
unstable,

View file

@ -231,7 +231,6 @@ impl MainWorker {
),
deno_webstorage::init_ops(options.origin_storage_dir.clone()),
deno_crypto::init_ops(options.seed),
deno_webgpu::init_ops(unstable),
deno_broadcast_channel::init_ops(
options.broadcast_channel.clone(),
unstable,

View file

@ -1474,7 +1474,7 @@ async fn wrap_client_auth_https_server() {
}
Err(e) => {
eprintln!("https-client-auth accept error: {:?}", e);
eprintln!("https-client-auth accept error: {e:?}");
yield Err(e);
}
}

View file

@ -60,19 +60,6 @@ Tip: the `[bench_filter]` argument doesn't have to be an exact bench name, you
can use a shorthand or a partial match to profile a group of benches, e.g:
`./tools/flamebench.js de v8`
## wgpu_sync.js
`wgpu_sync.js` streamlines updating `deno_webgpu` from
[gfx-rs/wgpu](https://github.com/gfx-rs/wgpu/).
It essentially vendors the `deno_webgpu` tree with a few minor patches applied
on top, somewhat similar to `git subtree`.
1. Update `COMMIT` or `V_WGPU` in `./tools/wgpu_sync.js`
2. Run `./tools/wgpu_sync.js`
3. Double check changes, possibly patch
4. Commit & send a PR with the updates
## copyright_checker.js
`copyright_checker.js` is used to check copyright headers in the codebase.

View file

@ -1,115 +0,0 @@
#!/usr/bin/env -S deno run --unstable --allow-read --allow-write --allow-run
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { join, ROOT_PATH } from "./util.js";
const COMMIT = "659f6977051345e4e06ab4832c6f7d268f25a1ad";
const REPO = "gfx-rs/wgpu";
const V_WGPU = "0.15";
const TARGET_DIR = join(ROOT_PATH, "ext", "webgpu");
async function bash(subcmd, opts = {}) {
const { success, code } = await new Deno.Command("bash", {
...opts,
args: ["-c", subcmd],
stdout: "inherit",
sdterr: "inherit",
}).output();
// Exit process on failure
if (!success) {
Deno.exit(code);
}
}
async function clearTargetDir() {
await bash(`rm -r ${TARGET_DIR}/*`);
}
async function checkoutUpstream() {
// Path of deno_webgpu inside the TAR
const tarPrefix = `${REPO.replace("/", "-")}-${
COMMIT.slice(0, 7)
}/deno_webgpu/`;
const cmd =
`curl -L https://api.github.com/repos/${REPO}/tarball/${COMMIT} | tar -C '${TARGET_DIR}' -xzvf - --strip=2 '${tarPrefix}'`;
// console.log(cmd);
await bash(cmd);
}
async function denoWebgpuVersion() {
const coreCargo = join(ROOT_PATH, "Cargo.toml");
const contents = await Deno.readTextFile(coreCargo);
return contents.match(
/^deno_webgpu = { version = "(\d+\.\d+\.\d+)", path = ".\/ext\/webgpu" }$/m,
)[1];
}
async function patchFile(path, patcher) {
const data = await Deno.readTextFile(path);
const patched = patcher(data);
await Deno.writeTextFile(path, patched);
}
async function patchCargo() {
const vDenoWebgpu = await denoWebgpuVersion();
await patchFile(
join(TARGET_DIR, "Cargo.toml"),
(data) =>
data
.replace(/^version = .*/m, `version = "${vDenoWebgpu}"`)
.replace(
/^repository.workspace = true/m,
`repository = "https://github.com/gfx-rs/wgpu"`,
)
.replace(
/^serde = { workspace = true, features = ["derive"] }/m,
`serde.workspace = true`,
)
.replace(
/^tokio = { workspace = true, features = ["full"] }/m,
`tokio.workspace = true`,
),
);
await patchFile(
join(ROOT_PATH, "Cargo.toml"),
(data) =>
data
.replace(/^wgpu-core = .*/m, `wgpu-core = "${V_WGPU}"`)
.replace(/^wgpu-types = .*/m, `wgpu-types = "${V_WGPU}"`),
);
}
async function patchSrcLib() {
await patchFile(
join(TARGET_DIR, "src", "lib.rs"),
(data) =>
data.replace(
`prefix "ext:deno_webgpu",`,
`prefix "ext:deno_webgpu",`,
),
);
}
async function patchSurface() {
await patchFile(
join(TARGET_DIR, "src", "surface.rs"),
(data) =>
data.replace(
`prefix "ext:deno_webgpu",`,
`prefix "ext:deno_webgpu",`,
),
);
}
async function main() {
await clearTargetDir();
await checkoutUpstream();
await patchCargo();
await patchSrcLib();
await patchSurface();
await bash(join(ROOT_PATH, "tools", "format.js"));
}
await main();