summaryrefslogtreecommitdiff
path: root/op_crates/webgpu/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'op_crates/webgpu/lib.rs')
-rw-r--r--op_crates/webgpu/lib.rs541
1 files changed, 541 insertions, 0 deletions
diff --git a/op_crates/webgpu/lib.rs b/op_crates/webgpu/lib.rs
new file mode 100644
index 000000000..2f1769daf
--- /dev/null
+++ b/op_crates/webgpu/lib.rs
@@ -0,0 +1,541 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+#![deny(warnings)]
+
+use deno_core::error::AnyError;
+use deno_core::error::{bad_resource_id, not_supported};
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::OpState;
+use deno_core::ZeroCopyBuf;
+use deno_core::{BufVec, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::path::PathBuf;
+use std::rc::Rc;
+pub use wgpu_core;
+pub use wgpu_types;
+
+use error::DOMExceptionOperationError;
+use error::WebGPUError;
+
+#[macro_use]
+mod macros {
+ macro_rules! gfx_select {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
+ match $id.backend() {
+ #[cfg(all(not(target_arch = "wasm32"), not(any(target_os = "ios", target_os = "macos"))))]
+ wgpu_types::Backend::Vulkan => $global.$method::<wgpu_core::backend::Vulkan>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
+ wgpu_types::Backend::Metal => $global.$method::<wgpu_core::backend::Metal>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx12 => $global.$method::<wgpu_core::backend::Dx12>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx11 => $global.$method::<wgpu_core::backend::Dx11>( $($param),* ),
+ #[cfg(any(target_arch = "wasm32", all(unix, not(any(target_os = "ios", target_os = "macos")))))]
+ wgpu_types::Backend::Gl => $global.$method::<wgpu_core::backend::Gl>( $($param),+ ),
+ other => panic!("Unexpected backend {:?}", other),
+ }
+ };
+ }
+}
+
+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;
+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 '{}'. The --unstable flag must be provided.",
+ api_name
+ );
+ std::process::exit(70);
+ }
+}
+
+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()
+ }
+}
+
+/// Execute this crates' JS source files.
+pub fn init(isolate: &mut deno_core::JsRuntime) {
+ let files = vec![
+ (
+ "deno:op_crates/webgpu/01_webgpu.js",
+ include_str!("01_webgpu.js"),
+ ),
+ (
+ "deno:op_crates/webgpu/02_idl_types.js",
+ include_str!("02_idl_types.js"),
+ ),
+ ];
+ for (url, source_code) in files {
+ isolate.execute(url, source_code).unwrap();
+ }
+}
+
+pub fn get_declaration() -> PathBuf {
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_webgpu.d.ts")
+}
+
+fn deserialize_features(features: &wgpu_types::Features) -> Vec<&str> {
+ let mut return_features: Vec<&str> = vec![];
+
+ if features.contains(wgpu_types::Features::DEPTH_CLAMPING) {
+ return_features.push("depth-clamping");
+ }
+ 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::TIMESTAMP_QUERY) {
+ return_features.push("timestamp-query");
+ }
+
+ // extended from spec
+ if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
+ return_features.push("mappable-primary-buffers");
+ }
+ if features.contains(wgpu_types::Features::SAMPLED_TEXTURE_BINDING_ARRAY) {
+ return_features.push("sampled-texture-binding-array");
+ }
+ if features
+ .contains(wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING)
+ {
+ return_features.push("sampled-texture-array-dynamic-indexing");
+ }
+ if features
+ .contains(wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING)
+ {
+ return_features.push("sampled-texture-array-non-uniform-indexing");
+ }
+ if features.contains(wgpu_types::Features::UNSIZED_BINDING_ARRAY) {
+ return_features.push("unsized-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_BORDER) {
+ return_features.push("address-mode-clamp-to-border");
+ }
+ if features.contains(wgpu_types::Features::NON_FILL_POLYGON_MODE) {
+ return_features.push("non-fill-polygon-mode");
+ }
+ 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-ldr");
+ }
+ 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");
+ }
+
+ return_features
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RequestAdapterArgs {
+ power_preference: Option<String>,
+}
+
+pub async fn op_webgpu_request_adapter(
+ state: Rc<RefCell<OpState>>,
+ args: RequestAdapterArgs,
+ _bufs: BufVec,
+) -> Result<Value, AnyError> {
+ let mut state = state.borrow_mut();
+ check_unstable(&state, "navigator.gpu.requestAdapter");
+ let instance = state.borrow::<Instance>();
+
+ let descriptor = wgpu_core::instance::RequestAdapterOptions {
+ power_preference: match args.power_preference {
+ Some(power_preference) => match power_preference.as_str() {
+ "low-power" => wgpu_types::PowerPreference::LowPower,
+ "high-performance" => wgpu_types::PowerPreference::HighPerformance,
+ _ => unreachable!(),
+ },
+ None => Default::default(),
+ },
+ compatible_surface: None, // windowless
+ };
+ let res = instance.request_adapter(
+ &descriptor,
+ wgpu_core::instance::AdapterInputs::Mask(
+ wgpu_types::BackendBit::PRIMARY,
+ |_| std::marker::PhantomData,
+ ),
+ );
+
+ let adapter = match res {
+ Ok(adapter) => adapter,
+ Err(err) => {
+ return Ok(json!({
+ "err": err.to_string()
+ }))
+ }
+ };
+ let name = gfx_select!(adapter => instance.adapter_get_info(adapter))?.name;
+ 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 limits = json!({
+ "maxBindGroups": adapter_limits.max_bind_groups,
+ "maxDynamicUniformBuffersPerPipelineLayout": adapter_limits.max_dynamic_uniform_buffers_per_pipeline_layout,
+ "maxDynamicStorageBuffersPerPipelineLayout": adapter_limits.max_dynamic_storage_buffers_per_pipeline_layout,
+ "maxSampledTexturesPerShaderStage": adapter_limits.max_sampled_textures_per_shader_stage,
+ "maxSamplersPerShaderStage": adapter_limits.max_samplers_per_shader_stage,
+ "maxStorageBuffersPerShaderStage": adapter_limits.max_storage_buffers_per_shader_stage,
+ "maxStorageTexturesPerShaderStage": adapter_limits.max_storage_textures_per_shader_stage,
+ "maxUniformBuffersPerShaderStage": adapter_limits.max_uniform_buffers_per_shader_stage,
+ "maxUniformBufferBindingSize": adapter_limits.max_uniform_buffer_binding_size
+ });
+
+ let rid = state.resource_table.add(WebGPUAdapter(adapter));
+
+ Ok(json!({
+ "rid": rid,
+ "name": name,
+ "features": features,
+ "limits": limits
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPULimits {
+ _max_texture_dimension1d: Option<u32>,
+ _max_texture_dimension2d: Option<u32>,
+ _max_texture_dimension3d: Option<u32>,
+ _max_texture_array_layers: Option<u32>,
+ max_bind_groups: Option<u32>,
+ max_dynamic_uniform_buffers_per_pipeline_layout: Option<u32>,
+ max_dynamic_storage_buffers_per_pipeline_layout: Option<u32>,
+ max_sampled_textures_per_shader_stage: Option<u32>,
+ max_samplers_per_shader_stage: Option<u32>,
+ max_storage_buffers_per_shader_stage: Option<u32>,
+ max_storage_textures_per_shader_stage: Option<u32>,
+ max_uniform_buffers_per_shader_stage: Option<u32>,
+ max_uniform_buffer_binding_size: Option<u32>,
+ _max_storage_buffer_binding_size: Option<u32>,
+ _max_vertex_buffers: Option<u32>,
+ _max_vertex_attributes: Option<u32>,
+ _max_vertex_buffer_array_stride: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RequestDeviceArgs {
+ adapter_rid: u32,
+ label: Option<String>,
+ non_guaranteed_features: Option<Vec<String>>,
+ non_guaranteed_limits: Option<GPULimits>,
+}
+
+pub async fn op_webgpu_request_device(
+ state: Rc<RefCell<OpState>>,
+ args: RequestDeviceArgs,
+ _bufs: BufVec,
+) -> Result<Value, AnyError> {
+ let mut state = state.borrow_mut();
+ let adapter_resource = state
+ .resource_table
+ .get::<WebGPUAdapter>(args.adapter_rid)
+ .ok_or_else(bad_resource_id)?;
+ let adapter = adapter_resource.0;
+ let instance = state.borrow::<Instance>();
+
+ let mut features: wgpu_types::Features = wgpu_types::Features::empty();
+
+ if let Some(passed_features) = args.non_guaranteed_features {
+ if passed_features.contains(&"depth-clamping".to_string()) {
+ features.set(wgpu_types::Features::DEPTH_CLAMPING, true);
+ }
+ if passed_features.contains(&"pipeline-statistics-query".to_string()) {
+ features.set(wgpu_types::Features::PIPELINE_STATISTICS_QUERY, true);
+ }
+ if passed_features.contains(&"texture-compression-bc".to_string()) {
+ features.set(wgpu_types::Features::TEXTURE_COMPRESSION_BC, true);
+ }
+ if passed_features.contains(&"timestamp-query".to_string()) {
+ features.set(wgpu_types::Features::TIMESTAMP_QUERY, true);
+ }
+
+ // extended from spec
+ if passed_features.contains(&"mappable-primary-buffers".to_string()) {
+ features.set(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS, true);
+ }
+ if passed_features.contains(&"sampled-texture-binding-array".to_string()) {
+ features.set(wgpu_types::Features::SAMPLED_TEXTURE_BINDING_ARRAY, true);
+ }
+ if passed_features
+ .contains(&"sampled-texture-array-dynamic-indexing".to_string())
+ {
+ features.set(
+ wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
+ true,
+ );
+ }
+ if passed_features
+ .contains(&"sampled-texture-array-non-uniform-indexing".to_string())
+ {
+ features.set(
+ wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ true,
+ );
+ }
+ if passed_features.contains(&"unsized-binding-array".to_string()) {
+ features.set(wgpu_types::Features::UNSIZED_BINDING_ARRAY, true);
+ }
+ if passed_features.contains(&"multi-draw-indirect".to_string()) {
+ features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT, true);
+ }
+ if passed_features.contains(&"multi-draw-indirect-count".to_string()) {
+ features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT, true);
+ }
+ if passed_features.contains(&"push-constants".to_string()) {
+ features.set(wgpu_types::Features::PUSH_CONSTANTS, true);
+ }
+ if passed_features.contains(&"address-mode-clamp-to-border".to_string()) {
+ features.set(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true);
+ }
+ if passed_features.contains(&"non-fill-polygon-mode".to_string()) {
+ features.set(wgpu_types::Features::NON_FILL_POLYGON_MODE, true);
+ }
+ if passed_features.contains(&"texture-compression-etc2".to_string()) {
+ features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2, true);
+ }
+ if passed_features.contains(&"texture-compression-astc-ldr".to_string()) {
+ features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR, true);
+ }
+ if passed_features
+ .contains(&"texture-adapter-specific-format-features".to_string())
+ {
+ features.set(
+ wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
+ true,
+ );
+ }
+ if passed_features.contains(&"shader-float64".to_string()) {
+ features.set(wgpu_types::Features::SHADER_FLOAT64, true);
+ }
+ if passed_features.contains(&"vertex-attribute-64bit".to_string()) {
+ features.set(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT, true);
+ }
+ }
+
+ let descriptor = wgpu_types::DeviceDescriptor {
+ label: args.label.map(Cow::from),
+ features,
+ limits: args
+ .non_guaranteed_limits
+ .map_or(Default::default(), |limits| wgpu_types::Limits {
+ max_bind_groups: limits.max_bind_groups.unwrap_or(4),
+ max_dynamic_uniform_buffers_per_pipeline_layout: limits
+ .max_dynamic_uniform_buffers_per_pipeline_layout
+ .unwrap_or(8),
+ max_dynamic_storage_buffers_per_pipeline_layout: limits
+ .max_dynamic_storage_buffers_per_pipeline_layout
+ .unwrap_or(4),
+ max_sampled_textures_per_shader_stage: limits
+ .max_sampled_textures_per_shader_stage
+ .unwrap_or(16),
+ max_samplers_per_shader_stage: limits
+ .max_samplers_per_shader_stage
+ .unwrap_or(16),
+ max_storage_buffers_per_shader_stage: limits
+ .max_storage_buffers_per_shader_stage
+ .unwrap_or(4),
+ max_storage_textures_per_shader_stage: limits
+ .max_storage_textures_per_shader_stage
+ .unwrap_or(4),
+ max_uniform_buffers_per_shader_stage: limits
+ .max_uniform_buffers_per_shader_stage
+ .unwrap_or(12),
+ max_uniform_buffer_binding_size: limits
+ .max_uniform_buffer_binding_size
+ .unwrap_or(16384),
+ max_push_constant_size: 0,
+ }),
+ };
+
+ 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),
+ std::marker::PhantomData
+ ));
+ 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 json_limits = json!({
+ "maxBindGroups": limits.max_bind_groups,
+ "maxDynamicUniformBuffersPerPipelineLayout": limits.max_dynamic_uniform_buffers_per_pipeline_layout,
+ "maxDynamicStorageBuffersPerPipelineLayout": limits.max_dynamic_storage_buffers_per_pipeline_layout,
+ "maxSampledTexturesPerShaderStage": limits.max_sampled_textures_per_shader_stage,
+ "maxSamplersPerShaderStage": limits.max_samplers_per_shader_stage,
+ "maxStorageBuffersPerShaderStage": limits.max_storage_buffers_per_shader_stage,
+ "maxStorageTexturesPerShaderStage": limits.max_storage_textures_per_shader_stage,
+ "maxUniformBuffersPerShaderStage": limits.max_uniform_buffers_per_shader_stage,
+ "maxUniformBufferBindingSize": limits.max_uniform_buffer_binding_size,
+ });
+
+ let rid = state.resource_table.add(WebGPUDevice(device));
+
+ Ok(json!({
+ "rid": rid,
+ "features": features,
+ "limits": json_limits,
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateQuerySetArgs {
+ device_rid: u32,
+ _label: Option<String>, // not yet implemented
+ #[serde(rename = "type")]
+ kind: String,
+ count: u32,
+ pipeline_statistics: Option<Vec<String>>,
+}
+
+pub fn op_webgpu_create_query_set(
+ state: &mut OpState,
+ args: CreateQuerySetArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let device_resource = state
+ .resource_table
+ .get::<WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+ let instance = &state.borrow::<Instance>();
+
+ let descriptor = wgpu_types::QuerySetDescriptor {
+ ty: match args.kind.as_str() {
+ "pipeline-statistics" => {
+ let mut pipeline_statistics_names =
+ wgpu_types::PipelineStatisticsTypes::empty();
+
+ if let Some(pipeline_statistics) = args.pipeline_statistics {
+ if pipeline_statistics
+ .contains(&"vertex-shader-invocations".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS,
+ true,
+ );
+ }
+ if pipeline_statistics.contains(&"clipper-invocations".to_string()) {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::CLIPPER_INVOCATIONS,
+ true,
+ );
+ }
+ if pipeline_statistics.contains(&"clipper-primitives-out".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT,
+ true,
+ );
+ }
+ if pipeline_statistics
+ .contains(&"fragment-shader-invocations".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS,
+ true,
+ );
+ }
+ if pipeline_statistics
+ .contains(&"compute-shader-invocations".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS,
+ true,
+ );
+ }
+ };
+
+ wgpu_types::QueryType::PipelineStatistics(pipeline_statistics_names)
+ }
+ "occlusion" => return Err(not_supported()),
+ "timestamp" => wgpu_types::QueryType::Timestamp,
+ _ => unreachable!(),
+ },
+ count: args.count,
+ };
+
+ let (query_set, maybe_err) = gfx_select!(device => instance.device_create_query_set(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUQuerySet(query_set));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from),
+ }))
+}