From 8e76d1845b3106ca2da6f4ef92b372d9d6f06fa4 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sat, 4 Apr 2020 00:14:55 -0230 Subject: [PATCH 01/17] Add web backend --- Cargo.toml | 99 ++- README.md | 13 + examples/framework.rs | 95 ++- examples/hello-compute/main.rs | 17 +- examples/hello-triangle/main.rs | 36 +- examples/msaa-line/main.rs | 24 +- examples/msaa-line/shader.frag.spv | Bin 0 -> 372 bytes examples/msaa-line/shader.vert.spv | Bin 0 -> 968 bytes src/backend/mod.rs | 14 +- src/backend/native.rs | 671 ++++++++++++++++++++ src/backend/web.rs | 922 ++++++++++++++++++++++++++++ src/lib.rs | 945 +++++++++-------------------- 12 files changed, 2102 insertions(+), 734 deletions(-) create mode 100644 examples/msaa-line/shader.frag.spv create mode 100644 examples/msaa-line/shader.vert.spv create mode 100644 src/backend/native.rs create mode 100644 src/backend/web.rs diff --git a/Cargo.toml b/Cargo.toml index 6918f62ce..6495ddc04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,13 +23,13 @@ default = [] # Make Vulkan backend available on platforms where it is by default not, e.g. macOS vulkan = ["wgn/vulkan-portability"] -[dependencies.wgn] +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgn] package = "wgpu-native" version = "0.5" git = "https://github.com/gfx-rs/wgpu" rev = "49dbe08f37f8396cff0d6672667a48116ec487f5" -[dependencies.wgc] +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc] package = "wgpu-core" version = "0.5" git = "https://github.com/gfx-rs/wgpu" @@ -49,11 +49,10 @@ parking_lot = "0.10" [dev-dependencies] cgmath = "0.17" -env_logger = "0.7" -glsl-to-spirv = "0.1" +#glsl-to-spirv = "0.1" log = "0.4" png = "0.15" -winit = "0.22" +winit = { version = "0.22", features = ["web-sys"] } rand = "0.7.2" bytemuck = "1" futures = "0.3" @@ -76,3 +75,93 @@ test = true #gfx-backend-dx11 = { version = "0.5.0", path = "../gfx/src/backend/dx11" } #gfx-descriptor = { version = "0.1.0", path = "../gfx-extras/gfx-descriptor" } #gfx-memory = { version = "0.1.0", path = "../gfx-extras/gfx-memory" } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +env_logger = "0.7" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.59" +web-sys = { version = "0.3.36", features = [ + "Document", + "Navigator", + "Node", + "NodeList", + "Gpu", + "GpuAdapter", + "GpuBindGroup", + "GpuBindGroupBinding", + "GpuBindGroupDescriptor", + "GpuBindGroupLayout", + "GpuBindGroupLayoutBinding", + "GpuBindGroupLayoutDescriptor", + "GpuBlendDescriptor", + "GpuBlendFactor", + "GpuBlendOperation", + "GpuBindingType", + "GpuBuffer", + "GpuBufferBinding", + "GpuBufferDescriptor", + "GpuCanvasContext", + "GpuColorDict", + "GpuColorStateDescriptor", + "GpuCommandBuffer", + "GpuCommandBufferDescriptor", + "GpuCommandEncoder", + "GpuCommandEncoderDescriptor", + "GpuCompareFunction", + "GpuComputePassDescriptor", + "GpuComputePassEncoder", + "GpuComputePipeline", + "GpuComputePipelineDescriptor", + "GpuCullMode", + "GpuDepthStencilStateDescriptor", + "GpuDevice", + "GpuDeviceDescriptor", + "GpuExtent3dDict", + "GpuFrontFace", + "GpuIndexFormat", + "GpuInputStepMode", + "GpuLimits", + "GpuLoadOp", + "GpuPipelineLayout", + "GpuPipelineLayoutDescriptor", + "GpuPowerPreference", + "GpuPrimitiveTopology", + "GpuProgrammableStageDescriptor", + "GpuQueue", + "GpuRasterizationStateDescriptor", + "GpuRenderPassColorAttachmentDescriptor", + "GpuRenderPassDepthStencilAttachmentDescriptor", + "GpuRenderPassDescriptor", + "GpuRenderPassEncoder", + "GpuRenderPipeline", + "GpuRenderPipelineDescriptor", + "GpuRequestAdapterOptions", + "GpuSampler", + "GpuShaderModule", + "GpuShaderModuleDescriptor", + "GpuStencilOperation", + "GpuStencilStateFaceDescriptor", + "GpuStoreOp", + "GpuSwapChain", + "GpuSwapChainDescriptor", + "GpuTexture", + "GpuTextureDescriptor", + "GpuTextureDimension", + "GpuTextureFormat", + "GpuTextureViewDimension", + "GpuTextureView", + "GpuVertexAttributeDescriptor", + "GpuVertexBufferLayoutDescriptor", + "GpuVertexFormat", + "GpuVertexStateDescriptor", + "GpuVertexAttributeDescriptor", + "HtmlCanvasElement", + "Window", +]} +js-sys = "0.3.36" +wasm-bindgen-futures = "0.4.9" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +console_error_panic_hook = "0.1.6" +console_log = "0.1.2" diff --git a/README.md b/README.md index 89b99051f..0e0d1a75c 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,19 @@ The `hello-triangle` and `hello-compute` examples show bare-bones setup without cargo run --example hello-compute 1 2 3 4 ``` +#### Run Examples on the Web (`wasm32-unknown-unknown`) + +To run examples on the `wasm32-unknown-unknown` target, first build the example as usual, then run `wasm-bindgen`: + +```bash +# Install or update wasm-bindgen-cli +cargo install -f wasm-bindgen-cli +# Build with the wasm target +RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --target wasm32-unknown-unknown --example hello-triangle +# Generate bindings in a `target/generated` directory +wasm-bindgen --out-dir target/generated --web target/wasm32-unknown-unknown/debug/examples/hello-triangle.wasm +``` + ## Friends Shout out to the following projects that work best with wgpu-rs: diff --git a/examples/framework.rs b/examples/framework.rs index cb8e65e59..444531806 100644 --- a/examples/framework.rs +++ b/examples/framework.rs @@ -1,4 +1,8 @@ -use winit::event::WindowEvent; +use winit::{ + event::{self, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::Window, +}; #[cfg_attr(rustfmt, rustfmt_skip)] #[allow(unused)] @@ -24,16 +28,6 @@ pub enum ShaderStage { Compute, } -pub fn load_glsl(code: &str, stage: ShaderStage) -> Vec { - let ty = match stage { - ShaderStage::Vertex => glsl_to_spirv::ShaderType::Vertex, - ShaderStage::Fragment => glsl_to_spirv::ShaderType::Fragment, - ShaderStage::Compute => glsl_to_spirv::ShaderType::Compute, - }; - - wgpu::read_spirv(glsl_to_spirv::compile(&code, ty).unwrap()).unwrap() -} - pub trait Example: 'static + Sized { fn init( sc_desc: &wgpu::SwapChainDescriptor, @@ -52,51 +46,13 @@ pub trait Example: 'static + Sized { ) -> wgpu::CommandBuffer; } -async fn run_async(title: &str) { - use winit::{ - event, - event_loop::{ControlFlow, EventLoop}, - }; - - env_logger::init(); - let event_loop = EventLoop::new(); - log::info!("Initializing the window..."); +async fn run_async(event_loop: EventLoop<()>, window: Window) { + log::info!("Initializing the surface..."); - #[cfg(not(feature = "gl"))] - let (window, size, surface) = { - let mut builder = winit::window::WindowBuilder::new(); - builder = builder.with_title(title); - #[cfg(windows_OFF)] //TODO - { - use winit::platform::windows::WindowBuilderExtWindows; - builder = builder.with_no_redirection_bitmap(true); - } - let window = builder.build(&event_loop).unwrap(); + let (size, surface) = { let size = window.inner_size(); let surface = wgpu::Surface::create(&window); - (window, size, surface) - }; - - #[cfg(feature = "gl")] - let (window, instance, size, surface) = { - let wb = winit::WindowBuilder::new(); - let cb = wgpu::glutin::ContextBuilder::new().with_vsync(true); - let context = cb.build_windowed(wb, &event_loop).unwrap(); - context.window().set_title(title); - - let hidpi_factor = context.window().hidpi_factor(); - let size = context - .window() - .get_inner_size() - .unwrap() - .to_physical(hidpi_factor); - - let (context, window) = unsafe { context.make_current().unwrap().split() }; - - let instance = wgpu::Instance::new(context); - let surface = instance.get_surface(); - - (window, instance, size, surface) + (size, surface) }; let adapter = wgpu::Adapter::request( @@ -120,7 +76,7 @@ async fn run_async(title: &str) { let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format: wgpu::TextureFormat::Bgra8Unorm, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, @@ -185,7 +141,36 @@ async fn run_async(title: &str) { } pub fn run(title: &str) { - futures::executor::block_on(run_async::(title)); + let event_loop = EventLoop::new(); + let mut builder = winit::window::WindowBuilder::new(); + builder = builder.with_title(title); + #[cfg(windows_OFF)] //TODO + { + use winit::platform::windows::WindowBuilderExtWindows; + builder = builder.with_no_redirection_bitmap(true); + } + let window = builder.build(&event_loop).unwrap(); + + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init(); + futures::executor::block_on(run_async::(event_loop, window)); + } + #[cfg(target_arch = "wasm32")] + { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + use winit::platform::web::WindowExtWebSys; + // On wasm, append the canvas to the document body + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.body()) + .and_then(|body| { + body.append_child(&web_sys::Element::from(window.canvas())) + .ok() + }) + .expect("couldn't append canvas to document body"); + wasm_bindgen_futures::spawn_local(run_async::(event_loop, window)); + } } // This allows treating the framework as a standalone example, diff --git a/examples/hello-compute/main.rs b/examples/hello-compute/main.rs index 4eadc7694..3851ecf4b 100644 --- a/examples/hello-compute/main.rs +++ b/examples/hello-compute/main.rs @@ -1,7 +1,7 @@ -use std::{convert::TryInto as _, str::FromStr}; +use std::{convert::TryInto, str::FromStr}; async fn run() { - let numbers = if std::env::args().len() == 1 { + let numbers = if std::env::args().len() <= 1 { let default = vec![1, 2, 3, 4]; log::info!("No numbers were provided, defaulting to {:?}", default); default @@ -124,9 +124,20 @@ async fn execute_gpu(numbers: Vec) -> Vec { } } +#[cfg(target_arch = "wasm32")] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] +pub fn wasm_main() { + console_log::init().expect("could not initialize log"); + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + wasm_bindgen_futures::spawn_local(run()); +} + +#[cfg(target_arch = "wasm32")] +fn main() {} + +#[cfg(not(target_arch = "wasm32"))] fn main() { env_logger::init(); - futures::executor::block_on(run()); } diff --git a/examples/hello-triangle/main.rs b/examples/hello-triangle/main.rs index 54b1b7349..a2f143880 100644 --- a/examples/hello-triangle/main.rs +++ b/examples/hello-triangle/main.rs @@ -67,7 +67,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format: wgpu::TextureFormat::Bgra8Unorm, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, @@ -84,7 +84,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format: wgpu::TextureFormat::Bgra8Unorm, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, @@ -93,7 +93,8 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Poll; + // TODO: ControlFlow::Poll; + *control_flow = ControlFlow::Wait; match event { Event::MainEventsCleared => window.request_redraw(), Event::WindowEvent { @@ -136,9 +137,34 @@ async fn run(event_loop: EventLoop<()>, window: Window) { } }); } + +#[cfg(target_arch = "wasm32")] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] +pub fn wasm_main() { + main(); +} + fn main() { let event_loop = EventLoop::new(); let window = winit::window::Window::new(&event_loop).unwrap(); - env_logger::init(); - futures::executor::block_on(run(event_loop, window)); + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init(); + futures::executor::block_on(run(event_loop, window)); + } + #[cfg(target_arch = "wasm32")] + { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + use winit::platform::web::WindowExtWebSys; + // On wasm, append the canvas to the document body + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.body()) + .and_then(|body| { + body.append_child(&web_sys::Element::from(window.canvas())) + .ok() + }) + .expect("couldn't append canvas to document body"); + wasm_bindgen_futures::spawn_local(run(event_loop, window)); + } } diff --git a/examples/msaa-line/main.rs b/examples/msaa-line/main.rs index 1a8525699..ad63c9076 100644 --- a/examples/msaa-line/main.rs +++ b/examples/msaa-line/main.rs @@ -46,7 +46,7 @@ impl Example { pipeline_layout: &wgpu::PipelineLayout, sample_count: u32, ) -> wgpu::RenderPipeline { - println!("sample_count: {}", sample_count); + log::info!("sample_count: {}", sample_count); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { @@ -117,17 +117,15 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, ) -> (Self, Option) { - println!("Press left/right arrow keys to change sample_count."); + log::info!("Press left/right arrow keys to change sample_count."); let sample_count = 4; - let vs_bytes = - framework::load_glsl(include_str!("shader.vert"), framework::ShaderStage::Vertex); - let fs_bytes = framework::load_glsl( - include_str!("shader.frag"), - framework::ShaderStage::Fragment, - ); - let vs_module = device.create_shader_module(&vs_bytes); - let fs_module = device.create_shader_module(&fs_bytes); + let vs_bytes = include_bytes!("shader.vert.spv"); + let fs_bytes = include_bytes!("shader.frag.spv"); + let vs_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap()); + let fs_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap()); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[], @@ -270,6 +268,12 @@ impl framework::Example for Example { } } +#[cfg(target_arch = "wasm32")] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] +pub fn wasm_main() { + main(); +} + fn main() { framework::run::("msaa-line"); } diff --git a/examples/msaa-line/shader.frag.spv b/examples/msaa-line/shader.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..46d3355b7c1a4ad700fd8da3b6ebbd6e7ea114ee GIT binary patch literal 372 zcmYk1O$z~06o!u(W-vZe>_qmmKuT;ZW-~iKKuSts3i*M4m5uT~*N{7%d+z&jKF0S3 zp;=B}(W>@2P4k4!A`4uOlXWt@-gd+BWQ3z;zBA&fTWEP@;b1N8FM z)$F07g4lmQn40}&l^)ZJ)2Y8_IL`F0I1U*7i(BeU@4lO2?%mcq5Z6<5 literal 0 HcmV?d00001 diff --git a/examples/msaa-line/shader.vert.spv b/examples/msaa-line/shader.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..adab992197bca7580dbd519b023c9508bed7183f GIT binary patch literal 968 zcmYk4%}WAN6vbcaq-AMlY9D4s%RpLG1W^$Nws2txZAEH|foUdmf@sy>)?d{o==`4Z ztT$ZmJLi6#xnnkSkT+&QPr>Y&*{PU}urWomXnn6g?02TgW9Rtfh>ViS+C*~7X35@j z`h6J(5nxreF58jq%CdSg@?R?OOis^VuBRDA;pcEP@F(~FB=O$`#m;!{#Nug?1aTxb zZ}XXL`5;P$!G}+ti!n>FZWz4Y1k+?3J^1q&%89*&;XLL^ADTJnL;IMKDds(%M%_4! zCpOMIjZ=Ip2K^l89XHz{+cm~}?(V0H<2s&9%8cEq9PTZ|`vAr?(l8G7!-qNSd4a&xYca`YT%60=(HUJKO*O literal 0 HcmV?d00001 diff --git a/src/backend/mod.rs b/src/backend/mod.rs index c59ade973..e447274f6 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1 +1,13 @@ -pub(crate) mod native_gpu_future; +#[cfg(target_arch = "wasm32")] +mod web; +#[cfg(target_arch = "wasm32")] +pub use web::*; + +#[cfg(not(target_arch = "wasm32"))] +mod native; + +#[cfg(not(target_arch = "wasm32"))] +pub use native::*; + +#[cfg(not(target_arch = "wasm32"))] +mod native_gpu_future; diff --git a/src/backend/native.rs b/src/backend/native.rs new file mode 100644 index 000000000..8c00efa32 --- /dev/null +++ b/src/backend/native.rs @@ -0,0 +1,671 @@ +use wgn; + +use crate::{ + backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, + BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, + PipelineLayoutDescriptor, RenderPipelineDescriptor, TextureDescriptor, TextureViewDimension, +}; + +use arrayvec::ArrayVec; +use smallvec::SmallVec; +use std::{ffi::CString, future::Future, ops::Range, ptr, slice}; + +pub type AdapterId = wgc::id::AdapterId; +pub type DeviceId = wgc::id::DeviceId; +pub type QueueId = wgc::id::QueueId; +pub type ShaderModuleId = wgc::id::ShaderModuleId; +pub type BindGroupLayoutId = wgc::id::BindGroupLayoutId; +pub type BindGroupId = wgc::id::BindGroupId; +pub type TextureViewId = wgc::id::TextureViewId; +pub type SamplerId = wgc::id::SamplerId; +pub type BufferId = wgc::id::BufferId; +pub type TextureId = wgc::id::TextureId; +pub type PipelineLayoutId = wgc::id::PipelineLayoutId; +pub type RenderPipelineId = wgc::id::RenderPipelineId; +pub type ComputePipelineId = wgc::id::ComputePipelineId; +pub type CommandEncoderId = wgc::id::CommandEncoderId; +pub type ComputePassId = wgc::id::ComputePassId; +pub type CommandBufferId = wgc::id::CommandBufferId; +pub type SurfaceId = wgc::id::SurfaceId; +pub type SwapChainId = wgc::id::SwapChainId; +pub type RenderPassEncoderId = wgc::id::RenderPassId; + +pub(crate) async fn request_adapter( + options: &crate::RequestAdapterOptions<'_>, + backends: wgt::BackendBit, +) -> Option { + unsafe extern "C" fn adapter_callback( + id: Option, + user_data: *mut std::ffi::c_void, + ) { + *(user_data as *mut Option) = id; + } + + let mut id_maybe = None; + unsafe { + wgn::wgpu_request_adapter_async( + Some(&wgc::instance::RequestAdapterOptions { + power_preference: options.power_preference, + compatible_surface: options.compatible_surface.map(|surface| surface.id), + }), + backends, + adapter_callback, + &mut id_maybe as *mut _ as *mut std::ffi::c_void, + ) + }; + id_maybe +} + +pub(crate) async fn request_device_and_queue( + adapter: &AdapterId, + desc: Option<&wgt::DeviceDescriptor>, +) -> (DeviceId, QueueId) { + let device_id = wgn::wgpu_adapter_request_device(*adapter, desc); + (device_id, wgn::wgpu_device_get_default_queue(device_id)) +} + +pub(crate) fn create_shader_module(device: &DeviceId, spv: &[u32]) -> ShaderModuleId { + let desc = wgc::pipeline::ShaderModuleDescriptor { + code: wgc::U32Array { + bytes: spv.as_ptr(), + length: spv.len(), + }, + }; + wgn::wgpu_device_create_shader_module(*device, &desc) +} + +pub(crate) fn create_bind_group_layout( + device: &DeviceId, + desc: &BindGroupLayoutDescriptor, +) -> BindGroupLayoutId { + use wgc::binding_model as bm; + + let temp_layouts = desc + .bindings + .iter() + .map(|bind| bm::BindGroupLayoutEntry { + binding: bind.binding, + visibility: bind.visibility, + ty: match bind.ty { + BindingType::UniformBuffer { .. } => bm::BindingType::UniformBuffer, + BindingType::StorageBuffer { + readonly: false, .. + } => bm::BindingType::StorageBuffer, + BindingType::StorageBuffer { readonly: true, .. } => { + bm::BindingType::ReadonlyStorageBuffer + } + BindingType::Sampler { comparison: false } => bm::BindingType::Sampler, + BindingType::Sampler { .. } => bm::BindingType::ComparisonSampler, + BindingType::SampledTexture { .. } => bm::BindingType::SampledTexture, + BindingType::StorageTexture { readonly: true, .. } => { + bm::BindingType::ReadonlyStorageTexture + } + BindingType::StorageTexture { .. } => bm::BindingType::WriteonlyStorageTexture, + }, + has_dynamic_offset: match bind.ty { + BindingType::UniformBuffer { dynamic } + | BindingType::StorageBuffer { dynamic, .. } => dynamic, + _ => false, + }, + multisampled: match bind.ty { + BindingType::SampledTexture { multisampled, .. } => multisampled, + _ => false, + }, + view_dimension: match bind.ty { + BindingType::SampledTexture { dimension, .. } + | BindingType::StorageTexture { dimension, .. } => dimension, + _ => TextureViewDimension::D2, + }, + texture_component_type: match bind.ty { + BindingType::SampledTexture { component_type, .. } + | BindingType::StorageTexture { component_type, .. } => component_type, + _ => wgt::TextureComponentType::Float, + }, + storage_texture_format: match bind.ty { + BindingType::StorageTexture { format, .. } => format, + _ => wgt::TextureFormat::Rgb10a2Unorm, // doesn't matter + }, + }) + .collect::>(); + + let owned_label = OwnedLabel::new(desc.label.as_deref()); + wgn::wgpu_device_create_bind_group_layout( + *device, + &bm::BindGroupLayoutDescriptor { + entries: temp_layouts.as_ptr(), + entries_length: temp_layouts.len(), + label: owned_label.as_ptr(), + }, + ) +} + +pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) -> BindGroupId { + use wgc::binding_model as bm; + + let bindings = desc + .bindings + .iter() + .map(|binding| bm::BindGroupEntry { + binding: binding.binding, + resource: match binding.resource { + BindingResource::Buffer { + ref buffer, + ref range, + } => bm::BindingResource::Buffer(bm::BufferBinding { + buffer: buffer.id, + offset: range.start, + size: range.end - range.start, + }), + BindingResource::Sampler(ref sampler) => bm::BindingResource::Sampler(sampler.id), + BindingResource::TextureView(ref texture_view) => { + bm::BindingResource::TextureView(texture_view.id) + } + }, + }) + .collect::>(); + + let owned_label = OwnedLabel::new(desc.label.as_deref()); + wgn::wgpu_device_create_bind_group( + *device, + &bm::BindGroupDescriptor { + layout: desc.layout.id, + entries: bindings.as_ptr(), + entries_length: bindings.len(), + label: owned_label.as_ptr(), + }, + ) +} + +pub(crate) fn create_pipeline_layout( + device: &DeviceId, + desc: &PipelineLayoutDescriptor, +) -> PipelineLayoutId { + //TODO: avoid allocation here + let temp_layouts = desc + .bind_group_layouts + .iter() + .map(|bgl| bgl.id) + .collect::>(); + wgn::wgpu_device_create_pipeline_layout( + *device, + &wgc::binding_model::PipelineLayoutDescriptor { + bind_group_layouts: temp_layouts.as_ptr(), + bind_group_layouts_length: temp_layouts.len(), + }, + ) +} + +pub(crate) fn create_render_pipeline( + device: &DeviceId, + desc: &RenderPipelineDescriptor, +) -> RenderPipelineId { + use wgc::pipeline as pipe; + + let vertex_entry_point = CString::new(desc.vertex_stage.entry_point).unwrap(); + let vertex_stage = pipe::ProgrammableStageDescriptor { + module: desc.vertex_stage.module.id, + entry_point: vertex_entry_point.as_ptr(), + }; + let (_fragment_entry_point, fragment_stage) = if let Some(fragment_stage) = &desc.fragment_stage + { + let fragment_entry_point = CString::new(fragment_stage.entry_point).unwrap(); + let fragment_stage = pipe::ProgrammableStageDescriptor { + module: fragment_stage.module.id, + entry_point: fragment_entry_point.as_ptr(), + }; + (fragment_entry_point, Some(fragment_stage)) + } else { + (CString::default(), None) + }; + + let temp_color_states = desc.color_states.to_vec(); + let temp_vertex_buffers = desc + .vertex_state + .vertex_buffers + .iter() + .map(|vbuf| pipe::VertexBufferLayoutDescriptor { + array_stride: vbuf.stride, + step_mode: vbuf.step_mode, + attributes: vbuf.attributes.as_ptr(), + attributes_length: vbuf.attributes.len(), + }) + .collect::>(); + + wgn::wgpu_device_create_render_pipeline( + *device, + &pipe::RenderPipelineDescriptor { + layout: desc.layout.id, + vertex_stage, + fragment_stage: fragment_stage + .as_ref() + .map_or(ptr::null(), |fs| fs as *const _), + rasterization_state: desc + .rasterization_state + .as_ref() + .map_or(ptr::null(), |p| p as *const _), + primitive_topology: desc.primitive_topology, + color_states: temp_color_states.as_ptr(), + color_states_length: temp_color_states.len(), + depth_stencil_state: desc + .depth_stencil_state + .as_ref() + .map_or(ptr::null(), |p| p as *const _), + vertex_state: pipe::VertexStateDescriptor { + index_format: desc.vertex_state.index_format, + vertex_buffers: temp_vertex_buffers.as_ptr(), + vertex_buffers_length: temp_vertex_buffers.len(), + }, + sample_count: desc.sample_count, + sample_mask: desc.sample_mask, + alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled, + }, + ) +} + +pub(crate) fn create_compute_pipeline( + device: &DeviceId, + desc: &ComputePipelineDescriptor, +) -> ComputePipelineId { + use wgc::pipeline as pipe; + + let entry_point = CString::new(desc.compute_stage.entry_point).unwrap(); + + wgn::wgpu_device_create_compute_pipeline( + *device, + &pipe::ComputePipelineDescriptor { + layout: desc.layout.id, + compute_stage: pipe::ProgrammableStageDescriptor { + module: desc.compute_stage.module.id, + entry_point: entry_point.as_ptr(), + }, + }, + ) +} + +pub(crate) type CreateBufferMappedDetail = BufferDetail; + +pub(crate) fn create_buffer_mapped<'a>( + device: &DeviceId, + desc: &BufferDescriptor, +) -> crate::CreateBufferMapped<'a> { + let owned_label = OwnedLabel::new(desc.label.as_deref()); + let mut data_ptr: *mut u8 = std::ptr::null_mut(); + unsafe { + let id = wgn::wgpu_device_create_buffer_mapped( + *device, + &wgt::BufferDescriptor { + label: owned_label.as_ptr(), + size: desc.size, + usage: desc.usage, + }, + &mut data_ptr as *mut *mut u8, + ); + let mapped_data = std::slice::from_raw_parts_mut(data_ptr as *mut u8, desc.size as usize); + crate::CreateBufferMapped { + id, + mapped_data, + detail: CreateBufferMappedDetail { device_id: *device }, + } + } +} + +#[derive(Debug, Hash, PartialEq)] +pub(crate) struct BufferDetail { + /// On native we need to track the device in order to later destroy the + /// buffer. + device_id: DeviceId, +} + +pub(crate) fn create_buffer_mapped_finish( + create_buffer_mapped: crate::CreateBufferMapped<'_>, +) -> crate::Buffer { + buffer_unmap(&create_buffer_mapped.id); + crate::Buffer { + id: create_buffer_mapped.id, + detail: BufferDetail { + device_id: create_buffer_mapped.detail.device_id, + }, + } +} + +pub(crate) fn buffer_unmap(buffer: &BufferId) { + wgn::wgpu_buffer_unmap(*buffer); +} + +pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { + let owned_label = OwnedLabel::new(desc.label.as_deref()); + crate::Buffer { + id: wgn::wgpu_device_create_buffer( + *device, + &wgt::BufferDescriptor { + label: owned_label.as_ptr(), + size: desc.size, + usage: desc.usage, + }, + ), + detail: BufferDetail { device_id: *device }, + } +} + +pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { + let owned_label = OwnedLabel::new(desc.label.as_deref()); + wgn::wgpu_device_create_texture( + *device, + &wgt::TextureDescriptor { + label: owned_label.as_ptr(), + size: desc.size, + mip_level_count: desc.mip_level_count, + sample_count: desc.sample_count, + dimension: desc.dimension, + format: desc.format, + usage: desc.usage, + }, + ) +} + +pub(crate) fn create_command_encoder( + device: &DeviceId, + desc: &CommandEncoderDescriptor, +) -> CommandEncoderId { + let owned_label = OwnedLabel::new(desc.label.as_deref()); + wgn::wgpu_device_create_command_encoder( + *device, + Some(&wgt::CommandEncoderDescriptor { + label: owned_label.as_ptr(), + }), + ) +} + +pub(crate) fn 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, +) { + wgn::wgpu_command_encoder_copy_buffer_to_buffer( + *command_encoder, + source.id, + source_offset, + destination.id, + destination_offset, + copy_size, + ); +} + +pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { + unsafe { wgn::wgpu_command_encoder_begin_compute_pass(*command_encoder, None) } +} + +pub(crate) fn compute_pass_set_pipeline( + compute_pass: &ComputePassId, + pipeline: &ComputePipelineId, +) { + unsafe { + wgn::wgpu_compute_pass_set_pipeline(compute_pass.as_mut().unwrap(), *pipeline); + } +} + +pub(crate) fn compute_pass_set_bind_group<'a>( + compute_pass: &ComputePassId, + index: u32, + bind_group: &BindGroupId, + offsets: &[wgt::DynamicOffset], +) { + unsafe { + wgn::wgpu_compute_pass_set_bind_group( + compute_pass.as_mut().unwrap(), + index, + *bind_group, + offsets.as_ptr(), + offsets.len(), + ); + } +} + +pub(crate) fn compute_pass_dispatch(compute_pass: &ComputePassId, x: u32, y: u32, z: u32) { + unsafe { + wgn::wgpu_compute_pass_dispatch(compute_pass.as_mut().unwrap(), x, y, z); + } +} + +pub(crate) fn compute_pass_dispatch_indirect( + compute_pass: &ComputePassId, + indirect_buffer: &BufferId, + indirect_offset: wgt::BufferAddress, +) { + unsafe { + wgn::wgpu_compute_pass_dispatch_indirect( + compute_pass.as_mut().unwrap(), + *indirect_buffer, + indirect_offset, + ); + } +} + +pub(crate) fn compute_pass_end_pass(compute_pass: &ComputePassId) { + unsafe { + wgn::wgpu_compute_pass_end_pass(*compute_pass); + } +} + +pub(crate) fn command_encoder_finish(command_encoder: &CommandEncoderId) -> CommandBufferId { + wgn::wgpu_command_encoder_finish(*command_encoder, None) +} + +pub(crate) fn queue_submit(queue: &QueueId, command_buffers: &[crate::CommandBuffer]) { + let temp_command_buffers = command_buffers + .iter() + .map(|cb| cb.id) + .collect::>(); + + unsafe { wgn::wgpu_queue_submit(*queue, temp_command_buffers.as_ptr(), command_buffers.len()) }; +} + +pub(crate) fn buffer_map_read( + buffer: &crate::Buffer, + start: wgt::BufferAddress, + size: wgt::BufferAddress, +) -> impl Future> { + let (future, completion) = native_gpu_future::new_gpu_future(buffer.id, size); + + extern "C" fn buffer_map_read_future_wrapper( + status: wgc::resource::BufferMapAsyncStatus, + data: *const u8, + user_data: *mut u8, + ) { + let completion = + unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; + let (buffer_id, size) = completion.get_buffer_info(); + + if let wgc::resource::BufferMapAsyncStatus::Success = status { + completion.complete(Ok(crate::BufferReadMapping { + detail: BufferReadMappingDetail { + data, + size: size as usize, + buffer_id, + }, + })); + } else { + completion.complete(Err(crate::BufferAsyncErr)); + } + } + + wgn::wgpu_buffer_map_read_async( + buffer.id, + start, + size, + buffer_map_read_future_wrapper, + completion.to_raw() as _, + ); + + future +} + +pub(crate) struct BufferReadMappingDetail { + data: *const u8, + size: usize, + pub(crate) buffer_id: BufferId, +} + +impl BufferReadMappingDetail { + pub(crate) fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data as *const u8, self.size) } + } +} + +pub(crate) fn create_surface(window: &W) -> SurfaceId { + wgn::wgpu_create_surface(window.raw_window_handle()) +} + +pub(crate) fn device_create_swap_chain( + device: &DeviceId, + surface: &SurfaceId, + desc: &wgt::SwapChainDescriptor, +) -> SwapChainId { + wgn::wgpu_device_create_swap_chain(*device, *surface, desc) +} + +pub(crate) fn swap_chain_get_next_texture(swap_chain: &SwapChainId) -> Option { + wgn::wgpu_swap_chain_get_next_texture(*swap_chain).view_id +} + +pub(crate) fn command_encoder_begin_render_pass<'a>( + command_encoder: &CommandEncoderId, + desc: &crate::RenderPassDescriptor<'a, '_>, +) -> RenderPassEncoderId { + let colors = desc + .color_attachments + .iter() + .map(|ca| wgc::command::RenderPassColorAttachmentDescriptor { + attachment: ca.attachment.id, + resolve_target: ca.resolve_target.map(|rt| rt.id), + load_op: ca.load_op, + store_op: ca.store_op, + clear_color: ca.clear_color, + }) + .collect::>(); + + let depth_stencil = desc.depth_stencil_attachment.as_ref().map(|dsa| { + wgc::command::RenderPassDepthStencilAttachmentDescriptor { + attachment: dsa.attachment.id, + depth_load_op: dsa.depth_load_op, + depth_store_op: dsa.depth_store_op, + clear_depth: dsa.clear_depth, + stencil_load_op: dsa.stencil_load_op, + stencil_store_op: dsa.stencil_store_op, + clear_stencil: dsa.clear_stencil, + } + }); + + unsafe { + wgn::wgpu_command_encoder_begin_render_pass( + *command_encoder, + &wgc::command::RenderPassDescriptor { + color_attachments: colors.as_ptr(), + color_attachments_length: colors.len(), + depth_stencil_attachment: depth_stencil.as_ref(), + }, + ) + } +} + +pub(crate) fn render_pass_set_pipeline( + render_pass: &RenderPassEncoderId, + pipeline: &RenderPipelineId, +) { + unsafe { + wgn::wgpu_render_pass_set_pipeline(render_pass.as_mut().unwrap(), *pipeline); + } +} + +pub(crate) fn render_pass_set_bind_group( + render_pass: &RenderPassEncoderId, + index: u32, + bind_group: &BindGroupId, + offsets: &[wgt::DynamicOffset], +) { + unsafe { + wgn::wgpu_render_pass_set_bind_group( + render_pass.as_mut().unwrap(), + index, + *bind_group, + offsets.as_ptr(), + offsets.len(), + ); + } +} + +pub(crate) fn render_pass_set_vertex_buffer<'a>( + render_pass: &RenderPassEncoderId, + slot: u32, + buffer: &'a crate::Buffer, + offset: wgt::BufferAddress, + size: wgt::BufferAddress, +) { + unsafe { + wgn::wgpu_render_pass_set_vertex_buffer( + render_pass.as_mut().unwrap(), + slot, + buffer.id, + offset, + size, + ) + }; +} + +pub(crate) fn render_pass_draw( + render_pass: &RenderPassEncoderId, + vertices: Range, + instances: Range, +) { + unsafe { + wgn::wgpu_render_pass_draw( + render_pass.as_mut().unwrap(), + vertices.end - vertices.start, + instances.end - instances.start, + vertices.start, + instances.start, + ); + } +} + +pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { + unsafe { + wgn::wgpu_render_pass_end_pass(*render_pass); + } +} + +pub(crate) fn texture_create_default_view(texture: &TextureId) -> TextureViewId { + wgn::wgpu_texture_create_view(*texture, None) +} + +pub(crate) fn swap_chain_present(swap_chain: &SwapChainId) { + wgn::wgpu_swap_chain_present(*swap_chain); +} + +pub(crate) fn device_poll(device: &DeviceId, maintain: crate::Maintain) { + wgn::wgpu_device_poll( + *device, + match maintain { + crate::Maintain::Poll => false, + crate::Maintain::Wait => true, + }, + ); +} + +struct OwnedLabel(Option); + +impl OwnedLabel { + fn new(text: Option<&str>) -> Self { + Self(text.map(|t| CString::new(t).expect("invalid label"))) + } + + fn as_ptr(&self) -> *const std::os::raw::c_char { + match self.0 { + Some(ref c_string) => c_string.as_ptr(), + None => ptr::null(), + } + } +} diff --git a/src/backend/web.rs b/src/backend/web.rs new file mode 100644 index 000000000..8750c1743 --- /dev/null +++ b/src/backend/web.rs @@ -0,0 +1,922 @@ +use crate::{ + BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor, + CommandEncoderDescriptor, ComputePipelineDescriptor, PipelineLayoutDescriptor, + ProgrammableStageDescriptor, RenderPipelineDescriptor, TextureDescriptor, TextureViewDimension, +}; + +use std::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; + +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; + } + + 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_descriptor = web_sys::GpuDeviceDescriptor::new(); + // TODO: Extensions + let mut mapped_limits = web_sys::GpuLimits::new(); + mapped_limits.max_bind_groups(d.limits.max_bind_groups); + // TODO: Other fields + mapped_descriptor.limits(&mapped_limits); + adapter.request_device_with_descriptor(&mapped_descriptor) + } + 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)); + device.create_shader_module(&desc) +} + +pub(crate) fn create_bind_group_layout( + device: &DeviceId, + desc: &BindGroupLayoutDescriptor, +) -> BindGroupLayoutId { + use web_sys::GpuBindingType as bt; + use web_sys::GpuTextureViewDimension as tvd; + + 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 { .. } => unimplemented!(), // TODO: bt::ComparisonSampler, + BindingType::SampledTexture { .. } => bt::SampledTexture, + BindingType::StorageTexture { readonly: true, .. } => { + unimplemented!() // TODO: bt::ReadonlyStorageTexture + } + BindingType::StorageTexture { .. } => { + unimplemented!() // TODO: bt::WriteonlyStorageTexture + } + }; + + let mapped_dynamic = match bind.ty { + BindingType::UniformBuffer { dynamic } + | BindingType::StorageBuffer { dynamic, .. } => dynamic, + _ => false, + }; + + let mapped_multisampled = match bind.ty { + BindingType::SampledTexture { multisampled, .. } => multisampled, + _ => false, + }; + + let mapped_view_dimension = match bind.ty { + BindingType::SampledTexture { dimension, .. } + | BindingType::StorageTexture { dimension, .. } => match dimension { + TextureViewDimension::D1 => tvd::N1d, + TextureViewDimension::D2 => tvd::N2d, + TextureViewDimension::D2Array => tvd::N2dArray, + TextureViewDimension::Cube => tvd::Cube, + TextureViewDimension::CubeArray => tvd::CubeArray, + TextureViewDimension::D3 => tvd::N3d, + }, + _ => tvd::N2d, + }; + + let mut mapped_binding = web_sys::GpuBindGroupLayoutBinding::new( + bind.binding, + mapped_type, + bind.visibility.bits(), + ); + mapped_binding.has_dynamic_offset(mapped_dynamic); + mapped_binding.multisampled(mapped_multisampled); + mapped_binding.texture_dimension(mapped_view_dimension); + + // TODO: Texture component type, storage texture format + + mapped_binding + }) + .collect::(); + + let mapped_desc = web_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings); + device.create_bind_group_layout(&mapped_desc) +} + +pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) -> BindGroupId { + let mapped_bindings = 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::GpuBindGroupBinding::new(binding.binding, &mapped_resource) + }) + .collect::(); + + let mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_bindings, &desc.layout.id); + device.create_bind_group(&mapped_desc) +} + +pub(crate) fn create_pipeline_layout( + device: &DeviceId, + desc: &PipelineLayoutDescriptor, +) -> PipelineLayoutId { + //TODO: avoid allocation here + let temp_layouts = desc + .bind_group_layouts + .iter() + .map(|bgl| bgl.id.clone()) + .collect::(); + let mapped_desc = web_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts); + device.create_pipeline_layout(&mapped_desc) +} + +fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTextureFormat { + use web_sys::GpuTextureFormat as tf; + use wgt::TextureFormat; + match texture_format { + TextureFormat::R8Unorm => tf::R8unorm, + TextureFormat::R8Snorm => tf::R8snorm, + TextureFormat::R8Uint => tf::R8uint, + TextureFormat::R8Sint => tf::R8sint, + TextureFormat::R16Uint => tf::R16uint, + TextureFormat::R16Sint => tf::R16sint, + TextureFormat::R16Float => tf::R16float, + TextureFormat::Rg8Unorm => tf::Rg8unorm, + TextureFormat::Rg8Snorm => tf::Rg8snorm, + TextureFormat::Rg8Uint => tf::Rg8uint, + TextureFormat::Rg8Sint => tf::Rg8sint, + TextureFormat::R32Uint => tf::R32uint, + TextureFormat::R32Sint => tf::R32sint, + TextureFormat::R32Float => tf::R32float, + TextureFormat::Rg16Uint => tf::Rg16uint, + TextureFormat::Rg16Sint => tf::Rg16sint, + TextureFormat::Rg16Float => tf::Rg16float, + TextureFormat::Rgba8Unorm => tf::Rgba8unorm, + TextureFormat::Rgba8UnormSrgb => tf::Rgba8unormSrgb, + TextureFormat::Rgba8Snorm => tf::Rgba8snorm, + TextureFormat::Rgba8Uint => tf::Rgba8uint, + TextureFormat::Rgba8Sint => tf::Rgba8sint, + TextureFormat::Bgra8Unorm => tf::Bgra8unorm, + TextureFormat::Bgra8UnormSrgb => tf::Bgra8unormSrgb, + TextureFormat::Rgb10a2Unorm => tf::Rgb10a2unorm, + TextureFormat::Rg11b10Float => tf::Rg11b10float, + TextureFormat::Rg32Uint => tf::Rg32uint, + TextureFormat::Rg32Sint => tf::Rg32sint, + TextureFormat::Rg32Float => tf::Rg32float, + TextureFormat::Rgba16Uint => tf::Rgba16uint, + TextureFormat::Rgba16Sint => tf::Rgba16sint, + TextureFormat::Rgba16Float => tf::Rgba16float, + TextureFormat::Rgba32Uint => tf::Rgba32uint, + TextureFormat::Rgba32Sint => tf::Rgba32sint, + TextureFormat::Rgba32Float => tf::Rgba32float, + TextureFormat::Depth32Float => tf::Depth32float, + TextureFormat::Depth24Plus => tf::Depth24plus, + TextureFormat::Depth24PlusStencil8 => tf::Depth24plusStencil8, + } +} + +fn map_stage_descriptor( + desc: &ProgrammableStageDescriptor, +) -> web_sys::GpuProgrammableStageDescriptor { + web_sys::GpuProgrammableStageDescriptor::new(desc.entry_point, &desc.module.id) +} + +fn map_cull_mode(cull_mode: wgt::CullMode) -> web_sys::GpuCullMode { + use web_sys::GpuCullMode as cm; + use wgt::CullMode; + match cull_mode { + CullMode::None => cm::None, + CullMode::Front => cm::Front, + CullMode::Back => cm::Back, + } +} + +fn map_front_face(front_face: wgt::FrontFace) -> web_sys::GpuFrontFace { + use web_sys::GpuFrontFace as ff; + use wgt::FrontFace; + match front_face { + FrontFace::Ccw => ff::Ccw, + FrontFace::Cw => ff::Cw, + } +} + +fn map_rasterization_state_descriptor( + desc: &wgt::RasterizationStateDescriptor, +) -> web_sys::GpuRasterizationStateDescriptor { + let mut mapped = web_sys::GpuRasterizationStateDescriptor::new(); + mapped.cull_mode(map_cull_mode(desc.cull_mode)); + mapped.depth_bias(desc.depth_bias); + mapped.depth_bias_clamp(desc.depth_bias_clamp); + mapped.depth_bias_slope_scale(desc.depth_bias_slope_scale); + mapped.front_face(map_front_face(desc.front_face)); + mapped +} + +fn map_compare_function(compare_fn: wgt::CompareFunction) -> Option { + use web_sys::GpuCompareFunction as cf; + use wgt::CompareFunction; + match compare_fn { + CompareFunction::Undefined => None, + CompareFunction::Never => Some(cf::Never), + CompareFunction::Less => Some(cf::Less), + CompareFunction::Equal => Some(cf::Equal), + CompareFunction::LessEqual => Some(cf::LessEqual), + CompareFunction::Greater => Some(cf::Greater), + CompareFunction::NotEqual => Some(cf::NotEqual), + CompareFunction::GreaterEqual => Some(cf::GreaterEqual), + CompareFunction::Always => Some(cf::Always), + } +} + +fn map_stencil_operation(op: wgt::StencilOperation) -> web_sys::GpuStencilOperation { + use web_sys::GpuStencilOperation as so; + use wgt::StencilOperation; + match op { + StencilOperation::Keep => so::Keep, + StencilOperation::Zero => so::Zero, + StencilOperation::Replace => so::Replace, + StencilOperation::Invert => so::Invert, + StencilOperation::IncrementClamp => so::IncrementClamp, + StencilOperation::DecrementClamp => so::DecrementClamp, + StencilOperation::IncrementWrap => so::IncrementWrap, + StencilOperation::DecrementWrap => so::DecrementWrap, + } +} + +fn map_stencil_state_face_descriptor( + desc: &wgt::StencilStateFaceDescriptor, +) -> web_sys::GpuStencilStateFaceDescriptor { + let mut mapped = web_sys::GpuStencilStateFaceDescriptor::new(); + if let Some(compare) = map_compare_function(desc.compare) { + mapped.compare(compare); + } + mapped.depth_fail_op(map_stencil_operation(desc.depth_fail_op)); + mapped.fail_op(map_stencil_operation(desc.fail_op)); + mapped.pass_op(map_stencil_operation(desc.pass_op)); + mapped +} + +fn map_depth_stencil_state_descriptor( + desc: &wgt::DepthStencilStateDescriptor, +) -> web_sys::GpuDepthStencilStateDescriptor { + let mut mapped = web_sys::GpuDepthStencilStateDescriptor::new(map_texture_format(desc.format)); + if let Some(depth_compare) = map_compare_function(desc.depth_compare) { + mapped.depth_compare(depth_compare); + } + mapped.depth_write_enabled(desc.depth_write_enabled); + mapped.stencil_back(&map_stencil_state_face_descriptor(&desc.stencil_back)); + mapped.stencil_front(&map_stencil_state_face_descriptor(&desc.stencil_front)); + mapped.stencil_read_mask(desc.stencil_read_mask); + mapped.stencil_write_mask(desc.stencil_write_mask); + mapped +} + +fn map_blend_descriptor(desc: &wgt::BlendDescriptor) -> web_sys::GpuBlendDescriptor { + let mut mapped = web_sys::GpuBlendDescriptor::new(); + mapped.dst_factor(map_blend_factor(desc.dst_factor)); + mapped.operation(map_blend_operation(desc.operation)); + mapped.src_factor(map_blend_factor(desc.src_factor)); + mapped +} + +fn map_blend_factor(factor: wgt::BlendFactor) -> web_sys::GpuBlendFactor { + use web_sys::GpuBlendFactor as bf; + use wgt::BlendFactor; + match factor { + BlendFactor::Zero => bf::Zero, + BlendFactor::One => bf::One, + BlendFactor::SrcColor => bf::SrcColor, + BlendFactor::OneMinusSrcColor => bf::OneMinusSrcColor, + BlendFactor::SrcAlpha => bf::SrcAlpha, + BlendFactor::OneMinusSrcAlpha => bf::OneMinusSrcAlpha, + BlendFactor::DstColor => bf::DstColor, + BlendFactor::OneMinusDstColor => bf::OneMinusDstColor, + BlendFactor::DstAlpha => bf::DstAlpha, + BlendFactor::OneMinusDstAlpha => bf::OneMinusDstAlpha, + BlendFactor::SrcAlphaSaturated => bf::SrcAlphaSaturated, + BlendFactor::BlendColor => bf::BlendColor, + BlendFactor::OneMinusBlendColor => bf::OneMinusBlendColor, + } +} + +fn map_blend_operation(op: wgt::BlendOperation) -> web_sys::GpuBlendOperation { + use web_sys::GpuBlendOperation as bo; + use wgt::BlendOperation; + match op { + BlendOperation::Add => bo::Add, + BlendOperation::Subtract => bo::Subtract, + BlendOperation::ReverseSubtract => bo::ReverseSubtract, + BlendOperation::Min => bo::Min, + BlendOperation::Max => bo::Max, + } +} + +fn map_index_format(format: wgt::IndexFormat) -> web_sys::GpuIndexFormat { + use web_sys::GpuIndexFormat as f; + use wgt::IndexFormat; + match format { + IndexFormat::Uint16 => f::Uint16, + IndexFormat::Uint32 => f::Uint32, + } +} + +fn map_vertex_format(format: wgt::VertexFormat) -> web_sys::GpuVertexFormat { + use web_sys::GpuVertexFormat as vf; + use wgt::VertexFormat; + match format { + VertexFormat::Uchar2 => vf::Uchar2, + VertexFormat::Uchar4 => vf::Uchar4, + VertexFormat::Char2 => vf::Char2, + VertexFormat::Char4 => vf::Char4, + VertexFormat::Uchar2Norm => vf::Uchar2norm, + VertexFormat::Uchar4Norm => vf::Uchar4norm, + VertexFormat::Char2Norm => vf::Char2norm, + VertexFormat::Char4Norm => vf::Char4norm, + VertexFormat::Ushort2 => vf::Ushort2, + VertexFormat::Ushort4 => vf::Ushort4, + VertexFormat::Short2 => vf::Short2, + VertexFormat::Short4 => vf::Short4, + VertexFormat::Ushort2Norm => vf::Ushort2norm, + VertexFormat::Ushort4Norm => vf::Ushort4norm, + VertexFormat::Short2Norm => vf::Short2norm, + VertexFormat::Short4Norm => vf::Short4norm, + VertexFormat::Half2 => vf::Half2, + VertexFormat::Half4 => vf::Half4, + VertexFormat::Float => vf::Float, + VertexFormat::Float2 => vf::Float2, + VertexFormat::Float3 => vf::Float3, + VertexFormat::Float4 => vf::Float4, + VertexFormat::Uint => vf::Uint, + VertexFormat::Uint2 => vf::Uint2, + VertexFormat::Uint3 => vf::Uint3, + VertexFormat::Uint4 => vf::Uint4, + VertexFormat::Int => vf::Int, + VertexFormat::Int2 => vf::Int2, + VertexFormat::Int3 => vf::Int3, + VertexFormat::Int4 => vf::Int4, + } +} + +fn map_input_step_mode(mode: wgt::InputStepMode) -> web_sys::GpuInputStepMode { + use web_sys::GpuInputStepMode as sm; + use wgt::InputStepMode; + match mode { + InputStepMode::Vertex => sm::Vertex, + InputStepMode::Instance => sm::Instance, + } +} + +fn map_vertex_state_descriptor( + desc: &RenderPipelineDescriptor, +) -> web_sys::GpuVertexStateDescriptor { + let mapped_vertex_buffers = desc + .vertex_state + .vertex_buffers + .iter() + .map(|vbuf| { + let mapped_attributes = vbuf + .attributes + .iter() + .map(|attr| { + web_sys::GpuVertexAttributeDescriptor::new( + map_vertex_format(attr.format), + attr.offset as f64, + attr.shader_location, + ) + }) + .collect::(); + + let mut mapped_vbuf = web_sys::GpuVertexBufferLayoutDescriptor::new( + vbuf.stride as f64, + &mapped_attributes, + ); + mapped_vbuf.step_mode(map_input_step_mode(vbuf.step_mode)); + mapped_vbuf + }) + .collect::(); + + let mut mapped = web_sys::GpuVertexStateDescriptor::new(); + mapped.index_format(map_index_format(desc.vertex_state.index_format)); + mapped.vertex_buffers(&mapped_vertex_buffers); + mapped +} + +fn map_extent_3d(extent: wgt::Extent3d) -> web_sys::GpuExtent3dDict { + web_sys::GpuExtent3dDict::new(extent.depth, extent.height, extent.width) +} + +fn map_texture_dimension(texture_dimension: wgt::TextureDimension) -> web_sys::GpuTextureDimension { + match texture_dimension { + wgt::TextureDimension::D1 => web_sys::GpuTextureDimension::N1d, + wgt::TextureDimension::D2 => web_sys::GpuTextureDimension::N2d, + wgt::TextureDimension::D3 => web_sys::GpuTextureDimension::N3d, + } +} + +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, + ); + + 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) +} + +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); + + device.create_compute_pipeline(&mapped_desc) +} + +pub(crate) struct CreateBufferMappedDetail { + /// On wasm we need to allocate our own temporary storage for `data`. Later + /// we copy this temporary storage into the `Uint8Array` which was returned + /// by the browser originally. + array_buffer: js_sys::ArrayBuffer, +} + +pub(crate) fn create_buffer_mapped<'a>( + device: &DeviceId, + desc: &BufferDescriptor, +) -> crate::CreateBufferMapped<'a> { + let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + 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 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 create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { + let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + crate::Buffer { + id: device.create_buffer(&mapped_desc), + detail: (), + } +} + +pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { + let extent = map_extent_3d(desc.size); + let mut mapped_desc = web_sys::GpuTextureDescriptor::new( + map_texture_format(desc.format), + &extent, + desc.usage.bits(), + ); + mapped_desc.array_layer_count(desc.array_layer_count); + mapped_desc.dimension(map_texture_dimension(desc.dimension)); + mapped_desc.mip_level_count(desc.mip_level_count); + mapped_desc.sample_count(desc.sample_count); + mapped_desc.array_layer_count(desc.array_layer_count); + device.create_texture(&mapped_desc) +} + +pub(crate) fn create_command_encoder( + device: &DeviceId, + _desc: &CommandEncoderDescriptor, +) -> CommandEncoderId { + let mapped_desc = web_sys::GpuCommandEncoderDescriptor::new(); + device.create_command_encoder_with_descriptor(&mapped_desc) +} + +pub(crate) fn 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 begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { + let mapped_desc = web_sys::GpuComputePassDescriptor::new(); + 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, + // TODO: `offsets` currently requires `&mut` so we have to clone it + // here, but this should be fixed upstream in web-sys in the future + &mut offsets.to_vec(), + 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 mapped_desc = web_sys::GpuCommandBufferDescriptor::new(); + 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) struct BufferReadMappingDetail { + pub(crate) buffer_id: BufferId, + mapped: Vec, +} + +impl BufferReadMappingDetail { + pub(crate) fn as_slice(&self) -> &[u8] { + &self.mapped[..] + } +} + +pub(crate) fn 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 swap_chain_get_next_texture(swap_chain: &SwapChainId) -> Option { + // TODO: Should we pass a descriptor here? + // Or is the default view always correct? + Some(swap_chain.get_current_texture().create_view()) +} + +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 => { + let color = ca.clear_color; + let mapped_color = + web_sys::GpuColorDict::new(color.a, color.b, color.g, color.r); + wasm_bindgen::JsValue::from(mapped_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 = web_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments); + + 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.depth_stencil_attachment(&mapped_depth_stencil_attachment); + } + + command_encoder.begin_render_pass(&mapped) +} + +pub(crate) fn render_pass_set_pipeline( + render_pass: &RenderPassEncoderId, + pipeline: &RenderPipelineId, +) { + render_pass.set_pipeline(&pipeline); +} + +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, + // TODO: `offsets` currently requires `&mut` so we have to clone it + // here, but this should be fixed upstream in web-sys in the future + &mut offsets.to_vec(), + 0f64, + offsets.len() as u32, + ); +} + +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( + slot, + &buffer.id, + offset as f64, + // TODO: size, + ); +} + +pub(crate) fn render_pass_draw( + render_pass: &RenderPassEncoderId, + vertices: Range, + instances: Range, +) { + render_pass.draw( + vertices.end - vertices.start, + instances.end - instances.start, + vertices.start, + instances.start, + ) +} + +pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { + render_pass.end_pass(); +} + +pub(crate) fn texture_create_default_view(texture: &TextureId) -> TextureViewId { + texture.create_view() +} + +pub(crate) fn swap_chain_present(_swap_chain: &SwapChainId) { + // Swapchain is presented automatically +} + +pub(crate) fn device_poll(_device: &DeviceId, _maintain: crate::Maintain) { + // Device is polled automatically +} diff --git a/src/lib.rs b/src/lib.rs index d247dcecd..fe0d8afa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,11 @@ //! A cross-platform graphics and compute library based on WebGPU. mod backend; -use crate::backend::native_gpu_future; #[macro_use] mod macros; -use arrayvec::ArrayVec; -use smallvec::SmallVec; - -use std::{ffi::CString, future::Future, ops::Range, ptr, slice, thread}; +use std::{future::Future, ops::Range, thread}; pub use wgc::instance::{AdapterInfo, DeviceType}; pub use wgt::{ @@ -23,6 +19,12 @@ pub use wgt::{ TextureFormat, TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttributeDescriptor, VertexFormat, BIND_BUFFER_ALIGNMENT, MAX_BIND_GROUPS, }; +/* +pub use wgc::instance::{ + AdapterInfo, + DeviceType, +}; +*/ //TODO: avoid heap allocating vectors during resource creation. #[derive(Default, Debug)] @@ -37,7 +39,7 @@ struct Temp { /// yielding a [`Device`] object. #[derive(Debug, PartialEq)] pub struct Adapter { - id: wgc::id::AdapterId, + id: backend::AdapterId, } /// Options for requesting adapter. @@ -55,7 +57,7 @@ pub struct RequestAdapterOptions<'a> { /// well as exposing [`Queue`] objects. #[derive(Debug)] pub struct Device { - id: wgc::id::DeviceId, + id: backend::DeviceId, temp: Temp, } @@ -70,14 +72,14 @@ pub enum Maintain { /// A handle to a GPU-accessible buffer. #[derive(Debug, PartialEq)] pub struct Buffer { - id: wgc::id::BufferId, - device_id: wgc::id::DeviceId, + id: backend::BufferId, + detail: backend::BufferDetail, } /// A handle to a texture on the GPU. #[derive(Debug, PartialEq)] pub struct Texture { - id: wgc::id::TextureId, + id: backend::TextureId, owned: bool, } @@ -87,7 +89,7 @@ pub struct Texture { /// [`RenderPipeline`] or [`BindGroup`]. #[derive(Debug, PartialEq)] pub struct TextureView { - id: wgc::id::TextureViewId, + id: backend::TextureViewId, owned: bool, } @@ -98,7 +100,7 @@ pub struct TextureView { /// the documentation for [`SamplerDescriptor`] for more information. #[derive(Debug, PartialEq)] pub struct Sampler { - id: wgc::id::SamplerId, + id: backend::SamplerId, } /// A handle to a presentable surface. @@ -107,7 +109,7 @@ pub struct Sampler { /// be presented. A `Surface` may be created with [`Surface::create`]. #[derive(Debug, PartialEq)] pub struct Surface { - id: wgc::id::SurfaceId, + id: backend::SurfaceId, } /// A handle to a swap chain. @@ -116,7 +118,7 @@ pub struct Surface { /// A `SwapChain` may be created with [`Device::create_swap_chain`]. #[derive(Debug, PartialEq)] pub struct SwapChain { - id: wgc::id::SwapChainId, + id: backend::SwapChainId, } /// An opaque handle to a binding group layout. @@ -127,7 +129,7 @@ pub struct SwapChain { /// create a [`PipelineLayoutDescriptor`], which can be used to create a [`PipelineLayout`]. #[derive(Debug, PartialEq)] pub struct BindGroupLayout { - id: wgc::id::BindGroupLayoutId, + id: backend::BindGroupLayoutId, } /// An opaque handle to a binding group. @@ -138,11 +140,12 @@ pub struct BindGroupLayout { /// [`ComputePass`] with [`ComputePass::set_bind_group`]. #[derive(Debug, PartialEq)] pub struct BindGroup { - id: wgc::id::BindGroupId, + id: backend::BindGroupId, } impl Drop for BindGroup { fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] wgn::wgpu_bind_group_destroy(self.id); } } @@ -154,7 +157,7 @@ impl Drop for BindGroup { /// programmable stages of a pipeline. #[derive(Debug, PartialEq)] pub struct ShaderModule { - id: wgc::id::ShaderModuleId, + id: backend::ShaderModuleId, } /// An opaque handle to a pipeline layout. @@ -162,7 +165,7 @@ pub struct ShaderModule { /// A `PipelineLayout` object describes the available binding groups of a pipeline. #[derive(Debug, PartialEq)] pub struct PipelineLayout { - id: wgc::id::PipelineLayoutId, + id: backend::PipelineLayoutId, } /// A handle to a rendering (graphics) pipeline. @@ -171,13 +174,13 @@ pub struct PipelineLayout { /// buffers and targets. A `RenderPipeline` may be created with [`Device::create_render_pipeline`]. #[derive(Debug, PartialEq)] pub struct RenderPipeline { - id: wgc::id::RenderPipelineId, + id: backend::RenderPipelineId, } /// A handle to a compute pipeline. #[derive(Debug, PartialEq)] pub struct ComputePipeline { - id: wgc::id::ComputePipelineId, + id: backend::ComputePipelineId, } /// An opaque handle to a command buffer on the GPU. @@ -187,7 +190,7 @@ pub struct ComputePipeline { /// a [`CommandEncoder`] and then calling [`CommandEncoder::finish`]. #[derive(Debug, PartialEq)] pub struct CommandBuffer { - id: wgc::id::CommandBufferId, + id: backend::CommandBufferId, } /// An object that encodes GPU operations. @@ -199,7 +202,7 @@ pub struct CommandBuffer { /// be submitted for execution. #[derive(Debug)] pub struct CommandEncoder { - id: wgc::id::CommandEncoderId, + id: backend::CommandEncoderId, /// This type should be !Send !Sync, because it represents an allocation on this thread's /// command buffer. _p: std::marker::PhantomData<*const u8>, @@ -208,14 +211,14 @@ pub struct CommandEncoder { /// An in-progress recording of a render pass. #[derive(Debug)] pub struct RenderPass<'a> { - id: wgc::id::RenderPassId, + id: backend::RenderPassEncoderId, _parent: &'a mut CommandEncoder, } /// An in-progress recording of a compute pass. #[derive(Debug)] pub struct ComputePass<'a> { - id: wgc::id::ComputePassId, + id: backend::ComputePassId, _parent: &'a mut CommandEncoder, } @@ -224,7 +227,7 @@ pub struct ComputePass<'a> { /// A `Queue` executes recorded [`CommandBuffer`] objects. #[derive(Debug, PartialEq)] pub struct Queue { - id: wgc::id::QueueId, + id: backend::QueueId, } /// A resource that can be bound to a pipeline. @@ -462,9 +465,10 @@ pub struct TextureDescriptor<'a> { #[derive(Debug)] pub struct SwapChainOutput { pub view: TextureView, - swap_chain_id: wgc::id::SwapChainId, + swap_chain_id: backend::SwapChainId, } +/* /// A view of a buffer which can be used to copy to or from a texture. #[derive(Clone, Debug)] pub struct BufferCopyView<'a> { @@ -521,22 +525,27 @@ impl<'a> TextureCopyView<'a> { } } } +*/ /// A buffer being created, mapped in host memory. pub struct CreateBufferMapped<'a> { - id: wgc::id::BufferId, - pub data: &'a mut [u8], - device_id: wgc::id::DeviceId, + id: backend::BufferId, + /// The backing field for `data()`. This isn't `pub` because users shouldn't + /// be able to replace it to point somewhere else. We rely on it pointing to + /// to the correct memory later during `unmap()`. + mapped_data: &'a mut [u8], + detail: backend::CreateBufferMappedDetail, } impl CreateBufferMapped<'_> { + /// The mapped data. + pub fn data(&mut self) -> &mut [u8] { + self.mapped_data + } + /// Unmaps the buffer from host memory and returns a [`Buffer`]. pub fn finish(self) -> Buffer { - wgn::wgpu_buffer_unmap(self.id); - Buffer { - device_id: self.device_id, - id: self.id, - } + backend::create_buffer_mapped_finish(self) } } @@ -544,26 +553,30 @@ impl Surface { /// Creates a surface from a raw window handle. pub fn create(window: &W) -> Self { Surface { - id: wgn::wgpu_create_surface(window.raw_window_handle()), + id: backend::create_surface(window), } } - #[cfg(any(target_os = "ios", target_os = "macos"))] - pub fn create_surface_from_core_animation_layer(layer: *mut std::ffi::c_void) -> Self { - Surface { - id: wgn::wgpu_create_surface_from_metal_layer(layer), + /* + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub fn create_surface_from_core_animation_layer(layer: *mut std::ffi::c_void) -> Self { + Surface { + id: wgn::wgpu_create_surface_from_metal_layer(layer), + } } - } + */ } impl Adapter { - /// Retrieves all available [`Adapter`]s that match the given backends. - pub fn enumerate(backends: BackendBit) -> Vec { - wgn::wgpu_enumerate_adapters(backends) - .into_iter() - .map(|id| Adapter { id }) - .collect() - } + /* + /// Retrieves all available [`Adapter`]s that match the given backends. + pub fn enumerate(backends: BackendBit) -> Vec { + wgn::wgpu_enumerate_adapters(backends) + .into_iter() + .map(|id| Adapter { id }) + .collect() + } + */ /// Retrieves an [`Adapter`] which matches the given options. /// @@ -574,26 +587,9 @@ impl Adapter { options: &RequestAdapterOptions<'_>, backends: BackendBit, ) -> Option { - unsafe extern "C" fn adapter_callback( - id: Option, - user_data: *mut std::ffi::c_void, - ) { - *(user_data as *mut Option) = id; - } - - let mut id_maybe = None; - unsafe { - wgn::wgpu_request_adapter_async( - Some(&wgc::instance::RequestAdapterOptions { - power_preference: options.power_preference, - compatible_surface: options.compatible_surface.map(|surface| surface.id), - }), - backends, - adapter_callback, - &mut id_maybe as *mut _ as *mut std::ffi::c_void, - ) - }; - id_maybe.map(|id| Adapter { id }) + backend::request_adapter(options, backends) + .await + .map(|id| Adapter { id }) } /// Requests a connection to a physical device, creating a logical device. @@ -603,286 +599,76 @@ impl Adapter { /// /// Panics if the extensions specified by `desc` are not supported by this adapter. pub async fn request_device(&self, desc: &DeviceDescriptor) -> (Device, Queue) { + let (device_id, queue_id) = backend::request_device_and_queue(&self.id, Some(desc)).await; let device = Device { - id: wgn::wgpu_adapter_request_device(self.id, Some(desc)), + id: device_id, temp: Temp::default(), }; - let queue = Queue { - id: wgn::wgpu_device_get_default_queue(device.id), - }; + let queue = Queue { id: queue_id }; (device, queue) } - pub fn get_info(&self) -> AdapterInfo { - wgn::adapter_get_info(self.id) - } + /* + pub fn get_info(&self) -> AdapterInfo { + wgn::adapter_get_info(self.id) + } + */ } impl Device { /// Check for resource cleanups and mapping callbacks. pub fn poll(&self, maintain: Maintain) { - wgn::wgpu_device_poll( - self.id, - match maintain { - Maintain::Poll => false, - Maintain::Wait => true, - }, - ); + backend::device_poll(&self.id, maintain); } /// Creates a shader module from SPIR-V source code. pub fn create_shader_module(&self, spv: &[u32]) -> ShaderModule { - let desc = wgc::pipeline::ShaderModuleDescriptor { - code: wgc::U32Array { - bytes: spv.as_ptr(), - length: spv.len(), - }, - }; ShaderModule { - id: wgn::wgpu_device_create_shader_module(self.id, &desc), + id: backend::create_shader_module(&self.id, spv), } } /// Creates an empty [`CommandEncoder`]. pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor) -> CommandEncoder { - let owned_label = OwnedLabel::new(desc.label.as_deref()); CommandEncoder { - id: wgn::wgpu_device_create_command_encoder( - self.id, - Some(&wgt::CommandEncoderDescriptor { - label: owned_label.as_ptr(), - }), - ), + id: backend::create_command_encoder(&self.id, desc), _p: Default::default(), } } /// Creates a new bind group. pub fn create_bind_group(&self, desc: &BindGroupDescriptor) -> BindGroup { - use wgc::binding_model as bm; - - let bindings = desc - .bindings - .iter() - .map(|binding| bm::BindGroupEntry { - binding: binding.binding, - resource: match binding.resource { - BindingResource::Buffer { - ref buffer, - ref range, - } => bm::BindingResource::Buffer(bm::BufferBinding { - buffer: buffer.id, - offset: range.start, - size: range.end - range.start, - }), - BindingResource::Sampler(ref sampler) => { - bm::BindingResource::Sampler(sampler.id) - } - BindingResource::TextureView(ref texture_view) => { - bm::BindingResource::TextureView(texture_view.id) - } - }, - }) - .collect::>(); - - let owned_label = OwnedLabel::new(desc.label.as_deref()); - BindGroup { - id: wgn::wgpu_device_create_bind_group( - self.id, - &bm::BindGroupDescriptor { - layout: desc.layout.id, - entries: bindings.as_ptr(), - entries_length: bindings.len(), - label: owned_label.as_ptr(), - }, - ), - } + let id = backend::create_bind_group(&self.id, desc); + BindGroup { id } } /// Creates a bind group layout. pub fn create_bind_group_layout(&self, desc: &BindGroupLayoutDescriptor) -> BindGroupLayout { - use wgc::binding_model as bm; - - let temp_layouts = desc - .bindings - .iter() - .map(|bind| bm::BindGroupLayoutEntry { - binding: bind.binding, - visibility: bind.visibility, - ty: match bind.ty { - BindingType::UniformBuffer { .. } => bm::BindingType::UniformBuffer, - BindingType::StorageBuffer { - readonly: false, .. - } => bm::BindingType::StorageBuffer, - BindingType::StorageBuffer { readonly: true, .. } => { - bm::BindingType::ReadonlyStorageBuffer - } - BindingType::Sampler { comparison: false } => bm::BindingType::Sampler, - BindingType::Sampler { .. } => bm::BindingType::ComparisonSampler, - BindingType::SampledTexture { .. } => bm::BindingType::SampledTexture, - BindingType::StorageTexture { readonly: true, .. } => { - bm::BindingType::ReadonlyStorageTexture - } - BindingType::StorageTexture { .. } => bm::BindingType::WriteonlyStorageTexture, - }, - has_dynamic_offset: match bind.ty { - BindingType::UniformBuffer { dynamic } - | BindingType::StorageBuffer { dynamic, .. } => dynamic, - _ => false, - }, - multisampled: match bind.ty { - BindingType::SampledTexture { multisampled, .. } => multisampled, - _ => false, - }, - view_dimension: match bind.ty { - BindingType::SampledTexture { dimension, .. } - | BindingType::StorageTexture { dimension, .. } => dimension, - _ => TextureViewDimension::D2, - }, - texture_component_type: match bind.ty { - BindingType::SampledTexture { component_type, .. } - | BindingType::StorageTexture { component_type, .. } => component_type, - _ => TextureComponentType::Float, - }, - storage_texture_format: match bind.ty { - BindingType::StorageTexture { format, .. } => format, - _ => TextureFormat::Rgb10a2Unorm, // doesn't matter - }, - }) - .collect::>(); - let owned_label = OwnedLabel::new(desc.label.as_deref()); - BindGroupLayout { - id: wgn::wgpu_device_create_bind_group_layout( - self.id, - &bm::BindGroupLayoutDescriptor { - entries: temp_layouts.as_ptr(), - entries_length: temp_layouts.len(), - label: owned_label.as_ptr(), - }, - ), - } + let id = backend::create_bind_group_layout(&self.id, desc); + BindGroupLayout { id } } /// Creates a pipeline layout. pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor) -> PipelineLayout { - //TODO: avoid allocation here - let temp_layouts = desc - .bind_group_layouts - .iter() - .map(|bgl| bgl.id) - .collect::>(); - PipelineLayout { - id: wgn::wgpu_device_create_pipeline_layout( - self.id, - &wgc::binding_model::PipelineLayoutDescriptor { - bind_group_layouts: temp_layouts.as_ptr(), - bind_group_layouts_length: temp_layouts.len(), - }, - ), - } + let id = backend::create_pipeline_layout(&self.id, desc); + PipelineLayout { id } } /// Creates a render pipeline. pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor) -> RenderPipeline { - use wgc::pipeline as pipe; - - let vertex_entry_point = CString::new(desc.vertex_stage.entry_point).unwrap(); - let vertex_stage = pipe::ProgrammableStageDescriptor { - module: desc.vertex_stage.module.id, - entry_point: vertex_entry_point.as_ptr(), - }; - let (_fragment_entry_point, fragment_stage) = - if let Some(fragment_stage) = &desc.fragment_stage { - let fragment_entry_point = CString::new(fragment_stage.entry_point).unwrap(); - let fragment_stage = pipe::ProgrammableStageDescriptor { - module: fragment_stage.module.id, - entry_point: fragment_entry_point.as_ptr(), - }; - (fragment_entry_point, Some(fragment_stage)) - } else { - (CString::default(), None) - }; - - let temp_color_states = desc.color_states.to_vec(); - let temp_vertex_buffers = desc - .vertex_state - .vertex_buffers - .iter() - .map(|vbuf| pipe::VertexBufferLayoutDescriptor { - array_stride: vbuf.stride, - step_mode: vbuf.step_mode, - attributes: vbuf.attributes.as_ptr(), - attributes_length: vbuf.attributes.len(), - }) - .collect::>(); - - RenderPipeline { - id: wgn::wgpu_device_create_render_pipeline( - self.id, - &pipe::RenderPipelineDescriptor { - layout: desc.layout.id, - vertex_stage, - fragment_stage: fragment_stage - .as_ref() - .map_or(ptr::null(), |fs| fs as *const _), - rasterization_state: desc - .rasterization_state - .as_ref() - .map_or(ptr::null(), |p| p as *const _), - primitive_topology: desc.primitive_topology, - color_states: temp_color_states.as_ptr(), - color_states_length: temp_color_states.len(), - depth_stencil_state: desc - .depth_stencil_state - .as_ref() - .map_or(ptr::null(), |p| p as *const _), - vertex_state: pipe::VertexStateDescriptor { - index_format: desc.vertex_state.index_format, - vertex_buffers: temp_vertex_buffers.as_ptr(), - vertex_buffers_length: temp_vertex_buffers.len(), - }, - sample_count: desc.sample_count, - sample_mask: desc.sample_mask, - alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled, - }, - ), - } + let id = backend::create_render_pipeline(&self.id, desc); + RenderPipeline { id } } /// Creates a compute pipeline. pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor) -> ComputePipeline { - use wgc::pipeline as pipe; - - let entry_point = CString::new(desc.compute_stage.entry_point).unwrap(); - - ComputePipeline { - id: wgn::wgpu_device_create_compute_pipeline( - self.id, - &pipe::ComputePipelineDescriptor { - layout: desc.layout.id, - compute_stage: pipe::ProgrammableStageDescriptor { - module: desc.compute_stage.module.id, - entry_point: entry_point.as_ptr(), - }, - }, - ), - } + let id = backend::create_compute_pipeline(&self.id, desc); + ComputePipeline { id } } /// Creates a new buffer. pub fn create_buffer(&self, desc: &BufferDescriptor) -> Buffer { - let owned_label = OwnedLabel::new(desc.label.as_deref()); - Buffer { - device_id: self.id, - id: wgn::wgpu_device_create_buffer( - self.id, - &wgt::BufferDescriptor { - label: owned_label.as_ptr(), - size: desc.size, - usage: desc.usage, - }, - ), - } + backend::create_buffer(&self.id, desc) } /// Creates a new buffer and maps it into host-visible memory. @@ -891,40 +677,18 @@ impl Device { /// will not be created until calling [`CreateBufferMapped::finish`]. pub fn create_buffer_mapped(&self, desc: &BufferDescriptor) -> CreateBufferMapped<'_> { assert_ne!(desc.size, 0); - - let owned_label = OwnedLabel::new(desc.label.as_deref()); - let mut data_ptr: *mut u8 = std::ptr::null_mut(); - - let (id, data) = unsafe { - let id = wgn::wgpu_device_create_buffer_mapped( - self.id, - &wgt::BufferDescriptor { - label: owned_label.as_ptr(), - size: desc.size, - usage: desc.usage, - }, - &mut data_ptr as *mut *mut u8, - ); - let data = std::slice::from_raw_parts_mut(data_ptr as *mut u8, desc.size as usize); - (id, data) - }; - - CreateBufferMapped { - device_id: self.id, - id, - data, - } + backend::create_buffer_mapped(&self.id, desc) } /// Creates a new buffer, maps it into host-visible memory, copies data from the given slice, /// and finally unmaps it, returning a [`Buffer`]. pub fn create_buffer_with_data(&self, data: &[u8], usage: BufferUsage) -> Buffer { - let mapped = self.create_buffer_mapped(&BufferDescriptor { + let mut mapped = self.create_buffer_mapped(&BufferDescriptor { size: data.len() as u64, usage, label: None, }); - mapped.data.copy_from_slice(data); + mapped.data().copy_from_slice(data); mapped.finish() } @@ -932,45 +696,38 @@ impl Device { /// /// `desc` specifies the general format of the texture. pub fn create_texture(&self, desc: &TextureDescriptor) -> Texture { - let owned_label = OwnedLabel::new(desc.label.as_deref()); Texture { - id: wgn::wgpu_device_create_texture( - self.id, - &wgt::TextureDescriptor { - label: owned_label.as_ptr(), - size: desc.size, - mip_level_count: desc.mip_level_count, - sample_count: desc.sample_count, - dimension: desc.dimension, - format: desc.format, - usage: desc.usage, - }, - ), + id: backend::create_texture(&self.id, desc), owned: true, } } - /// Creates a new [`Sampler`]. - /// - /// `desc` specifies the behavior of the sampler. - pub fn create_sampler(&self, desc: &SamplerDescriptor) -> Sampler { - Sampler { - id: wgn::wgpu_device_create_sampler(self.id, desc), + /* + /// Creates a new [`Sampler`]. + /// + /// `desc` specifies the behavior of the sampler. + pub fn create_sampler(&self, desc: &SamplerDescriptor) -> Sampler { + Sampler { + id: wgn::wgpu_device_create_sampler(self.id, desc), + } } - } + */ /// Create a new [`SwapChain`] which targets `surface`. pub fn create_swap_chain(&self, surface: &Surface, desc: &SwapChainDescriptor) -> SwapChain { SwapChain { - id: wgn::wgpu_device_create_swap_chain(self.id, surface.id, desc), + id: backend::device_create_swap_chain(&self.id, &surface.id, desc), } } } +// TODO impl Drop for Device { fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] wgn::wgpu_device_poll(self.id, true); //TODO: make this work in general + #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "metal-auto-capture")] wgn::wgpu_device_destroy(self.id); } @@ -980,9 +737,7 @@ impl Drop for Device { pub struct BufferAsyncErr; pub struct BufferReadMapping { - data: *const u8, - size: usize, - buffer_id: wgc::id::BufferId, + detail: backend::BufferReadMappingDetail, } unsafe impl Send for BufferReadMapping {} @@ -990,16 +745,17 @@ unsafe impl Sync for BufferReadMapping {} impl BufferReadMapping { pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.data as *const u8, self.size) } + self.detail.as_slice() } } impl Drop for BufferReadMapping { fn drop(&mut self) { - wgn::wgpu_buffer_unmap(self.buffer_id); + backend::buffer_unmap(&self.detail.buffer_id); } } +/* pub struct BufferWriteMapping { data: *mut u8, size: usize, @@ -1020,6 +776,7 @@ impl Drop for BufferWriteMapping { wgn::wgpu_buffer_unmap(self.buffer_id); } } +*/ impl Buffer { /// Map the buffer for reading. The result is returned in a future. @@ -1034,112 +791,89 @@ impl Buffer { &self, start: BufferAddress, size: BufferAddress, - ) -> impl Future> { - let (future, completion) = native_gpu_future::new_gpu_future(self.id, size); - - extern "C" fn buffer_map_read_future_wrapper( - status: wgc::resource::BufferMapAsyncStatus, - data: *const u8, - user_data: *mut u8, - ) { - let completion = - unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; - let (buffer_id, size) = completion.get_buffer_info(); - - if let wgc::resource::BufferMapAsyncStatus::Success = status { - completion.complete(Ok(BufferReadMapping { - data, - size: size as usize, - buffer_id, - })); - } else { - completion.complete(Err(BufferAsyncErr)); + ) -> impl Future> + '_ { + backend::buffer_map_read(self, start, size) + } + + /* + /// Map the buffer for writing. The result is returned in a future. + /// + /// See the documentation of (map_read)[#method.map_read] for more information about + /// how to run this future. + pub fn map_write( + &self, + start: BufferAddress, + size: BufferAddress, + ) -> impl Future> { + let (future, completion) = native_gpu_future::new_gpu_future(self.id, size); + + extern "C" fn buffer_map_write_future_wrapper( + status: wgc::resource::BufferMapAsyncStatus, + data: *mut u8, + user_data: *mut u8, + ) { + let completion = + unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; + let (buffer_id, size) = completion.get_buffer_info(); + + if let wgc::resource::BufferMapAsyncStatus::Success = status { + completion.complete(Ok(BufferWriteMapping { + data, + size: size as usize, + buffer_id, + })); + } else { + completion.complete(Err(BufferAsyncErr)); + } } - } - - wgn::wgpu_buffer_map_read_async( - self.id, - start, - size, - buffer_map_read_future_wrapper, - completion.to_raw() as _, - ); - - future - } - /// Map the buffer for writing. The result is returned in a future. - /// - /// See the documentation of (map_read)[#method.map_read] for more information about - /// how to run this future. - pub fn map_write( - &self, - start: BufferAddress, - size: BufferAddress, - ) -> impl Future> { - let (future, completion) = native_gpu_future::new_gpu_future(self.id, size); + wgn::wgpu_buffer_map_write_async( + self.id, + start, + size, + buffer_map_write_future_wrapper, + completion.to_raw() as _, + ); - extern "C" fn buffer_map_write_future_wrapper( - status: wgc::resource::BufferMapAsyncStatus, - data: *mut u8, - user_data: *mut u8, - ) { - let completion = - unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; - let (buffer_id, size) = completion.get_buffer_info(); - - if let wgc::resource::BufferMapAsyncStatus::Success = status { - completion.complete(Ok(BufferWriteMapping { - data, - size: size as usize, - buffer_id, - })); - } else { - completion.complete(Err(BufferAsyncErr)); - } + future } - wgn::wgpu_buffer_map_write_async( - self.id, - start, - size, - buffer_map_write_future_wrapper, - completion.to_raw() as _, - ); - - future - } - - /// Flushes any pending write operations and unmaps the buffer from host memory. - pub fn unmap(&self) { - wgn::wgpu_buffer_unmap(self.id); - } + /// Flushes any pending write operations and unmaps the buffer from host memory. + pub fn unmap(&self) { + wgn::wgpu_buffer_unmap(self.id); + } + */ } +/* impl Drop for Buffer { fn drop(&mut self) { wgn::wgpu_buffer_destroy(self.id); } } +*/ impl Texture { - /// Creates a view of this texture. - pub fn create_view(&self, desc: &TextureViewDescriptor) -> TextureView { - TextureView { - id: wgn::wgpu_texture_create_view(self.id, Some(desc)), - owned: true, + /* + /// Creates a view of this texture. + pub fn create_view(&self, desc: &TextureViewDescriptor) -> TextureView { + TextureView { + id: wgn::wgpu_texture_create_view(self.id, Some(desc)), + owned: true, + } } - } + */ /// Creates a default view of this whole texture. pub fn create_default_view(&self) -> TextureView { TextureView { - id: wgn::wgpu_texture_create_view(self.id, None), + id: backend::texture_create_default_view(&self.id), owned: true, } } } +/* impl Drop for Texture { fn drop(&mut self) { if self.owned { @@ -1155,12 +889,13 @@ impl Drop for TextureView { } } } +*/ impl CommandEncoder { /// Finishes recording and returns a [`CommandBuffer`] that can be submitted for execution. pub fn finish(self) -> CommandBuffer { CommandBuffer { - id: wgn::wgpu_command_encoder_finish(self.id, None), + id: backend::command_encoder_finish(&self.id), } } @@ -1171,41 +906,8 @@ impl CommandEncoder { &'a mut self, desc: &RenderPassDescriptor<'a, '_>, ) -> RenderPass<'a> { - let colors = desc - .color_attachments - .iter() - .map(|ca| wgc::command::RenderPassColorAttachmentDescriptor { - attachment: ca.attachment.id, - resolve_target: ca.resolve_target.map(|rt| rt.id), - load_op: ca.load_op, - store_op: ca.store_op, - clear_color: ca.clear_color, - }) - .collect::>(); - - let depth_stencil = desc.depth_stencil_attachment.as_ref().map(|dsa| { - wgc::command::RenderPassDepthStencilAttachmentDescriptor { - attachment: dsa.attachment.id, - depth_load_op: dsa.depth_load_op, - depth_store_op: dsa.depth_store_op, - clear_depth: dsa.clear_depth, - stencil_load_op: dsa.stencil_load_op, - stencil_store_op: dsa.stencil_store_op, - clear_stencil: dsa.clear_stencil, - } - }); - RenderPass { - id: unsafe { - wgn::wgpu_command_encoder_begin_render_pass( - self.id, - &wgc::command::RenderPassDescriptor { - color_attachments: colors.as_ptr(), - color_attachments_length: colors.len(), - depth_stencil_attachment: depth_stencil.as_ref(), - }, - ) - }, + id: backend::command_encoder_begin_render_pass(&self.id, desc), _parent: self, } } @@ -1215,7 +917,7 @@ impl CommandEncoder { /// This function returns a [`ComputePass`] object which records a single compute pass. pub fn begin_compute_pass(&mut self) -> ComputePass { ComputePass { - id: unsafe { wgn::wgpu_command_encoder_begin_compute_pass(self.id, None) }, + id: backend::begin_compute_pass(&self.id), _parent: self, } } @@ -1229,60 +931,62 @@ impl CommandEncoder { destination_offset: BufferAddress, copy_size: BufferAddress, ) { - wgn::wgpu_command_encoder_copy_buffer_to_buffer( - self.id, - source.id, + backend::copy_buffer_to_buffer( + &self.id, + source, source_offset, - destination.id, + destination, destination_offset, copy_size, ); } - /// Copy data from a buffer to a texture. - pub fn copy_buffer_to_texture( - &mut self, - source: BufferCopyView, - destination: TextureCopyView, - copy_size: Extent3d, - ) { - wgn::wgpu_command_encoder_copy_buffer_to_texture( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); - } + /* + /// Copy data from a buffer to a texture. + pub fn copy_buffer_to_texture( + &mut self, + source: BufferCopyView, + destination: TextureCopyView, + copy_size: Extent3d, + ) { + wgn::wgpu_command_encoder_copy_buffer_to_texture( + self.id, + &source.into_native(), + &destination.into_native(), + copy_size, + ); + } - /// Copy data from a texture to a buffer. - pub fn copy_texture_to_buffer( - &mut self, - source: TextureCopyView, - destination: BufferCopyView, - copy_size: Extent3d, - ) { - wgn::wgpu_command_encoder_copy_texture_to_buffer( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); - } + /// Copy data from a texture to a buffer. + pub fn copy_texture_to_buffer( + &mut self, + source: TextureCopyView, + destination: BufferCopyView, + copy_size: Extent3d, + ) { + wgn::wgpu_command_encoder_copy_texture_to_buffer( + self.id, + &source.into_native(), + &destination.into_native(), + copy_size, + ); + } - /// Copy data from one texture to another. - pub fn copy_texture_to_texture( - &mut self, - source: TextureCopyView, - destination: TextureCopyView, - copy_size: Extent3d, - ) { - wgn::wgpu_command_encoder_copy_texture_to_texture( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); - } + /// Copy data from one texture to another. + pub fn copy_texture_to_texture( + &mut self, + source: TextureCopyView, + destination: TextureCopyView, + copy_size: Extent3d, + ) { + wgn::wgpu_command_encoder_copy_texture_to_texture( + self.id, + &source.into_native(), + &destination.into_native(), + copy_size, + ); + } + */ } impl<'a> RenderPass<'a> { @@ -1293,53 +997,45 @@ impl<'a> RenderPass<'a> { bind_group: &'a BindGroup, offsets: &[DynamicOffset], ) { - unsafe { - wgn::wgpu_render_pass_set_bind_group( - self.id.as_mut().unwrap(), - index, - bind_group.id, - offsets.as_ptr(), - offsets.len(), - ); - } + backend::render_pass_set_bind_group(&self.id, index, &bind_group.id, offsets) } /// Sets the active render pipeline. /// /// Subsequent draw calls will exhibit the behavior defined by `pipeline`. pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) { - unsafe { - wgn::wgpu_render_pass_set_pipeline(self.id.as_mut().unwrap(), pipeline.id); - } + backend::render_pass_set_pipeline(&self.id, &pipeline.id) } - pub fn set_blend_color(&mut self, color: Color) { - unsafe { - wgn::wgpu_render_pass_set_blend_color(self.id.as_mut().unwrap(), &color); + /* + pub fn set_blend_color(&mut self, color: Color) { + unsafe { + wgn::wgpu_render_pass_set_blend_color(self.id.as_mut().unwrap(), &color); + } } - } - /// Sets the active index buffer. - /// - /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will - /// use `buffer` as the source index buffer. - /// - /// If `size == 0`, the remaining part of the buffer is considered. - pub fn set_index_buffer( - &mut self, - buffer: &'a Buffer, - offset: BufferAddress, - size: BufferAddress, - ) { - unsafe { - wgn::wgpu_render_pass_set_index_buffer( - self.id.as_mut().unwrap(), - buffer.id, - offset, - size, - ); + /// Sets the active index buffer. + /// + /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will + /// use `buffer` as the source index buffer. + /// + /// If `size == 0`, the remaining part of the buffer is considered. + pub fn set_index_buffer( + &mut self, + buffer: &'a Buffer, + offset: BufferAddress, + size: BufferAddress, + ) { + unsafe { + wgn::wgpu_render_pass_set_index_buffer( + self.id.as_mut().unwrap(), + buffer.id, + offset, + size, + ); + } } - } + */ /// Assign a vertex buffer to a slot. /// @@ -1362,67 +1058,54 @@ impl<'a> RenderPass<'a> { offset: BufferAddress, size: BufferAddress, ) { - unsafe { - wgn::wgpu_render_pass_set_vertex_buffer( - self.id.as_mut().unwrap(), - slot, - buffer.id, - offset, - size, - ) - }; + backend::render_pass_set_vertex_buffer(&self.id, slot, buffer, offset, size) } - /// Sets the scissor region. - /// - /// Subsequent draw calls will discard any fragments that fall outside this region. - pub fn set_scissor_rect(&mut self, x: u32, y: u32, w: u32, h: u32) { - unsafe { - wgn::wgpu_render_pass_set_scissor_rect(self.id.as_mut().unwrap(), x, y, w, h); + /* + /// Sets the scissor region. + /// + /// Subsequent draw calls will discard any fragments that fall outside this region. + pub fn set_scissor_rect(&mut self, x: u32, y: u32, w: u32, h: u32) { + unsafe { + wgn::wgpu_render_pass_set_scissor_rect(self.id.as_mut().unwrap(), x, y, w, h); + } } - } - /// Sets the viewport region. - /// - /// Subsequent draw calls will draw any fragments in this region. - pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) { - unsafe { - wgn::wgpu_render_pass_set_viewport( - self.id.as_mut().unwrap(), - x, - y, - w, - h, - min_depth, - max_depth, - ); + /// Sets the viewport region. + /// + /// Subsequent draw calls will draw any fragments in this region. + pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) { + unsafe { + wgn::wgpu_render_pass_set_viewport( + self.id.as_mut().unwrap(), + x, + y, + w, + h, + min_depth, + max_depth, + ); + } } - } - /// Sets the stencil reference. - /// - /// Subsequent stencil tests will test against this value. - pub fn set_stencil_reference(&mut self, reference: u32) { - unsafe { - wgn::wgpu_render_pass_set_stencil_reference(self.id.as_mut().unwrap(), reference); + /// Sets the stencil reference. + /// + /// Subsequent stencil tests will test against this value. + pub fn set_stencil_reference(&mut self, reference: u32) { + unsafe { + wgn::wgpu_render_pass_set_stencil_reference(self.id.as_mut().unwrap(), reference); + } } - } + */ /// Draws primitives from the active vertex buffer(s). /// /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. pub fn draw(&mut self, vertices: Range, instances: Range) { - unsafe { - wgn::wgpu_render_pass_draw( - self.id.as_mut().unwrap(), - vertices.end - vertices.start, - instances.end - instances.start, - vertices.start, - instances.start, - ); - } + backend::render_pass_draw(&self.id, vertices, instances) } + /* /// Draws indexed primitives using the active index buffer and the active vertex buffers. /// /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active @@ -1496,14 +1179,13 @@ impl<'a> RenderPass<'a> { ); } } + */ } impl<'a> Drop for RenderPass<'a> { fn drop(&mut self) { if !thread::panicking() { - unsafe { - wgn::wgpu_render_pass_end_pass(self.id); - } + backend::render_pass_end_pass(&self.id); } } } @@ -1516,31 +1198,19 @@ impl<'a> ComputePass<'a> { bind_group: &'a BindGroup, offsets: &[DynamicOffset], ) { - unsafe { - wgn::wgpu_compute_pass_set_bind_group( - self.id.as_mut().unwrap(), - index, - bind_group.id, - offsets.as_ptr(), - offsets.len(), - ); - } + backend::compute_pass_set_bind_group(&self.id, index, &bind_group.id, offsets); } /// Sets the active compute pipeline. pub fn set_pipeline(&mut self, pipeline: &'a ComputePipeline) { - unsafe { - wgn::wgpu_compute_pass_set_pipeline(self.id.as_mut().unwrap(), pipeline.id); - } + backend::compute_pass_set_pipeline(&self.id, &pipeline.id); } /// Dispatches compute work operations. /// /// `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) { - unsafe { - wgn::wgpu_compute_pass_dispatch(self.id.as_mut().unwrap(), x, y, z); - } + backend::compute_pass_dispatch(&self.id, x, y, z); } /// Dispatches compute work operations, based on the contents of the `indirect_buffer`. @@ -1549,22 +1219,14 @@ impl<'a> ComputePass<'a> { indirect_buffer: &'a Buffer, indirect_offset: BufferAddress, ) { - unsafe { - wgn::wgpu_compute_pass_dispatch_indirect( - self.id.as_mut().unwrap(), - indirect_buffer.id, - indirect_offset, - ); - } + backend::compute_pass_dispatch_indirect(&self.id, &indirect_buffer.id, indirect_offset); } } impl<'a> Drop for ComputePass<'a> { fn drop(&mut self) { if !thread::panicking() { - unsafe { - wgn::wgpu_compute_pass_end_pass(self.id); - } + backend::compute_pass_end_pass(&self.id); } } } @@ -1572,25 +1234,14 @@ impl<'a> Drop for ComputePass<'a> { impl Queue { /// Submits a series of finished command buffers for execution. pub fn submit(&self, command_buffers: &[CommandBuffer]) { - let temp_command_buffers = command_buffers - .iter() - .map(|cb| cb.id) - .collect::>(); - - unsafe { - wgn::wgpu_queue_submit( - self.id, - temp_command_buffers.as_ptr(), - command_buffers.len(), - ) - }; + backend::queue_submit(&self.id, command_buffers); } } impl Drop for SwapChainOutput { fn drop(&mut self) { if !thread::panicking() { - wgn::wgpu_swap_chain_present(self.swap_chain_id); + backend::swap_chain_present(&self.swap_chain_id); } } } @@ -1606,8 +1257,7 @@ impl SwapChain { /// When the [`SwapChainOutput`] returned by this method is dropped, the swapchain will present /// the texture to the associated [`Surface`]. pub fn get_next_texture(&mut self) -> Result { - let output = wgn::wgpu_swap_chain_get_next_texture(self.id); - match output.view_id { + match backend::swap_chain_get_next_texture(&self.id) { Some(id) => Ok(SwapChainOutput { view: TextureView { id, owned: false }, swap_chain_id: self.id, @@ -1616,18 +1266,3 @@ impl SwapChain { } } } - -struct OwnedLabel(Option); - -impl OwnedLabel { - fn new(text: Option<&str>) -> Self { - Self(text.map(|t| CString::new(t).expect("invalid label"))) - } - - fn as_ptr(&self) -> *const std::os::raw::c_char { - match self.0 { - Some(ref c_string) => c_string.as_ptr(), - None => ptr::null(), - } - } -} From f113451f177a8a949ea421e41b8b78e4991e7289 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sat, 4 Apr 2020 00:38:44 -0230 Subject: [PATCH 02/17] Get boids working --- Cargo.toml | 2 +- examples/boids/boids.comp | 5 +++-- examples/boids/boids.comp.spv | Bin 0 -> 5444 bytes examples/boids/main.rs | 35 +++++++++------------------------ examples/boids/shader.frag.spv | Bin 0 -> 336 bytes examples/boids/shader.vert.spv | Bin 0 -> 1684 bytes examples/framework.rs | 3 ++- 7 files changed, 15 insertions(+), 30 deletions(-) create mode 100644 examples/boids/boids.comp.spv create mode 100644 examples/boids/shader.frag.spv create mode 100644 examples/boids/shader.vert.spv diff --git a/Cargo.toml b/Cargo.toml index 6495ddc04..5b5b0d6bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ cgmath = "0.17" log = "0.4" png = "0.15" winit = { version = "0.22", features = ["web-sys"] } -rand = "0.7.2" +rand = { version = "0.7.2", features = ["wasm-bindgen"] } bytemuck = "1" futures = "0.3" diff --git a/examples/boids/boids.comp b/examples/boids/boids.comp index 4a3d9d10b..69a4ca6dd 100644 --- a/examples/boids/boids.comp +++ b/examples/boids/boids.comp @@ -1,7 +1,8 @@ #version 450 -// this shader expects NUM_PARTICLES and PARTICLES_PER_GROUP -// defines to be inserted by main.rs +// These should match the Rust constants defined in main.rs +#define NUM_PARTICLES 1500 +#define PARTICLES_PER_GROUP 64 layout(local_size_x = PARTICLES_PER_GROUP) in; diff --git a/examples/boids/boids.comp.spv b/examples/boids/boids.comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..cadd91ef222be77360dfc232fc39d1b26de64356 GIT binary patch literal 5444 zcmZ{n`Ipya6~{lrERu|h3W^)fU=azSETW|-1T<1m=qTDIew`U<>N_*Y45Q_STWX7@ z*+!c!E?JOR6uIP9uAsL5@`q5wAjfFL4ie;!f?wXTZIKL)@t`5r1&24I#bHA$k|ks`8WaF70$lMIg{=)-a4lv z#yB^w?CaqT4|AKvwqmaX4<-6JK ze(_As^t>*_)rHNERw?pFVt)WFc8~r8(JhGl>6Psna`Ul`wQnxt4?^pckKf;}R~gtD zRB;&i2(llE?m|4%-f3H-IjrBh}zr8osv*MozPP}z4KwJx$J>x!F;tAdyCu3WG7m^$w2it?$bkZ3y&-;lv zK8xQvjP)Mc&X1h_b1H0(oUy6mVh>^ajdj~l0X5M>{{rmPU_q<(T z@09b@yOE;U{4v&U*dLoNGCA??ZbpIq$0NY|ACrf50n{1!Q&> zopm|SP?m8M=Ig_CUm?qt7jzG#x|-<-PN zzc;Zxf7K+rr?OX4wcpXfh~Jvu*K%sMr-vZ&XL3XDqaELLu-q>8`<)$*$oZYTM*K{) z>rHa$^LI?nK8fGrwZwACIR|Xcl~nGx@j=AzsTp3&quJ6 zN8h|E);uR7`px5;Xp4OhzcsOM5$rnz`v%rzjQtO`_u@CW-%_;qRR5pam!VHZT>4K# z>sMQU7y4s}Id)gQJ-y=muI;DxDTuk;E6(0C!0x?@INxwNBA4Er3AQ&6RJqS8ecoZ> z-BzMqo}+ICBIh|`YqoxE`JB5N?74ZnHQ@AIUpHc`J^v)4-@ZAQ+R1Y^xSnS%cJk<} zzul*>^_$0e*3KE92AezfIYM9x}` z*C*#Y<$QI{|01}4{+F=peYprbeKA&l`f@SYzHCCwXI~n~#Yp;c3D_L+_N5=~vM>7j z5IOrIPF(}wdR>=dTh|R$Up9k{kBR_GKE2oiHix{u*ot=93w@U%a`r-;x<>G?2Epu5IOUR(}!!oeh~j{kBXS$6nYpgw%r#Hm%KNC>$7$vwsG?L2Iah8>QbkUo51!o_xlD|F88|y?DBs4Zbsz1pEz~g3id6N z_nTnz=6>G-%jJIG2FoeA-*>>~(U;kfv-X^+&Y8D??P=!vyI|+~cElL_dIutZTh+rm zOFY3R&-dW`-je70VDsFCB+n1P^2zf0gQ+dl%EUteAM?A{4c?Bo&0s3h5i-Nf~-OGCGOYY#M#5& zAjW+H(bvgeVflLzdEda81;{-}?r|U3GmJ|;ayjRIu;-*VzXjWyZp0YRcmR=4&fkIE zC!ajO2m1}A4?Dqf#(J(kInUXJ{sSV{iR7FID?Yz}4`IvgM*PnIgm(EI==&og=XW4Z z?mvT*+q1RzAkJ{&)rtR0Ek3pEg_D1ndJjYY6_GQKwLF4$S&P1h5jkrSC-sr@_`@Zu_F0z9e3q_`ib_ zpIZI_C!fAN1C~o)o&&qAMc=cCoVAFP`+0D3TZ?x3l6bY}cA#HErdGD!&C7`U^WTfF qfcGQLt+8tJ${U+J{{+|b{0n<7l05$gCy%l75c9|z>;HG0hP(|V*ZAlF literal 0 HcmV?d00001 diff --git a/examples/boids/main.rs b/examples/boids/main.rs index 2cec94beb..d171786e6 100644 --- a/examples/boids/main.rs +++ b/examples/boids/main.rs @@ -6,8 +6,6 @@ extern crate rand; #[path = "../framework.rs"] mod framework; -use std::fmt::Write; - use wgpu::vertex_attr_array; // number of boid particles to simulate @@ -35,34 +33,19 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, ) -> (Self, Option) { - // loads comp shader source and adds shared constants as defines to comp shader - - const BOIDS_SOURCE: &str = include_str!("boids.comp"); - const HEADER: &str = "#version 450"; - assert_eq!(BOIDS_SOURCE.lines().next(), Some(HEADER)); - - let mut boids_source_str = String::from(HEADER); - write!( - boids_source_str, - "\n#define NUM_PARTICLES {}\n#define PARTICLES_PER_GROUP {}", - NUM_PARTICLES, PARTICLES_PER_GROUP - ) - .unwrap(); - boids_source_str += &BOIDS_SOURCE[HEADER.len()..]; - // load (and compile) shaders and create shader modules - let boids = framework::load_glsl(&boids_source_str, framework::ShaderStage::Compute); - let boids_module = device.create_shader_module(&boids); + let boids = include_bytes!("boids.comp.spv"); + let boids_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&boids[..])).unwrap()); - let vs = framework::load_glsl(include_str!("shader.vert"), framework::ShaderStage::Vertex); - let vs_module = device.create_shader_module(&vs); + let vs = include_bytes!("shader.vert.spv"); + let vs_module = + device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs[..])).unwrap()); - let fs = framework::load_glsl( - include_str!("shader.frag"), - framework::ShaderStage::Fragment, - ); - let fs_module = device.create_shader_module(&fs); + let fs = include_bytes!("shader.frag.spv"); + let fs_module = + device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs[..])).unwrap()); // create compute bind layout group and compute pipeline layout diff --git a/examples/boids/shader.frag.spv b/examples/boids/shader.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..3b3d8fbd71aa5caddb46b0f72484b62483cbc60a GIT binary patch literal 336 zcmYk2%?`m(6od~|O2tnk?4)8z!~&5>SXgw^ohN7_M1m&e0X&+}vd@qeY;HU+0J`JK`PCbxY~1 rA6KTd5nSFHFnesqxa13gw0|tPpUzgUO8YMqpT34EQ+sdz)DnCE5W*4Y literal 0 HcmV?d00001 diff --git a/examples/boids/shader.vert.spv b/examples/boids/shader.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..3e95e49fbd63255d50a8008f0b1a3f9ea2850516 GIT binary patch literal 1684 zcmZ9LTTc^F6h@Df0t$kNfEUyv-ch^(qR356@PP*m39qEV37KRpgx19PqQ>~g{8he~ zxYp^M(r}vHz4!O+>zSEK&rE*^XZQ_-x1oJTLl1rkgP||#&8?lSxn}Fr-2B2*BZfmI z1{yOG&PKbJ|IhWjp)-hFM&^+vq{6QU{fC5V=;e13t3QBWZyXdEx=K&;>w8D_<5s?3 zWV=}r@k*FPA00Mh4eJww*Uov4J}CCKv*X?DxRrgyrBYuI_sAVK^HzS?0PT;s^G7^y zw081uw(oGx3-_+bkKX6aR=u&GbvU<|`%)AguF8(?>v0G7KG}iR_?ztNZZ{Bn>$``( z{XUSH<~7cfFyqD42{T8`c@m~A<{fXsSkE|nIg`CSW1!;G#F(Q!fz^kuMd{_1LKLw{ z@_o|U#@6u9p`Ay3x%AVF7Q@AF@G4_d}}6tw8X4Gf$a?4^}R21oX3~Z zTz8$~|5@o<;|7eorx-cKx-Zb~EbrX)6(ImPMYi_vg$W@xLHdw6)#W??J8a9r)hsZxH33jUe8$cez50{VpKlDdr-! zG2TUr8N(JIqMA9E5O=tOm@ogmtT&D=zsiKpqn~OU?{nzxe-&Rm^}2>_-VmMaHHFyA z`Q?7Ii}-cKT=`Vrchvgbv){xQPxWiq-SwyOt?x`VB-Ou#ZLa*UvgZu8edSWm+t{af zx&tPjdfvtE?s*SC^^{9J?_--QpXxurKE2aJF!5A>7Q4ItBYf+7XL70jV{CKff0jGV zVcS=38_aL!3F5ADK1bgpG5g8CM8uN+3VR8$ru=Kf81au~pOq3vb+FYEbJlI%E#LDs d#8^4M4ZjEPVm<2i_vSa{TpMWP|FP{7@*kECVD|t3 literal 0 HcmV?d00001 diff --git a/examples/framework.rs b/examples/framework.rs index 444531806..a6702c7ec 100644 --- a/examples/framework.rs +++ b/examples/framework.rs @@ -94,7 +94,8 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { *control_flow = if cfg!(feature = "metal-auto-capture") { ControlFlow::Exit } else { - ControlFlow::Poll + // TODO: ControlFlow::Poll + ControlFlow::Wait }; match event { event::Event::MainEventsCleared => window.request_redraw(), From 213469d5cd9cccae20e511fb121226653e57bee0 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sun, 5 Apr 2020 11:46:58 -0230 Subject: [PATCH 03/17] Get cube working --- Cargo.toml | 6 ++ examples/cube/main.rs | 16 ++-- examples/cube/shader.frag | 2 +- examples/cube/shader.frag.spv | Bin 0 -> 1208 bytes examples/cube/shader.vert.spv | Bin 0 -> 1176 bytes src/backend/native.rs | 87 ++++++++++++++++-- src/backend/web.rs | 112 +++++++++++++++++++++-- src/lib.rs | 162 ++++++++++++---------------------- 8 files changed, 257 insertions(+), 128 deletions(-) create mode 100644 examples/cube/shader.frag.spv create mode 100644 examples/cube/shader.vert.spv diff --git a/Cargo.toml b/Cargo.toml index 5b5b0d6bd..13ff8118b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,7 @@ web-sys = { version = "0.3.36", features = [ "NodeList", "Gpu", "GpuAdapter", + "GpuAddressMode", "GpuBindGroup", "GpuBindGroupBinding", "GpuBindGroupDescriptor", @@ -100,6 +101,7 @@ web-sys = { version = "0.3.36", features = [ "GpuBindingType", "GpuBuffer", "GpuBufferBinding", + "GpuBufferCopyView", "GpuBufferDescriptor", "GpuCanvasContext", "GpuColorDict", @@ -118,11 +120,13 @@ web-sys = { version = "0.3.36", features = [ "GpuDevice", "GpuDeviceDescriptor", "GpuExtent3dDict", + "GpuFilterMode", "GpuFrontFace", "GpuIndexFormat", "GpuInputStepMode", "GpuLimits", "GpuLoadOp", + "GpuOrigin3dDict", "GpuPipelineLayout", "GpuPipelineLayoutDescriptor", "GpuPowerPreference", @@ -138,6 +142,7 @@ web-sys = { version = "0.3.36", features = [ "GpuRenderPipelineDescriptor", "GpuRequestAdapterOptions", "GpuSampler", + "GpuSamplerDescriptor", "GpuShaderModule", "GpuShaderModuleDescriptor", "GpuStencilOperation", @@ -146,6 +151,7 @@ web-sys = { version = "0.3.36", features = [ "GpuSwapChain", "GpuSwapChainDescriptor", "GpuTexture", + "GpuTextureCopyView", "GpuTextureDescriptor", "GpuTextureDimension", "GpuTextureFormat", diff --git a/examples/cube/main.rs b/examples/cube/main.rs index f5710abd5..689da44d8 100644 --- a/examples/cube/main.rs +++ b/examples/cube/main.rs @@ -206,7 +206,7 @@ impl framework::Example for Example { mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Nearest, - lod_min_clamp: -100.0, + lod_min_clamp: 0.0, lod_max_clamp: 100.0, compare: wgpu::CompareFunction::Undefined, }); @@ -241,14 +241,12 @@ impl framework::Example for Example { }); // Create the render pipeline - let vs_bytes = - framework::load_glsl(include_str!("shader.vert"), framework::ShaderStage::Vertex); - let fs_bytes = framework::load_glsl( - include_str!("shader.frag"), - framework::ShaderStage::Fragment, - ); - let vs_module = device.create_shader_module(&vs_bytes); - let fs_module = device.create_shader_module(&fs_bytes); + let vs_bytes = include_bytes!("shader.vert.spv"); + let fs_bytes = include_bytes!("shader.frag.spv"); + let vs_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap()); + let fs_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap()); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, diff --git a/examples/cube/shader.frag b/examples/cube/shader.frag index b88e3abf0..966bfdb02 100644 --- a/examples/cube/shader.frag +++ b/examples/cube/shader.frag @@ -8,5 +8,5 @@ layout(set = 0, binding = 2) uniform sampler s_Color; void main() { vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord); float mag = length(v_TexCoord-vec2(0.5)); - o_Target = mix(tex, vec4(0.0), mag*mag); + o_Target = vec4(mix(tex.xyz, vec3(0.0), mag*mag), 1.0); } diff --git a/examples/cube/shader.frag.spv b/examples/cube/shader.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..95a79e2e150d21fc6355879051a320e20d17de58 GIT binary patch literal 1208 zcmYk4TT4`76o%J0ZZ(}eWO?i`lUbSLXkZb93VGp;kdf5oFyJ78wqYE?OCkDW{VH#S zo@dYgVm^51`_{YOb(~!tSsRb2#CS}^+Zdd=7=ekHim~E8I{tdxy6T^`cHix&n2u^8 zR5L^5D7U!m{P?M30W`oYI(Z84s**M0FF_gXus*|g4`07~UHEFg+lTo@-Ye``?5qEF zR^Ie1TPXK=qBbKSyZ z+}3p8eq*Tz=kS}+-ox)c?gRI!?e6A)d;3NfcQy~y$g6SIMc{tEqcPVKP$$1n7uJ+Z z`c>ZQf1@|vZ%EFa)tJ>iJOdT*3FtSEy~>-%I%~k(pP9`4yeK(&kMZt9y*Xb2b0qt9 zY5$=Q{To1y{0WRX-vIf9T3Kro&ipN4UH==kU&>rtyw!LTr#8hUZoA~{^Mt9KYX=yw RUcdjZ^ZUK%yQA_4@DG50Hjw}T literal 0 HcmV?d00001 diff --git a/examples/cube/shader.vert.spv b/examples/cube/shader.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..a5539aff6f4a8893162ed594d04ea4478c645297 GIT binary patch literal 1176 zcmY+CU279j5Qa~aCaqRmYwd?x|O?YoXuR@#J)JRL?JtUcP!^#Cqs9 zfyQivJI#5S-^(mzQ3_O7~{WB&$8>|vaBwQ={5J$|G0kG zB<^c5{ghRovud7Q1L?5kJ?ks$e4dvx*p(*U9veQN%}?{2%(&>YCw#|6{_SI4&(qm? zcI$IOeOE+3OxGbzroi=C9`X;HU*h?T49S(VP}FJ*N}TsJ&!>E@36NNp24CWo`j zDPs2~w(k>eII|+JL{7ilzj4kZXYVo2vz$90<1wFcCyV|$Ql*WL{f{IOb3~3u5+0!D zkTZwn=MgqR z&1)U;HmdA_cd+KQuK~B*shqj@u+A@MZn1lgJ9)A=$M|Pp>zL1YaePDO8-ndi|7-qJ z^H^7r{{`b4cn9kq@1gGSJzjS${y>CvpQ85SEXLczJ}NJ^#&gvAKe!`KP-EoHV?PI| Q`!bJp#0mPFN#3A;07fE7i2wiq literal 0 HcmV?d00001 diff --git a/src/backend/native.rs b/src/backend/native.rs index 8c00efa32..775c14df2 100644 --- a/src/backend/native.rs +++ b/src/backend/native.rs @@ -3,7 +3,8 @@ use wgn; use crate::{ backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, - PipelineLayoutDescriptor, RenderPipelineDescriptor, TextureDescriptor, TextureViewDimension, + PipelineLayoutDescriptor, RenderPipelineDescriptor, SamplerDescriptor, TextureDescriptor, + TextureViewDimension, }; use arrayvec::ArrayVec; @@ -30,6 +31,24 @@ pub type SurfaceId = wgc::id::SurfaceId; pub type SwapChainId = wgc::id::SwapChainId; pub type RenderPassEncoderId = wgc::id::RenderPassId; +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(crate) async fn request_adapter( options: &crate::RequestAdapterOptions<'_>, backends: wgt::BackendBit, @@ -284,7 +303,7 @@ pub(crate) fn create_compute_pipeline( pub(crate) type CreateBufferMappedDetail = BufferDetail; -pub(crate) fn create_buffer_mapped<'a>( +pub(crate) fn device_create_buffer_mapped<'a>( device: &DeviceId, desc: &BufferDescriptor, ) -> crate::CreateBufferMapped<'a> { @@ -316,7 +335,7 @@ pub(crate) struct BufferDetail { device_id: DeviceId, } -pub(crate) fn create_buffer_mapped_finish( +pub(crate) fn device_create_buffer_mapped_finish( create_buffer_mapped: crate::CreateBufferMapped<'_>, ) -> crate::Buffer { buffer_unmap(&create_buffer_mapped.id); @@ -332,7 +351,7 @@ pub(crate) fn buffer_unmap(buffer: &BufferId) { wgn::wgpu_buffer_unmap(*buffer); } -pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { +pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { let owned_label = OwnedLabel::new(desc.label.as_deref()); crate::Buffer { id: wgn::wgpu_device_create_buffer( @@ -347,7 +366,7 @@ pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate } } -pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { +pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { let owned_label = OwnedLabel::new(desc.label.as_deref()); wgn::wgpu_device_create_texture( *device, @@ -363,6 +382,10 @@ pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> Tex ) } +pub(crate) fn device_create_sampler(device: &DeviceId, desc: &SamplerDescriptor) -> SamplerId { + wgn::wgpu_device_create_sampler(*device, desc) +} + pub(crate) fn create_command_encoder( device: &DeviceId, desc: &CommandEncoderDescriptor, @@ -376,7 +399,7 @@ pub(crate) fn create_command_encoder( ) } -pub(crate) fn copy_buffer_to_buffer( +pub(crate) fn command_encoder_copy_buffer_to_buffer( command_encoder: &CommandEncoderId, source: &crate::Buffer, source_offset: wgt::BufferAddress, @@ -394,6 +417,20 @@ pub(crate) fn copy_buffer_to_buffer( ); } +pub(crate) fn command_encoder_copy_buffer_to_texture( + command_encoder: &CommandEncoderId, + source: crate::BufferCopyView, + destination: crate::TextureCopyView, + copy_size: wgt::Extent3d, +) { + wgn::wgpu_command_encoder_copy_buffer_to_texture( + *command_encoder, + &map_buffer_copy_view(source), + &map_texture_copy_view(destination), + copy_size, + ); +} + pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { unsafe { wgn::wgpu_command_encoder_begin_compute_pass(*command_encoder, None) } } @@ -515,7 +552,9 @@ impl BufferReadMappingDetail { } } -pub(crate) fn create_surface(window: &W) -> SurfaceId { +pub(crate) fn device_create_surface( + window: &W, +) -> SurfaceId { wgn::wgpu_create_surface(window.raw_window_handle()) } @@ -597,6 +636,22 @@ pub(crate) fn render_pass_set_bind_group( } } +pub(crate) fn render_pass_set_index_buffer<'a>( + render_pass: &RenderPassEncoderId, + buffer: &'a crate::Buffer, + offset: wgt::BufferAddress, + size: wgt::BufferAddress, +) { + unsafe { + wgn::wgpu_render_pass_set_index_buffer( + render_pass.as_mut().unwrap(), + buffer.id, + offset, + size, + ); + } +} + pub(crate) fn render_pass_set_vertex_buffer<'a>( render_pass: &RenderPassEncoderId, slot: u32, @@ -631,6 +686,24 @@ pub(crate) fn render_pass_draw( } } +pub(crate) fn render_pass_draw_indexed( + render_pass: &RenderPassEncoderId, + indices: Range, + base_vertex: i32, + instances: Range, +) { + unsafe { + wgn::wgpu_render_pass_draw_indexed( + render_pass.as_mut().unwrap(), + indices.end - indices.start, + instances.end - instances.start, + indices.start, + base_vertex, + instances.start, + ); + } +} + pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { unsafe { wgn::wgpu_render_pass_end_pass(*render_pass); diff --git a/src/backend/web.rs b/src/backend/web.rs index 8750c1743..2e0332969 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -1,7 +1,8 @@ use crate::{ BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, PipelineLayoutDescriptor, - ProgrammableStageDescriptor, RenderPipelineDescriptor, TextureDescriptor, TextureViewDimension, + ProgrammableStageDescriptor, RenderPipelineDescriptor, SamplerDescriptor, TextureDescriptor, + TextureViewDimension, }; use std::ops::Range; @@ -474,6 +475,14 @@ fn map_extent_3d(extent: wgt::Extent3d) -> web_sys::GpuExtent3dDict { web_sys::GpuExtent3dDict::new(extent.depth, extent.height, extent.width) } +fn map_origin_3d(origin: wgt::Origin3d) -> web_sys::GpuOrigin3dDict { + let mut mapped = web_sys::GpuOrigin3dDict::new(); + mapped.x(origin.x); + mapped.y(origin.y); + mapped.z(origin.z); + mapped +} + fn map_texture_dimension(texture_dimension: wgt::TextureDimension) -> web_sys::GpuTextureDimension { match texture_dimension { wgt::TextureDimension::D1 => web_sys::GpuTextureDimension::N1d, @@ -482,6 +491,36 @@ fn map_texture_dimension(texture_dimension: wgt::TextureDimension) -> web_sys::G } } +fn map_buffer_copy_view(view: crate::BufferCopyView<'_>) -> web_sys::GpuBufferCopyView { + let mut mapped = + web_sys::GpuBufferCopyView::new(&view.buffer.id, view.rows_per_image, view.bytes_per_row); + mapped.offset(view.offset as f64); + mapped +} + +fn map_texture_copy_view<'a>(view: crate::TextureCopyView<'a>) -> web_sys::GpuTextureCopyView { + let mut mapped = web_sys::GpuTextureCopyView::new(&view.texture.id); + mapped.array_layer(view.array_layer); + mapped.mip_level(view.mip_level); + mapped.origin(&map_origin_3d(view.origin)); + mapped +} + +fn map_filter_mode(mode: wgt::FilterMode) -> web_sys::GpuFilterMode { + match mode { + wgt::FilterMode::Nearest => web_sys::GpuFilterMode::Nearest, + wgt::FilterMode::Linear => web_sys::GpuFilterMode::Linear, + } +} + +fn map_address_mode(mode: wgt::AddressMode) -> web_sys::GpuAddressMode { + match mode { + wgt::AddressMode::ClampToEdge => web_sys::GpuAddressMode::ClampToEdge, + wgt::AddressMode::Repeat => web_sys::GpuAddressMode::Repeat, + wgt::AddressMode::MirrorRepeat => web_sys::GpuAddressMode::MirrorRepeat, + } +} + pub(crate) fn create_render_pipeline( device: &DeviceId, desc: &RenderPipelineDescriptor, @@ -558,7 +597,7 @@ pub(crate) struct CreateBufferMappedDetail { array_buffer: js_sys::ArrayBuffer, } -pub(crate) fn create_buffer_mapped<'a>( +pub(crate) fn device_create_buffer_mapped<'a>( device: &DeviceId, desc: &BufferDescriptor, ) -> crate::CreateBufferMapped<'a> { @@ -581,7 +620,7 @@ pub(crate) fn create_buffer_mapped<'a>( pub type BufferDetail = (); -pub(crate) fn create_buffer_mapped_finish( +pub(crate) fn device_create_buffer_mapped_finish( create_buffer_mapped: crate::CreateBufferMapped<'_>, ) -> crate::Buffer { unsafe { @@ -614,7 +653,7 @@ pub(crate) fn buffer_unmap(buffer: &BufferId) { buffer.unmap(); } -pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { +pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); crate::Buffer { id: device.create_buffer(&mapped_desc), @@ -622,7 +661,7 @@ pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate } } -pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { +pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { let extent = map_extent_3d(desc.size); let mut mapped_desc = web_sys::GpuTextureDescriptor::new( map_texture_format(desc.format), @@ -637,6 +676,22 @@ pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> Tex device.create_texture(&mapped_desc) } +pub(crate) fn device_create_sampler(device: &DeviceId, desc: &SamplerDescriptor) -> SamplerId { + let mut mapped_desc = web_sys::GpuSamplerDescriptor::new(); + 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, @@ -645,7 +700,7 @@ pub(crate) fn create_command_encoder( device.create_command_encoder_with_descriptor(&mapped_desc) } -pub(crate) fn copy_buffer_to_buffer( +pub(crate) fn command_encoder_copy_buffer_to_buffer( command_encoder: &CommandEncoderId, source: &crate::Buffer, source_offset: wgt::BufferAddress, @@ -662,6 +717,19 @@ pub(crate) fn copy_buffer_to_buffer( ); } +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 begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { let mapped_desc = web_sys::GpuComputePassDescriptor::new(); command_encoder.begin_compute_pass_with_descriptor(&mapped_desc) @@ -752,7 +820,9 @@ impl BufferReadMappingDetail { } } -pub(crate) fn create_surface(window: &W) -> SurfaceId { +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, @@ -877,6 +947,19 @@ pub(crate) fn render_pass_set_bind_group( ); } +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( + &buffer.id, + offset as f64, + // TODO: size, + ); +} + pub(crate) fn render_pass_set_vertex_buffer<'a>( render_pass: &RenderPassEncoderId, slot: u32, @@ -905,6 +988,21 @@ pub(crate) fn render_pass_draw( ) } +pub(crate) fn render_pass_draw_indexed( + render_pass: &RenderPassEncoderId, + indices: Range, + base_vertex: i32, + instances: Range, +) { + render_pass.draw_indexed( + indices.end - indices.start, + instances.end - instances.start, + indices.start, + base_vertex, + instances.start, + ); +} + pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { render_pass.end_pass(); } diff --git a/src/lib.rs b/src/lib.rs index fe0d8afa6..ed0cdbe57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -468,7 +468,6 @@ pub struct SwapChainOutput { swap_chain_id: backend::SwapChainId, } -/* /// A view of a buffer which can be used to copy to or from a texture. #[derive(Clone, Debug)] pub struct BufferCopyView<'a> { @@ -488,17 +487,6 @@ pub struct BufferCopyView<'a> { pub rows_per_image: u32, } -impl BufferCopyView<'_> { - fn into_native(self) -> wgc::command::BufferCopyView { - wgc::command::BufferCopyView { - buffer: self.buffer.id, - offset: self.offset, - bytes_per_row: self.bytes_per_row, - rows_per_image: self.rows_per_image, - } - } -} - /// A view of a texture which can be used to copy to or from a buffer or another texture. #[derive(Clone, Debug)] pub struct TextureCopyView<'a> { @@ -515,18 +503,6 @@ pub struct TextureCopyView<'a> { pub origin: Origin3d, } -impl<'a> TextureCopyView<'a> { - fn into_native(self) -> wgc::command::TextureCopyView { - wgc::command::TextureCopyView { - texture: self.texture.id, - mip_level: self.mip_level, - array_layer: self.array_layer, - origin: self.origin, - } - } -} -*/ - /// A buffer being created, mapped in host memory. pub struct CreateBufferMapped<'a> { id: backend::BufferId, @@ -545,7 +521,7 @@ impl CreateBufferMapped<'_> { /// Unmaps the buffer from host memory and returns a [`Buffer`]. pub fn finish(self) -> Buffer { - backend::create_buffer_mapped_finish(self) + backend::device_create_buffer_mapped_finish(self) } } @@ -553,7 +529,7 @@ impl Surface { /// Creates a surface from a raw window handle. pub fn create(window: &W) -> Self { Surface { - id: backend::create_surface(window), + id: backend::device_create_surface(window), } } @@ -668,7 +644,7 @@ impl Device { /// Creates a new buffer. pub fn create_buffer(&self, desc: &BufferDescriptor) -> Buffer { - backend::create_buffer(&self.id, desc) + backend::device_create_buffer(&self.id, desc) } /// Creates a new buffer and maps it into host-visible memory. @@ -677,7 +653,7 @@ impl Device { /// will not be created until calling [`CreateBufferMapped::finish`]. pub fn create_buffer_mapped(&self, desc: &BufferDescriptor) -> CreateBufferMapped<'_> { assert_ne!(desc.size, 0); - backend::create_buffer_mapped(&self.id, desc) + backend::device_create_buffer_mapped(&self.id, desc) } /// Creates a new buffer, maps it into host-visible memory, copies data from the given slice, @@ -697,21 +673,19 @@ impl Device { /// `desc` specifies the general format of the texture. pub fn create_texture(&self, desc: &TextureDescriptor) -> Texture { Texture { - id: backend::create_texture(&self.id, desc), + id: backend::device_create_texture(&self.id, desc), owned: true, } } - /* - /// Creates a new [`Sampler`]. - /// - /// `desc` specifies the behavior of the sampler. - pub fn create_sampler(&self, desc: &SamplerDescriptor) -> Sampler { - Sampler { - id: wgn::wgpu_device_create_sampler(self.id, desc), - } + /// Creates a new [`Sampler`]. + /// + /// `desc` specifies the behavior of the sampler. + pub fn create_sampler(&self, desc: &SamplerDescriptor) -> Sampler { + Sampler { + id: backend::device_create_sampler(&self.id, desc), } - */ + } /// Create a new [`SwapChain`] which targets `surface`. pub fn create_swap_chain(&self, surface: &Surface, desc: &SwapChainDescriptor) -> SwapChain { @@ -931,7 +905,7 @@ impl CommandEncoder { destination_offset: BufferAddress, copy_size: BufferAddress, ) { - backend::copy_buffer_to_buffer( + backend::command_encoder_copy_buffer_to_buffer( &self.id, source, source_offset, @@ -941,36 +915,31 @@ impl CommandEncoder { ); } - /* - /// Copy data from a buffer to a texture. - pub fn copy_buffer_to_texture( - &mut self, - source: BufferCopyView, - destination: TextureCopyView, - copy_size: Extent3d, - ) { - wgn::wgpu_command_encoder_copy_buffer_to_texture( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); - } + /// Copy data from a buffer to a texture. + pub fn copy_buffer_to_texture( + &mut self, + source: BufferCopyView, + destination: TextureCopyView, + copy_size: Extent3d, + ) { + backend::command_encoder_copy_buffer_to_texture(&self.id, source, destination, copy_size); + } - /// Copy data from a texture to a buffer. - pub fn copy_texture_to_buffer( - &mut self, - source: TextureCopyView, - destination: BufferCopyView, - copy_size: Extent3d, - ) { - wgn::wgpu_command_encoder_copy_texture_to_buffer( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); - } + /* + /// Copy data from a texture to a buffer. + pub fn copy_texture_to_buffer( + &mut self, + source: TextureCopyView, + destination: BufferCopyView, + copy_size: Extent3d, + ) { + wgn::wgpu_command_encoder_copy_texture_to_buffer( + self.id, + &source.into_native(), + &destination.into_native(), + copy_size, + ); + } /// Copy data from one texture to another. pub fn copy_texture_to_texture( @@ -1008,35 +977,29 @@ impl<'a> RenderPass<'a> { } /* - pub fn set_blend_color(&mut self, color: Color) { - unsafe { - wgn::wgpu_render_pass_set_blend_color(self.id.as_mut().unwrap(), &color); - } - } - - /// Sets the active index buffer. - /// - /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will - /// use `buffer` as the source index buffer. - /// - /// If `size == 0`, the remaining part of the buffer is considered. - pub fn set_index_buffer( - &mut self, - buffer: &'a Buffer, - offset: BufferAddress, - size: BufferAddress, - ) { - unsafe { - wgn::wgpu_render_pass_set_index_buffer( - self.id.as_mut().unwrap(), - buffer.id, - offset, - size, - ); + pub fn set_blend_color(&mut self, color: Color) { + unsafe { + wgn::wgpu_render_pass_set_blend_color(self.id.as_mut().unwrap(), &color); + } } } */ + /// Sets the active index buffer. + /// + /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will + /// use `buffer` as the source index buffer. + /// + /// If `size == 0`, the remaining part of the buffer is considered. + pub fn set_index_buffer( + &mut self, + buffer: &'a Buffer, + offset: BufferAddress, + size: BufferAddress, + ) { + backend::render_pass_set_index_buffer(&self.id, buffer, offset, size) + } + /// Assign a vertex buffer to a slot. /// /// Subsequent calls to [`draw`] and [`draw_indexed`] on this @@ -1105,24 +1068,15 @@ impl<'a> RenderPass<'a> { backend::render_pass_draw(&self.id, vertices, instances) } - /* /// Draws indexed primitives using the active index buffer and the active vertex buffers. /// /// 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) { - unsafe { - wgn::wgpu_render_pass_draw_indexed( - self.id.as_mut().unwrap(), - indices.end - indices.start, - instances.end - instances.start, - indices.start, - base_vertex, - instances.start, - ); - } + backend::render_pass_draw_indexed(&self.id, indices, base_vertex, instances); } + /* /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`. /// /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. From 150a20ffb547c276942c43994c5f8b4202cc0d4c Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sun, 5 Apr 2020 23:20:49 -0230 Subject: [PATCH 04/17] Get skybox working --- Cargo.toml | 2 + examples/skybox/main.rs | 22 +++---- .../skybox/{skybox_frag.glsl => shader.frag} | 0 examples/skybox/shader.frag.spv | Bin 0 -> 684 bytes .../skybox/{skybox_vert.glsl => shader.vert} | 0 examples/skybox/shader.vert.spv | Bin 0 -> 2076 bytes src/backend/native.rs | 9 ++- src/backend/web.rs | 58 ++++++++++++++---- src/lib.rs | 16 +++-- 9 files changed, 69 insertions(+), 38 deletions(-) rename examples/skybox/{skybox_frag.glsl => shader.frag} (100%) create mode 100644 examples/skybox/shader.frag.spv rename examples/skybox/{skybox_vert.glsl => shader.vert} (100%) create mode 100644 examples/skybox/shader.vert.spv diff --git a/Cargo.toml b/Cargo.toml index 13ff8118b..5ae3444b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -151,10 +151,12 @@ web-sys = { version = "0.3.36", features = [ "GpuSwapChain", "GpuSwapChainDescriptor", "GpuTexture", + "GpuTextureAspect", "GpuTextureCopyView", "GpuTextureDescriptor", "GpuTextureDimension", "GpuTextureFormat", + "GpuTextureViewDescriptor", "GpuTextureViewDimension", "GpuTextureView", "GpuVertexAttributeDescriptor", diff --git a/examples/skybox/main.rs b/examples/skybox/main.rs index aeb010029..4359b2096 100644 --- a/examples/skybox/main.rs +++ b/examples/skybox/main.rs @@ -32,7 +32,7 @@ fn buffer_from_uniforms( uniforms: &Uniforms, usage: wgpu::BufferUsage, ) -> wgpu::Buffer { - let uniform_buf = device.create_buffer_mapped(&wgpu::BufferDescriptor { + let mut uniform_buf = device.create_buffer_mapped(&wgpu::BufferDescriptor { size: std::mem::size_of::() as u64, usage, label: None, @@ -40,7 +40,7 @@ fn buffer_from_uniforms( // FIXME: Align and use `LayoutVerified` for (u, slot) in uniforms.iter().zip( uniform_buf - .data + .data() .chunks_exact_mut(std::mem::size_of::()), ) { slot.copy_from_slice(bytemuck::cast_slice(AsRef::<[[f32; 4]; 4]>::as_ref(u))); @@ -82,16 +82,12 @@ impl framework::Example for Skybox { }); // Create the render pipeline - let vs_bytes = framework::load_glsl( - include_str!("skybox_vert.glsl"), - framework::ShaderStage::Vertex, - ); - let fs_bytes = framework::load_glsl( - include_str!("skybox_frag.glsl"), - framework::ShaderStage::Fragment, - ); - let vs_module = device.create_shader_module(&vs_bytes); - let fs_module = device.create_shader_module(&fs_bytes); + let vs_bytes = include_bytes!("shader.vert.spv"); + let fs_bytes = include_bytes!("shader.frag.spv"); + let vs_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap()); + let fs_module = device + .create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap()); let aspect = sc_desc.width as f32 / sc_desc.height as f32; let uniforms = Self::generate_uniforms(aspect); @@ -148,7 +144,7 @@ impl framework::Example for Skybox { mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Nearest, - lod_min_clamp: -100.0, + lod_min_clamp: 0.0, lod_max_clamp: 100.0, compare: wgpu::CompareFunction::Undefined, }); diff --git a/examples/skybox/skybox_frag.glsl b/examples/skybox/shader.frag similarity index 100% rename from examples/skybox/skybox_frag.glsl rename to examples/skybox/shader.frag diff --git a/examples/skybox/shader.frag.spv b/examples/skybox/shader.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..65ab23b9adf8b6282447b16eb5467515562ce877 GIT binary patch literal 684 zcmY+BO-q7N5QQhd%hJr!EC{W=w5SN8HZI(>i)txG1wm${`2+h^ZGxWXeUMFu9K9EBwJuWzoqlj&pkA=f@)&T-(lz3XkMgj~da>U;4|?O*@dx#Mtb?b6-si*9X!y=uZj(Iu$K?{A+2D3Y zDf;t12Lg`RK8FeV!yT}*Tes(OU2NT1t-HsfUmaGylWdb}8r%zTZK8?ZUB&L#=C?%l zwd616*j?pXY`sQSpS~!U`EfpvJ<91d(b_}oUY}XCH?xvVP9F zO5QYyeT~rW61Th+HK|}%ZgAY*z3N02Aj2M)^q@f$LrfvycD)E=M;1!v8 zQ)-Nf@3Xsu8&C6gX5N`OXJ*bRNqN29(F@7CzR45WQb`A#4eGeR7iD6*sSA|)(50<)Y&=D%ezUOS_OUP zB~QaWW%x&t--!36_RitEP7>p}iWJqg=bt2{!F&3^v+%XrK@_w?u{|-*nK2)P(SGpR zC(eA#L%vEgIIafms2(2q=e~UEJ8d@4eawkjvTB+j^X(O;g>Bi^cOka_&B6F>b!ECP z+g-`zQD-&}o7l2od2p6jQ!V)Efm$Xd#|4(zrNoSFW;vGH>{m&3>Sr0p8E{DBI0G)9 z;n3lDN7TZ8#0}{iSrSJK`iL}7vM5|v20dVtThuo%7Y@wG#&=0z&5DKREVwL&727AS zC{QnZT~2lQmDJ{*@Y~+kq&xTJo7uyOy>4fX$R-AG9)61*mF`@*Y_T_F!DVC3cQfVj z+dlNqJ TextureViewId { - wgn::wgpu_texture_create_view(*texture, None) +pub(crate) fn texture_create_view( + texture: &TextureId, + desc: Option<&TextureViewDescriptor>, +) -> TextureViewId { + wgn::wgpu_texture_create_view(*texture, desc) } pub(crate) fn swap_chain_present(swap_chain: &SwapChainId) { diff --git a/src/backend/web.rs b/src/backend/web.rs index 2e0332969..290463e2f 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -2,7 +2,7 @@ use crate::{ BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, PipelineLayoutDescriptor, ProgrammableStageDescriptor, RenderPipelineDescriptor, SamplerDescriptor, TextureDescriptor, - TextureViewDimension, + TextureViewDescriptor, TextureViewDimension, }; use std::ops::Range; @@ -91,7 +91,6 @@ pub(crate) fn create_bind_group_layout( desc: &BindGroupLayoutDescriptor, ) -> BindGroupLayoutId { use web_sys::GpuBindingType as bt; - use web_sys::GpuTextureViewDimension as tvd; let mapped_bindings = desc .bindings @@ -127,15 +126,10 @@ pub(crate) fn create_bind_group_layout( let mapped_view_dimension = match bind.ty { BindingType::SampledTexture { dimension, .. } - | BindingType::StorageTexture { dimension, .. } => match dimension { - TextureViewDimension::D1 => tvd::N1d, - TextureViewDimension::D2 => tvd::N2d, - TextureViewDimension::D2Array => tvd::N2dArray, - TextureViewDimension::Cube => tvd::Cube, - TextureViewDimension::CubeArray => tvd::CubeArray, - TextureViewDimension::D3 => tvd::N3d, - }, - _ => tvd::N2d, + | BindingType::StorageTexture { dimension, .. } => { + map_texture_view_dimension(dimension) + } + _ => web_sys::GpuTextureViewDimension::N2d, }; let mut mapped_binding = web_sys::GpuBindGroupLayoutBinding::new( @@ -491,6 +485,20 @@ fn map_texture_dimension(texture_dimension: wgt::TextureDimension) -> web_sys::G } } +fn map_texture_view_dimension( + texture_view_dimension: wgt::TextureViewDimension, +) -> web_sys::GpuTextureViewDimension { + use web_sys::GpuTextureViewDimension as tvd; + match texture_view_dimension { + TextureViewDimension::D1 => tvd::N1d, + TextureViewDimension::D2 => tvd::N2d, + TextureViewDimension::D2Array => tvd::N2dArray, + TextureViewDimension::Cube => tvd::Cube, + TextureViewDimension::CubeArray => tvd::CubeArray, + TextureViewDimension::D3 => tvd::N3d, + } +} + fn map_buffer_copy_view(view: crate::BufferCopyView<'_>) -> web_sys::GpuBufferCopyView { let mut mapped = web_sys::GpuBufferCopyView::new(&view.buffer.id, view.rows_per_image, view.bytes_per_row); @@ -506,6 +514,14 @@ fn map_texture_copy_view<'a>(view: crate::TextureCopyView<'a>) -> web_sys::GpuTe mapped } +fn map_texture_aspect(aspect: wgt::TextureAspect) -> web_sys::GpuTextureAspect { + match aspect { + wgt::TextureAspect::All => web_sys::GpuTextureAspect::All, + wgt::TextureAspect::StencilOnly => web_sys::GpuTextureAspect::StencilOnly, + wgt::TextureAspect::DepthOnly => web_sys::GpuTextureAspect::DepthOnly, + } +} + fn map_filter_mode(mode: wgt::FilterMode) -> web_sys::GpuFilterMode { match mode { wgt::FilterMode::Nearest => web_sys::GpuFilterMode::Nearest, @@ -1007,8 +1023,24 @@ pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { render_pass.end_pass(); } -pub(crate) fn texture_create_default_view(texture: &TextureId) -> TextureViewId { - texture.create_view() +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); + texture.create_view_with_descriptor(&mapped_desc) + } + None => texture.create_view(), + } } pub(crate) fn swap_chain_present(_swap_chain: &SwapChainId) { diff --git a/src/lib.rs b/src/lib.rs index ed0cdbe57..00c89dcbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -828,20 +828,18 @@ impl Drop for Buffer { */ impl Texture { - /* - /// Creates a view of this texture. - pub fn create_view(&self, desc: &TextureViewDescriptor) -> TextureView { - TextureView { - id: wgn::wgpu_texture_create_view(self.id, Some(desc)), - owned: true, - } + /// Creates a view of this texture. + pub fn create_view(&self, desc: &TextureViewDescriptor) -> TextureView { + TextureView { + id: backend::texture_create_view(&self.id, Some(desc)), + owned: true, } - */ + } /// Creates a default view of this whole texture. pub fn create_default_view(&self) -> TextureView { TextureView { - id: backend::texture_create_default_view(&self.id), + id: backend::texture_create_view(&self.id, None), owned: true, } } From c40fb6b016853490d7d9fac7174fff11696021b6 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Mon, 6 Apr 2020 00:05:44 -0230 Subject: [PATCH 05/17] Get mipmap mostly working Subresource tracking for mip levels isn't available in Canary yet, so we can't verify that the web example output matches native --- examples/mipmap/blit.frag.spv | Bin 0 -> 708 bytes examples/mipmap/blit.vert.spv | Bin 0 -> 1348 bytes examples/mipmap/draw.frag.spv | Bin 0 -> 684 bytes examples/mipmap/draw.vert.spv | Bin 0 -> 1244 bytes examples/mipmap/main.rs | 28 ++++++++++++++-------------- 5 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 examples/mipmap/blit.frag.spv create mode 100644 examples/mipmap/blit.vert.spv create mode 100644 examples/mipmap/draw.frag.spv create mode 100644 examples/mipmap/draw.vert.spv diff --git a/examples/mipmap/blit.frag.spv b/examples/mipmap/blit.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..ff08dac769279e3b6e62a4464bc7ef0d70d435e9 GIT binary patch literal 708 zcmYk3%}WAN6vb~&qn71ImZm|dEdyy$5kzfVxN(<6yFpMQs2+?%^!N2wwFx@EtJYk& zbKg1d+^;uL;jCn~WhEKi0av_?UGPf1+x7o`( z7d!Hnye=KQ8t4?Ms@a#4VzGIBEQXwrOlRryBGb_|dx}{y3a=q`To+#c;n;h8B-6#` zD1>xwHda;YoXRmUH3p9UaX*KtnZE~f&u-+i$EIFW8?Zy)w=qArbS{eP$=lMiE13Oy z`j!=WE%@6tW=6Py%z7)k)wmBAN}vujrZg3N7LQNe*x6vB3=Lh literal 0 HcmV?d00001 diff --git a/examples/mipmap/blit.vert.spv b/examples/mipmap/blit.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..d4dd66b0745df3102ea5861f68d17270d3057f78 GIT binary patch literal 1348 zcmYk6U279j5QdM5Nz`gTtgV7l6Kk!uHGWkQM5|PYLN6i}Z%b(wS%`@ws|2qE@BLB! zDHRcXp4~k)CroGNedo-_nKaePY9oXbp%G4n?NFSBPyq>HCe(A?+1uZ1kF#ET`{7+9 zWt_Gj5T;>K2$W+RIpVbQ8JA4HlgHa!q_UCZ=rS8+!)vY|*Q@wZxPq zV$PzQh<%#lTxp9xDSh8SKIUA)4&m!g%(;vwX3mIRDlzehT`pt9tmQj8n|)bJ-}e=B zz82aUd>ebchHfGrd(mG-?ClEvb+qwfD_Fi|%ps>(_XgS;^48T~FER5v`%T0XXWl5> z=h1H${*L+XW1G)fk9cSIVb31%Um!KKwcL^a8_xU;R@|)10{@=N_jybKAc&H4$er)?MmH z?iRM(_wu{Ai7n>ac1vsv+wX_+t;B9)8!Pt)%pKoB#61!7M@-DO>C#OLeGf5K&YhX} W1v%W=1H`?%Yx~tV{x2CGBmV$!zgENm literal 0 HcmV?d00001 diff --git a/examples/mipmap/draw.frag.spv b/examples/mipmap/draw.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..57d41137679dd4b19bcf840cdcecd848b9236b16 GIT binary patch literal 684 zcmYk3O-q7N5QT5rtCr7b;328-;4h+}tJ`Z{wGD z3p+%esGyU_fUhiFL-^uS0=ubCV7!x#hU3}ubirrT?2#9vUizBOBCdeve>jOAAEV*) zvzMl`NeF$q+UJb$i0#pd=pVPi&TieFn{}~uXSMDg>wYy@`F7l9sj2gwgKH5D^zJHl zzZSm*vacn7yTZ;W*JRpVvU=y)JZo%^;;Nw59%A?MoG?*|tgajx|DUB!>^`1T91qC$ zaLz-rzV74QBeI_IdWhX!?wZ$E=T07H|AtA(cd+){C!9&Ff0fYt5tY6*LXEr{eHw%w VPiTCfV?v$$JubeJ+z(US5Wm89}u3dIW*K~zeG^dexO#oG|tRTk2$B&ihdyz8z1 zz~ADp@<#A^cJ~xdc$t}Z-ZMKhIrUnn8Nys>hHK$jD9=i$!Gy3F77E+#9rgC6v(vp7 zFQ2Pe3iU#$W|@1Qd2lw&#`@R6O>iI7`PHz$5jRl%uWP`l{WdsGFAnm2a$0JLsxId2hupy^`}!f9 z&W7WY^eX3M=FUf>t6YN_oWnuj4IX6R^R3>l&L&h1FPP7)W;pj zt2cIsb6}TP?J@}A*M5xe6mfXJMt%)HDdR$Kybip-MScTo4Y?Mc`-?r86ZUZrYmesb z!?=9sfp5%*2gC`mu6-D90naA!L#%b3t3@SuENAU)toxC(ws8XD&f1k9^-qZH#d@#! z57k-MBmW1+|NJeinp?n~ePrwIeW~K)J@(w^MtlSAQ(RWu3ch!IA5pi8@1DhvN_Q7u zt+{t};{PhQPu>}JKmy#0y*W<@$j4qE;oIv^?%4Zdphn(0&i)j*d+YceV|9O- Date: Mon, 6 Apr 2020 00:33:57 -0230 Subject: [PATCH 06/17] Get capture mostly working `File::create` isn't available on wasm, so we can't actually write the output image anywhere unless we target something else as the file system (e.g. local storage) --- examples/capture/main.rs | 20 ++++++++++++++++++-- src/backend/native.rs | 14 ++++++++++++++ src/backend/web.rs | 13 +++++++++++++ src/lib.rs | 9 ++------- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/examples/capture/main.rs b/examples/capture/main.rs index a4fefe1fe..15296b601 100644 --- a/examples/capture/main.rs +++ b/examples/capture/main.rs @@ -96,7 +96,12 @@ async fn run() { // be called in an event loop or on another thread. device.poll(wgpu::Maintain::Wait); - // Write the buffer as a PNG + // If a file system is available, write the buffer as a PNG + let has_file_system_available = cfg!(not(target_arch = "wasm32")); + if !has_file_system_available { + return; + } + if let Ok(mapping) = buffer_future.await { let mut png_encoder = png::Encoder::new(File::create("red.png").unwrap(), size, size); png_encoder.set_depth(png::BitDepth::Eight); @@ -109,8 +114,19 @@ async fn run() { } } +#[cfg(target_arch = "wasm32")] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] +pub fn wasm_main() { + console_log::init().expect("could not initialize log"); + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + wasm_bindgen_futures::spawn_local(run()); +} + +#[cfg(target_arch = "wasm32")] +fn main() {} + +#[cfg(not(target_arch = "wasm32"))] fn main() { env_logger::init(); - futures::executor::block_on(run()); } diff --git a/src/backend/native.rs b/src/backend/native.rs index 7c64ff738..c0f757eaa 100644 --- a/src/backend/native.rs +++ b/src/backend/native.rs @@ -431,6 +431,20 @@ pub(crate) fn command_encoder_copy_buffer_to_texture( ); } +pub(crate) fn command_encoder_copy_texture_to_buffer( + command_encoder: &CommandEncoderId, + source: crate::TextureCopyView, + destination: crate::BufferCopyView, + copy_size: wgt::Extent3d, +) { + wgn::wgpu_command_encoder_copy_texture_to_buffer( + *command_encoder, + &map_texture_copy_view(source), + &map_buffer_copy_view(destination), + copy_size, + ); +} + pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { unsafe { wgn::wgpu_command_encoder_begin_compute_pass(*command_encoder, None) } } diff --git a/src/backend/web.rs b/src/backend/web.rs index 290463e2f..f067ac8da 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -746,6 +746,19 @@ pub(crate) fn command_encoder_copy_buffer_to_texture( ); } +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 begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { let mapped_desc = web_sys::GpuComputePassDescriptor::new(); command_encoder.begin_compute_pass_with_descriptor(&mapped_desc) diff --git a/src/lib.rs b/src/lib.rs index 00c89dcbb..193873521 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -923,7 +923,6 @@ impl CommandEncoder { backend::command_encoder_copy_buffer_to_texture(&self.id, source, destination, copy_size); } - /* /// Copy data from a texture to a buffer. pub fn copy_texture_to_buffer( &mut self, @@ -931,14 +930,10 @@ impl CommandEncoder { destination: BufferCopyView, copy_size: Extent3d, ) { - wgn::wgpu_command_encoder_copy_texture_to_buffer( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); + backend::command_encoder_copy_texture_to_buffer(&self.id, source, destination, copy_size); } + /* /// Copy data from one texture to another. pub fn copy_texture_to_texture( &mut self, From 4a5848229a08eab907f641f076f31b3060f82fe7 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Mon, 6 Apr 2020 00:41:38 -0230 Subject: [PATCH 07/17] Only expose adapter info on native --- examples/describe/main.rs | 2 ++ src/lib.rs | 16 +++++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/describe/main.rs b/examples/describe/main.rs index 72e40493a..d035df44a 100644 --- a/examples/describe/main.rs +++ b/examples/describe/main.rs @@ -1,9 +1,11 @@ /// This example shows how to describe the adapter in use. +#[cfg(not(target_arch = "wasm32"))] fn main() { env_logger::init(); futures::executor::block_on(run()); } +#[cfg(not(target_arch = "wasm32"))] async fn run() { let adapter = wgpu::Adapter::request( &wgpu::RequestAdapterOptions { diff --git a/src/lib.rs b/src/lib.rs index 193873521..e95436d07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ mod macros; use std::{future::Future, ops::Range, thread}; +#[cfg(not(target_arch = "wasm32"))] pub use wgc::instance::{AdapterInfo, DeviceType}; pub use wgt::{ read_spirv, AddressMode, Backend, BackendBit, BlendDescriptor, BlendFactor, BlendOperation, @@ -19,12 +20,6 @@ pub use wgt::{ TextureFormat, TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttributeDescriptor, VertexFormat, BIND_BUFFER_ALIGNMENT, MAX_BIND_GROUPS, }; -/* -pub use wgc::instance::{ - AdapterInfo, - DeviceType, -}; -*/ //TODO: avoid heap allocating vectors during resource creation. #[derive(Default, Debug)] @@ -584,11 +579,10 @@ impl Adapter { (device, queue) } - /* - pub fn get_info(&self) -> AdapterInfo { - wgn::adapter_get_info(self.id) - } - */ + #[cfg(not(target_arch = "wasm32"))] + pub fn get_info(&self) -> AdapterInfo { + wgn::adapter_get_info(self.id) + } } impl Device { From 62f0c409395f0a01c388a432f0d9f09476351462 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sat, 11 Apr 2020 11:40:47 -0230 Subject: [PATCH 08/17] Update to latest winit and wasm-bindgen --- Cargo.toml | 22 ++++++++++++---------- src/backend/web.rs | 42 ++++++++++++++++++++---------------------- src/lib.rs | 3 ++- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ae3444b5..a75ea4be0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ cgmath = "0.17" #glsl-to-spirv = "0.1" log = "0.4" png = "0.15" -winit = { version = "0.22", features = ["web-sys"] } +winit = { version = "0.22.1", features = ["web-sys"] } rand = { version = "0.7.2", features = ["wasm-bindgen"] } bytemuck = "1" futures = "0.3" @@ -62,12 +62,10 @@ name="hello-compute" path="examples/hello-compute/main.rs" test = true -#[patch.crates-io] +[patch.crates-io] #wgpu-types = { version = "0.5.0", path = "../wgpu/wgpu-types" } #wgpu-core = { version = "0.5.0", path = "../wgpu/wgpu-core" } #wgpu-native = { version = "0.5.0", path = "../wgpu/wgpu-native" } - -#[patch.crates-io] #gfx-hal = { version = "0.5.0", path = "../gfx/src/hal" } #gfx-backend-empty = { version = "0.5.0", path = "../gfx/src/backend/empty" } #gfx-backend-vulkan = { version = "0.5.0", path = "../gfx/src/backend/vulkan" } @@ -75,13 +73,17 @@ test = true #gfx-backend-dx11 = { version = "0.5.0", path = "../gfx/src/backend/dx11" } #gfx-descriptor = { version = "0.1.0", path = "../gfx-extras/gfx-descriptor" } #gfx-memory = { version = "0.1.0", path = "../gfx-extras/gfx-memory" } +wasm-bindgen = { git = "https://github.com/rustwasm/wasm-bindgen" } +wasm-bindgen-futures = { git = "https://github.com/rustwasm/wasm-bindgen" } +web-sys = { git = "https://github.com/rustwasm/wasm-bindgen" } +js-sys = { git = "https://github.com/rustwasm/wasm-bindgen" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] env_logger = "0.7" [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = "0.2.59" -web-sys = { version = "0.3.36", features = [ +wasm-bindgen = "0.2.60" +web-sys = { version = "0.3.37", features = [ "Document", "Navigator", "Node", @@ -90,10 +92,10 @@ web-sys = { version = "0.3.36", features = [ "GpuAdapter", "GpuAddressMode", "GpuBindGroup", - "GpuBindGroupBinding", + "GpuBindGroupEntry", "GpuBindGroupDescriptor", "GpuBindGroupLayout", - "GpuBindGroupLayoutBinding", + "GpuBindGroupLayoutEntry", "GpuBindGroupLayoutDescriptor", "GpuBlendDescriptor", "GpuBlendFactor", @@ -167,8 +169,8 @@ web-sys = { version = "0.3.36", features = [ "HtmlCanvasElement", "Window", ]} -js-sys = "0.3.36" -wasm-bindgen-futures = "0.4.9" +js-sys = "0.3.37" +wasm-bindgen-futures = "0.4.10" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] console_error_panic_hook = "0.1.6" diff --git a/src/backend/web.rs b/src/backend/web.rs index f067ac8da..42fa91474 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -132,18 +132,18 @@ pub(crate) fn create_bind_group_layout( _ => web_sys::GpuTextureViewDimension::N2d, }; - let mut mapped_binding = web_sys::GpuBindGroupLayoutBinding::new( + let mut mapped_entry = web_sys::GpuBindGroupLayoutEntry::new( bind.binding, mapped_type, bind.visibility.bits(), ); - mapped_binding.has_dynamic_offset(mapped_dynamic); - mapped_binding.multisampled(mapped_multisampled); - mapped_binding.texture_dimension(mapped_view_dimension); + mapped_entry.has_dynamic_offset(mapped_dynamic); + mapped_entry.multisampled(mapped_multisampled); + mapped_entry.view_dimension(mapped_view_dimension); // TODO: Texture component type, storage texture format - mapped_binding + mapped_entry }) .collect::(); @@ -152,7 +152,7 @@ pub(crate) fn create_bind_group_layout( } pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) -> BindGroupId { - let mapped_bindings = desc + let mapped_entries = desc .bindings .iter() .map(|binding| { @@ -172,11 +172,11 @@ pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) - } }; - web_sys::GpuBindGroupBinding::new(binding.binding, &mapped_resource) + web_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource) }) .collect::(); - let mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_bindings, &desc.layout.id); + let mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &desc.layout.id); device.create_bind_group(&mapped_desc) } @@ -500,8 +500,8 @@ fn map_texture_view_dimension( } fn map_buffer_copy_view(view: crate::BufferCopyView<'_>) -> web_sys::GpuBufferCopyView { - let mut mapped = - web_sys::GpuBufferCopyView::new(&view.buffer.id, view.rows_per_image, view.bytes_per_row); + let mut mapped = web_sys::GpuBufferCopyView::new(&view.buffer.id, view.bytes_per_row); + mapped.rows_per_image(view.rows_per_image); mapped.offset(view.offset as f64); mapped } @@ -678,17 +678,14 @@ pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) - } pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { - let extent = map_extent_3d(desc.size); let mut mapped_desc = web_sys::GpuTextureDescriptor::new( map_texture_format(desc.format), - &extent, + &map_extent_3d(desc.size), desc.usage.bits(), ); - mapped_desc.array_layer_count(desc.array_layer_count); mapped_desc.dimension(map_texture_dimension(desc.dimension)); mapped_desc.mip_level_count(desc.mip_level_count); mapped_desc.sample_count(desc.sample_count); - mapped_desc.array_layer_count(desc.array_layer_count); device.create_texture(&mapped_desc) } @@ -1009,7 +1006,7 @@ pub(crate) fn render_pass_draw( vertices: Range, instances: Range, ) { - render_pass.draw( + render_pass.draw_with_instance_count_and_first_vertex_and_first_instance( vertices.end - vertices.start, instances.end - instances.start, vertices.start, @@ -1023,13 +1020,14 @@ pub(crate) fn render_pass_draw_indexed( base_vertex: i32, instances: Range, ) { - render_pass.draw_indexed( - indices.end - indices.start, - instances.end - instances.start, - indices.start, - base_vertex, - instances.start, - ); + 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_end_pass(render_pass: &RenderPassEncoderId) { diff --git a/src/lib.rs b/src/lib.rs index e95436d07..c18514b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1201,7 +1201,8 @@ impl SwapChain { match backend::swap_chain_get_next_texture(&self.id) { Some(id) => Ok(SwapChainOutput { view: TextureView { id, owned: false }, - swap_chain_id: self.id, + // TODO: Remove from web backend + swap_chain_id: self.id.clone(), }), None => Err(TimeOut), } From 00dcab11104f08dcfe82f632e15af86f5cea76de Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Mon, 6 Apr 2020 22:12:13 -0230 Subject: [PATCH 09/17] Get shadow mostly working Some of the implementation is still missing in Canary --- examples/shadow/bake.frag.spv | Bin 0 -> 180 bytes examples/shadow/bake.vert.spv | Bin 0 -> 1312 bytes examples/shadow/forward.frag.spv | Bin 0 -> 4612 bytes examples/shadow/forward.vert.spv | Bin 0 -> 1948 bytes examples/shadow/main.rs | 36 ++++++++++++++++--------------- src/backend/web.rs | 10 +++------ 6 files changed, 22 insertions(+), 24 deletions(-) create mode 100644 examples/shadow/bake.frag.spv create mode 100644 examples/shadow/bake.vert.spv create mode 100644 examples/shadow/forward.frag.spv create mode 100644 examples/shadow/forward.vert.spv diff --git a/examples/shadow/bake.frag.spv b/examples/shadow/bake.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..dbd293829162151b6ac404866890ba941bf278c9 GIT binary patch literal 180 zcmZQ(Qf6mhU}Rut;9y{5fB-=TCZGrd12<3vM7#S0`{)&yr0AKL8h}*rF))KDupB=F zD+3FV4Z^vJnRy^_0iZmH4>TCaVg{PVz;Fm?5J)AmI$@v;NKO%~iUG_v1F8avfz*Kj S$fh4a5mq4m7igme5CZ_i&0=pd zq1_PWtW&96M!$@+LjO8?A8qs3!v2BSfw9c#(0i5Kr+jppR_AF|r{4)dXSlw(a+1|q zS%CH$zB3y9^CW{4B2>HU$xDcChb?I;(%UN2iI+O-Jv`D!*7@?mZnHm3dh;J#I@v z?BT7(J{5Zm;$4(zBW4dVZNz-DnD2|2zL@tOlF^rUJbxoDQ)YbMtQ^ct(<5Sj*&C=m z#OyJohwtARhon*PA(4j&qIUTx0LpN0m*g ztQ*k7d+2ZDb+HM$1K-)k-f4^fE7$~89?jz`_|DrSzKWGE*2nX%v4=S|fp-n-O!F`1 zvA;7h=d589)V|I!UPqN(VsCC=_v%yS?ZxbU6YE>V>}~A#5Ho*k?#u5Jd%xJv{O&w2 zZoRR0wBDQC!@7^N<$F)}6qj%8oyE-it$dgJKI0qO$67`FFNnW`H~e?EE!2H|?@O5O zwvCEM&K-PrTISp(7XM8jYxYp<#O>pL3F>YovHJ|r1pP&g@jhyuxU<~-0qVDL)+;tP LmiLbrK1crphS^Qc literal 0 HcmV?d00001 diff --git a/examples/shadow/forward.frag.spv b/examples/shadow/forward.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..bc571bb9bcf4478d82df32a94a8ad2ad9b28cdd0 GIT binary patch literal 4612 zcmZ9O`*T&*6~_-IH&8$X1yqVL5g%9)wFMCo!4VT;d|fWd4Y~FvC(XS9-x42) z+A3P#wbl42#oEsF_s(?2pY`AIHKXI_bM9XDkUjHdt@T}xz4qGs+|YH-n)z8aFPon& z$o`#m*0Sszm@HeA^%Qc~;NHRYt@gQ=^i(LF+88JKG62 zkxz}2R^)l6qt)Gs>GXdWTy6Ytqdw6lraRk>J)Af5DP)DS4j~(b-ksfx+||qv*P1P+ z+Edt2*>v?lqke2?Dt{>ZiFdBk)!ozMgC4e3)Vkkbmivq$3m38nM%y)6RFUh%SZ6YK zzfO#^|9^ko8C!PdiL~zTk>$GDu5w+ua$W7Yb$tY>_2Nv$qkx;P2HTm>B%I5=pFnnQ zUAt1epQF{GywzwoatavpEHRU{soHqTIdPU<$xdQ7i}x|o=-fefb_#B!F*-Wksuy)< zvGeM_+SHMH8?~r=8hOV=yU~86Gf!>TboHKmsyR~3Pp)$>w--0;{hleJxL11fU0pWE z?#FgE`h8<%zqWqAB>ldxUf>$Q_Pw|&V1R$aUQ@7VbADS(OL6vT+;*^w9n6hBd`IOz zwC%&?zH#TVZ`kF1gDdYVSKim2<=od?zXi|f-SvP&z?gU&QQv;$qF>)gxnFJjbua79 zAe~#-v&lvj=ZU+F^MoDy*+X#U^T=7#xb48c~C>ub@}Y z-3QF+!*)L*zmmuW1)pUX;`HF%2VB=Adl|YtSmS)8 z@#g7Qt^ls(bh#24{HlUIzRg}PK{wWOguf4+wJq%aj{U!N^k%%X=>zh{*xNc_jf*+O zMx-_8m4I5D_e$`#6YnCfdFH z>o;nQynWnn#Z}o|iZTzz(+xed;+3tU`WSf5~*<0yv2l}tU z09c&f?Vae(&Rl)Baw^XtuWtXo?a1GQE~n44Xsde;-}<-A_EN*^ISk zc`ns{3*8v`nBCjx&d&ReS-gWTAG3HDT~4@S7B8n+`~}V!>&H3YL-)I%Nily#mowJh z^~w32???LWD9!Id{tfs(j+3wbe&W1i^YzJ+zRB^>~k^F z!*98uy-`v~K&6VE{yni|0>W^Zecl-bV literal 0 HcmV?d00001 diff --git a/examples/shadow/forward.vert.spv b/examples/shadow/forward.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..9bd02dfae3d0d07909d3d1c8ba557d06fb346247 GIT binary patch literal 1948 zcmZ9MX-^YT6oxO|Kv5Bq#jQ@MJ6bnDlxQSq%7+GPNbuvNN`qsj9W$Nc@-O^L{wlwi z_&zgpO64|Z&N=Tr+kNkaVxd0i+=QESQ*P5`Yt|KnI5+JoscyFRT8%++&{$o2M8r8) zOaqCTai>$eq;Ibi^l?r}s*;x44Xm^0FE z`$>@eaGIp-@L5CuLllPxBaAtQ{&o~bv7(BaFI&`Qj?RB|%jsN4emfcjNf2pb=3&kb zm~>f{A4(sFe!Cm*cjKh{U9Qj!$7at8RKyug{nn17FWG$H86UgDUMo2KoD7s- zbZcp}YN(do573T$s8NlGyFl;d19k z^Mc`5_2qsHquyEh^aZCLKKBMTFMmeblT1ifa-aKWkv}U1f$LF1}Z~43vYBKMAX-~ppUi=4=tY^tJWWx_wRTgi+ zIAafCPX5u+gMIqoXaC1Co}b94w(-iH!vQ$?tJ3fSPCh<&54Ntij3$>wpFcu)dt3U# z=>-Y*0M{=SN$oia_yf@vm_fFVV%smB#a#D zSk7hH;0Vid+TxTqa7Dr_)FzfQaOa+c`=${)@peo0N#1S?1GgFO%I3|`10L^5!03U$A|VHy9=ta&>#;7IyBxopXY#@6L5&Rw cc~(); - let temp_buf_data = device.create_buffer_mapped(&wgpu::BufferDescriptor { + let mut temp_buf_data = device.create_buffer_mapped(&wgpu::BufferDescriptor { size: (self.entities.len() * size) as u64, usage: wgpu::BufferUsage::COPY_SRC, label: None, @@ -731,7 +733,7 @@ impl framework::Example for Example { for (entity, slot) in self .entities .iter_mut() - .zip(temp_buf_data.data.chunks_exact_mut(size)) + .zip(temp_buf_data.data().chunks_exact_mut(size)) { if entity.rotation_speed != 0.0 { let rotation = @@ -766,7 +768,7 @@ impl framework::Example for Example { self.lights_are_dirty = false; let size = mem::size_of::(); let total_size = size * self.lights.len(); - let temp_buf_data = device.create_buffer_mapped(&wgpu::BufferDescriptor { + let mut temp_buf_data = device.create_buffer_mapped(&wgpu::BufferDescriptor { size: total_size as u64, usage: wgpu::BufferUsage::COPY_SRC, label: None, @@ -775,7 +777,7 @@ impl framework::Example for Example { for (light, slot) in self .lights .iter() - .zip(temp_buf_data.data.chunks_exact_mut(size)) + .zip(temp_buf_data.data().chunks_exact_mut(size)) { slot.copy_from_slice(bytemuck::bytes_of(&light.to_raw())); } diff --git a/src/backend/web.rs b/src/backend/web.rs index 42fa91474..b8018521a 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -103,14 +103,10 @@ pub(crate) fn create_bind_group_layout( } => bt::StorageBuffer, BindingType::StorageBuffer { readonly: true, .. } => bt::ReadonlyStorageBuffer, BindingType::Sampler { comparison: false } => bt::Sampler, - BindingType::Sampler { .. } => unimplemented!(), // TODO: bt::ComparisonSampler, + BindingType::Sampler { .. } => bt::ComparisonSampler, BindingType::SampledTexture { .. } => bt::SampledTexture, - BindingType::StorageTexture { readonly: true, .. } => { - unimplemented!() // TODO: bt::ReadonlyStorageTexture - } - BindingType::StorageTexture { .. } => { - unimplemented!() // TODO: bt::WriteonlyStorageTexture - } + BindingType::StorageTexture { readonly: true, .. } => bt::ReadonlyStorageTexture, + BindingType::StorageTexture { .. } => bt::WriteonlyStorageTexture, }; let mapped_dynamic = match bind.ty { From f20a17b43a7745c0f5d7ebe9f21e257484662f9f Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sun, 12 Apr 2020 14:42:07 -0230 Subject: [PATCH 10/17] Allow describe example to run on the web --- examples/describe/main.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/examples/describe/main.rs b/examples/describe/main.rs index d035df44a..c2b573960 100644 --- a/examples/describe/main.rs +++ b/examples/describe/main.rs @@ -1,11 +1,4 @@ /// This example shows how to describe the adapter in use. -#[cfg(not(target_arch = "wasm32"))] -fn main() { - env_logger::init(); - futures::executor::block_on(run()); -} - -#[cfg(not(target_arch = "wasm32"))] async fn run() { let adapter = wgpu::Adapter::request( &wgpu::RequestAdapterOptions { @@ -17,5 +10,25 @@ async fn run() { .await .unwrap(); + #[cfg(not(target_arch = "wasm32"))] println!("{:?}", adapter.get_info()) } + +fn main() { + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init(); + futures::executor::block_on(run()); + } + #[cfg(target_arch = "wasm32")] + { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + wasm_bindgen_futures::spawn_local(run()); + } +} + +#[cfg(target_arch = "wasm32")] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] +pub fn wasm_main() { + main(); +} From a2c24c737d61b51a3c51787da31500c45989f3aa Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Mon, 13 Apr 2020 20:56:01 -0230 Subject: [PATCH 11/17] Switch event loop back to `ControlFlow::Poll` --- examples/framework.rs | 3 +-- examples/hello-triangle/main.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/framework.rs b/examples/framework.rs index a6702c7ec..444531806 100644 --- a/examples/framework.rs +++ b/examples/framework.rs @@ -94,8 +94,7 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { *control_flow = if cfg!(feature = "metal-auto-capture") { ControlFlow::Exit } else { - // TODO: ControlFlow::Poll - ControlFlow::Wait + ControlFlow::Poll }; match event { event::Event::MainEventsCleared => window.request_redraw(), diff --git a/examples/hello-triangle/main.rs b/examples/hello-triangle/main.rs index a2f143880..5ad4d016f 100644 --- a/examples/hello-triangle/main.rs +++ b/examples/hello-triangle/main.rs @@ -93,8 +93,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); event_loop.run(move |event, _, control_flow| { - // TODO: ControlFlow::Poll; - *control_flow = ControlFlow::Wait; + *control_flow = ControlFlow::Poll; match event { Event::MainEventsCleared => window.request_redraw(), Event::WindowEvent { From db6ab34d6024cfce7bfdc156789c14ee7ab64e74 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Mon, 13 Apr 2020 21:38:21 -0230 Subject: [PATCH 12/17] Combine `wasm_main`/`main` and add `console_log` --- examples/capture/main.rs | 25 +++++++++++-------------- examples/describe/main.rs | 7 +------ examples/framework.rs | 1 + examples/hello-compute/main.rs | 25 +++++++++++-------------- examples/hello-triangle/main.rs | 7 +------ examples/msaa-line/main.rs | 6 ------ 6 files changed, 25 insertions(+), 46 deletions(-) diff --git a/examples/capture/main.rs b/examples/capture/main.rs index 15296b601..5f3cc8429 100644 --- a/examples/capture/main.rs +++ b/examples/capture/main.rs @@ -114,19 +114,16 @@ async fn run() { } } -#[cfg(target_arch = "wasm32")] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] -pub fn wasm_main() { - console_log::init().expect("could not initialize log"); - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - wasm_bindgen_futures::spawn_local(run()); -} - -#[cfg(target_arch = "wasm32")] -fn main() {} - -#[cfg(not(target_arch = "wasm32"))] fn main() { - env_logger::init(); - futures::executor::block_on(run()); + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init(); + futures::executor::block_on(run()); + } + #[cfg(target_arch = "wasm32")] + { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); + wasm_bindgen_futures::spawn_local(run()); + } } diff --git a/examples/describe/main.rs b/examples/describe/main.rs index c2b573960..2b5d75939 100644 --- a/examples/describe/main.rs +++ b/examples/describe/main.rs @@ -23,12 +23,7 @@ fn main() { #[cfg(target_arch = "wasm32")] { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); wasm_bindgen_futures::spawn_local(run()); } } - -#[cfg(target_arch = "wasm32")] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] -pub fn wasm_main() { - main(); -} diff --git a/examples/framework.rs b/examples/framework.rs index 444531806..96e96e1eb 100644 --- a/examples/framework.rs +++ b/examples/framework.rs @@ -159,6 +159,7 @@ pub fn run(title: &str) { #[cfg(target_arch = "wasm32")] { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); use winit::platform::web::WindowExtWebSys; // On wasm, append the canvas to the document body web_sys::window() diff --git a/examples/hello-compute/main.rs b/examples/hello-compute/main.rs index 3851ecf4b..2afafe2ec 100644 --- a/examples/hello-compute/main.rs +++ b/examples/hello-compute/main.rs @@ -124,21 +124,18 @@ async fn execute_gpu(numbers: Vec) -> Vec { } } -#[cfg(target_arch = "wasm32")] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] -pub fn wasm_main() { - console_log::init().expect("could not initialize log"); - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - wasm_bindgen_futures::spawn_local(run()); -} - -#[cfg(target_arch = "wasm32")] -fn main() {} - -#[cfg(not(target_arch = "wasm32"))] fn main() { - env_logger::init(); - futures::executor::block_on(run()); + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init(); + futures::executor::block_on(run()); + } + #[cfg(target_arch = "wasm32")] + { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); + wasm_bindgen_futures::spawn_local(run()); + } } #[cfg(test)] diff --git a/examples/hello-triangle/main.rs b/examples/hello-triangle/main.rs index 5ad4d016f..ad7a996d4 100644 --- a/examples/hello-triangle/main.rs +++ b/examples/hello-triangle/main.rs @@ -137,12 +137,6 @@ async fn run(event_loop: EventLoop<()>, window: Window) { }); } -#[cfg(target_arch = "wasm32")] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] -pub fn wasm_main() { - main(); -} - fn main() { let event_loop = EventLoop::new(); let window = winit::window::Window::new(&event_loop).unwrap(); @@ -154,6 +148,7 @@ fn main() { #[cfg(target_arch = "wasm32")] { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); use winit::platform::web::WindowExtWebSys; // On wasm, append the canvas to the document body web_sys::window() diff --git a/examples/msaa-line/main.rs b/examples/msaa-line/main.rs index ad63c9076..25368b9b9 100644 --- a/examples/msaa-line/main.rs +++ b/examples/msaa-line/main.rs @@ -268,12 +268,6 @@ impl framework::Example for Example { } } -#[cfg(target_arch = "wasm32")] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))] -pub fn wasm_main() { - main(); -} - fn main() { framework::run::("msaa-line"); } From ab1bb53ab2c6a499a7c7324ababdb95541f6177e Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Tue, 14 Apr 2020 00:03:36 -0230 Subject: [PATCH 13/17] Expose more functions on the web backend --- src/backend/native.rs | 154 +++++++++++++++++++++++++++++- src/backend/web.rs | 113 ++++++++++++++++++++-- src/lib.rs | 217 +++++++++++++----------------------------- 3 files changed, 326 insertions(+), 158 deletions(-) diff --git a/src/backend/native.rs b/src/backend/native.rs index c0f757eaa..a7d7b57a3 100644 --- a/src/backend/native.rs +++ b/src/backend/native.rs @@ -351,6 +351,10 @@ pub(crate) fn buffer_unmap(buffer: &BufferId) { wgn::wgpu_buffer_unmap(*buffer); } +pub(crate) fn buffer_drop(buffer: &BufferId) { + wgn::wgpu_buffer_destroy(*buffer); +} + pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { let owned_label = OwnedLabel::new(desc.label.as_deref()); crate::Buffer { @@ -445,6 +449,20 @@ pub(crate) fn command_encoder_copy_texture_to_buffer( ); } +pub(crate) fn command_encoder_copy_texture_to_texture( + command_encoder: &CommandEncoderId, + source: crate::TextureCopyView, + destination: crate::TextureCopyView, + copy_size: wgt::Extent3d, +) { + wgn::wgpu_command_encoder_copy_texture_to_texture( + *command_encoder, + &map_texture_copy_view(source), + &map_texture_copy_view(destination), + copy_size, + ); +} + pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { unsafe { wgn::wgpu_command_encoder_begin_compute_pass(*command_encoder, None) } } @@ -554,10 +572,50 @@ pub(crate) fn buffer_map_read( future } +pub(crate) fn buffer_map_write( + buffer: &crate::Buffer, + start: wgt::BufferAddress, + size: wgt::BufferAddress, +) -> impl Future> { + let (future, completion) = native_gpu_future::new_gpu_future(buffer.id, size); + + extern "C" fn buffer_map_write_future_wrapper( + status: wgc::resource::BufferMapAsyncStatus, + data: *mut u8, + user_data: *mut u8, + ) { + let completion = + unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; + let (buffer_id, size) = completion.get_buffer_info(); + + if let wgc::resource::BufferMapAsyncStatus::Success = status { + completion.complete(Ok(crate::BufferWriteMapping { + detail: BufferWriteMappingDetail { + data, + size: size as usize, + buffer_id, + }, + })); + } else { + completion.complete(Err(crate::BufferAsyncErr)); + } + } + + wgn::wgpu_buffer_map_write_async( + buffer.id, + start, + size, + buffer_map_write_future_wrapper, + completion.to_raw() as _, + ); + + future +} + pub(crate) struct BufferReadMappingDetail { + pub(crate) buffer_id: BufferId, data: *const u8, size: usize, - pub(crate) buffer_id: BufferId, } impl BufferReadMappingDetail { @@ -566,6 +624,18 @@ impl BufferReadMappingDetail { } } +pub(crate) struct BufferWriteMappingDetail { + pub(crate) buffer_id: BufferId, + data: *mut u8, + size: usize, +} + +impl BufferWriteMappingDetail { + pub(crate) fn as_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data as *mut u8, self.size) } + } +} + pub(crate) fn device_create_surface( window: &W, ) -> SurfaceId { @@ -633,6 +703,12 @@ pub(crate) fn render_pass_set_pipeline( } } +pub(crate) fn render_pass_set_blend_color(render_pass: &RenderPassEncoderId, color: wgt::Color) { + unsafe { + wgn::wgpu_render_pass_set_blend_color(render_pass.as_mut().unwrap(), &color); + } +} + pub(crate) fn render_pass_set_bind_group( render_pass: &RenderPassEncoderId, index: u32, @@ -684,6 +760,46 @@ pub(crate) fn render_pass_set_vertex_buffer<'a>( }; } +pub(crate) fn render_pass_set_scissor_rect( + render_pass: &RenderPassEncoderId, + x: u32, + y: u32, + width: u32, + height: u32, +) { + unsafe { + wgn::wgpu_render_pass_set_scissor_rect(render_pass.as_mut().unwrap(), 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, +) { + unsafe { + wgn::wgpu_render_pass_set_viewport( + render_pass.as_mut().unwrap(), + x, + y, + width, + height, + min_depth, + max_depth, + ); + } +} + +pub(crate) fn render_pass_set_stencil_reference(render_pass: &RenderPassEncoderId, reference: u32) { + unsafe { + wgn::wgpu_render_pass_set_stencil_reference(render_pass.as_mut().unwrap(), reference); + } +} + pub(crate) fn render_pass_draw( render_pass: &RenderPassEncoderId, vertices: Range, @@ -718,6 +834,34 @@ pub(crate) fn render_pass_draw_indexed( } } +pub(crate) fn render_pass_draw_indirect<'a>( + render_pass: &RenderPassEncoderId, + indirect_buffer: &'a crate::Buffer, + indirect_offset: wgt::BufferAddress, +) { + unsafe { + wgn::wgpu_render_pass_draw_indirect( + render_pass.as_mut().unwrap(), + indirect_buffer.id, + indirect_offset, + ); + } +} + +pub(crate) fn render_pass_draw_indexed_indirect<'a>( + render_pass: &RenderPassEncoderId, + indirect_buffer: &'a crate::Buffer, + indirect_offset: wgt::BufferAddress, +) { + unsafe { + wgn::wgpu_render_pass_draw_indexed_indirect( + render_pass.as_mut().unwrap(), + indirect_buffer.id, + indirect_offset, + ); + } +} + pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { unsafe { wgn::wgpu_render_pass_end_pass(*render_pass); @@ -731,6 +875,14 @@ pub(crate) fn texture_create_view( wgn::wgpu_texture_create_view(*texture, desc) } +pub(crate) fn texture_drop(texture: &TextureId) { + wgn::wgpu_texture_destroy(*texture); +} + +pub(crate) fn texture_view_drop(texture_view: &TextureViewId) { + wgn::wgpu_texture_view_destroy(*texture_view); +} + pub(crate) fn swap_chain_present(swap_chain: &SwapChainId) { wgn::wgpu_swap_chain_present(*swap_chain); } diff --git a/src/backend/web.rs b/src/backend/web.rs index b8018521a..ec3b55f03 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -533,6 +533,10 @@ fn map_address_mode(mode: wgt::AddressMode) -> web_sys::GpuAddressMode { } } +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, @@ -665,6 +669,10 @@ 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 mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); crate::Buffer { @@ -752,6 +760,19 @@ pub(crate) fn command_encoder_copy_texture_to_buffer( ); } +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 mapped_desc = web_sys::GpuComputePassDescriptor::new(); command_encoder.begin_compute_pass_with_descriptor(&mapped_desc) @@ -831,6 +852,26 @@ pub(crate) async fn buffer_map_read( }) } +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, mapped: Vec, @@ -842,6 +883,17 @@ impl BufferReadMappingDetail { } } +pub(crate) struct BufferWriteMappingDetail { + pub(crate) buffer_id: BufferId, + mapped: Vec, +} + +impl BufferWriteMappingDetail { + pub(crate) fn as_slice(&mut self) -> &mut [u8] { + &mut self.mapped[..] + } +} + pub(crate) fn device_create_surface( window: &W, ) -> SurfaceId { @@ -901,12 +953,7 @@ pub(crate) fn command_encoder_begin_render_pass<'a>( let mut mapped_color_attachment = web_sys::GpuRenderPassColorAttachmentDescriptor::new( &ca.attachment.id, &match ca.load_op { - wgt::LoadOp::Clear => { - let color = ca.clear_color; - let mapped_color = - web_sys::GpuColorDict::new(color.a, color.b, color.g, color.r); - wasm_bindgen::JsValue::from(mapped_color) - } + wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(map_color(ca.clear_color)), wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), }, ); @@ -952,6 +999,10 @@ pub(crate) fn render_pass_set_pipeline( 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, @@ -997,6 +1048,32 @@ pub(crate) fn render_pass_set_vertex_buffer<'a>( ); } +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, @@ -1026,6 +1103,22 @@ pub(crate) fn render_pass_draw_indexed( ); } +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(); } @@ -1050,6 +1143,14 @@ pub(crate) fn 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 swap_chain_present(_swap_chain: &SwapChainId) { // Swapchain is presented automatically } diff --git a/src/lib.rs b/src/lib.rs index c18514b14..38a535d3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -528,26 +528,23 @@ impl Surface { } } - /* - #[cfg(any(target_os = "ios", target_os = "macos"))] - pub fn create_surface_from_core_animation_layer(layer: *mut std::ffi::c_void) -> Self { - Surface { - id: wgn::wgpu_create_surface_from_metal_layer(layer), - } + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub fn create_surface_from_core_animation_layer(layer: *mut std::ffi::c_void) -> Self { + Surface { + id: wgn::wgpu_create_surface_from_metal_layer(layer), } - */ + } } impl Adapter { - /* - /// Retrieves all available [`Adapter`]s that match the given backends. - pub fn enumerate(backends: BackendBit) -> Vec { - wgn::wgpu_enumerate_adapters(backends) - .into_iter() - .map(|id| Adapter { id }) - .collect() - } - */ + /// Retrieves all available [`Adapter`]s that match the given backends. + #[cfg(not(target_arch = "wasm32"))] + pub fn enumerate(backends: BackendBit) -> Vec { + wgn::wgpu_enumerate_adapters(backends) + .into_iter() + .map(|id| Adapter { id }) + .collect() + } /// Retrieves an [`Adapter`] which matches the given options. /// @@ -723,11 +720,8 @@ impl Drop for BufferReadMapping { } } -/* pub struct BufferWriteMapping { - data: *mut u8, - size: usize, - buffer_id: wgc::id::BufferId, + detail: backend::BufferWriteMappingDetail, } unsafe impl Send for BufferWriteMapping {} @@ -735,16 +729,15 @@ unsafe impl Sync for BufferWriteMapping {} impl BufferWriteMapping { pub fn as_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.data as *mut u8, self.size) } + self.detail.as_slice() } } impl Drop for BufferWriteMapping { fn drop(&mut self) { - wgn::wgpu_buffer_unmap(self.buffer_id); + backend::buffer_unmap(&self.detail.buffer_id); } } -*/ impl Buffer { /// Map the buffer for reading. The result is returned in a future. @@ -763,63 +756,29 @@ impl Buffer { backend::buffer_map_read(self, start, size) } - /* - /// Map the buffer for writing. The result is returned in a future. - /// - /// See the documentation of (map_read)[#method.map_read] for more information about - /// how to run this future. - pub fn map_write( - &self, - start: BufferAddress, - size: BufferAddress, - ) -> impl Future> { - let (future, completion) = native_gpu_future::new_gpu_future(self.id, size); - - extern "C" fn buffer_map_write_future_wrapper( - status: wgc::resource::BufferMapAsyncStatus, - data: *mut u8, - user_data: *mut u8, - ) { - let completion = - unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; - let (buffer_id, size) = completion.get_buffer_info(); - - if let wgc::resource::BufferMapAsyncStatus::Success = status { - completion.complete(Ok(BufferWriteMapping { - data, - size: size as usize, - buffer_id, - })); - } else { - completion.complete(Err(BufferAsyncErr)); - } - } - - wgn::wgpu_buffer_map_write_async( - self.id, - start, - size, - buffer_map_write_future_wrapper, - completion.to_raw() as _, - ); - - future - } + /// Map the buffer for writing. The result is returned in a future. + /// + /// See the documentation of (map_read)[#method.map_read] for more information about + /// how to run this future. + pub fn map_write( + &self, + start: BufferAddress, + size: BufferAddress, + ) -> impl Future> + '_ { + backend::buffer_map_write(self, start, size) + } - /// Flushes any pending write operations and unmaps the buffer from host memory. - pub fn unmap(&self) { - wgn::wgpu_buffer_unmap(self.id); - } - */ + /// Flushes any pending write operations and unmaps the buffer from host memory. + pub fn unmap(&self) { + backend::buffer_unmap(&self.id); + } } -/* impl Drop for Buffer { fn drop(&mut self) { - wgn::wgpu_buffer_destroy(self.id); + backend::buffer_drop(&self.id); } } -*/ impl Texture { /// Creates a view of this texture. @@ -839,11 +798,10 @@ impl Texture { } } -/* impl Drop for Texture { fn drop(&mut self) { if self.owned { - wgn::wgpu_texture_destroy(self.id); + backend::texture_drop(&self.id); } } } @@ -851,11 +809,10 @@ impl Drop for Texture { impl Drop for TextureView { fn drop(&mut self) { if self.owned { - wgn::wgpu_texture_view_destroy(self.id); + backend::texture_view_drop(&self.id); } } } -*/ impl CommandEncoder { /// Finishes recording and returns a [`CommandBuffer`] that can be submitted for execution. @@ -927,22 +884,15 @@ impl CommandEncoder { backend::command_encoder_copy_texture_to_buffer(&self.id, source, destination, copy_size); } - /* - /// Copy data from one texture to another. - pub fn copy_texture_to_texture( - &mut self, - source: TextureCopyView, - destination: TextureCopyView, - copy_size: Extent3d, - ) { - wgn::wgpu_command_encoder_copy_texture_to_texture( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); - } - */ + /// Copy data from one texture to another. + pub fn copy_texture_to_texture( + &mut self, + source: TextureCopyView, + destination: TextureCopyView, + copy_size: Extent3d, + ) { + backend::command_encoder_copy_texture_to_texture(&self.id, source, destination, copy_size); + } } impl<'a> RenderPass<'a> { @@ -963,14 +913,9 @@ impl<'a> RenderPass<'a> { backend::render_pass_set_pipeline(&self.id, &pipeline.id) } - /* - pub fn set_blend_color(&mut self, color: Color) { - unsafe { - wgn::wgpu_render_pass_set_blend_color(self.id.as_mut().unwrap(), &color); - } - } - } - */ + pub fn set_blend_color(&mut self, color: Color) { + backend::render_pass_set_blend_color(&self.id, color) + } /// Sets the active index buffer. /// @@ -1011,42 +956,26 @@ impl<'a> RenderPass<'a> { backend::render_pass_set_vertex_buffer(&self.id, slot, buffer, offset, size) } - /* - /// Sets the scissor region. - /// - /// Subsequent draw calls will discard any fragments that fall outside this region. - pub fn set_scissor_rect(&mut self, x: u32, y: u32, w: u32, h: u32) { - unsafe { - wgn::wgpu_render_pass_set_scissor_rect(self.id.as_mut().unwrap(), x, y, w, h); - } - } + /// Sets the scissor region. + /// + /// Subsequent draw calls will discard any fragments that fall outside this region. + pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) { + backend::render_pass_set_scissor_rect(&self.id, x, y, width, height); + } - /// Sets the viewport region. - /// - /// Subsequent draw calls will draw any fragments in this region. - pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) { - unsafe { - wgn::wgpu_render_pass_set_viewport( - self.id.as_mut().unwrap(), - x, - y, - w, - h, - min_depth, - max_depth, - ); - } - } + /// Sets the viewport region. + /// + /// Subsequent draw calls will draw any fragments in this region. + pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) { + backend::render_pass_set_viewport(&self.id, x, y, w, h, min_depth, max_depth); + } - /// Sets the stencil reference. - /// - /// Subsequent stencil tests will test against this value. - pub fn set_stencil_reference(&mut self, reference: u32) { - unsafe { - wgn::wgpu_render_pass_set_stencil_reference(self.id.as_mut().unwrap(), reference); - } - } - */ + /// Sets the stencil reference. + /// + /// Subsequent stencil tests will test against this value. + pub fn set_stencil_reference(&mut self, reference: u32) { + backend::render_pass_set_stencil_reference(&self.id, reference); + } /// Draws primitives from the active vertex buffer(s). /// @@ -1063,7 +992,6 @@ impl<'a> RenderPass<'a> { backend::render_pass_draw_indexed(&self.id, indices, base_vertex, instances); } - /* /// Draws primitives from the active vertex buffer(s) based on the contents of the `indirect_buffer`. /// /// The active vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. @@ -1080,13 +1008,7 @@ impl<'a> RenderPass<'a> { /// } /// ``` pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress) { - unsafe { - wgn::wgpu_render_pass_draw_indirect( - self.id.as_mut().unwrap(), - indirect_buffer.id, - indirect_offset, - ); - } + backend::render_pass_draw_indirect(&self.id, indirect_buffer, indirect_offset); } /// Draws indexed primitives using the active index buffer and the active vertex buffers, @@ -1112,15 +1034,8 @@ impl<'a> RenderPass<'a> { indirect_buffer: &'a Buffer, indirect_offset: BufferAddress, ) { - unsafe { - wgn::wgpu_render_pass_draw_indexed_indirect( - self.id.as_mut().unwrap(), - indirect_buffer.id, - indirect_offset, - ); - } + backend::render_pass_draw_indexed_indirect(&self.id, indirect_buffer, indirect_offset); } - */ } impl<'a> Drop for RenderPass<'a> { From ab86d7c7e645df9b3d2a0328656c11a7a31af1b1 Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Fri, 17 Apr 2020 00:06:07 -0230 Subject: [PATCH 14/17] Fix some remaining TODOs in the web backend --- Cargo.toml | 2 +- src/backend/native.rs | 36 ++++++++++++-- src/backend/web.rs | 111 +++++++++++++++++++++++++----------------- src/lib.rs | 23 ++------- 4 files changed, 104 insertions(+), 68 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a75ea4be0..06971ca61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ parking_lot = "0.10" [dev-dependencies] cgmath = "0.17" -#glsl-to-spirv = "0.1" log = "0.4" png = "0.15" winit = { version = "0.22.1", features = ["web-sys"] } @@ -154,6 +153,7 @@ web-sys = { version = "0.3.37", features = [ "GpuSwapChainDescriptor", "GpuTexture", "GpuTextureAspect", + "GpuTextureComponentType", "GpuTextureCopyView", "GpuTextureDescriptor", "GpuTextureDimension", diff --git a/src/backend/native.rs b/src/backend/native.rs index a7d7b57a3..b6f46bd0b 100644 --- a/src/backend/native.rs +++ b/src/backend/native.rs @@ -650,8 +650,32 @@ pub(crate) fn device_create_swap_chain( wgn::wgpu_device_create_swap_chain(*device, *surface, desc) } -pub(crate) fn swap_chain_get_next_texture(swap_chain: &SwapChainId) -> Option { - wgn::wgpu_swap_chain_get_next_texture(*swap_chain).view_id +pub(crate) fn device_drop(device: &DeviceId) { + #[cfg(not(target_arch = "wasm32"))] + wgn::wgpu_device_poll(*device, true); + //TODO: make this work in general + #[cfg(not(target_arch = "wasm32"))] + #[cfg(feature = "metal-auto-capture")] + wgn::wgpu_device_destroy(*device); +} + +pub(crate) fn swap_chain_get_next_texture( + swap_chain: &SwapChainId, +) -> Result { + match wgn::wgpu_swap_chain_get_next_texture(*swap_chain).view_id { + Some(id) => Ok(crate::SwapChainOutput { + view: crate::TextureView { id, owned: false }, + detail: SwapChainOutputDetail { + swap_chain_id: *swap_chain, + }, + }), + None => Err(crate::TimeOut), + } +} + +#[derive(Debug)] +pub(crate) struct SwapChainOutputDetail { + swap_chain_id: SwapChainId, } pub(crate) fn command_encoder_begin_render_pass<'a>( @@ -883,8 +907,12 @@ pub(crate) fn texture_view_drop(texture_view: &TextureViewId) { wgn::wgpu_texture_view_destroy(*texture_view); } -pub(crate) fn swap_chain_present(swap_chain: &SwapChainId) { - wgn::wgpu_swap_chain_present(*swap_chain); +pub(crate) fn bind_group_drop(bind_group: &BindGroupId) { + wgn::wgpu_bind_group_destroy(*bind_group); +} + +pub(crate) fn swap_chain_present(swap_chain_output: &crate::SwapChainOutput) { + wgn::wgpu_swap_chain_present(swap_chain_output.detail.swap_chain_id); } pub(crate) fn device_poll(device: &DeviceId, maintain: crate::Maintain) { diff --git a/src/backend/web.rs b/src/backend/web.rs index ec3b55f03..e69f66de8 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -67,7 +67,6 @@ pub(crate) async fn request_device_and_queue( // TODO: Extensions let mut mapped_limits = web_sys::GpuLimits::new(); mapped_limits.max_bind_groups(d.limits.max_bind_groups); - // TODO: Other fields mapped_descriptor.limits(&mapped_limits); adapter.request_device_with_descriptor(&mapped_descriptor) } @@ -109,35 +108,43 @@ pub(crate) fn create_bind_group_layout( BindingType::StorageTexture { .. } => bt::WriteonlyStorageTexture, }; - let mapped_dynamic = match bind.ty { + let mut mapped_entry = web_sys::GpuBindGroupLayoutEntry::new( + bind.binding, + mapped_type, + bind.visibility.bits(), + ); + + match bind.ty { BindingType::UniformBuffer { dynamic } - | BindingType::StorageBuffer { dynamic, .. } => dynamic, - _ => false, - }; + | BindingType::StorageBuffer { dynamic, .. } => { + mapped_entry.has_dynamic_offset(dynamic); + } + _ => {} + } - let mapped_multisampled = match bind.ty { - BindingType::SampledTexture { multisampled, .. } => multisampled, - _ => false, - }; + if let BindingType::SampledTexture { multisampled, .. } = bind.ty { + mapped_entry.multisampled(multisampled); + } - let mapped_view_dimension = match bind.ty { + match bind.ty { BindingType::SampledTexture { dimension, .. } | BindingType::StorageTexture { dimension, .. } => { - map_texture_view_dimension(dimension) + mapped_entry.view_dimension(map_texture_view_dimension(dimension)); } - _ => web_sys::GpuTextureViewDimension::N2d, - }; + _ => {} + } - let mut mapped_entry = web_sys::GpuBindGroupLayoutEntry::new( - bind.binding, - mapped_type, - bind.visibility.bits(), - ); - mapped_entry.has_dynamic_offset(mapped_dynamic); - mapped_entry.multisampled(mapped_multisampled); - mapped_entry.view_dimension(mapped_view_dimension); + if let BindingType::StorageTexture { format, .. } = bind.ty { + mapped_entry.storage_texture_format(map_texture_format(format)); + } - // TODO: Texture component type, storage texture 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 }) @@ -180,7 +187,6 @@ pub(crate) fn create_pipeline_layout( device: &DeviceId, desc: &PipelineLayoutDescriptor, ) -> PipelineLayoutId { - //TODO: avoid allocation here let temp_layouts = desc .bind_group_layouts .iter() @@ -235,6 +241,16 @@ fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTexture } } +fn map_texture_component_type( + texture_component_type: wgt::TextureComponentType, +) -> web_sys::GpuTextureComponentType { + match texture_component_type { + wgt::TextureComponentType::Float => web_sys::GpuTextureComponentType::Float, + wgt::TextureComponentType::Sint => web_sys::GpuTextureComponentType::Sint, + wgt::TextureComponentType::Uint => web_sys::GpuTextureComponentType::Uint, + } +} + fn map_stage_descriptor( desc: &ProgrammableStageDescriptor, ) -> web_sys::GpuProgrammableStageDescriptor { @@ -794,9 +810,7 @@ pub(crate) fn compute_pass_set_bind_group<'a>( compute_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( index, bind_group, - // TODO: `offsets` currently requires `&mut` so we have to clone it - // here, but this should be fixed upstream in web-sys in the future - &mut offsets.to_vec(), + offsets, 0f64, offsets.len() as u32, ); @@ -929,12 +943,26 @@ pub(crate) fn device_create_swap_chain( surface.configure_swap_chain(&mapped) } -pub(crate) fn swap_chain_get_next_texture(swap_chain: &SwapChainId) -> Option { +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? - Some(swap_chain.get_current_texture().create_view()) + 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, @@ -1012,9 +1040,7 @@ pub(crate) fn render_pass_set_bind_group( render_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( index, bind_group, - // TODO: `offsets` currently requires `&mut` so we have to clone it - // here, but this should be fixed upstream in web-sys in the future - &mut offsets.to_vec(), + offsets, 0f64, offsets.len() as u32, ); @@ -1024,13 +1050,9 @@ pub(crate) fn render_pass_set_index_buffer<'a>( render_pass: &RenderPassEncoderId, buffer: &'a crate::Buffer, offset: wgt::BufferAddress, - _size: wgt::BufferAddress, + size: wgt::BufferAddress, ) { - render_pass.set_index_buffer_with_f64( - &buffer.id, - offset as f64, - // TODO: size, - ); + 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>( @@ -1038,14 +1060,9 @@ pub(crate) fn render_pass_set_vertex_buffer<'a>( slot: u32, buffer: &'a crate::Buffer, offset: wgt::BufferAddress, - _size: wgt::BufferAddress, + size: wgt::BufferAddress, ) { - render_pass.set_vertex_buffer_with_f64( - slot, - &buffer.id, - offset as f64, - // TODO: size, - ); + 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( @@ -1151,7 +1168,11 @@ pub(crate) fn texture_view_drop(_texture_view: &TextureViewId) { // Texture view is dropped automatically } -pub(crate) fn swap_chain_present(_swap_chain: &SwapChainId) { +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 } diff --git a/src/lib.rs b/src/lib.rs index 38a535d3c..af805e22c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,8 +140,7 @@ pub struct BindGroup { impl Drop for BindGroup { fn drop(&mut self) { - #[cfg(not(target_arch = "wasm32"))] - wgn::wgpu_bind_group_destroy(self.id); + backend::bind_group_drop(&self.id); } } @@ -460,7 +459,7 @@ pub struct TextureDescriptor<'a> { #[derive(Debug)] pub struct SwapChainOutput { pub view: TextureView, - swap_chain_id: backend::SwapChainId, + detail: backend::SwapChainOutputDetail, } /// A view of a buffer which can be used to copy to or from a texture. @@ -689,12 +688,7 @@ impl Device { // TODO impl Drop for Device { fn drop(&mut self) { - #[cfg(not(target_arch = "wasm32"))] - wgn::wgpu_device_poll(self.id, true); - //TODO: make this work in general - #[cfg(not(target_arch = "wasm32"))] - #[cfg(feature = "metal-auto-capture")] - wgn::wgpu_device_destroy(self.id); + backend::device_drop(&self.id); } } @@ -1097,7 +1091,7 @@ impl Queue { impl Drop for SwapChainOutput { fn drop(&mut self) { if !thread::panicking() { - backend::swap_chain_present(&self.swap_chain_id); + backend::swap_chain_present(&self); } } } @@ -1113,13 +1107,6 @@ impl SwapChain { /// When the [`SwapChainOutput`] returned by this method is dropped, the swapchain will present /// the texture to the associated [`Surface`]. pub fn get_next_texture(&mut self) -> Result { - match backend::swap_chain_get_next_texture(&self.id) { - Some(id) => Ok(SwapChainOutput { - view: TextureView { id, owned: false }, - // TODO: Remove from web backend - swap_chain_id: self.id.clone(), - }), - None => Err(TimeOut), - } + backend::swap_chain_get_next_texture(&self.id) } } From 9ed998702a9b978813fa9c72db09df10b23e144b Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Fri, 17 Apr 2020 21:47:33 -0230 Subject: [PATCH 15/17] Add labels to mapped descriptors where possible --- src/backend/web.rs | 64 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/backend/web.rs b/src/backend/web.rs index e69f66de8..8c9fe3cb0 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -63,12 +63,12 @@ pub(crate) async fn request_device_and_queue( ) -> (DeviceId, QueueId) { let device_promise = match desc { Some(d) => { - let mut mapped_descriptor = web_sys::GpuDeviceDescriptor::new(); - // TODO: Extensions + 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_descriptor.limits(&mapped_limits); - adapter.request_device_with_descriptor(&mapped_descriptor) + mapped_desc.limits(&mapped_limits); + adapter.request_device_with_descriptor(&mapped_desc) } None => adapter.request_device(), }; @@ -82,6 +82,7 @@ pub(crate) async fn request_device_and_queue( 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) } @@ -150,7 +151,10 @@ pub(crate) fn create_bind_group_layout( }) .collect::(); - let mapped_desc = web_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings); + 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) } @@ -179,7 +183,10 @@ pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) - }) .collect::(); - let mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &desc.layout.id); + 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) } @@ -193,6 +200,7 @@ pub(crate) fn create_pipeline_layout( .map(|bgl| bgl.id.clone()) .collect::(); let mapped_desc = web_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts); + // TODO: label device.create_pipeline_layout(&mapped_desc) } @@ -591,6 +599,8 @@ pub(crate) fn create_render_pipeline( &mapped_vertex_stage, ); + // TODO: label + if let Some(ref frag) = desc.fragment_stage { mapped_desc.fragment_stage(&map_stage_descriptor(frag)); } @@ -618,7 +628,7 @@ pub(crate) fn create_compute_pipeline( 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) } @@ -633,7 +643,10 @@ pub(crate) fn device_create_buffer_mapped<'a>( device: &DeviceId, desc: &BufferDescriptor, ) -> crate::CreateBufferMapped<'a> { - let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + 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(); @@ -690,7 +703,10 @@ pub(crate) fn buffer_drop(_buffer: &BufferId) { } pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { - let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + 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: (), @@ -703,6 +719,9 @@ pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) &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); @@ -711,6 +730,7 @@ pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) 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)); @@ -727,9 +747,12 @@ pub(crate) fn device_create_sampler(device: &DeviceId, desc: &SamplerDescriptor) pub(crate) fn create_command_encoder( device: &DeviceId, - _desc: &CommandEncoderDescriptor, + desc: &CommandEncoderDescriptor, ) -> CommandEncoderId { - let mapped_desc = web_sys::GpuCommandEncoderDescriptor::new(); + 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) } @@ -790,7 +813,10 @@ pub(crate) fn command_encoder_copy_texture_to_texture( } pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { - let mapped_desc = web_sys::GpuComputePassDescriptor::new(); + 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) } @@ -833,7 +859,10 @@ pub(crate) fn compute_pass_end_pass(compute_pass: &ComputePassId) { } pub(crate) fn command_encoder_finish(command_encoder: &CommandEncoderId) -> CommandBufferId { - let mapped_desc = web_sys::GpuCommandBufferDescriptor::new(); + 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) } @@ -996,7 +1025,9 @@ pub(crate) fn command_encoder_begin_render_pass<'a>( }) .collect::(); - let mut mapped = web_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments); + 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 = @@ -1014,10 +1045,10 @@ pub(crate) fn command_encoder_begin_render_pass<'a>( map_store_op(dsa.stencil_store_op), ); - mapped.depth_stencil_attachment(&mapped_depth_stencil_attachment); + mapped_desc.depth_stencil_attachment(&mapped_depth_stencil_attachment); } - command_encoder.begin_render_pass(&mapped) + command_encoder.begin_render_pass(&mapped_desc) } pub(crate) fn render_pass_set_pipeline( @@ -1154,6 +1185,7 @@ pub(crate) fn texture_create_view( 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(), From cb9bf7ecc7f49433c889562de3257f18f553712c Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sat, 18 Apr 2020 00:22:34 -0230 Subject: [PATCH 16/17] Add wasm to CI --- .github/workflows/ci.yml | 22 ++++++++++++++++------ bors.toml | 9 ++++++--- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90644d719..8cda87f70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,19 +9,29 @@ on: jobs: build: runs-on: ${{ matrix.os }} + env: + RUSTFLAGS: ${{ matrix.RUSTFLAGS }} strategy: matrix: os: [macos-10.15, ubuntu-18.04, windows-2019] + wasm: [true, false] include: - os: macos-10.15 - CHECK_COMMAND: cargo check - TEST_COMMAND: cargo test --no-run + wasm: false + CHECK_COMMAND: cargo check --all-targets + TEST_COMMAND: cargo test --all-targets --no-run - os: ubuntu-18.04 - CHECK_COMMAND: cargo check - TEST_COMMAND: cargo test --no-run + wasm: false + CHECK_COMMAND: cargo check --all-targets + TEST_COMMAND: cargo test --all-targets --no-run - os: windows-2019 - CHECK_COMMAND: rustup default stable-msvc; cargo check - TEST_COMMAND: rustup default stable-msvc; cargo test --no-run + wasm: false + CHECK_COMMAND: rustup default stable-msvc && cargo check --all-targets + TEST_COMMAND: rustup default stable-msvc && cargo test --all-targets --no-run + - wasm: true + CHECK_COMMAND: rustup target add wasm32-unknown-unknown && cargo check --all-targets --target=wasm32-unknown-unknown + TEST_COMMAND: rustup target add wasm32-unknown-unknown && cargo test --all-targets --no-run --target=wasm32-unknown-unknown + RUSTFLAGS: --cfg=web_sys_unstable_apis steps: - uses: actions/checkout@v2 - name: cargo check diff --git a/bors.toml b/bors.toml index 4fe41dee1..2f750ca59 100644 --- a/bors.toml +++ b/bors.toml @@ -1,7 +1,10 @@ status = [ - "build (macos-10.15)", - "build (ubuntu-18.04)", - "build (windows-2019)", + "build (macos-10.15, true)", + "build (macos-10.15, false)", + "build (ubuntu-18.04, true)", + "build (ubuntu-18.04, false)", + "build (windows-2019, true)", + "build (windows-2019, false)", "docs", ] From ecb6a49542c21061ad7de8d548a07ba62628c54f Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Sat, 18 Apr 2020 00:48:19 -0230 Subject: [PATCH 17/17] Use srgb for swapchain when possible --- examples/framework.rs | 7 ++++++- examples/hello-triangle/main.rs | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/examples/framework.rs b/examples/framework.rs index 96e96e1eb..221dfbe77 100644 --- a/examples/framework.rs +++ b/examples/framework.rs @@ -76,7 +76,12 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8Unorm, + // TODO: Allow srgb unconditionally + format: if cfg!(target_arch = "wasm32") { + wgpu::TextureFormat::Bgra8Unorm + } else { + wgpu::TextureFormat::Bgra8UnormSrgb + }, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, diff --git a/examples/hello-triangle/main.rs b/examples/hello-triangle/main.rs index ad7a996d4..f46954c32 100644 --- a/examples/hello-triangle/main.rs +++ b/examples/hello-triangle/main.rs @@ -4,7 +4,7 @@ use winit::{ window::Window, }; -async fn run(event_loop: EventLoop<()>, window: Window) { +async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::TextureFormat) { let size = window.inner_size(); let surface = wgpu::Surface::create(&window); @@ -84,7 +84,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8Unorm, + format: swapchain_format, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, @@ -143,7 +143,8 @@ fn main() { #[cfg(not(target_arch = "wasm32"))] { env_logger::init(); - futures::executor::block_on(run(event_loop, window)); + // Temporarily avoid srgb formats for the swapchain on the web + futures::executor::block_on(run(event_loop, window, wgpu::TextureFormat::Bgra8Unorm)); } #[cfg(target_arch = "wasm32")] { @@ -159,6 +160,10 @@ fn main() { .ok() }) .expect("couldn't append canvas to document body"); - wasm_bindgen_futures::spawn_local(run(event_loop, window)); + wasm_bindgen_futures::spawn_local(run( + event_loop, + window, + wgpu::TextureFormat::Bgra8UnormSrgb, + )); } }