// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; use deno_core::include_js_files; use deno_core::op_async; use deno_core::op_sync; use deno_core::Extension; use deno_core::OpFn; 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::path::PathBuf; use std::rc::Rc; pub use wgpu_core; pub use wgpu_types; use wgpu_types::PowerPreference; 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(not(target_os = "macos"))] wgpu_types::Backend::Vulkan => $global.$method::( $($param),* ), #[cfg(target_os = "macos")] wgpu_types::Backend::Metal => $global.$method::( $($param),* ), #[cfg(windows)] wgpu_types::Backend::Dx12 => $global.$method::( $($param),* ), #[cfg(all(unix, not(target_os = "macos")))] wgpu_types::Backend::Gl => $global.$method::( $($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; pub mod texture; pub struct Unstable(pub bool); fn check_unstable(state: &OpState, api_name: &str) { let unstable = state.borrow::(); if !unstable.0 { eprintln!( "Unstable API '{}'. The --unstable flag must be provided.", api_name ); std::process::exit(70); } } type Instance = wgpu_core::hub::Global; struct WebGpuAdapter(wgpu_core::id::AdapterId); impl Resource for WebGpuAdapter { fn name(&self) -> Cow { "webGPUAdapter".into() } } struct WebGpuDevice(wgpu_core::id::DeviceId); impl Resource for WebGpuDevice { fn name(&self) -> Cow { "webGPUDevice".into() } } struct WebGpuQuerySet(wgpu_core::id::QuerySetId); impl Resource for WebGpuQuerySet { fn name(&self) -> Cow { "webGPUQuerySet".into() } } pub fn init(unstable: bool) -> Extension { Extension::builder() .js(include_js_files!( prefix "deno:ext/webgpu", "01_webgpu.js", "02_idl_types.js", )) .ops(declare_webgpu_ops()) .state(move |state| { // TODO: check & possibly streamline this // Unstable might be able to be OpMiddleware // let unstable_checker = state.borrow::(); // let unstable = unstable_checker.unstable; state.put(Unstable(unstable)); Ok(()) }) .build() } 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<&'static str> { let mut return_features: Vec<&'static 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::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::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"); } 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_COMMANDS) { return_features.push("clear-commands"); } if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) { return_features.push("spirv-shader-passthrough"); } if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) { return_features.push("shader-primitive-index"); } return_features } #[derive(Deserialize)] #[serde(rename_all = "kebab-case")] enum GpuPowerPreference { LowPower, HighPerformance, } impl From for wgpu_types::PowerPreference { fn from(value: GpuPowerPreference) -> wgpu_types::PowerPreference { match value { GpuPowerPreference::LowPower => wgpu_types::PowerPreference::LowPower, GpuPowerPreference::HighPerformance => { wgpu_types::PowerPreference::HighPerformance } } } } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct RequestAdapterArgs { power_preference: Option, } #[derive(Serialize)] #[serde(untagged)] pub enum GpuAdapterDeviceOrErr { Error { err: String }, Features(GpuAdapterDevice), } #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct GpuAdapterDevice { rid: ResourceId, name: Option, limits: wgpu_types::Limits, features: Vec<&'static str>, is_software: bool, } pub async fn op_webgpu_request_adapter( state: Rc>, args: RequestAdapterArgs, _: (), ) -> Result { let mut state = state.borrow_mut(); check_unstable(&state, "navigator.gpu.requestAdapter"); let instance = if let Some(instance) = state.try_borrow::() { instance } else { state.put(wgpu_core::hub::Global::new( "webgpu", wgpu_core::hub::IdentityManagerFactory, wgpu_types::Backends::PRIMARY, )); state.borrow::() }; let descriptor = wgpu_core::instance::RequestAdapterOptions { power_preference: match args.power_preference { Some(power_preference) => power_preference.into(), None => PowerPreference::default(), }, // TODO(lucacasonato): respect forceFallbackAdapter compatible_surface: None, // windowless }; let res = instance.request_adapter( &descriptor, wgpu_core::instance::AdapterInputs::Mask( wgpu_types::Backends::PRIMARY, |_| std::marker::PhantomData, ), ); let adapter = match res { Ok(adapter) => adapter, Err(err) => { return Ok(GpuAdapterDeviceOrErr::Error { 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 rid = state.resource_table.add(WebGpuAdapter(adapter)); Ok(GpuAdapterDeviceOrErr::Features(GpuAdapterDevice { rid, name: Some(name), features, limits: adapter_limits, is_software: false, })) } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct GpuLimits { max_texture_dimension_1d: Option, max_texture_dimension_2d: Option, max_texture_dimension_3d: Option, max_texture_array_layers: Option, max_bind_groups: Option, max_dynamic_uniform_buffers_per_pipeline_layout: Option, max_dynamic_storage_buffers_per_pipeline_layout: Option, max_sampled_textures_per_shader_stage: Option, max_samplers_per_shader_stage: Option, max_storage_buffers_per_shader_stage: Option, max_storage_textures_per_shader_stage: Option, max_uniform_buffers_per_shader_stage: Option, max_uniform_buffer_binding_size: Option, // TODO(@crowlkats): u64 max_storage_buffer_binding_size: Option, // TODO(@crowlkats): u64 // min_uniform_buffer_offset_alignment: Option, // min_storage_buffer_offset_alignment: Option, max_vertex_buffers: Option, max_vertex_attributes: Option, max_vertex_buffer_array_stride: Option, // max_inter_stage_shader_components: Option, // max_compute_workgroup_storage_size: Option, // max_compute_invocations_per_workgroup: Option, // max_compute_workgroup_size_x: Option, // max_compute_workgroup_size_y: Option, // max_compute_workgroup_size_z: Option, // max_compute_workgroups_per_dimension: Option, } impl From for wgpu_types::Limits { fn from(limits: GpuLimits) -> wgpu_types::Limits { wgpu_types::Limits { max_texture_dimension_1d: limits.max_texture_dimension_1d.unwrap_or(8192), max_texture_dimension_2d: limits.max_texture_dimension_2d.unwrap_or(8192), max_texture_dimension_3d: limits.max_texture_dimension_3d.unwrap_or(2048), max_texture_array_layers: limits.max_texture_array_layers.unwrap_or(2048), 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_storage_buffer_binding_size: limits .max_storage_buffer_binding_size .unwrap_or(134217728), // min_uniform_buffer_offset_alignment: limits // .min_uniform_buffer_offset_alignment // .unwrap_or(default), // min_storage_buffer_offset_alignment: limits // .min_storage_buffer_offset_alignment // .unwrap_or(default), max_vertex_buffers: limits.max_vertex_buffers.unwrap_or(8), max_vertex_attributes: limits.max_vertex_attributes.unwrap_or(16), max_vertex_buffer_array_stride: limits .max_vertex_buffer_array_stride .unwrap_or(2048), // max_inter_stage_shader_components: limits // .max_inter_stage_shader_components // .unwrap_or(default), // max_compute_workgroup_storage_size: limits // .max_compute_workgroup_storage_size // .unwrap_or(default), // max_compute_invocations_per_workgroup: limits // .max_compute_invocations_per_workgroup // .unwrap_or(default), // max_compute_workgroup_size_x: limits // .max_compute_workgroup_size_x // .unwrap_or(default), // max_compute_workgroup_size_y: limits // .max_compute_workgroup_size_y // .unwrap_or(default), // max_compute_workgroup_size_z: limits // .max_compute_workgroup_size_z // .unwrap_or(default), // max_compute_workgroups_per_dimension: limits // .max_compute_workgroups_per_dimension // .unwrap_or(default), max_push_constant_size: 0, } } } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct RequestDeviceArgs { adapter_rid: ResourceId, label: Option, required_features: Option, required_limits: Option, } #[derive(Deserialize)] pub struct GpuRequiredFeatures(HashSet); impl From for wgpu_types::Features { fn from(required_features: GpuRequiredFeatures) -> wgpu_types::Features { let mut features: wgpu_types::Features = wgpu_types::Features::empty(); if required_features.0.contains("depth-clamping") { features.set(wgpu_types::Features::DEPTH_CLAMPING, true); } if required_features.0.contains("pipeline-statistics-query") { features.set(wgpu_types::Features::PIPELINE_STATISTICS_QUERY, true); } if required_features.0.contains("texture-compression-bc") { features.set(wgpu_types::Features::TEXTURE_COMPRESSION_BC, true); } if required_features.0.contains("timestamp-query") { features.set(wgpu_types::Features::TIMESTAMP_QUERY, true); } // extended from spec if required_features.0.contains("mappable-primary-buffers") { features.set(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS, true); } if required_features.0.contains("texture-binding-array") { features.set(wgpu_types::Features::TEXTURE_BINDING_ARRAY, true); } if required_features.0.contains("buffer-binding-array") { features.set(wgpu_types::Features::BUFFER_BINDING_ARRAY, true); } if required_features .0 .contains("storage-resource-binding-array") { features.set(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY, true); } if required_features .0 .contains("sampled-texture-and-storage-buffer-array-non-uniform-indexing") { features.set(wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, true); } if required_features.0.contains( "uniform-buffer-and-storage-buffer-texture-non-uniform-indexing", ) { features.set(wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, true); } if required_features.0.contains("unsized-binding-array") { features.set(wgpu_types::Features::UNSIZED_BINDING_ARRAY, true); } if required_features.0.contains("multi-draw-indirect") { features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT, true); } if required_features.0.contains("multi-draw-indirect-count") { features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT, true); } if required_features.0.contains("push-constants") { features.set(wgpu_types::Features::PUSH_CONSTANTS, true); } if required_features.0.contains("address-mode-clamp-to-border") { features.set(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true); } if required_features.0.contains("non-fill-polygon-mode") { features.set(wgpu_types::Features::NON_FILL_POLYGON_MODE, true); } if required_features.0.contains("texture-compression-etc2") { features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2, true); } if required_features.0.contains("texture-compression-astc-ldr") { features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR, true); } if required_features .0 .contains("texture-adapter-specific-format-features") { features.set( wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, true, ); } if required_features.0.contains("shader-float64") { features.set(wgpu_types::Features::SHADER_FLOAT64, true); } if required_features.0.contains("vertex-attribute-64bit") { features.set(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT, true); } if required_features.0.contains("conservative-rasterization") { features.set(wgpu_types::Features::CONSERVATIVE_RASTERIZATION, true); } if required_features.0.contains("vertex-writable-storage") { features.set(wgpu_types::Features::VERTEX_WRITABLE_STORAGE, true); } if required_features.0.contains("clear-commands") { features.set(wgpu_types::Features::CLEAR_COMMANDS, true); } if required_features.0.contains("spirv-shader-passthrough") { features.set(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH, true); } if required_features.0.contains("shader-primitive-index") { features.set(wgpu_types::Features::SHADER_PRIMITIVE_INDEX, true); } features } } pub async fn op_webgpu_request_device( state: Rc>, args: RequestDeviceArgs, _: (), ) -> Result { let mut state = state.borrow_mut(); let adapter_resource = state .resource_table .get::(args.adapter_rid)?; let adapter = adapter_resource.0; let instance = state.borrow::(); let descriptor = wgpu_types::DeviceDescriptor { label: args.label.map(Cow::from), features: args.required_features.map(Into::into).unwrap_or_default(), limits: args.required_limits.map(Into::into).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), 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 rid = state.resource_table.add(WebGpuDevice(device)); Ok(GpuAdapterDevice { rid, name: None, features, limits, // TODO(lucacasonato): report correctly from wgpu is_software: false, }) } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct CreateQuerySetArgs { device_rid: ResourceId, label: Option, #[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, }, Timestamp, } impl From 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, } } } pub fn op_webgpu_create_query_set( state: &mut OpState, args: CreateQuerySetArgs, _: (), ) -> Result { let device_resource = state.resource_table.get::(args.device_rid)?; let device = device_resource.0; let instance = &state.borrow::(); 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, std::marker::PhantomData ) => state, WebGpuQuerySet) } fn declare_webgpu_ops() -> Vec<(&'static str, Box)> { vec![ // Request device/adapter ( "op_webgpu_request_adapter", op_async(op_webgpu_request_adapter), ), ( "op_webgpu_request_device", op_async(op_webgpu_request_device), ), // Query Set ( "op_webgpu_create_query_set", op_sync(op_webgpu_create_query_set), ), // buffer ( "op_webgpu_create_buffer", op_sync(buffer::op_webgpu_create_buffer), ), ( "op_webgpu_buffer_get_mapped_range", op_sync(buffer::op_webgpu_buffer_get_mapped_range), ), ( "op_webgpu_buffer_unmap", op_sync(buffer::op_webgpu_buffer_unmap), ), // buffer async ( "op_webgpu_buffer_get_map_async", op_async(buffer::op_webgpu_buffer_get_map_async), ), // remaining sync ops // texture ( "op_webgpu_create_texture", op_sync(texture::op_webgpu_create_texture), ), ( "op_webgpu_create_texture_view", op_sync(texture::op_webgpu_create_texture_view), ), // sampler ( "op_webgpu_create_sampler", op_sync(sampler::op_webgpu_create_sampler), ), // binding ( "op_webgpu_create_bind_group_layout", op_sync(binding::op_webgpu_create_bind_group_layout), ), ( "op_webgpu_create_pipeline_layout", op_sync(binding::op_webgpu_create_pipeline_layout), ), ( "op_webgpu_create_bind_group", op_sync(binding::op_webgpu_create_bind_group), ), // pipeline ( "op_webgpu_create_compute_pipeline", op_sync(pipeline::op_webgpu_create_compute_pipeline), ), ( "op_webgpu_compute_pipeline_get_bind_group_layout", op_sync(pipeline::op_webgpu_compute_pipeline_get_bind_group_layout), ), ( "op_webgpu_create_render_pipeline", op_sync(pipeline::op_webgpu_create_render_pipeline), ), ( "op_webgpu_render_pipeline_get_bind_group_layout", op_sync(pipeline::op_webgpu_render_pipeline_get_bind_group_layout), ), // command_encoder ( "op_webgpu_create_command_encoder", op_sync(command_encoder::op_webgpu_create_command_encoder), ), ( "op_webgpu_command_encoder_begin_render_pass", op_sync(command_encoder::op_webgpu_command_encoder_begin_render_pass), ), ( "op_webgpu_command_encoder_begin_compute_pass", op_sync(command_encoder::op_webgpu_command_encoder_begin_compute_pass), ), ( "op_webgpu_command_encoder_copy_buffer_to_buffer", op_sync(command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer), ), ( "op_webgpu_command_encoder_copy_buffer_to_texture", op_sync( command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture, ), ), ( "op_webgpu_command_encoder_copy_texture_to_buffer", op_sync( command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer, ), ), ( "op_webgpu_command_encoder_copy_texture_to_texture", op_sync( command_encoder::op_webgpu_command_encoder_copy_texture_to_texture, ), ), ( "op_webgpu_command_encoder_push_debug_group", op_sync(command_encoder::op_webgpu_command_encoder_push_debug_group), ), ( "op_webgpu_command_encoder_pop_debug_group", op_sync(command_encoder::op_webgpu_command_encoder_pop_debug_group), ), ( "op_webgpu_command_encoder_insert_debug_marker", op_sync(command_encoder::op_webgpu_command_encoder_insert_debug_marker), ), ( "op_webgpu_command_encoder_write_timestamp", op_sync(command_encoder::op_webgpu_command_encoder_write_timestamp), ), ( "op_webgpu_command_encoder_resolve_query_set", op_sync(command_encoder::op_webgpu_command_encoder_resolve_query_set), ), ( "op_webgpu_command_encoder_finish", op_sync(command_encoder::op_webgpu_command_encoder_finish), ), // render_pass ( "op_webgpu_render_pass_set_viewport", op_sync(render_pass::op_webgpu_render_pass_set_viewport), ), ( "op_webgpu_render_pass_set_scissor_rect", op_sync(render_pass::op_webgpu_render_pass_set_scissor_rect), ), ( "op_webgpu_render_pass_set_blend_constant", op_sync(render_pass::op_webgpu_render_pass_set_blend_constant), ), ( "op_webgpu_render_pass_set_stencil_reference", op_sync(render_pass::op_webgpu_render_pass_set_stencil_reference), ), ( "op_webgpu_render_pass_begin_pipeline_statistics_query", op_sync( render_pass::op_webgpu_render_pass_begin_pipeline_statistics_query, ), ), ( "op_webgpu_render_pass_end_pipeline_statistics_query", op_sync(render_pass::op_webgpu_render_pass_end_pipeline_statistics_query), ), ( "op_webgpu_render_pass_write_timestamp", op_sync(render_pass::op_webgpu_render_pass_write_timestamp), ), ( "op_webgpu_render_pass_execute_bundles", op_sync(render_pass::op_webgpu_render_pass_execute_bundles), ), ( "op_webgpu_render_pass_end_pass", op_sync(render_pass::op_webgpu_render_pass_end_pass), ), ( "op_webgpu_render_pass_set_bind_group", op_sync(render_pass::op_webgpu_render_pass_set_bind_group), ), ( "op_webgpu_render_pass_push_debug_group", op_sync(render_pass::op_webgpu_render_pass_push_debug_group), ), ( "op_webgpu_render_pass_pop_debug_group", op_sync(render_pass::op_webgpu_render_pass_pop_debug_group), ), ( "op_webgpu_render_pass_insert_debug_marker", op_sync(render_pass::op_webgpu_render_pass_insert_debug_marker), ), ( "op_webgpu_render_pass_set_pipeline", op_sync(render_pass::op_webgpu_render_pass_set_pipeline), ), ( "op_webgpu_render_pass_set_index_buffer", op_sync(render_pass::op_webgpu_render_pass_set_index_buffer), ), ( "op_webgpu_render_pass_set_vertex_buffer", op_sync(render_pass::op_webgpu_render_pass_set_vertex_buffer), ), ( "op_webgpu_render_pass_draw", op_sync(render_pass::op_webgpu_render_pass_draw), ), ( "op_webgpu_render_pass_draw_indexed", op_sync(render_pass::op_webgpu_render_pass_draw_indexed), ), ( "op_webgpu_render_pass_draw_indirect", op_sync(render_pass::op_webgpu_render_pass_draw_indirect), ), ( "op_webgpu_render_pass_draw_indexed_indirect", op_sync(render_pass::op_webgpu_render_pass_draw_indexed_indirect), ), // compute_pass ( "op_webgpu_compute_pass_set_pipeline", op_sync(compute_pass::op_webgpu_compute_pass_set_pipeline), ), ( "op_webgpu_compute_pass_dispatch", op_sync(compute_pass::op_webgpu_compute_pass_dispatch), ), ( "op_webgpu_compute_pass_dispatch_indirect", op_sync(compute_pass::op_webgpu_compute_pass_dispatch_indirect), ), ( "op_webgpu_compute_pass_end_pass", op_sync(compute_pass::op_webgpu_compute_pass_end_pass), ), ( "op_webgpu_compute_pass_set_bind_group", op_sync(compute_pass::op_webgpu_compute_pass_set_bind_group), ), ( "op_webgpu_compute_pass_push_debug_group", op_sync(compute_pass::op_webgpu_compute_pass_push_debug_group), ), ( "op_webgpu_compute_pass_pop_debug_group", op_sync(compute_pass::op_webgpu_compute_pass_pop_debug_group), ), ( "op_webgpu_compute_pass_insert_debug_marker", op_sync(compute_pass::op_webgpu_compute_pass_insert_debug_marker), ), // bundle ( "op_webgpu_create_render_bundle_encoder", op_sync(bundle::op_webgpu_create_render_bundle_encoder), ), ( "op_webgpu_render_bundle_encoder_finish", op_sync(bundle::op_webgpu_render_bundle_encoder_finish), ), ( "op_webgpu_render_bundle_encoder_set_bind_group", op_sync(bundle::op_webgpu_render_bundle_encoder_set_bind_group), ), ( "op_webgpu_render_bundle_encoder_push_debug_group", op_sync(bundle::op_webgpu_render_bundle_encoder_push_debug_group), ), ( "op_webgpu_render_bundle_encoder_pop_debug_group", op_sync(bundle::op_webgpu_render_bundle_encoder_pop_debug_group), ), ( "op_webgpu_render_bundle_encoder_insert_debug_marker", op_sync(bundle::op_webgpu_render_bundle_encoder_insert_debug_marker), ), ( "op_webgpu_render_bundle_encoder_set_pipeline", op_sync(bundle::op_webgpu_render_bundle_encoder_set_pipeline), ), ( "op_webgpu_render_bundle_encoder_set_index_buffer", op_sync(bundle::op_webgpu_render_bundle_encoder_set_index_buffer), ), ( "op_webgpu_render_bundle_encoder_set_vertex_buffer", op_sync(bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer), ), ( "op_webgpu_render_bundle_encoder_draw", op_sync(bundle::op_webgpu_render_bundle_encoder_draw), ), ( "op_webgpu_render_bundle_encoder_draw_indexed", op_sync(bundle::op_webgpu_render_bundle_encoder_draw_indexed), ), ( "op_webgpu_render_bundle_encoder_draw_indirect", op_sync(bundle::op_webgpu_render_bundle_encoder_draw_indirect), ), // queue ( "op_webgpu_queue_submit", op_sync(queue::op_webgpu_queue_submit), ), ( "op_webgpu_write_buffer", op_sync(queue::op_webgpu_write_buffer), ), ( "op_webgpu_write_texture", op_sync(queue::op_webgpu_write_texture), ), // shader ( "op_webgpu_create_shader_module", op_sync(shader::op_webgpu_create_shader_module), ), ] }