diff --git a/wgpu/examples/capture/main.rs b/wgpu/examples/capture/main.rs index df5324c679..1034a178d0 100644 --- a/wgpu/examples/capture/main.rs +++ b/wgpu/examples/capture/main.rs @@ -23,7 +23,8 @@ async fn run() { }, limits: wgpu::Limits::default(), }) - .await; + .await + .unwrap(); // Rendered image is 256×256 with 32-bit RGBA color let size = 256u32; diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 58a65d1830..84c55ddbf6 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -50,7 +50,7 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { log::info!("Initializing the surface..."); let instance = wgpu::Instance::new(); - let (size, surface) = { + let (size, surface) = unsafe { let size = window.inner_size(); let surface = instance.create_surface(&window); (size, surface) @@ -74,7 +74,8 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { }, limits: wgpu::Limits::default(), }) - .await; + .await + .unwrap(); let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, diff --git a/wgpu/examples/hello-compute/main.rs b/wgpu/examples/hello-compute/main.rs index 9c10c05072..9e60f9920d 100644 --- a/wgpu/examples/hello-compute/main.rs +++ b/wgpu/examples/hello-compute/main.rs @@ -39,7 +39,8 @@ async fn execute_gpu(numbers: Vec) -> Vec { }, limits: wgpu::Limits::default(), }) - .await; + .await + .unwrap(); let cs = include_bytes!("shader.comp.spv"); let cs_module = diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 6e13421ef1..7df29c320c 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -7,7 +7,7 @@ use winit::{ async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::TextureFormat) { let size = window.inner_size(); let instance = wgpu::Instance::new(); - let surface = instance.create_surface(&window); + let surface = unsafe { instance.create_surface(&window) }; let adapter = instance .request_adapter( &wgpu::RequestAdapterOptions { @@ -26,7 +26,8 @@ async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu:: }, limits: wgpu::Limits::default(), }) - .await; + .await + .unwrap(); let vs = include_bytes!("shader.vert.spv"); let vs_module = diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index c5295b439a..fed7fc3523 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -26,24 +26,6 @@ macro_rules! gfx_select { }; } -fn map_buffer_copy_view(view: crate::BufferCopyView<'_>) -> wgc::command::BufferCopyView { - wgc::command::BufferCopyView { - buffer: view.buffer.id, - offset: view.offset, - bytes_per_row: view.bytes_per_row, - rows_per_image: view.rows_per_image, - } -} - -fn map_texture_copy_view<'a>(view: crate::TextureCopyView<'a>) -> wgc::command::TextureCopyView { - wgc::command::TextureCopyView { - texture: view.texture.id, - mip_level: view.mip_level, - array_layer: view.array_layer, - origin: view.origin, - } -} - pub type Context = wgc::hub::Global; mod pass_impl { @@ -184,6 +166,24 @@ mod pass_impl { } } +fn map_buffer_copy_view(view: crate::BufferCopyView<'_>) -> wgc::command::BufferCopyView { + wgc::command::BufferCopyView { + buffer: view.buffer.id, + offset: view.offset, + bytes_per_row: view.bytes_per_row, + rows_per_image: view.rows_per_image, + } +} + +fn map_texture_copy_view<'a>(view: crate::TextureCopyView<'a>) -> wgc::command::TextureCopyView { + wgc::command::TextureCopyView { + texture: view.texture.id, + mip_level: view.mip_level, + array_layer: view.array_layer, + origin: view.origin, + } +} + impl crate::Context for Context { type AdapterId = wgc::id::AdapterId; type DeviceId = wgc::id::DeviceId; @@ -211,11 +211,12 @@ impl crate::Context for Context { type SwapChainOutputDetail = SwapChainOutputDetail; type RequestAdapterFuture = Ready>; - type RequestDeviceFuture = Ready<(Self::DeviceId, Self::QueueId)>; + type RequestDeviceFuture = + Ready>; type MapReadFuture = - native_gpu_future::GpuFuture>; + native_gpu_future::GpuFuture>; type MapWriteFuture = - native_gpu_future::GpuFuture>; + native_gpu_future::GpuFuture>; fn init() -> Self { wgc::hub::Global::new("wgpu", wgc::hub::IdentityManagerFactory) @@ -319,7 +320,7 @@ impl crate::Context for Context { ) -> Self::RequestDeviceFuture { let device_id = gfx_select!(*adapter => self.adapter_request_device(*adapter, desc, PhantomData)); - ready((device_id, device_id)) + ready(Ok((device_id, device_id))) } fn device_create_swap_chain( @@ -694,7 +695,7 @@ impl crate::Context for Context { buffer_id, })); } else { - completion.complete(Err(crate::BufferAsyncErr)); + completion.complete(Err(crate::BufferAsyncError)); } } @@ -731,7 +732,7 @@ impl crate::Context for Context { buffer_id, })); } else { - completion.complete(Err(crate::BufferAsyncErr)); + completion.complete(Err(crate::BufferAsyncError)); } } diff --git a/wgpu/src/backend/mod.rs b/wgpu/src/backend/mod.rs index 77aef896de..854488de91 100644 --- a/wgpu/src/backend/mod.rs +++ b/wgpu/src/backend/mod.rs @@ -1,7 +1,7 @@ #[cfg(target_arch = "wasm32")] mod web; -//#[cfg(target_arch = "wasm32")] -//pub use web::*; +#[cfg(target_arch = "wasm32")] +pub(crate) use web::Context; #[cfg(not(target_arch = "wasm32"))] mod direct; diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 8c9fe3cb00..2434089020 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -5,203 +5,141 @@ use crate::{ TextureViewDescriptor, TextureViewDimension, }; -use std::ops::Range; +use futures::FutureExt; +use std::{marker::PhantomData, ops::Range}; use wasm_bindgen::prelude::*; -pub type AdapterId = web_sys::GpuAdapter; -pub type DeviceId = web_sys::GpuDevice; -pub type QueueId = web_sys::GpuQueue; -pub type ShaderModuleId = web_sys::GpuShaderModule; -pub type BindGroupLayoutId = web_sys::GpuBindGroupLayout; -pub type BindGroupId = web_sys::GpuBindGroup; -pub type TextureViewId = web_sys::GpuTextureView; -pub type SamplerId = web_sys::GpuSampler; -pub type BufferId = web_sys::GpuBuffer; -pub type TextureId = web_sys::GpuTexture; -pub type PipelineLayoutId = web_sys::GpuPipelineLayout; -pub type RenderPipelineId = web_sys::GpuRenderPipeline; -pub type ComputePipelineId = web_sys::GpuComputePipeline; -pub type CommandEncoderId = web_sys::GpuCommandEncoder; -pub type ComputePassId = web_sys::GpuComputePassEncoder; -pub type CommandBufferId = web_sys::GpuCommandBuffer; -pub type SurfaceId = web_sys::GpuCanvasContext; -pub type SwapChainId = web_sys::GpuSwapChain; -pub type RenderPassEncoderId = web_sys::GpuRenderPassEncoder; +pub type Context = web_sys::Gpu; +pub(crate) struct ComputePass(web_sys::GpuComputePassEncoder); +pub(crate) struct RenderPass(web_sys::GpuRenderPassEncoder); -fn gpu() -> web_sys::Gpu { - web_sys::window().unwrap().navigator().gpu() -} - -pub(crate) async fn request_adapter( - options: &crate::RequestAdapterOptions<'_>, - backends: wgt::BackendBit, -) -> Option { - if !backends.contains(wgt::BackendBit::BROWSER_WEBGPU) { - return None; +impl crate::ComputePassInner for ComputePass { + fn set_pipeline(&mut self, pipeline: &web_sys::GpuComputePipeline) { + self.0.set_pipeline(pipeline); } - - let mut mapped_options = web_sys::GpuRequestAdapterOptions::new(); - let mapped_power_preference = match options.power_preference { - wgt::PowerPreference::LowPower => web_sys::GpuPowerPreference::LowPower, - wgt::PowerPreference::HighPerformance | wgt::PowerPreference::Default => { - web_sys::GpuPowerPreference::HighPerformance - } - }; - mapped_options.power_preference(mapped_power_preference); - let adapter_promise = gpu().request_adapter_with_options(&mapped_options); - Some( - wasm_bindgen_futures::JsFuture::from(adapter_promise) - .await - .expect("Unable to get adapter") - .into(), - ) -} - -pub(crate) async fn request_device_and_queue( - adapter: &AdapterId, - desc: Option<&wgt::DeviceDescriptor>, -) -> (DeviceId, QueueId) { - let device_promise = match desc { - Some(d) => { - let mut mapped_desc = web_sys::GpuDeviceDescriptor::new(); - // TODO: label, extensions - let mut mapped_limits = web_sys::GpuLimits::new(); - mapped_limits.max_bind_groups(d.limits.max_bind_groups); - mapped_desc.limits(&mapped_limits); - adapter.request_device_with_descriptor(&mapped_desc) - } - None => adapter.request_device(), - }; - let js_value = wasm_bindgen_futures::JsFuture::from(device_promise) - .await - .expect("Unable to get device"); - let device_id = DeviceId::from(js_value); - let queue_id = device_id.default_queue(); - (device_id, queue_id) -} - -pub(crate) fn create_shader_module(device: &DeviceId, spv: &[u32]) -> ShaderModuleId { - let desc = web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(spv)); - // TODO: label - device.create_shader_module(&desc) -} - -pub(crate) fn create_bind_group_layout( - device: &DeviceId, - desc: &BindGroupLayoutDescriptor, -) -> BindGroupLayoutId { - use web_sys::GpuBindingType as bt; - - let mapped_bindings = desc - .bindings - .iter() - .map(|bind| { - let mapped_type = match bind.ty { - BindingType::UniformBuffer { .. } => bt::UniformBuffer, - BindingType::StorageBuffer { - readonly: false, .. - } => bt::StorageBuffer, - BindingType::StorageBuffer { readonly: true, .. } => bt::ReadonlyStorageBuffer, - BindingType::Sampler { comparison: false } => bt::Sampler, - BindingType::Sampler { .. } => bt::ComparisonSampler, - BindingType::SampledTexture { .. } => bt::SampledTexture, - BindingType::StorageTexture { readonly: true, .. } => bt::ReadonlyStorageTexture, - BindingType::StorageTexture { .. } => bt::WriteonlyStorageTexture, - }; - - let mut mapped_entry = web_sys::GpuBindGroupLayoutEntry::new( - bind.binding, - mapped_type, - bind.visibility.bits(), + fn set_bind_group( + &mut self, + index: u32, + bind_group: &web_sys::GpuBindGroup, + offsets: &[wgt::DynamicOffset], + ) { + self.0 + .set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( + index, + bind_group, + offsets, + 0f64, + offsets.len() as u32, ); - - match bind.ty { - BindingType::UniformBuffer { dynamic } - | BindingType::StorageBuffer { dynamic, .. } => { - mapped_entry.has_dynamic_offset(dynamic); - } - _ => {} - } - - if let BindingType::SampledTexture { multisampled, .. } = bind.ty { - mapped_entry.multisampled(multisampled); - } - - match bind.ty { - BindingType::SampledTexture { dimension, .. } - | BindingType::StorageTexture { dimension, .. } => { - mapped_entry.view_dimension(map_texture_view_dimension(dimension)); - } - _ => {} - } - - if let BindingType::StorageTexture { format, .. } = bind.ty { - mapped_entry.storage_texture_format(map_texture_format(format)); - } - - match bind.ty { - BindingType::SampledTexture { component_type, .. } - | BindingType::StorageTexture { component_type, .. } => { - mapped_entry.texture_component_type(map_texture_component_type(component_type)); - } - _ => {} - } - - mapped_entry - }) - .collect::(); - - let mut mapped_desc = web_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings); - if let Some(label) = desc.label { - mapped_desc.label(label); } - device.create_bind_group_layout(&mapped_desc) + fn dispatch(&mut self, x: u32, y: u32, z: u32) { + self.0.dispatch_with_y_and_z(x, y, z); + } + fn dispatch_indirect( + &mut self, + indirect_buffer: &web_sys::GpuBuffer, + indirect_offset: wgt::BufferAddress, + ) { + self.0 + .dispatch_indirect_with_f64(indirect_buffer, indirect_offset as f64); + } } -pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) -> BindGroupId { - let mapped_entries = desc - .bindings - .iter() - .map(|binding| { - let mapped_resource = match binding.resource { - BindingResource::Buffer { - ref buffer, - ref range, - } => { - let mut mapped_buffer_binding = web_sys::GpuBufferBinding::new(&buffer.id); - mapped_buffer_binding.offset(range.start as f64); - mapped_buffer_binding.size((range.end - range.start) as f64); - JsValue::from(mapped_buffer_binding.clone()) - } - BindingResource::Sampler(ref sampler) => JsValue::from(sampler.id.clone()), - BindingResource::TextureView(ref texture_view) => { - JsValue::from(texture_view.id.clone()) - } - }; - - web_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource) - }) - .collect::(); - - let mut mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &desc.layout.id); - if let Some(label) = desc.label { - mapped_desc.label(label); +impl crate::RenderPassInner for RenderPass { + fn set_pipeline(&mut self, pipeline: &web_sys::GpuRenderPipeline) { + self.0.set_pipeline(pipeline); + } + fn set_bind_group( + &mut self, + index: u32, + bind_group: &web_sys::GpuBindGroup, + offsets: &[wgt::DynamicOffset], + ) { + self.0 + .set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( + index, + bind_group, + offsets, + 0f64, + offsets.len() as u32, + ); + } + fn set_index_buffer( + &mut self, + buffer: &web_sys::GpuBuffer, + offset: wgt::BufferAddress, + size: wgt::BufferAddress, + ) { + self.0 + .set_index_buffer_with_f64_and_f64(buffer, offset as f64, size as f64); + } + fn set_vertex_buffer( + &mut self, + slot: u32, + buffer: &web_sys::GpuBuffer, + offset: wgt::BufferAddress, + size: wgt::BufferAddress, + ) { + self.0 + .set_vertex_buffer_with_f64_and_f64(slot, buffer, offset as f64, size as f64); + } + fn set_blend_color(&mut self, color: wgt::Color) { + self.0 + .set_blend_color_with_gpu_color_dict(&map_color(color)); + } + fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) { + self.0.set_scissor_rect(x, y, width, height); + } + fn set_viewport( + &mut self, + x: f32, + y: f32, + width: f32, + height: f32, + min_depth: f32, + max_depth: f32, + ) { + self.0 + .set_viewport(x, y, width, height, min_depth, max_depth); + } + fn set_stencil_reference(&mut self, reference: u32) { + self.0.set_stencil_reference(reference); + } + fn draw(&mut self, vertices: Range, instances: Range) { + self.0 + .draw_with_instance_count_and_first_vertex_and_first_instance( + vertices.end - vertices.start, + instances.end - instances.start, + vertices.start, + instances.start, + ); + } + fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range) { + self.0 + .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance( + indices.end - indices.start, + instances.end - instances.start, + indices.start, + base_vertex, + instances.start, + ); + } + fn draw_indirect( + &mut self, + indirect_buffer: &web_sys::GpuBuffer, + indirect_offset: wgt::BufferAddress, + ) { + self.0 + .draw_indirect_with_f64(indirect_buffer, indirect_offset as f64); + } + fn draw_indexed_indirect( + &mut self, + indirect_buffer: &web_sys::GpuBuffer, + indirect_offset: wgt::BufferAddress, + ) { + self.0 + .draw_indexed_indirect_with_f64(indirect_buffer, indirect_offset as f64); } - device.create_bind_group(&mapped_desc) -} - -pub(crate) fn create_pipeline_layout( - device: &DeviceId, - desc: &PipelineLayoutDescriptor, -) -> PipelineLayoutId { - let temp_layouts = desc - .bind_group_layouts - .iter() - .map(|bgl| bgl.id.clone()) - .collect::(); - let mapped_desc = web_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts); - // TODO: label - device.create_pipeline_layout(&mapped_desc) } fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTextureFormat { @@ -561,75 +499,772 @@ fn map_color(color: wgt::Color) -> web_sys::GpuColorDict { web_sys::GpuColorDict::new(color.a, color.b, color.g, color.r) } -pub(crate) fn create_render_pipeline( - device: &DeviceId, - desc: &RenderPipelineDescriptor, -) -> RenderPipelineId { - use web_sys::GpuPrimitiveTopology as pt; - - let mapped_color_states = desc - .color_states - .iter() - .map(|color_state_desc| { - let mapped_format = map_texture_format(color_state_desc.format); - let mut mapped_color_state_desc = web_sys::GpuColorStateDescriptor::new(mapped_format); - mapped_color_state_desc - .alpha_blend(&map_blend_descriptor(&color_state_desc.alpha_blend)); - mapped_color_state_desc - .color_blend(&map_blend_descriptor(&color_state_desc.color_blend)); - mapped_color_state_desc.write_mask(color_state_desc.write_mask.bits()); - mapped_color_state_desc - }) - .collect::(); - - let mapped_primitive_topology = match desc.primitive_topology { - wgt::PrimitiveTopology::PointList => pt::PointList, - wgt::PrimitiveTopology::LineList => pt::LineList, - wgt::PrimitiveTopology::LineStrip => pt::LineStrip, - wgt::PrimitiveTopology::TriangleList => pt::TriangleList, - wgt::PrimitiveTopology::TriangleStrip => pt::TriangleStrip, - }; - - let mapped_vertex_stage = map_stage_descriptor(&desc.vertex_stage); - - let mut mapped_desc = web_sys::GpuRenderPipelineDescriptor::new( - &desc.layout.id, - &mapped_color_states, - mapped_primitive_topology, - &mapped_vertex_stage, - ); - - // TODO: label - - if let Some(ref frag) = desc.fragment_stage { - mapped_desc.fragment_stage(&map_stage_descriptor(frag)); +fn map_store_op(op: wgt::StoreOp) -> web_sys::GpuStoreOp { + match op { + wgt::StoreOp::Clear => web_sys::GpuStoreOp::Clear, + wgt::StoreOp::Store => web_sys::GpuStoreOp::Store, } - - if let Some(ref rasterization) = desc.rasterization_state { - mapped_desc.rasterization_state(&map_rasterization_state_descriptor(rasterization)); - } - - if let Some(ref depth_stencil) = desc.depth_stencil_state { - mapped_desc.depth_stencil_state(&map_depth_stencil_state_descriptor(depth_stencil)); - } - - mapped_desc.vertex_state(&map_vertex_state_descriptor(&desc)); - mapped_desc.sample_count(desc.sample_count); - mapped_desc.sample_mask(desc.sample_mask); - mapped_desc.alpha_to_coverage_enabled(desc.alpha_to_coverage_enabled); - - device.create_render_pipeline(&mapped_desc) } -pub(crate) fn create_compute_pipeline( - device: &DeviceId, - desc: &ComputePipelineDescriptor, -) -> ComputePipelineId { - let mapped_compute_stage = map_stage_descriptor(&desc.compute_stage); - let mapped_desc = - web_sys::GpuComputePipelineDescriptor::new(&desc.layout.id, &mapped_compute_stage); - // TODO: label - device.create_compute_pipeline(&mapped_desc) +type JsFutureResult = Result; +type FutureMap = futures::future::Map T>; + +fn future_request_adapter(result: JsFutureResult) -> Option { + match result { + Ok(js_value) => Some(web_sys::GpuAdapter::from(js_value)), + Err(_) => None, + } +} +fn future_request_device( + result: JsFutureResult, +) -> Result<(web_sys::GpuDevice, web_sys::GpuQueue), crate::RequestDeviceError> { + result + .map(|js_value| { + let device_id = web_sys::GpuDevice::from(js_value); + let queue_id = device_id.default_queue(); + (device_id, queue_id) + }) + .map_err(|_| crate::RequestDeviceError) +} + +pub(crate) struct MapFuture { + child: wasm_bindgen_futures::JsFuture, + buffer: Option, + marker: PhantomData, +} +impl Unpin for MapFuture {} +type MapData = (web_sys::GpuBuffer, Vec); +impl From for BufferReadMappingDetail { + fn from((buffer_id, mapped): MapData) -> Self { + BufferReadMappingDetail { buffer_id, mapped } + } +} +impl From for BufferWriteMappingDetail { + fn from((buffer_id, mapped): MapData) -> Self { + BufferWriteMappingDetail { buffer_id, mapped } + } +} +impl> std::future::Future for MapFuture { + type Output = Result; + fn poll( + mut self: std::pin::Pin<&mut Self>, + context: &mut std::task::Context, + ) -> std::task::Poll { + std::future::Future::poll( + std::pin::Pin::new(&mut self.as_mut().get_mut().child), + context, + ) + .map(|result| { + let buffer = self.buffer.take().unwrap(); + result + .map(|js_value| { + let array_buffer = js_sys::ArrayBuffer::from(js_value); + let view = js_sys::Uint8Array::new(&array_buffer); + T::from((buffer, view.to_vec())) + }) + .map_err(|_| crate::BufferAsyncError) + }) + } +} + +impl crate::Context for Context { + type AdapterId = web_sys::GpuAdapter; + type DeviceId = web_sys::GpuDevice; + type QueueId = web_sys::GpuQueue; + type ShaderModuleId = web_sys::GpuShaderModule; + type BindGroupLayoutId = web_sys::GpuBindGroupLayout; + type BindGroupId = web_sys::GpuBindGroup; + type TextureViewId = web_sys::GpuTextureView; + type SamplerId = web_sys::GpuSampler; + type BufferId = web_sys::GpuBuffer; + type TextureId = web_sys::GpuTexture; + type PipelineLayoutId = web_sys::GpuPipelineLayout; + type RenderPipelineId = web_sys::GpuRenderPipeline; + type ComputePipelineId = web_sys::GpuComputePipeline; + type CommandEncoderId = web_sys::GpuCommandEncoder; + type ComputePassId = ComputePass; + type CommandBufferId = web_sys::GpuCommandBuffer; + type SurfaceId = web_sys::GpuCanvasContext; + type SwapChainId = web_sys::GpuSwapChain; + type RenderPassId = RenderPass; + + type CreateBufferMappedDetail = CreateBufferMappedDetail; + type BufferReadMappingDetail = BufferReadMappingDetail; + type BufferWriteMappingDetail = BufferWriteMappingDetail; + type SwapChainOutputDetail = SwapChainOutputDetail; + + type RequestAdapterFuture = FutureMap>; + type RequestDeviceFuture = + FutureMap>; + type MapReadFuture = MapFuture; + type MapWriteFuture = MapFuture; + + fn init() -> Self { + web_sys::window().unwrap().navigator().gpu() + } + + fn instance_create_surface( + &self, + window: &W, + ) -> Self::SurfaceId { + let handle = window.raw_window_handle(); + let canvas_attribute = match handle { + raw_window_handle::RawWindowHandle::Web(web_handle) => web_handle.id, + _ => panic!("expected valid handle for canvas"), + }; + let canvas_node: wasm_bindgen::JsValue = web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| { + doc.query_selector_all(&format!("[data-raw-handle=\"{}\"]", canvas_attribute)) + .ok() + }) + .and_then(|nodes| nodes.get(0)) + .expect("expected to find single canvas") + .into(); + let canvas_element: web_sys::HtmlCanvasElement = canvas_node.into(); + let context: wasm_bindgen::JsValue = match canvas_element.get_context("gpupresent") { + Ok(Some(ctx)) => ctx.into(), + _ => panic!("expected to get context from canvas"), + }; + context.into() + } + + fn instance_request_adapter( + &self, + options: &crate::RequestAdapterOptions<'_>, + _backends: wgt::BackendBit, + ) -> Self::RequestAdapterFuture { + //assert!(backends.contains(wgt::BackendBit::BROWSER_WEBGPU)); + let mut mapped_options = web_sys::GpuRequestAdapterOptions::new(); + let mapped_power_preference = match options.power_preference { + wgt::PowerPreference::LowPower => web_sys::GpuPowerPreference::LowPower, + wgt::PowerPreference::HighPerformance | wgt::PowerPreference::Default => { + web_sys::GpuPowerPreference::HighPerformance + } + }; + mapped_options.power_preference(mapped_power_preference); + let adapter_promise = self.request_adapter_with_options(&mapped_options); + wasm_bindgen_futures::JsFuture::from(adapter_promise).map(future_request_adapter) + } + + fn adapter_request_device( + &self, + adapter: &Self::AdapterId, + desc: &crate::DeviceDescriptor, + ) -> Self::RequestDeviceFuture { + let mut mapped_desc = web_sys::GpuDeviceDescriptor::new(); + // TODO: label, extensions + let mut mapped_limits = web_sys::GpuLimits::new(); + mapped_limits.max_bind_groups(desc.limits.max_bind_groups); + mapped_desc.limits(&mapped_limits); + let device_promise = adapter.request_device_with_descriptor(&mapped_desc); + + wasm_bindgen_futures::JsFuture::from(device_promise).map(future_request_device) + } + + fn device_create_swap_chain( + &self, + device: &Self::DeviceId, + surface: &Self::SurfaceId, + desc: &wgt::SwapChainDescriptor, + ) -> Self::SwapChainId { + let mut mapped = + web_sys::GpuSwapChainDescriptor::new(device, map_texture_format(desc.format)); + mapped.usage(desc.usage.bits()); + surface.configure_swap_chain(&mapped) + } + + fn device_create_shader_module( + &self, + device: &Self::DeviceId, + spv: &[u32], + ) -> Self::ShaderModuleId { + let desc = web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(spv)); + // TODO: label + device.create_shader_module(&desc) + } + + fn device_create_bind_group_layout( + &self, + device: &Self::DeviceId, + desc: &BindGroupLayoutDescriptor, + ) -> Self::BindGroupLayoutId { + use web_sys::GpuBindingType as bt; + + let mapped_bindings = desc + .bindings + .iter() + .map(|bind| { + let mapped_type = match bind.ty { + BindingType::UniformBuffer { .. } => bt::UniformBuffer, + BindingType::StorageBuffer { + readonly: false, .. + } => bt::StorageBuffer, + BindingType::StorageBuffer { readonly: true, .. } => bt::ReadonlyStorageBuffer, + BindingType::Sampler { comparison: false } => bt::Sampler, + BindingType::Sampler { .. } => bt::ComparisonSampler, + BindingType::SampledTexture { .. } => bt::SampledTexture, + BindingType::StorageTexture { readonly: true, .. } => { + bt::ReadonlyStorageTexture + } + BindingType::StorageTexture { .. } => bt::WriteonlyStorageTexture, + }; + + let mut mapped_entry = web_sys::GpuBindGroupLayoutEntry::new( + bind.binding, + mapped_type, + bind.visibility.bits(), + ); + + match bind.ty { + BindingType::UniformBuffer { dynamic } + | BindingType::StorageBuffer { dynamic, .. } => { + mapped_entry.has_dynamic_offset(dynamic); + } + _ => {} + } + + if let BindingType::SampledTexture { multisampled, .. } = bind.ty { + mapped_entry.multisampled(multisampled); + } + + match bind.ty { + BindingType::SampledTexture { dimension, .. } + | BindingType::StorageTexture { dimension, .. } => { + mapped_entry.view_dimension(map_texture_view_dimension(dimension)); + } + _ => {} + } + + if let BindingType::StorageTexture { format, .. } = bind.ty { + mapped_entry.storage_texture_format(map_texture_format(format)); + } + + match bind.ty { + BindingType::SampledTexture { component_type, .. } + | BindingType::StorageTexture { component_type, .. } => { + mapped_entry + .texture_component_type(map_texture_component_type(component_type)); + } + _ => {} + } + + mapped_entry + }) + .collect::(); + + let mut mapped_desc = web_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_bind_group_layout(&mapped_desc) + } + + fn device_create_bind_group( + &self, + device: &Self::DeviceId, + desc: &BindGroupDescriptor, + ) -> Self::BindGroupId { + let mapped_entries = desc + .bindings + .iter() + .map(|binding| { + let mapped_resource = match binding.resource { + BindingResource::Buffer { + ref buffer, + ref range, + } => { + let mut mapped_buffer_binding = web_sys::GpuBufferBinding::new(&buffer.id); + mapped_buffer_binding.offset(range.start as f64); + mapped_buffer_binding.size((range.end - range.start) as f64); + JsValue::from(mapped_buffer_binding.clone()) + } + BindingResource::Sampler(ref sampler) => JsValue::from(sampler.id.clone()), + BindingResource::TextureView(ref texture_view) => { + JsValue::from(texture_view.id.clone()) + } + }; + + web_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource) + }) + .collect::(); + + let mut mapped_desc = + web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &desc.layout.id); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_bind_group(&mapped_desc) + } + + fn device_create_pipeline_layout( + &self, + device: &Self::DeviceId, + desc: &PipelineLayoutDescriptor, + ) -> Self::PipelineLayoutId { + let temp_layouts = desc + .bind_group_layouts + .iter() + .map(|bgl| bgl.id.clone()) + .collect::(); + let mapped_desc = web_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts); + // TODO: label + device.create_pipeline_layout(&mapped_desc) + } + + fn device_create_render_pipeline( + &self, + device: &Self::DeviceId, + desc: &RenderPipelineDescriptor, + ) -> Self::RenderPipelineId { + use web_sys::GpuPrimitiveTopology as pt; + + let mapped_color_states = desc + .color_states + .iter() + .map(|color_state_desc| { + let mapped_format = map_texture_format(color_state_desc.format); + let mut mapped_color_state_desc = + web_sys::GpuColorStateDescriptor::new(mapped_format); + mapped_color_state_desc + .alpha_blend(&map_blend_descriptor(&color_state_desc.alpha_blend)); + mapped_color_state_desc + .color_blend(&map_blend_descriptor(&color_state_desc.color_blend)); + mapped_color_state_desc.write_mask(color_state_desc.write_mask.bits()); + mapped_color_state_desc + }) + .collect::(); + + let mapped_primitive_topology = match desc.primitive_topology { + wgt::PrimitiveTopology::PointList => pt::PointList, + wgt::PrimitiveTopology::LineList => pt::LineList, + wgt::PrimitiveTopology::LineStrip => pt::LineStrip, + wgt::PrimitiveTopology::TriangleList => pt::TriangleList, + wgt::PrimitiveTopology::TriangleStrip => pt::TriangleStrip, + }; + + let mapped_vertex_stage = map_stage_descriptor(&desc.vertex_stage); + + let mut mapped_desc = web_sys::GpuRenderPipelineDescriptor::new( + &desc.layout.id, + &mapped_color_states, + mapped_primitive_topology, + &mapped_vertex_stage, + ); + + // TODO: label + + if let Some(ref frag) = desc.fragment_stage { + mapped_desc.fragment_stage(&map_stage_descriptor(frag)); + } + + if let Some(ref rasterization) = desc.rasterization_state { + mapped_desc.rasterization_state(&map_rasterization_state_descriptor(rasterization)); + } + + if let Some(ref depth_stencil) = desc.depth_stencil_state { + mapped_desc.depth_stencil_state(&map_depth_stencil_state_descriptor(depth_stencil)); + } + + mapped_desc.vertex_state(&map_vertex_state_descriptor(&desc)); + mapped_desc.sample_count(desc.sample_count); + mapped_desc.sample_mask(desc.sample_mask); + mapped_desc.alpha_to_coverage_enabled(desc.alpha_to_coverage_enabled); + + device.create_render_pipeline(&mapped_desc) + } + + fn device_create_compute_pipeline( + &self, + device: &Self::DeviceId, + desc: &ComputePipelineDescriptor, + ) -> Self::ComputePipelineId { + let mapped_compute_stage = map_stage_descriptor(&desc.compute_stage); + let mapped_desc = + web_sys::GpuComputePipelineDescriptor::new(&desc.layout.id, &mapped_compute_stage); + // TODO: label + device.create_compute_pipeline(&mapped_desc) + } + + fn device_create_buffer_mapped<'a>( + &self, + device: &Self::DeviceId, + desc: &BufferDescriptor, + ) -> (Self::BufferId, &'a mut [u8], Self::CreateBufferMappedDetail) { + let mut mapped_desc = + web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + unsafe { + let pair = device.create_buffer_mapped(&mapped_desc); + let id = pair.get(0).into(); + let array_buffer = pair.get(1).into(); + // TODO: Use `Vec::from_raw_parts` once it's stable + let memory = vec![0; desc.size as usize].into_boxed_slice(); + let mapped_data = std::slice::from_raw_parts_mut( + Box::into_raw(memory) as *mut u8, + desc.size as usize, + ); + (id, mapped_data, CreateBufferMappedDetail { array_buffer }) + } + } + + fn device_create_buffer( + &self, + device: &Self::DeviceId, + desc: &BufferDescriptor, + ) -> Self::BufferId { + let mut mapped_desc = + web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_buffer(&mapped_desc) + } + + fn device_create_texture( + &self, + device: &Self::DeviceId, + desc: &TextureDescriptor, + ) -> Self::TextureId { + let mut mapped_desc = web_sys::GpuTextureDescriptor::new( + map_texture_format(desc.format), + &map_extent_3d(desc.size), + desc.usage.bits(), + ); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + mapped_desc.dimension(map_texture_dimension(desc.dimension)); + mapped_desc.mip_level_count(desc.mip_level_count); + mapped_desc.sample_count(desc.sample_count); + device.create_texture(&mapped_desc) + } + + fn device_create_sampler( + &self, + device: &Self::DeviceId, + desc: &SamplerDescriptor, + ) -> Self::SamplerId { + let mut mapped_desc = web_sys::GpuSamplerDescriptor::new(); + // TODO: label + mapped_desc.address_mode_u(map_address_mode(desc.address_mode_u)); + mapped_desc.address_mode_v(map_address_mode(desc.address_mode_v)); + mapped_desc.address_mode_w(map_address_mode(desc.address_mode_w)); + if let Some(compare) = map_compare_function(desc.compare) { + mapped_desc.compare(compare); + } + mapped_desc.lod_max_clamp(desc.lod_max_clamp); + mapped_desc.lod_min_clamp(desc.lod_min_clamp); + mapped_desc.mag_filter(map_filter_mode(desc.mag_filter)); + mapped_desc.min_filter(map_filter_mode(desc.min_filter)); + mapped_desc.mipmap_filter(map_filter_mode(desc.mipmap_filter)); + device.create_sampler_with_descriptor(&mapped_desc) + } + + fn device_create_command_encoder( + &self, + device: &Self::DeviceId, + desc: &CommandEncoderDescriptor, + ) -> Self::CommandEncoderId { + let mut mapped_desc = web_sys::GpuCommandEncoderDescriptor::new(); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_command_encoder_with_descriptor(&mapped_desc) + } + + fn device_drop(&self, _device: &Self::DeviceId) { + // Device is dropped automatically + } + + fn device_poll(&self, _device: &Self::DeviceId, _maintain: crate::Maintain) { + // Device is polled automatically + } + + fn buffer_map_read( + &self, + buffer: &Self::BufferId, + _start: wgt::BufferAddress, + _size: wgt::BufferAddress, + ) -> Self::MapReadFuture { + MapFuture { + child: wasm_bindgen_futures::JsFuture::from(buffer.map_read_async()), + buffer: Some(buffer.clone()), + marker: PhantomData, + } + } + + fn buffer_map_write( + &self, + buffer: &Self::BufferId, + _start: wgt::BufferAddress, + _size: wgt::BufferAddress, + ) -> Self::MapWriteFuture { + MapFuture { + child: wasm_bindgen_futures::JsFuture::from(buffer.map_write_async()), + buffer: Some(buffer.clone()), + marker: PhantomData, + } + } + + fn buffer_unmap(&self, buffer: &Self::BufferId) { + buffer.unmap(); + } + + fn swap_chain_get_next_texture( + &self, + swap_chain: &Self::SwapChainId, + ) -> Result<(Self::TextureViewId, Self::SwapChainOutputDetail), crate::TimeOut> { + // TODO: Should we pass a descriptor here? + // Or is the default view always correct? + Ok((swap_chain.get_current_texture().create_view(), ())) + } + + fn swap_chain_present( + &self, + _view: &Self::TextureViewId, + _detail: &Self::SwapChainOutputDetail, + ) { + // Swapchain is presented automatically + } + + fn texture_create_view( + &self, + texture: &Self::TextureId, + desc: Option<&TextureViewDescriptor>, + ) -> Self::TextureViewId { + match desc { + Some(d) => { + let mut mapped_desc = web_sys::GpuTextureViewDescriptor::new(); + mapped_desc.array_layer_count(d.array_layer_count); + mapped_desc.aspect(map_texture_aspect(d.aspect)); + mapped_desc.base_array_layer(d.base_array_layer); + mapped_desc.base_mip_level(d.base_mip_level); + mapped_desc.dimension(map_texture_view_dimension(d.dimension)); + mapped_desc.format(map_texture_format(d.format)); + mapped_desc.mip_level_count(d.level_count); + // TODO: label + texture.create_view_with_descriptor(&mapped_desc) + } + None => texture.create_view(), + } + } + + fn texture_drop(&self, _texture: &Self::TextureId) { + // Buffer is dropped automatically + } + fn texture_view_drop(&self, _texture_view: &Self::TextureViewId) { + // Buffer is dropped automatically + } + fn sampler_drop(&self, _sampler: &Self::SamplerId) { + // Buffer is dropped automatically + } + fn buffer_drop(&self, _buffer: &Self::BufferId) { + // Buffer is dropped automatically + } + fn bind_group_drop(&self, _bind_group: &Self::BindGroupId) { + // Buffer is dropped automatically + } + fn bind_group_layout_drop(&self, _bind_group_layout: &Self::BindGroupLayoutId) { + // Buffer is dropped automatically + } + fn pipeline_layout_drop(&self, _pipeline_layout: &Self::PipelineLayoutId) { + // Buffer is dropped automatically + } + fn shader_module_drop(&self, _shader_module: &Self::ShaderModuleId) { + // Buffer is dropped automatically + } + fn command_buffer_drop(&self, _command_buffer: &Self::CommandBufferId) { + // Buffer is dropped automatically + } + fn compute_pipeline_drop(&self, _pipeline: &Self::ComputePipelineId) { + // Buffer is dropped automatically + } + fn render_pipeline_drop(&self, _pipeline: &Self::RenderPipelineId) { + // Buffer is dropped automatically + } + + fn flush_mapped_data(data: &mut [u8], detail: CreateBufferMappedDetail) { + unsafe { + // Convert the `mapped_data` slice back into a `Vec`. This should be + // safe because `mapped_data` is no longer accessible beyond this + // function. + let memory: Vec = Box::<[u8]>::from_raw(data).into(); + + // Create a view into the mapped `ArrayBuffer` that was provided by the + // browser + let mapped = js_sys::Uint8Array::new(&detail.array_buffer); + + // Convert `memory` into a temporary `Uint8Array` view. This should be + // safe as long as the backing wasm memory is not resized. + let memory_view = js_sys::Uint8Array::view(&memory[..]); + + // Finally copy into `mapped` and let `memory` drop + mapped.set(&memory_view, 0); + } + } + + fn encoder_copy_buffer_to_buffer( + &self, + encoder: &Self::CommandEncoderId, + source: &Self::BufferId, + source_offset: wgt::BufferAddress, + destination: &Self::BufferId, + destination_offset: wgt::BufferAddress, + copy_size: wgt::BufferAddress, + ) { + encoder.copy_buffer_to_buffer_with_f64_and_f64_and_f64( + source, + source_offset as f64, + destination, + destination_offset as f64, + copy_size as f64, + ) + } + + fn encoder_copy_buffer_to_texture( + &self, + encoder: &Self::CommandEncoderId, + source: crate::BufferCopyView, + destination: crate::TextureCopyView, + copy_size: wgt::Extent3d, + ) { + encoder.copy_buffer_to_texture_with_gpu_extent_3d_dict( + &map_buffer_copy_view(source), + &map_texture_copy_view(destination), + &map_extent_3d(copy_size), + ) + } + + fn encoder_copy_texture_to_buffer( + &self, + encoder: &Self::CommandEncoderId, + source: crate::TextureCopyView, + destination: crate::BufferCopyView, + copy_size: wgt::Extent3d, + ) { + encoder.copy_texture_to_buffer_with_gpu_extent_3d_dict( + &map_texture_copy_view(source), + &map_buffer_copy_view(destination), + &map_extent_3d(copy_size), + ) + } + + fn encoder_copy_texture_to_texture( + &self, + encoder: &Self::CommandEncoderId, + source: crate::TextureCopyView, + destination: crate::TextureCopyView, + copy_size: wgt::Extent3d, + ) { + encoder.copy_texture_to_texture_with_gpu_extent_3d_dict( + &map_texture_copy_view(source), + &map_texture_copy_view(destination), + &map_extent_3d(copy_size), + ) + } + + fn encoder_begin_compute_pass(&self, encoder: &Self::CommandEncoderId) -> Self::ComputePassId { + let mut mapped_desc = web_sys::GpuComputePassDescriptor::new(); + if let Some(ref label) = encoder.label() { + mapped_desc.label(label); + } + ComputePass(encoder.begin_compute_pass_with_descriptor(&mapped_desc)) + } + + fn encoder_end_compute_pass( + &self, + _encoder: &Self::CommandEncoderId, + pass: &mut Self::ComputePassId, + ) { + pass.0.end_pass(); + } + + fn encoder_begin_render_pass<'a>( + &self, + encoder: &Self::CommandEncoderId, + desc: &crate::RenderPassDescriptor<'a, '_>, + ) -> Self::RenderPassId { + let mapped_color_attachments = desc + .color_attachments + .iter() + .map(|ca| { + let mut mapped_color_attachment = + web_sys::GpuRenderPassColorAttachmentDescriptor::new( + &ca.attachment.id, + &match ca.load_op { + wgt::LoadOp::Clear => { + wasm_bindgen::JsValue::from(map_color(ca.clear_color)) + } + wgt::LoadOp::Load => { + wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load) + } + }, + ); + + if let Some(rt) = ca.resolve_target { + mapped_color_attachment.resolve_target(&rt.id); + } + + mapped_color_attachment.store_op(map_store_op(ca.store_op)); + + mapped_color_attachment + }) + .collect::(); + + let mut mapped_desc = web_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments); + + // TODO: label + + if let Some(dsa) = &desc.depth_stencil_attachment { + let mapped_depth_stencil_attachment = + web_sys::GpuRenderPassDepthStencilAttachmentDescriptor::new( + &dsa.attachment.id, + &match dsa.depth_load_op { + wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_depth), + wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), + }, + map_store_op(dsa.depth_store_op), + &match dsa.stencil_load_op { + wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_stencil), + wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), + }, + map_store_op(dsa.stencil_store_op), + ); + + mapped_desc.depth_stencil_attachment(&mapped_depth_stencil_attachment); + } + + RenderPass(encoder.begin_render_pass(&mapped_desc)) + } + + fn encoder_end_render_pass( + &self, + _encoder: &Self::CommandEncoderId, + pass: &mut Self::RenderPassId, + ) { + pass.0.end_pass(); + } + + fn encoder_finish(&self, encoder: &Self::CommandEncoderId) -> Self::CommandBufferId { + let mut mapped_desc = web_sys::GpuCommandBufferDescriptor::new(); + if let Some(ref label) = encoder.label() { + mapped_desc.label(label); + } + encoder.finish_with_descriptor(&mapped_desc) + } + + fn queue_submit>( + &self, + queue: &Self::QueueId, + command_buffers: I, + ) { + let temp_command_buffers = command_buffers.collect::(); + + queue.submit(&temp_command_buffers); + } } pub(crate) struct CreateBufferMappedDetail { @@ -639,284 +1274,8 @@ pub(crate) struct CreateBufferMappedDetail { array_buffer: js_sys::ArrayBuffer, } -pub(crate) fn device_create_buffer_mapped<'a>( - device: &DeviceId, - desc: &BufferDescriptor, -) -> crate::CreateBufferMapped<'a> { - let mut mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); - if let Some(label) = desc.label { - mapped_desc.label(label); - } - unsafe { - let pair = device.create_buffer_mapped(&mapped_desc); - let id = pair.get(0).into(); - let array_buffer = pair.get(1).into(); - // TODO: Use `Vec::from_raw_parts` once it's stable - let memory = vec![0; desc.size as usize].into_boxed_slice(); - let mapped_data = - std::slice::from_raw_parts_mut(Box::into_raw(memory) as *mut u8, desc.size as usize); - crate::CreateBufferMapped { - id, - mapped_data, - detail: CreateBufferMappedDetail { array_buffer }, - } - } -} - -pub type BufferDetail = (); - -pub(crate) fn device_create_buffer_mapped_finish( - create_buffer_mapped: crate::CreateBufferMapped<'_>, -) -> crate::Buffer { - unsafe { - // Convert the `mapped_data` slice back into a `Vec`. This should be - // safe because `mapped_data` is no longer accessible beyond this - // function. - let memory: Vec = Box::<[u8]>::from_raw(create_buffer_mapped.mapped_data).into(); - - // Create a view into the mapped `ArrayBuffer` that was provided by the - // browser - let mapped = js_sys::Uint8Array::new(&create_buffer_mapped.detail.array_buffer); - - // Convert `memory` into a temporary `Uint8Array` view. This should be - // safe as long as the backing wasm memory is not resized. - let memory_view = js_sys::Uint8Array::view(&memory[..]); - - // Finally copy into `mapped` and let `memory` drop - mapped.set(&memory_view, 0); - } - - buffer_unmap(&create_buffer_mapped.id); - - crate::Buffer { - id: create_buffer_mapped.id, - detail: (), - } -} - -pub(crate) fn buffer_unmap(buffer: &BufferId) { - buffer.unmap(); -} - -pub(crate) fn buffer_drop(_buffer: &BufferId) { - // Buffer is dropped automatically -} - -pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { - let mut mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); - if let Some(label) = desc.label { - mapped_desc.label(label); - } - crate::Buffer { - id: device.create_buffer(&mapped_desc), - detail: (), - } -} - -pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { - let mut mapped_desc = web_sys::GpuTextureDescriptor::new( - map_texture_format(desc.format), - &map_extent_3d(desc.size), - desc.usage.bits(), - ); - if let Some(label) = desc.label { - mapped_desc.label(label); - } - mapped_desc.dimension(map_texture_dimension(desc.dimension)); - mapped_desc.mip_level_count(desc.mip_level_count); - mapped_desc.sample_count(desc.sample_count); - device.create_texture(&mapped_desc) -} - -pub(crate) fn device_create_sampler(device: &DeviceId, desc: &SamplerDescriptor) -> SamplerId { - let mut mapped_desc = web_sys::GpuSamplerDescriptor::new(); - // TODO: label - mapped_desc.address_mode_u(map_address_mode(desc.address_mode_u)); - mapped_desc.address_mode_v(map_address_mode(desc.address_mode_v)); - mapped_desc.address_mode_w(map_address_mode(desc.address_mode_w)); - if let Some(compare) = map_compare_function(desc.compare) { - mapped_desc.compare(compare); - } - mapped_desc.lod_max_clamp(desc.lod_max_clamp); - mapped_desc.lod_min_clamp(desc.lod_min_clamp); - mapped_desc.mag_filter(map_filter_mode(desc.mag_filter)); - mapped_desc.min_filter(map_filter_mode(desc.min_filter)); - mapped_desc.mipmap_filter(map_filter_mode(desc.mipmap_filter)); - device.create_sampler_with_descriptor(&mapped_desc) -} - -pub(crate) fn create_command_encoder( - device: &DeviceId, - desc: &CommandEncoderDescriptor, -) -> CommandEncoderId { - let mut mapped_desc = web_sys::GpuCommandEncoderDescriptor::new(); - if let Some(label) = desc.label { - mapped_desc.label(label); - } - device.create_command_encoder_with_descriptor(&mapped_desc) -} - -pub(crate) fn command_encoder_copy_buffer_to_buffer( - command_encoder: &CommandEncoderId, - source: &crate::Buffer, - source_offset: wgt::BufferAddress, - destination: &crate::Buffer, - destination_offset: wgt::BufferAddress, - copy_size: wgt::BufferAddress, -) { - command_encoder.copy_buffer_to_buffer_with_f64_and_f64_and_f64( - &source.id, - source_offset as f64, - &destination.id, - destination_offset as f64, - copy_size as f64, - ); -} - -pub(crate) fn command_encoder_copy_buffer_to_texture( - command_encoder: &CommandEncoderId, - source: crate::BufferCopyView, - destination: crate::TextureCopyView, - copy_size: wgt::Extent3d, -) { - command_encoder.copy_buffer_to_texture_with_gpu_extent_3d_dict( - &map_buffer_copy_view(source), - &map_texture_copy_view(destination), - &map_extent_3d(copy_size), - ); -} - -pub(crate) fn command_encoder_copy_texture_to_buffer( - command_encoder: &CommandEncoderId, - source: crate::TextureCopyView, - destination: crate::BufferCopyView, - copy_size: wgt::Extent3d, -) { - command_encoder.copy_texture_to_buffer_with_gpu_extent_3d_dict( - &map_texture_copy_view(source), - &map_buffer_copy_view(destination), - &map_extent_3d(copy_size), - ); -} - -pub(crate) fn command_encoder_copy_texture_to_texture( - command_encoder: &CommandEncoderId, - source: crate::TextureCopyView, - destination: crate::TextureCopyView, - copy_size: wgt::Extent3d, -) { - command_encoder.copy_texture_to_texture_with_gpu_extent_3d_dict( - &map_texture_copy_view(source), - &map_texture_copy_view(destination), - &map_extent_3d(copy_size), - ); -} - -pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { - let mut mapped_desc = web_sys::GpuComputePassDescriptor::new(); - if let Some(ref label) = command_encoder.label() { - mapped_desc.label(label); - } - command_encoder.begin_compute_pass_with_descriptor(&mapped_desc) -} - -pub(crate) fn compute_pass_set_pipeline( - compute_pass: &ComputePassId, - pipeline: &ComputePipelineId, -) { - compute_pass.set_pipeline(&pipeline); -} - -pub(crate) fn compute_pass_set_bind_group<'a>( - compute_pass: &ComputePassId, - index: u32, - bind_group: &BindGroupId, - offsets: &[wgt::DynamicOffset], -) { - compute_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( - index, - bind_group, - offsets, - 0f64, - offsets.len() as u32, - ); -} - -pub(crate) fn compute_pass_dispatch(compute_pass: &ComputePassId, x: u32, y: u32, z: u32) { - compute_pass.dispatch_with_y_and_z(x, y, z); -} - -pub(crate) fn compute_pass_dispatch_indirect( - compute_pass: &ComputePassId, - indirect_buffer: &BufferId, - indirect_offset: wgt::BufferAddress, -) { - compute_pass.dispatch_indirect_with_f64(indirect_buffer, indirect_offset as f64); -} - -pub(crate) fn compute_pass_end_pass(compute_pass: &ComputePassId) { - compute_pass.end_pass(); -} - -pub(crate) fn command_encoder_finish(command_encoder: &CommandEncoderId) -> CommandBufferId { - let mut mapped_desc = web_sys::GpuCommandBufferDescriptor::new(); - if let Some(ref label) = command_encoder.label() { - mapped_desc.label(label); - } - command_encoder.finish_with_descriptor(&mapped_desc) -} - -pub(crate) fn queue_submit(queue: &QueueId, command_buffers: &[crate::CommandBuffer]) { - let temp_command_buffers = command_buffers - .iter() - .map(|cb| &cb.id) - .collect::(); - - queue.submit(&temp_command_buffers); -} - -pub(crate) async fn buffer_map_read( - buffer: &crate::Buffer, - _start: wgt::BufferAddress, - _size: wgt::BufferAddress, -) -> Result { - let array_buffer_promise = buffer.id.map_read_async(); - let array_buffer: js_sys::ArrayBuffer = - wasm_bindgen_futures::JsFuture::from(array_buffer_promise) - .await - .expect("Unable to map buffer") - .into(); - let view = js_sys::Uint8Array::new(&array_buffer); - Ok(crate::BufferReadMapping { - detail: BufferReadMappingDetail { - buffer_id: buffer.id.clone(), - mapped: view.to_vec(), - }, - }) -} - -pub(crate) async fn buffer_map_write( - buffer: &crate::Buffer, - _start: wgt::BufferAddress, - _size: wgt::BufferAddress, -) -> Result { - let array_buffer_promise = buffer.id.map_write_async(); - let array_buffer: js_sys::ArrayBuffer = - wasm_bindgen_futures::JsFuture::from(array_buffer_promise) - .await - .expect("Unable to map buffer") - .into(); - let view = js_sys::Uint8Array::new(&array_buffer); - Ok(crate::BufferWriteMapping { - detail: BufferWriteMappingDetail { - buffer_id: buffer.id.clone(), - mapped: view.to_vec(), - }, - }) -} - pub(crate) struct BufferReadMappingDetail { - pub(crate) buffer_id: BufferId, + pub(crate) buffer_id: web_sys::GpuBuffer, mapped: Vec, } @@ -927,7 +1286,7 @@ impl BufferReadMappingDetail { } pub(crate) struct BufferWriteMappingDetail { - pub(crate) buffer_id: BufferId, + pub(crate) buffer_id: web_sys::GpuBuffer, mapped: Vec, } @@ -937,277 +1296,4 @@ impl BufferWriteMappingDetail { } } -pub(crate) fn device_create_surface( - window: &W, -) -> SurfaceId { - let handle = window.raw_window_handle(); - let canvas_attribute = match handle { - raw_window_handle::RawWindowHandle::Web(web_handle) => web_handle.id, - _ => panic!("expected valid handle for canvas"), - }; - let canvas_node: wasm_bindgen::JsValue = web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| { - doc.query_selector_all(&format!("[data-raw-handle=\"{}\"]", canvas_attribute)) - .ok() - }) - .and_then(|nodes| nodes.get(0)) - .expect("expected to find single canvas") - .into(); - let canvas_element: web_sys::HtmlCanvasElement = canvas_node.into(); - let context: wasm_bindgen::JsValue = match canvas_element.get_context("gpupresent") { - Ok(Some(ctx)) => ctx.into(), - _ => panic!("expected to get context from canvas"), - }; - context.into() -} - -pub(crate) fn device_create_swap_chain( - device: &DeviceId, - surface: &SurfaceId, - desc: &wgt::SwapChainDescriptor, -) -> SwapChainId { - let mut mapped = web_sys::GpuSwapChainDescriptor::new(device, map_texture_format(desc.format)); - mapped.usage(desc.usage.bits()); - surface.configure_swap_chain(&mapped) -} - -pub(crate) fn device_drop(_device: &DeviceId) { - // Device is dropped automatically -} - -pub(crate) fn swap_chain_get_next_texture( - swap_chain: &SwapChainId, -) -> Result { - // TODO: Should we pass a descriptor here? - // Or is the default view always correct? - Ok(crate::SwapChainOutput { - view: crate::TextureView { - id: swap_chain.get_current_texture().create_view(), - owned: false, - }, - detail: (), - }) -} - pub(crate) type SwapChainOutputDetail = (); - -fn map_store_op(op: wgt::StoreOp) -> web_sys::GpuStoreOp { - match op { - wgt::StoreOp::Clear => web_sys::GpuStoreOp::Clear, - wgt::StoreOp::Store => web_sys::GpuStoreOp::Store, - } -} - -pub(crate) fn command_encoder_begin_render_pass<'a>( - command_encoder: &CommandEncoderId, - desc: &crate::RenderPassDescriptor<'a, '_>, -) -> RenderPassEncoderId { - let mapped_color_attachments = desc - .color_attachments - .iter() - .map(|ca| { - let mut mapped_color_attachment = web_sys::GpuRenderPassColorAttachmentDescriptor::new( - &ca.attachment.id, - &match ca.load_op { - wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(map_color(ca.clear_color)), - wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), - }, - ); - - if let Some(rt) = ca.resolve_target { - mapped_color_attachment.resolve_target(&rt.id); - } - - mapped_color_attachment.store_op(map_store_op(ca.store_op)); - - mapped_color_attachment - }) - .collect::(); - - let mut mapped_desc = web_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments); - - // TODO: label - - if let Some(dsa) = &desc.depth_stencil_attachment { - let mapped_depth_stencil_attachment = - web_sys::GpuRenderPassDepthStencilAttachmentDescriptor::new( - &dsa.attachment.id, - &match dsa.depth_load_op { - wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_depth), - wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), - }, - map_store_op(dsa.depth_store_op), - &match dsa.stencil_load_op { - wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_stencil), - wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), - }, - map_store_op(dsa.stencil_store_op), - ); - - mapped_desc.depth_stencil_attachment(&mapped_depth_stencil_attachment); - } - - command_encoder.begin_render_pass(&mapped_desc) -} - -pub(crate) fn render_pass_set_pipeline( - render_pass: &RenderPassEncoderId, - pipeline: &RenderPipelineId, -) { - render_pass.set_pipeline(&pipeline); -} - -pub(crate) fn render_pass_set_blend_color(render_pass: &RenderPassEncoderId, color: wgt::Color) { - render_pass.set_blend_color_with_gpu_color_dict(&map_color(color)); -} - -pub(crate) fn render_pass_set_bind_group( - render_pass: &RenderPassEncoderId, - index: u32, - bind_group: &BindGroupId, - offsets: &[wgt::DynamicOffset], -) { - render_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( - index, - bind_group, - offsets, - 0f64, - offsets.len() as u32, - ); -} - -pub(crate) fn render_pass_set_index_buffer<'a>( - render_pass: &RenderPassEncoderId, - buffer: &'a crate::Buffer, - offset: wgt::BufferAddress, - size: wgt::BufferAddress, -) { - render_pass.set_index_buffer_with_f64_and_f64(&buffer.id, offset as f64, size as f64); -} - -pub(crate) fn render_pass_set_vertex_buffer<'a>( - render_pass: &RenderPassEncoderId, - slot: u32, - buffer: &'a crate::Buffer, - offset: wgt::BufferAddress, - size: wgt::BufferAddress, -) { - render_pass.set_vertex_buffer_with_f64_and_f64(slot, &buffer.id, offset as f64, size as f64); -} - -pub(crate) fn render_pass_set_scissor_rect( - render_pass: &RenderPassEncoderId, - x: u32, - y: u32, - width: u32, - height: u32, -) { - render_pass.set_scissor_rect(x, y, width, height); -} - -pub(crate) fn render_pass_set_viewport( - render_pass: &RenderPassEncoderId, - x: f32, - y: f32, - width: f32, - height: f32, - min_depth: f32, - max_depth: f32, -) { - render_pass.set_viewport(x, y, width, height, min_depth, max_depth); -} - -pub(crate) fn render_pass_set_stencil_reference(render_pass: &RenderPassEncoderId, reference: u32) { - render_pass.set_stencil_reference(reference); -} - -pub(crate) fn render_pass_draw( - render_pass: &RenderPassEncoderId, - vertices: Range, - instances: Range, -) { - render_pass.draw_with_instance_count_and_first_vertex_and_first_instance( - vertices.end - vertices.start, - instances.end - instances.start, - vertices.start, - instances.start, - ) -} - -pub(crate) fn render_pass_draw_indexed( - render_pass: &RenderPassEncoderId, - indices: Range, - base_vertex: i32, - instances: Range, -) { - render_pass - .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance( - indices.end - indices.start, - instances.end - instances.start, - indices.start, - base_vertex, - instances.start, - ); -} - -pub(crate) fn render_pass_draw_indirect<'a>( - render_pass: &RenderPassEncoderId, - indirect_buffer: &'a crate::Buffer, - indirect_offset: wgt::BufferAddress, -) { - render_pass.draw_indirect_with_f64(&indirect_buffer.id, indirect_offset as f64); -} - -pub(crate) fn render_pass_draw_indexed_indirect<'a>( - render_pass: &RenderPassEncoderId, - indirect_buffer: &'a crate::Buffer, - indirect_offset: wgt::BufferAddress, -) { - render_pass.draw_indexed_indirect_with_f64(&indirect_buffer.id, indirect_offset as f64); -} - -pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { - render_pass.end_pass(); -} - -pub(crate) fn texture_create_view( - texture: &TextureId, - desc: Option<&TextureViewDescriptor>, -) -> TextureViewId { - match desc { - Some(d) => { - let mut mapped_desc = web_sys::GpuTextureViewDescriptor::new(); - mapped_desc.array_layer_count(d.array_layer_count); - mapped_desc.aspect(map_texture_aspect(d.aspect)); - mapped_desc.base_array_layer(d.base_array_layer); - mapped_desc.base_mip_level(d.base_mip_level); - mapped_desc.dimension(map_texture_view_dimension(d.dimension)); - mapped_desc.format(map_texture_format(d.format)); - mapped_desc.mip_level_count(d.level_count); - // TODO: label - texture.create_view_with_descriptor(&mapped_desc) - } - None => texture.create_view(), - } -} - -pub(crate) fn texture_drop(_texture: &TextureId) { - // Texture is dropped automatically -} - -pub(crate) fn texture_view_drop(_texture_view: &TextureViewId) { - // Texture view is dropped automatically -} - -pub(crate) fn bind_group_drop(_bind_group: &BindGroupId) { - // Bind group is dropped automatically -} - -pub(crate) fn swap_chain_present(_swap_chain_output: &crate::SwapChainOutput) { - // Swapchain is presented automatically -} - -pub(crate) fn device_poll(_device: &DeviceId, _maintain: crate::Maintain) { - // Device is polled automatically -} diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index cc9f64e90e..af6a7cecbe 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -110,9 +110,11 @@ trait Context: Sized { type SwapChainOutputDetail; type RequestAdapterFuture: Future>; - type RequestDeviceFuture: Future; - type MapReadFuture: Future>; - type MapWriteFuture: Future>; + type RequestDeviceFuture: Future< + Output = Result<(Self::DeviceId, Self::QueueId), RequestDeviceError>, + >; + type MapReadFuture: Future>; + type MapWriteFuture: Future>; fn init() -> Self; fn instance_create_surface( @@ -483,21 +485,13 @@ impl Drop for ComputePipeline { /// a [`CommandEncoder`] and then calling [`CommandEncoder::finish`]. pub struct CommandBuffer { context: Arc, - id: ::CommandBufferId, - alive: bool, -} - -impl CommandBuffer { - fn commit(mut self) -> ::CommandBufferId { - self.alive = false; - self.id - } + id: Option<::CommandBufferId>, } impl Drop for CommandBuffer { fn drop(&mut self) { - if self.alive { - self.context.command_buffer_drop(&self.id); + if let Some(ref id) = self.id { + self.context.command_buffer_drop(id); } } } @@ -514,7 +508,7 @@ pub struct CommandEncoder { id: ::CommandEncoderId, /// This type should be !Send !Sync, because it represents an allocation on this thread's /// command buffer. - _p: std::marker::PhantomData<*const u8>, + _p: PhantomData<*const u8>, } /// An in-progress recording of a render pass. @@ -913,26 +907,29 @@ impl Instance { /// Retrieves all available [`Adapter`]s that match the given backends. #[cfg(not(target_arch = "wasm32"))] - pub fn enumerate_adapters(&self, backends: wgt::BackendBit) -> Vec { + pub fn enumerate_adapters(&self, backends: wgt::BackendBit) -> impl Iterator { + let context = Arc::clone(&self.context); self.context .enumerate_adapters(wgc::instance::AdapterInputs::Mask(backends, || PhantomData)) .into_iter() - .map(|id| crate::Adapter { + .map(move |id| crate::Adapter { id, - context: Arc::clone(&self.context), + context: Arc::clone(&context), }) - .collect() } /// Creates a surface from a raw window handle. - pub fn create_surface(&self, window: &W) -> Surface { + pub unsafe fn create_surface( + &self, + window: &W, + ) -> Surface { Surface { id: self.context.instance_create_surface(window), } } #[cfg(any(target_os = "ios", target_os = "macos"))] - pub fn create_surface_from_core_animation_layer( + pub unsafe fn create_surface_from_core_animation_layer( &self, layer: *mut std::ffi::c_void, ) -> Surface { @@ -983,10 +980,13 @@ impl Adapter { /// # Panics /// /// Panics if the extensions specified by `desc` are not supported by this adapter. - pub fn request_device(&self, desc: &DeviceDescriptor) -> impl Future { + pub fn request_device( + &self, + desc: &DeviceDescriptor, + ) -> impl Future> { let context = Arc::clone(&self.context); - Context::adapter_request_device(&*self.context, &self.id, desc).map( - |(device_id, queue_id)| { + Context::adapter_request_device(&*self.context, &self.id, desc).map(|result| { + result.map(|(device_id, queue_id)| { ( Device { context: Arc::clone(&context), @@ -997,8 +997,8 @@ impl Adapter { id: queue_id, }, ) - }, - ) + }) + }) } #[cfg(not(target_arch = "wasm32"))] @@ -1144,7 +1144,10 @@ impl Drop for Device { } #[derive(Clone, PartialEq, Eq, Debug)] -pub struct BufferAsyncErr; +pub struct RequestDeviceError; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct BufferAsyncError; pub struct BufferReadMapping { context: Arc, @@ -1199,7 +1202,7 @@ impl Buffer { &self, start: BufferAddress, size: BufferAddress, - ) -> impl Future> { + ) -> impl Future> { let context = Arc::clone(&self.context); self.context .buffer_map_read(&self.id, start, size) @@ -1214,7 +1217,7 @@ impl Buffer { &self, start: BufferAddress, size: BufferAddress, - ) -> impl Future> { + ) -> impl Future> { let context = Arc::clone(&self.context); self.context .buffer_map_write(&self.id, start, size) @@ -1274,8 +1277,7 @@ impl CommandEncoder { pub fn finish(self) -> CommandBuffer { CommandBuffer { context: Arc::clone(&self.context), - id: Context::encoder_finish(&*self.context, &self.id), - alive: true, + id: Some(Context::encoder_finish(&*self.context, &self.id)), } } @@ -1405,7 +1407,7 @@ impl<'a> RenderPass<'a> { offset: BufferAddress, size: BufferAddress, ) { - self.id.set_index_buffer(&buffer.id, offset, size) + RenderPassInner::set_index_buffer(&mut self.id, &buffer.id, offset, size) } /// Assign a vertex buffer to a slot. @@ -1429,7 +1431,7 @@ impl<'a> RenderPass<'a> { offset: BufferAddress, size: BufferAddress, ) { - self.id.set_vertex_buffer(slot, &buffer.id, offset, size) + RenderPassInner::set_vertex_buffer(&mut self.id, slot, &buffer.id, offset, size) } /// Sets the scissor region. @@ -1457,7 +1459,7 @@ impl<'a> RenderPass<'a> { /// /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. pub fn draw(&mut self, vertices: Range, instances: Range) { - self.id.draw(vertices, instances) + RenderPassInner::draw(&mut self.id, vertices, instances) } /// Draws indexed primitives using the active index buffer and the active vertex buffers. @@ -1465,7 +1467,7 @@ impl<'a> RenderPass<'a> { /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active /// vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. pub fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range) { - self.id.draw_indexed(indices, base_vertex, instances); + RenderPassInner::draw_indexed(&mut self.id, indices, base_vertex, instances); } /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`. @@ -1545,7 +1547,7 @@ impl<'a> ComputePass<'a> { /// /// `x`, `y` and `z` denote the number of work groups to dispatch in each dimension. pub fn dispatch(&mut self, x: u32, y: u32, z: u32) { - self.id.dispatch(x, y, z); + ComputePassInner::dispatch(&mut self.id, x, y, z); } /// Dispatches compute work operations, based on the contents of the `indirect_buffer`. @@ -1554,8 +1556,7 @@ impl<'a> ComputePass<'a> { indirect_buffer: &'a Buffer, indirect_offset: BufferAddress, ) { - self.id - .dispatch_indirect(&indirect_buffer.id, indirect_offset); + ComputePassInner::dispatch_indirect(&mut self.id, &indirect_buffer.id, indirect_offset); } } @@ -1575,7 +1576,9 @@ impl Queue { Context::queue_submit( &*self.context, &self.id, - command_buffers.into_iter().map(CommandBuffer::commit), + command_buffers + .into_iter() + .map(|mut comb| comb.id.take().unwrap()), ); } }