diff options
Diffstat (limited to 'cli/tests')
-rw-r--r-- | cli/tests/integration/js_unit_tests.rs | 1 | ||||
-rw-r--r-- | cli/tests/integration/run_tests.rs | 11 | ||||
-rw-r--r-- | cli/tests/integration/shared_library_tests.rs | 27 | ||||
-rw-r--r-- | cli/tests/testdata/run/unstable_webgpu.disabled.out | 2 | ||||
-rw-r--r-- | cli/tests/testdata/run/unstable_webgpu.enabled.out | 2 | ||||
-rw-r--r-- | cli/tests/testdata/run/unstable_webgpu.js | 10 | ||||
-rw-r--r-- | cli/tests/testdata/webgpu/computepass_shader.wgsl | 38 | ||||
-rw-r--r-- | cli/tests/testdata/webgpu/hellotriangle.out | bin | 0 -> 204800 bytes | |||
-rw-r--r-- | cli/tests/testdata/webgpu/hellotriangle_shader.wgsl | 11 | ||||
-rw-r--r-- | cli/tests/unit/webgpu_test.ts | 242 |
10 files changed, 336 insertions, 8 deletions
diff --git a/cli/tests/integration/js_unit_tests.rs b/cli/tests/integration/js_unit_tests.rs index 165ab25bf..10bd137d9 100644 --- a/cli/tests/integration/js_unit_tests.rs +++ b/cli/tests/integration/js_unit_tests.rs @@ -101,6 +101,7 @@ util::unit_test_factory!( version_test, wasm_test, webcrypto_test, + webgpu_test, websocket_test, webstorage_test, worker_permissions_test, diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index c96d68b93..32df04483 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -1660,6 +1660,17 @@ itest!(unstable_kv_enabled { output: "run/unstable_kv.enabled.out", }); +itest!(unstable_webgpu_disabled { + args: "run --quiet --reload --allow-read run/unstable_webgpu.js", + output: "run/unstable_webgpu.disabled.out", +}); + +itest!(unstable_webgpu_enabled { + args: + "run --quiet --reload --allow-read --unstable-webgpu run/unstable_webgpu.js", + output: "run/unstable_webgpu.enabled.out", +}); + itest!(import_compression { args: "run --quiet --reload --allow-net run/import_compression/main.ts", output: "run/import_compression/main.out", diff --git a/cli/tests/integration/shared_library_tests.rs b/cli/tests/integration/shared_library_tests.rs index 506c537f6..3e05f8efc 100644 --- a/cli/tests/integration/shared_library_tests.rs +++ b/cli/tests/integration/shared_library_tests.rs @@ -45,14 +45,25 @@ fn macos_shared_libraries() { // target/release/deno: // /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1953.1.0) // /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 1228.0.0) + // /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.11.0, weak) + // /System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 341.16.0, weak) + // /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics (compatibility version 64.0.0, current version 1774.0.4, weak) + // /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders (compatibility version 1.0.0, current version 127.0.19, weak) // /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0) // /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0) - const EXPECTED: [&str; 5] = [ - "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", - "/usr/lib/libiconv.2.dylib", - "/usr/lib/libSystem.B.dylib", - "/usr/lib/libobjc.A.dylib", + // /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0) + + // path and whether its weak or not + const EXPECTED: [(&str, bool); 9] = [ + ("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", false), + ("/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", false), + ("/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", true), + ("/System/Library/Frameworks/Metal.framework/Versions/A/Metal", true), + ("/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics", true), + ("/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders", true), + ("/usr/lib/libiconv.2.dylib", false), + ("/usr/lib/libSystem.B.dylib", false), + ("/usr/lib/libobjc.A.dylib", false), ]; let otool = std::process::Command::new("otool") @@ -64,9 +75,9 @@ fn macos_shared_libraries() { let output = std::str::from_utf8(&otool.stdout).unwrap(); // Ensure that the output contains only the expected shared libraries. for line in output.lines().skip(1) { - let path = line.split_whitespace().next().unwrap(); + let (path, attributes) = line.trim().split_once(' ').unwrap(); assert!( - EXPECTED.contains(&path), + EXPECTED.contains(&(path, attributes.ends_with("weak)"))), "Unexpected shared library: {}", path ); diff --git a/cli/tests/testdata/run/unstable_webgpu.disabled.out b/cli/tests/testdata/run/unstable_webgpu.disabled.out new file mode 100644 index 000000000..775866352 --- /dev/null +++ b/cli/tests/testdata/run/unstable_webgpu.disabled.out @@ -0,0 +1,2 @@ +main undefined +worker undefined diff --git a/cli/tests/testdata/run/unstable_webgpu.enabled.out b/cli/tests/testdata/run/unstable_webgpu.enabled.out new file mode 100644 index 000000000..e2cc915ba --- /dev/null +++ b/cli/tests/testdata/run/unstable_webgpu.enabled.out @@ -0,0 +1,2 @@ +main [class GPU] +worker [class GPU] diff --git a/cli/tests/testdata/run/unstable_webgpu.js b/cli/tests/testdata/run/unstable_webgpu.js new file mode 100644 index 000000000..a796b1c4d --- /dev/null +++ b/cli/tests/testdata/run/unstable_webgpu.js @@ -0,0 +1,10 @@ +const scope = import.meta.url.slice(-7) === "#worker" ? "worker" : "main"; + +console.log(scope, globalThis.GPU); + +if (scope === "worker") { + postMessage("done"); +} else { + const worker = new Worker(`${import.meta.url}#worker`, { type: "module" }); + worker.onmessage = () => Deno.exit(0); +} diff --git a/cli/tests/testdata/webgpu/computepass_shader.wgsl b/cli/tests/testdata/webgpu/computepass_shader.wgsl new file mode 100644 index 000000000..41af4363a --- /dev/null +++ b/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]); +} diff --git a/cli/tests/testdata/webgpu/hellotriangle.out b/cli/tests/testdata/webgpu/hellotriangle.out Binary files differnew file mode 100644 index 000000000..52972ec9e --- /dev/null +++ b/cli/tests/testdata/webgpu/hellotriangle.out diff --git a/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl b/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl new file mode 100644 index 000000000..f84ccfe94 --- /dev/null +++ b/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); +} diff --git a/cli/tests/unit/webgpu_test.ts b/cli/tests/unit/webgpu_test.ts new file mode 100644 index 000000000..2d98167cf --- /dev/null +++ b/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; +} 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; + } + } +} |