mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 23:34:47 -05:00
40febd9dd1
This PR contains the implementation of the External webgpu surfaces /
BYOW proposal. BYOW stands for "Bring your own window".
Closes #21713
Adds `Deno.UnsafeWindowSurface` ( `--unstable-webgpu` API) to the `Deno`
namespace:
```typescript
class UnsafeWindowSurface {
constructor(
system: "cocoa" | "x11" | "win32",
winHandle: Deno.PointerValue,
displayHandle: Deno.PointerValue | null
);
getContext(type: "webgpu"): GPUCanvasContext;
present(): void;
}
```
For the initial pass, I've opted to support the three major windowing
systems. The parameters correspond to the table below:
| system | winHandle | displayHandle |
| ----------------- | ---------- | ------- |
| "cocoa" (macOS) | `NSView*` | - |
| "win32" (Windows) | `HWND` | `HINSTANCE` |
| "x11" (Linux) | Xlib `Window` | Xlib `Display*` |
Ecosystem support:
- [x] deno_sdl2 (sdl2) -
[mod.ts#L1209](7e177bc652/mod.ts (L1209)
)
- [x] dwm (glfw) - https://github.com/deno-windowing/dwm/issues/29
- [ ] pane (winit)
<details>
<summary>Example</summary>
```typescript
// A simple clear screen pass, colors based on mouse position.
import { EventType, WindowBuilder } from "https://deno.land/x/sdl2@0.7.0/mod.ts";
const window = new WindowBuilder("sdl2 + deno + webgpu", 640, 480).build();
const [system, windowHandle, displayHandle] = window.rawHandle();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = Deno.createWindowSurface(system, windowHandle, displayHandle);
context.configure({
device: device,
format: "bgra8unorm",
height: 480,
width: 640,
});
let r = 0.0;
let g = 0.0;
let b = 0.0;
for (const event of window.events()) {
if (event.type === EventType.Quit) {
break;
} else if (event.type === EventType.Draw) {
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: { r, g, b, a: 1.0 },
loadOp: "clear",
storeOp: "store",
},
],
};
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
Deno.presentGPUCanvasContext(context);
}
if (event.type === EventType.MouseMotion) {
r = event.x / 640;
g = event.y / 480;
b = 1.0 - r - g;
}
}
```
You can find more examples in the linked tracking issue.
</details>
---------
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
775 lines
26 KiB
Rust
775 lines
26 KiB
Rust
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
#![cfg(not(target_arch = "wasm32"))]
|
|
#![warn(unsafe_op_in_unsafe_fn)]
|
|
|
|
use deno_core::error::AnyError;
|
|
use deno_core::op2;
|
|
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::rc::Rc;
|
|
pub use wgpu_core;
|
|
pub use wgpu_types;
|
|
|
|
use error::DomExceptionOperationError;
|
|
use error::WebGpuResult;
|
|
|
|
pub const UNSTABLE_FEATURE_NAME: &str = "webgpu";
|
|
|
|
#[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($global.clone(), 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 byow;
|
|
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;
|
|
pub mod surface;
|
|
pub mod texture;
|
|
|
|
pub type Instance = std::sync::Arc<
|
|
wgpu_core::global::Global<wgpu_core::identity::IdentityManagerFactory>,
|
|
>;
|
|
|
|
struct WebGpuAdapter(Instance, wgpu_core::id::AdapterId);
|
|
impl Resource for WebGpuAdapter {
|
|
fn name(&self) -> Cow<str> {
|
|
"webGPUAdapter".into()
|
|
}
|
|
|
|
fn close(self: Rc<Self>) {
|
|
let instance = &self.0;
|
|
gfx_select!(self.1 => instance.adapter_drop(self.1));
|
|
}
|
|
}
|
|
|
|
struct WebGpuDevice(Instance, wgpu_core::id::DeviceId);
|
|
impl Resource for WebGpuDevice {
|
|
fn name(&self) -> Cow<str> {
|
|
"webGPUDevice".into()
|
|
}
|
|
|
|
fn close(self: Rc<Self>) {
|
|
let instance = &self.0;
|
|
gfx_select!(self.1 => instance.device_drop(self.1));
|
|
}
|
|
}
|
|
|
|
struct WebGpuQuerySet(Instance, wgpu_core::id::QuerySetId);
|
|
impl Resource for WebGpuQuerySet {
|
|
fn name(&self) -> Cow<str> {
|
|
"webGPUQuerySet".into()
|
|
}
|
|
|
|
fn close(self: Rc<Self>) {
|
|
let instance = &self.0;
|
|
gfx_select!(self.1 => instance.query_set_drop(self.1));
|
|
}
|
|
}
|
|
|
|
deno_core::extension!(
|
|
deno_webgpu,
|
|
deps = [deno_webidl, deno_web],
|
|
ops = [
|
|
// Request device/adapter
|
|
op_webgpu_request_adapter,
|
|
op_webgpu_request_device,
|
|
op_webgpu_request_adapter_info,
|
|
// Query Set
|
|
op_webgpu_create_query_set,
|
|
// buffer
|
|
buffer::op_webgpu_create_buffer,
|
|
buffer::op_webgpu_buffer_get_mapped_range,
|
|
buffer::op_webgpu_buffer_unmap,
|
|
// buffer async
|
|
buffer::op_webgpu_buffer_get_map_async,
|
|
// remaining sync ops
|
|
|
|
// texture
|
|
texture::op_webgpu_create_texture,
|
|
texture::op_webgpu_create_texture_view,
|
|
// sampler
|
|
sampler::op_webgpu_create_sampler,
|
|
// binding
|
|
binding::op_webgpu_create_bind_group_layout,
|
|
binding::op_webgpu_create_pipeline_layout,
|
|
binding::op_webgpu_create_bind_group,
|
|
// pipeline
|
|
pipeline::op_webgpu_create_compute_pipeline,
|
|
pipeline::op_webgpu_compute_pipeline_get_bind_group_layout,
|
|
pipeline::op_webgpu_create_render_pipeline,
|
|
pipeline::op_webgpu_render_pipeline_get_bind_group_layout,
|
|
// command_encoder
|
|
command_encoder::op_webgpu_create_command_encoder,
|
|
command_encoder::op_webgpu_command_encoder_begin_render_pass,
|
|
command_encoder::op_webgpu_command_encoder_begin_compute_pass,
|
|
command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer,
|
|
command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture,
|
|
command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer,
|
|
command_encoder::op_webgpu_command_encoder_copy_texture_to_texture,
|
|
command_encoder::op_webgpu_command_encoder_clear_buffer,
|
|
command_encoder::op_webgpu_command_encoder_push_debug_group,
|
|
command_encoder::op_webgpu_command_encoder_pop_debug_group,
|
|
command_encoder::op_webgpu_command_encoder_insert_debug_marker,
|
|
command_encoder::op_webgpu_command_encoder_write_timestamp,
|
|
command_encoder::op_webgpu_command_encoder_resolve_query_set,
|
|
command_encoder::op_webgpu_command_encoder_finish,
|
|
render_pass::op_webgpu_render_pass_set_viewport,
|
|
render_pass::op_webgpu_render_pass_set_scissor_rect,
|
|
render_pass::op_webgpu_render_pass_set_blend_constant,
|
|
render_pass::op_webgpu_render_pass_set_stencil_reference,
|
|
render_pass::op_webgpu_render_pass_begin_occlusion_query,
|
|
render_pass::op_webgpu_render_pass_end_occlusion_query,
|
|
render_pass::op_webgpu_render_pass_execute_bundles,
|
|
render_pass::op_webgpu_render_pass_end,
|
|
render_pass::op_webgpu_render_pass_set_bind_group,
|
|
render_pass::op_webgpu_render_pass_push_debug_group,
|
|
render_pass::op_webgpu_render_pass_pop_debug_group,
|
|
render_pass::op_webgpu_render_pass_insert_debug_marker,
|
|
render_pass::op_webgpu_render_pass_set_pipeline,
|
|
render_pass::op_webgpu_render_pass_set_index_buffer,
|
|
render_pass::op_webgpu_render_pass_set_vertex_buffer,
|
|
render_pass::op_webgpu_render_pass_draw,
|
|
render_pass::op_webgpu_render_pass_draw_indexed,
|
|
render_pass::op_webgpu_render_pass_draw_indirect,
|
|
render_pass::op_webgpu_render_pass_draw_indexed_indirect,
|
|
compute_pass::op_webgpu_compute_pass_set_pipeline,
|
|
compute_pass::op_webgpu_compute_pass_dispatch_workgroups,
|
|
compute_pass::op_webgpu_compute_pass_dispatch_workgroups_indirect,
|
|
compute_pass::op_webgpu_compute_pass_end,
|
|
compute_pass::op_webgpu_compute_pass_set_bind_group,
|
|
compute_pass::op_webgpu_compute_pass_push_debug_group,
|
|
compute_pass::op_webgpu_compute_pass_pop_debug_group,
|
|
compute_pass::op_webgpu_compute_pass_insert_debug_marker,
|
|
// bundle
|
|
bundle::op_webgpu_create_render_bundle_encoder,
|
|
bundle::op_webgpu_render_bundle_encoder_finish,
|
|
bundle::op_webgpu_render_bundle_encoder_set_bind_group,
|
|
bundle::op_webgpu_render_bundle_encoder_push_debug_group,
|
|
bundle::op_webgpu_render_bundle_encoder_pop_debug_group,
|
|
bundle::op_webgpu_render_bundle_encoder_insert_debug_marker,
|
|
bundle::op_webgpu_render_bundle_encoder_set_pipeline,
|
|
bundle::op_webgpu_render_bundle_encoder_set_index_buffer,
|
|
bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer,
|
|
bundle::op_webgpu_render_bundle_encoder_draw,
|
|
bundle::op_webgpu_render_bundle_encoder_draw_indexed,
|
|
bundle::op_webgpu_render_bundle_encoder_draw_indirect,
|
|
// queue
|
|
queue::op_webgpu_queue_submit,
|
|
queue::op_webgpu_write_buffer,
|
|
queue::op_webgpu_write_texture,
|
|
// shader
|
|
shader::op_webgpu_create_shader_module,
|
|
// surface
|
|
surface::op_webgpu_surface_configure,
|
|
surface::op_webgpu_surface_get_current_texture,
|
|
surface::op_webgpu_surface_present,
|
|
// byow
|
|
byow::op_webgpu_surface_create,
|
|
],
|
|
esm = ["00_init.js", "02_surface.js"],
|
|
lazy_loaded_esm = ["01_webgpu.js"],
|
|
);
|
|
|
|
fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
|
|
let mut return_features: Vec<&'static str> = vec![];
|
|
|
|
// api
|
|
if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
|
|
return_features.push("depth-clip-control");
|
|
}
|
|
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");
|
|
}
|
|
// shader
|
|
if features.contains(wgpu_types::Features::SHADER_F16) {
|
|
return_features.push("shader-f16");
|
|
}
|
|
// texture formats
|
|
if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) {
|
|
return_features.push("depth32float-stencil8");
|
|
}
|
|
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) {
|
|
return_features.push("texture-compression-astc");
|
|
}
|
|
if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) {
|
|
return_features.push("rg11b10ufloat-renderable");
|
|
}
|
|
if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) {
|
|
return_features.push("bgra8unorm-storage");
|
|
}
|
|
|
|
// extended from spec
|
|
|
|
// texture formats
|
|
if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) {
|
|
return_features.push("texture-format-16-bit-norm");
|
|
}
|
|
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
|
|
return_features.push("texture-compression-astc-hdr");
|
|
}
|
|
if features
|
|
.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
|
|
{
|
|
return_features.push("texture-adapter-specific-format-features");
|
|
}
|
|
// api
|
|
if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
|
|
return_features.push("pipeline-statistics-query");
|
|
}
|
|
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
|
|
return_features.push("timestamp-query-inside-passes");
|
|
}
|
|
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-texture-array-non-uniform-indexing");
|
|
}
|
|
if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
|
|
return_features.push("partially-bound-binding-array");
|
|
}
|
|
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
|
|
return_features.push("multi-draw-indirect");
|
|
}
|
|
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
|
|
return_features.push("multi-draw-indirect-count");
|
|
}
|
|
if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
|
|
return_features.push("push-constants");
|
|
}
|
|
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) {
|
|
return_features.push("address-mode-clamp-to-zero");
|
|
}
|
|
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::POLYGON_MODE_LINE) {
|
|
return_features.push("polygon-mode-line");
|
|
}
|
|
if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) {
|
|
return_features.push("polygon-mode-point");
|
|
}
|
|
if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) {
|
|
return_features.push("conservative-rasterization");
|
|
}
|
|
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::SPIRV_SHADER_PASSTHROUGH) {
|
|
return_features.push("spirv-shader-passthrough");
|
|
}
|
|
if features.contains(wgpu_types::Features::MULTIVIEW) {
|
|
return_features.push("multiview");
|
|
}
|
|
if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
|
|
return_features.push("vertex-attribute-64-bit");
|
|
}
|
|
// shader
|
|
if features.contains(wgpu_types::Features::SHADER_F64) {
|
|
return_features.push("shader-f64");
|
|
}
|
|
if features.contains(wgpu_types::Features::SHADER_I16) {
|
|
return_features.push("shader-i16");
|
|
}
|
|
if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
|
|
return_features.push("shader-primitive-index");
|
|
}
|
|
if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) {
|
|
return_features.push("shader-early-depth-test");
|
|
}
|
|
if features.contains(wgpu_types::Features::SHADER_UNUSED_VERTEX_OUTPUT) {
|
|
return_features.push("shader-unused-vertex-output");
|
|
}
|
|
|
|
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,
|
|
}
|
|
|
|
#[op2(async)]
|
|
#[serde]
|
|
pub async fn op_webgpu_request_adapter(
|
|
state: Rc<RefCell<OpState>>,
|
|
#[serde] power_preference: Option<wgpu_types::PowerPreference>,
|
|
force_fallback_adapter: bool,
|
|
) -> Result<GpuAdapterDeviceOrErr, AnyError> {
|
|
let mut state = state.borrow_mut();
|
|
|
|
// TODO(bartlomieju): replace with `state.feature_checker.check_or_exit`
|
|
// once we phase out `check_or_exit_with_legacy_fallback`
|
|
state.feature_checker.check_or_exit_with_legacy_fallback(
|
|
UNSTABLE_FEATURE_NAME,
|
|
"navigator.gpu.requestAdapter",
|
|
);
|
|
|
|
let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else(
|
|
|_| wgpu_types::Backends::all(),
|
|
|s| wgpu_core::instance::parse_backends_from_comma_list(&s),
|
|
);
|
|
let instance = if let Some(instance) = state.try_borrow::<Instance>() {
|
|
instance
|
|
} else {
|
|
state.put(std::sync::Arc::new(wgpu_core::global::Global::new(
|
|
"webgpu",
|
|
wgpu_core::identity::IdentityManagerFactory,
|
|
wgpu_types::InstanceDescriptor {
|
|
backends,
|
|
flags: wgpu_types::InstanceFlags::from_build_config(),
|
|
dx12_shader_compiler: wgpu_types::Dx12Compiler::Fxc,
|
|
gles_minor_version: wgpu_types::Gles3MinorVersion::default(),
|
|
},
|
|
)));
|
|
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 instance = instance.clone();
|
|
|
|
let rid = state.resource_table.add(WebGpuAdapter(instance, 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();
|
|
// api
|
|
features.set(
|
|
wgpu_types::Features::DEPTH_CLIP_CONTROL,
|
|
required_features.0.contains("depth-clip-control"),
|
|
);
|
|
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"),
|
|
);
|
|
// shader
|
|
features.set(
|
|
wgpu_types::Features::SHADER_F16,
|
|
required_features.0.contains("shader-f16"),
|
|
);
|
|
// texture formats
|
|
features.set(
|
|
wgpu_types::Features::DEPTH32FLOAT_STENCIL8,
|
|
required_features.0.contains("depth32float-stencil8"),
|
|
);
|
|
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,
|
|
required_features.0.contains("texture-compression-astc"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::RG11B10UFLOAT_RENDERABLE,
|
|
required_features.0.contains("rg11b10ufloat-renderable"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::BGRA8UNORM_STORAGE,
|
|
required_features.0.contains("bgra8unorm-storage"),
|
|
);
|
|
|
|
// extended from spec
|
|
|
|
// texture formats
|
|
features.set(
|
|
wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM,
|
|
required_features.0.contains("texture-format-16-bit-norm"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR,
|
|
required_features.0.contains("texture-compression-astc-hdr"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
|
required_features
|
|
.0
|
|
.contains("texture-adapter-specific-format-features"),
|
|
);
|
|
// api
|
|
features.set(
|
|
wgpu_types::Features::PIPELINE_STATISTICS_QUERY,
|
|
required_features.0.contains("pipeline-statistics-query"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
|
required_features
|
|
.0
|
|
.contains("timestamp-query-inside-passes"),
|
|
);
|
|
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-texture-array-non-uniform-indexing"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY,
|
|
required_features
|
|
.0
|
|
.contains("partially-bound-binding-array"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::MULTI_DRAW_INDIRECT,
|
|
required_features.0.contains("multi-draw-indirect"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT,
|
|
required_features.0.contains("multi-draw-indirect-count"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::PUSH_CONSTANTS,
|
|
required_features.0.contains("push-constants"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
|
|
required_features.0.contains("address-mode-clamp-to-zero"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
|
|
required_features.0.contains("address-mode-clamp-to-border"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::POLYGON_MODE_LINE,
|
|
required_features.0.contains("polygon-mode-line"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::POLYGON_MODE_POINT,
|
|
required_features.0.contains("polygon-mode-point"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::CONSERVATIVE_RASTERIZATION,
|
|
required_features.0.contains("conservative-rasterization"),
|
|
);
|
|
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-texture"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH,
|
|
required_features.0.contains("spirv-shader-passthrough"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::MULTIVIEW,
|
|
required_features.0.contains("multiview"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT,
|
|
required_features.0.contains("vertex-attribute-64-bit"),
|
|
);
|
|
// shader
|
|
features.set(
|
|
wgpu_types::Features::SHADER_F64,
|
|
required_features.0.contains("shader-f64"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::SHADER_I16,
|
|
required_features.0.contains("shader-i16"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::SHADER_PRIMITIVE_INDEX,
|
|
required_features.0.contains("shader-primitive-index"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::SHADER_EARLY_DEPTH_TEST,
|
|
required_features.0.contains("shader-early-depth-test"),
|
|
);
|
|
features.set(
|
|
wgpu_types::Features::SHADER_UNUSED_VERTEX_OUTPUT,
|
|
required_features.0.contains("shader-unused-vertex-output"),
|
|
);
|
|
|
|
features
|
|
}
|
|
}
|
|
|
|
#[op2(async)]
|
|
#[serde]
|
|
pub async fn op_webgpu_request_device(
|
|
state: Rc<RefCell<OpState>>,
|
|
#[smi] adapter_rid: ResourceId,
|
|
#[string] label: String,
|
|
#[serde] required_features: GpuRequiredFeatures,
|
|
#[serde] 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.1;
|
|
let instance = state.borrow::<Instance>();
|
|
|
|
let descriptor = wgpu_types::DeviceDescriptor {
|
|
label: Some(Cow::Owned(label)),
|
|
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 instance = instance.clone();
|
|
let rid = state.resource_table.add(WebGpuDevice(instance, 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,
|
|
}
|
|
|
|
#[op2(async)]
|
|
#[serde]
|
|
pub async fn op_webgpu_request_adapter_info(
|
|
state: Rc<RefCell<OpState>>,
|
|
#[smi] 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.1;
|
|
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: String,
|
|
#[serde(flatten)]
|
|
r#type: GpuQueryType,
|
|
count: u32,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "kebab-case", tag = "type")]
|
|
enum GpuQueryType {
|
|
Occlusion,
|
|
Timestamp,
|
|
}
|
|
|
|
impl From<GpuQueryType> for wgpu_types::QueryType {
|
|
fn from(query_type: GpuQueryType) -> Self {
|
|
match query_type {
|
|
GpuQueryType::Occlusion => wgpu_types::QueryType::Occlusion,
|
|
GpuQueryType::Timestamp => wgpu_types::QueryType::Timestamp,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[op2]
|
|
#[serde]
|
|
pub fn op_webgpu_create_query_set(
|
|
state: &mut OpState,
|
|
#[serde] args: CreateQuerySetArgs,
|
|
) -> Result<WebGpuResult, AnyError> {
|
|
let device_resource =
|
|
state.resource_table.get::<WebGpuDevice>(args.device_rid)?;
|
|
let device = device_resource.1;
|
|
let instance = state.borrow::<Instance>();
|
|
|
|
let descriptor = wgpu_types::QuerySetDescriptor {
|
|
label: Some(Cow::Owned(args.label)),
|
|
ty: args.r#type.into(),
|
|
count: args.count,
|
|
};
|
|
|
|
gfx_put!(device => instance.device_create_query_set(
|
|
device,
|
|
&descriptor,
|
|
()
|
|
) => state, WebGpuQuerySet)
|
|
}
|