Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bring back WebGPU #20812

Merged
merged 48 commits into from Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
bc3fbf5
bring back webgpu infrastructure
crowlKats Oct 4, 2023
1b1e475
pull in webgpu
crowlKats Oct 4, 2023
6d56745
use git dep
crowlKats Oct 7, 2023
b54794f
fix
crowlKats Oct 10, 2023
b7019a6
feat(streams): ReadableStream.read min option
crowlKats Oct 10, 2023
d7e4d02
fix converters and asserts
crowlKats Oct 11, 2023
d88baf4
add CoreGraphics
crowlKats Oct 11, 2023
e7e024b
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
a5dc8f2
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
15c8e6a
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
f3d8934
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
c0ac159
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
59e5b5d
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
786de65
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
b142f39
Apply suggestions from code review
crowlKats Nov 29, 2023
c47e3ff
Update ext/webgpu/01_webgpu.js
crowlKats Nov 29, 2023
d317f0c
address comments
crowlKats Nov 29, 2023
91532f1
Merge branch 'main' into webgpu
crowlKats Nov 29, 2023
ddd6879
some fixes
crowlKats Nov 30, 2023
8ea8ddc
format and fix
crowlKats Nov 30, 2023
44c0674
lazy load
crowlKats Dec 1, 2023
d4e85d3
cleanup
crowlKats Dec 4, 2023
e64d31f
Merge branch 'main' into webgpu
crowlKats Dec 7, 2023
59c7397
update primordials import
crowlKats Dec 7, 2023
f1fbfd9
remove duplicate brand assert
crowlKats Dec 7, 2023
3f15523
fmt
crowlKats Dec 7, 2023
d41b6c5
ci
crowlKats Dec 7, 2023
7825789
safety comments
crowlKats Dec 7, 2023
41e3929
fix
crowlKats Dec 7, 2023
554df0c
fmt
crowlKats Dec 7, 2023
de9bc5f
primordials
crowlKats Dec 7, 2023
36846d2
fix config.toml
crowlKats Dec 7, 2023
7d68d7c
fix primordials
crowlKats Dec 7, 2023
7665e8e
Merge branch 'main' into webgpu
crowlKats Dec 7, 2023
bb0bd0e
update wgpu
crowlKats Dec 7, 2023
569fd1b
fix the build
bartlomieju Dec 8, 2023
0602fc6
pin reqwest
crowlKats Dec 8, 2023
ab295e5
fix
crowlKats Dec 8, 2023
68465cb
fix unstable flag order
crowlKats Dec 8, 2023
14f0375
fix test
crowlKats Dec 8, 2023
bbdba51
update macos_shared_libraries to check for weak linking
crowlKats Dec 8, 2023
76d5234
Merge branch 'main' into webgpu
crowlKats Dec 8, 2023
99fa18b
fix unstable handling
crowlKats Dec 8, 2023
3800f11
fmt
crowlKats Dec 8, 2023
6c91999
fix test
crowlKats Dec 8, 2023
dcd94a2
Merge branch 'main' into webgpu
crowlKats Dec 8, 2023
1efcd93
adress comments
crowlKats Dec 8, 2023
42aff1b
fmt
crowlKats Dec 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.toml
Expand Up @@ -24,6 +24,7 @@ members = [
"ext/node",
"ext/url",
"ext/web",
"ext/webgpu",
"ext/webidl",
"ext/websocket",
"ext/webstorage",
Expand Down Expand Up @@ -65,6 +66,7 @@ deno_kv = { version = "0.28.0", path = "./ext/kv" }
deno_tls = { version = "0.107.0", path = "./ext/tls" }
deno_url = { version = "0.120.0", path = "./ext/url" }
deno_web = { version = "0.151.0", path = "./ext/web" }
deno_webgpu = { version = "0.94.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.120.0", path = "./ext/webidl" }
deno_websocket = { version = "0.125.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.115.0", path = "./ext/webstorage" }
Expand Down Expand Up @@ -158,6 +160,11 @@ p384 = { version = "0.13.0", features = ["ecdh"] }
rsa = { version = "0.7.0", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node
hkdf = "0.12.3"

# webgpu
raw-window-handle = "0.5.0"
wgpu-core = "0.17"
wgpu-types = "0.17"

# macros
proc-macro2 = "1"
quote = "1"
Expand Down
1 change: 1 addition & 0 deletions cli/args/flags.rs
Expand Up @@ -755,6 +755,7 @@ 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.
Expand Down
9 changes: 9 additions & 0 deletions cli/build.rs
Expand Up @@ -164,6 +164,7 @@ 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());
Expand Down Expand Up @@ -532,3 +533,11 @@ fn main() {
res.compile().unwrap();
}
}

fn deno_webgpu_get_declaration() -> PathBuf {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
manifest_dir
.join("tsc")
.join("dts")
.join("lib.deno_webgpu.d.ts")
}
3 changes: 3 additions & 0 deletions cli/js/40_testing.js
Expand Up @@ -141,6 +141,9 @@ const OP_DETAILS = {
"op_utime_async": ["change file timestamps", "awaiting the result of a `Deno.utime` call"],
"op_host_recv_message": ["receive a message from a web worker", "terminating a `Worker`"],
"op_host_recv_ctrl": ["receive a message from a web worker", "terminating a `Worker`"],
"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_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"],
"op_ws_next_event": ["receive the next message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
Expand Down
38 changes: 38 additions & 0 deletions cli/tests/testdata/webgpu/computepass_shader.wgsl
@@ -0,0 +1,38 @@
@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 added cli/tests/testdata/webgpu/hellotriangle.out
Binary file not shown.
11 changes: 11 additions & 0 deletions cli/tests/testdata/webgpu/hellotriangle_shader.wgsl
@@ -0,0 +1,11 @@
@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);
}
242 changes: 242 additions & 0 deletions cli/tests/unit/webgpu_test.ts
@@ -0,0 +1,242 @@
// 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is 4th place we define this env var :) we should move it to test_util.ts in a follow up. Can you handle that @crowlKats?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

} 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,
bartlomieju marked this conversation as resolved.
Show resolved Hide resolved
}, 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;
}
}
}
2 changes: 2 additions & 0 deletions cli/tsc/dts/lib.deno.window.d.ts
Expand Up @@ -3,6 +3,7 @@
/// <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" />
Expand Down Expand Up @@ -101,6 +102,7 @@ declare var caches: CacheStorage;

/** @category Web APIs */
declare interface Navigator {
readonly gpu: GPU;
readonly hardwareConcurrency: number;
readonly userAgent: string;
readonly language: string;
Expand Down
1 change: 1 addition & 0 deletions cli/tsc/dts/lib.deno.worker.d.ts
Expand Up @@ -62,6 +62,7 @@ declare var WorkerGlobalScope: {

/** @category Web APIs */
declare interface WorkerNavigator {
readonly gpu: GPU;
readonly hardwareConcurrency: number;
readonly userAgent: string;
readonly language: string;
Expand Down