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/Cargo.toml b/Cargo.toml index 6918f62ce..06971ca61 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,12 +49,10 @@ parking_lot = "0.10" [dev-dependencies] cgmath = "0.17" -env_logger = "0.7" -glsl-to-spirv = "0.1" log = "0.4" png = "0.15" -winit = "0.22" -rand = "0.7.2" +winit = { version = "0.22.1", features = ["web-sys"] } +rand = { version = "0.7.2", features = ["wasm-bindgen"] } bytemuck = "1" futures = "0.3" @@ -63,12 +61,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" } @@ -76,3 +72,106 @@ 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.60" +web-sys = { version = "0.3.37", features = [ + "Document", + "Navigator", + "Node", + "NodeList", + "Gpu", + "GpuAdapter", + "GpuAddressMode", + "GpuBindGroup", + "GpuBindGroupEntry", + "GpuBindGroupDescriptor", + "GpuBindGroupLayout", + "GpuBindGroupLayoutEntry", + "GpuBindGroupLayoutDescriptor", + "GpuBlendDescriptor", + "GpuBlendFactor", + "GpuBlendOperation", + "GpuBindingType", + "GpuBuffer", + "GpuBufferBinding", + "GpuBufferCopyView", + "GpuBufferDescriptor", + "GpuCanvasContext", + "GpuColorDict", + "GpuColorStateDescriptor", + "GpuCommandBuffer", + "GpuCommandBufferDescriptor", + "GpuCommandEncoder", + "GpuCommandEncoderDescriptor", + "GpuCompareFunction", + "GpuComputePassDescriptor", + "GpuComputePassEncoder", + "GpuComputePipeline", + "GpuComputePipelineDescriptor", + "GpuCullMode", + "GpuDepthStencilStateDescriptor", + "GpuDevice", + "GpuDeviceDescriptor", + "GpuExtent3dDict", + "GpuFilterMode", + "GpuFrontFace", + "GpuIndexFormat", + "GpuInputStepMode", + "GpuLimits", + "GpuLoadOp", + "GpuOrigin3dDict", + "GpuPipelineLayout", + "GpuPipelineLayoutDescriptor", + "GpuPowerPreference", + "GpuPrimitiveTopology", + "GpuProgrammableStageDescriptor", + "GpuQueue", + "GpuRasterizationStateDescriptor", + "GpuRenderPassColorAttachmentDescriptor", + "GpuRenderPassDepthStencilAttachmentDescriptor", + "GpuRenderPassDescriptor", + "GpuRenderPassEncoder", + "GpuRenderPipeline", + "GpuRenderPipelineDescriptor", + "GpuRequestAdapterOptions", + "GpuSampler", + "GpuSamplerDescriptor", + "GpuShaderModule", + "GpuShaderModuleDescriptor", + "GpuStencilOperation", + "GpuStencilStateFaceDescriptor", + "GpuStoreOp", + "GpuSwapChain", + "GpuSwapChainDescriptor", + "GpuTexture", + "GpuTextureAspect", + "GpuTextureComponentType", + "GpuTextureCopyView", + "GpuTextureDescriptor", + "GpuTextureDimension", + "GpuTextureFormat", + "GpuTextureViewDescriptor", + "GpuTextureViewDimension", + "GpuTextureView", + "GpuVertexAttributeDescriptor", + "GpuVertexBufferLayoutDescriptor", + "GpuVertexFormat", + "GpuVertexStateDescriptor", + "GpuVertexAttributeDescriptor", + "HtmlCanvasElement", + "Window", +]} +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" +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/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", ] 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 000000000..cadd91ef2 Binary files /dev/null and b/examples/boids/boids.comp.spv differ 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 000000000..3b3d8fbd7 Binary files /dev/null and b/examples/boids/shader.frag.spv differ diff --git a/examples/boids/shader.vert.spv b/examples/boids/shader.vert.spv new file mode 100644 index 000000000..3e95e49fb Binary files /dev/null and b/examples/boids/shader.vert.spv differ diff --git a/examples/capture/main.rs b/examples/capture/main.rs index a4fefe1fe..5f3cc8429 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); @@ -110,7 +115,15 @@ async fn run() { } 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/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 000000000..95a79e2e1 Binary files /dev/null and b/examples/cube/shader.frag.spv differ diff --git a/examples/cube/shader.vert.spv b/examples/cube/shader.vert.spv new file mode 100644 index 000000000..a5539aff6 Binary files /dev/null and b/examples/cube/shader.vert.spv differ diff --git a/examples/describe/main.rs b/examples/describe/main.rs index 72e40493a..2b5d75939 100644 --- a/examples/describe/main.rs +++ b/examples/describe/main.rs @@ -1,9 +1,4 @@ /// This example shows how to describe the adapter in use. -fn main() { - env_logger::init(); - futures::executor::block_on(run()); -} - async fn run() { let adapter = wgpu::Adapter::request( &wgpu::RequestAdapterOptions { @@ -15,5 +10,20 @@ 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)); + console_log::init().expect("could not initialize logger"); + wasm_bindgen_futures::spawn_local(run()); + } +} diff --git a/examples/framework.rs b/examples/framework.rs index cb8e65e59..221dfbe77 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}, - }; +async fn run_async(event_loop: EventLoop<()>, window: Window) { + log::info!("Initializing the surface..."); - env_logger::init(); - let event_loop = EventLoop::new(); - log::info!("Initializing the window..."); - - #[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,12 @@ async fn run_async(title: &str) { let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + // 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, @@ -185,7 +146,37 @@ 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)); + 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() + .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..2afafe2ec 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 @@ -125,9 +125,17 @@ async fn execute_gpu(numbers: Vec) -> Vec { } 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 54b1b7349..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); @@ -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: swapchain_format, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, @@ -136,9 +136,34 @@ async fn run(event_loop: EventLoop<()>, window: Window) { } }); } + 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(); + // 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")] + { + 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() + .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, + wgpu::TextureFormat::Bgra8UnormSrgb, + )); + } } diff --git a/examples/mipmap/blit.frag.spv b/examples/mipmap/blit.frag.spv new file mode 100644 index 000000000..ff08dac76 Binary files /dev/null and b/examples/mipmap/blit.frag.spv differ diff --git a/examples/mipmap/blit.vert.spv b/examples/mipmap/blit.vert.spv new file mode 100644 index 000000000..d4dd66b07 Binary files /dev/null and b/examples/mipmap/blit.vert.spv differ diff --git a/examples/mipmap/draw.frag.spv b/examples/mipmap/draw.frag.spv new file mode 100644 index 000000000..57d411376 Binary files /dev/null and b/examples/mipmap/draw.frag.spv differ diff --git a/examples/mipmap/draw.vert.spv b/examples/mipmap/draw.vert.spv new file mode 100644 index 000000000..d8128ba45 Binary files /dev/null and b/examples/mipmap/draw.vert.spv differ diff --git a/examples/mipmap/main.rs b/examples/mipmap/main.rs index 1364cdee1..437c6334a 100644 --- a/examples/mipmap/main.rs +++ b/examples/mipmap/main.rs @@ -105,12 +105,12 @@ impl Example { bind_group_layouts: &[&bind_group_layout], }); - let vs_bytes = - framework::load_glsl(include_str!("blit.vert"), framework::ShaderStage::Vertex); - let fs_bytes = - framework::load_glsl(include_str!("blit.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!("blit.vert.spv"); + let fs_bytes = include_bytes!("blit.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, @@ -153,7 +153,7 @@ impl Example { mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest, - lod_min_clamp: -100.0, + lod_min_clamp: 0.0, lod_max_clamp: 100.0, compare: wgpu::CompareFunction::Undefined, }); @@ -299,7 +299,7 @@ impl framework::Example for Example { mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, - lod_min_clamp: -100.0, + lod_min_clamp: 0.0, lod_max_clamp: 100.0, compare: wgpu::CompareFunction::Undefined, }); @@ -334,12 +334,12 @@ impl framework::Example for Example { }); // Create the render pipeline - let vs_bytes = - framework::load_glsl(include_str!("draw.vert"), framework::ShaderStage::Vertex); - let fs_bytes = - framework::load_glsl(include_str!("draw.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!("draw.vert.spv"); + let fs_bytes = include_bytes!("draw.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 draw_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, diff --git a/examples/msaa-line/main.rs b/examples/msaa-line/main.rs index 1a8525699..25368b9b9 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: &[], diff --git a/examples/msaa-line/shader.frag.spv b/examples/msaa-line/shader.frag.spv new file mode 100644 index 000000000..46d3355b7 Binary files /dev/null and b/examples/msaa-line/shader.frag.spv differ diff --git a/examples/msaa-line/shader.vert.spv b/examples/msaa-line/shader.vert.spv new file mode 100644 index 000000000..adab99219 Binary files /dev/null and b/examples/msaa-line/shader.vert.spv differ diff --git a/examples/shadow/bake.frag.spv b/examples/shadow/bake.frag.spv new file mode 100644 index 000000000..dbd293829 Binary files /dev/null and b/examples/shadow/bake.frag.spv differ diff --git a/examples/shadow/bake.vert.spv b/examples/shadow/bake.vert.spv new file mode 100644 index 000000000..26f1000fd Binary files /dev/null and b/examples/shadow/bake.vert.spv differ diff --git a/examples/shadow/forward.frag.spv b/examples/shadow/forward.frag.spv new file mode 100644 index 000000000..bc571bb9b Binary files /dev/null and b/examples/shadow/forward.frag.spv differ diff --git a/examples/shadow/forward.vert.spv b/examples/shadow/forward.vert.spv new file mode 100644 index 000000000..9bd02dfae Binary files /dev/null and b/examples/shadow/forward.vert.spv differ diff --git a/examples/shadow/main.rs b/examples/shadow/main.rs index 716473afc..6730df3c9 100644 --- a/examples/shadow/main.rs +++ b/examples/shadow/main.rs @@ -459,12 +459,14 @@ impl framework::Example for Example { }); // Create the render pipeline - let vs_bytes = - framework::load_glsl(include_str!("bake.vert"), framework::ShaderStage::Vertex); - let fs_bytes = - framework::load_glsl(include_str!("bake.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!("bake.vert.spv"); + let fs_bytes = include_bytes!("bake.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, @@ -588,14 +590,14 @@ impl framework::Example for Example { }); // Create the render pipeline - let vs_bytes = - framework::load_glsl(include_str!("forward.vert"), framework::ShaderStage::Vertex); - let fs_bytes = framework::load_glsl( - include_str!("forward.frag"), - framework::ShaderStage::Fragment, + let vs_bytes = include_bytes!("forward.vert.spv"); + let fs_bytes = include_bytes!("forward.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 vs_module = device.create_shader_module(&vs_bytes); - let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, @@ -721,7 +723,7 @@ impl framework::Example for Example { { let size = mem::size_of::(); - 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/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 000000000..65ab23b9a Binary files /dev/null and b/examples/skybox/shader.frag.spv differ diff --git a/examples/skybox/skybox_vert.glsl b/examples/skybox/shader.vert similarity index 100% rename from examples/skybox/skybox_vert.glsl rename to examples/skybox/shader.vert diff --git a/examples/skybox/shader.vert.spv b/examples/skybox/shader.vert.spv new file mode 100644 index 000000000..5b10601f1 Binary files /dev/null and b/examples/skybox/shader.vert.spv differ 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..b6f46bd0b --- /dev/null +++ b/src/backend/native.rs @@ -0,0 +1,941 @@ +use wgn; + +use crate::{ + backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, + BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, + PipelineLayoutDescriptor, RenderPipelineDescriptor, SamplerDescriptor, TextureDescriptor, + TextureViewDescriptor, 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; + +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, +) -> 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 device_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 device_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 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 { + 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 device_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 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, +) -> 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 command_encoder_copy_buffer_to_buffer( + command_encoder: &CommandEncoderId, + source: &crate::Buffer, + source_offset: wgt::BufferAddress, + destination: &crate::Buffer, + destination_offset: wgt::BufferAddress, + copy_size: wgt::BufferAddress, +) { + wgn::wgpu_command_encoder_copy_buffer_to_buffer( + *command_encoder, + source.id, + source_offset, + destination.id, + destination_offset, + copy_size, + ); +} + +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 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 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) } +} + +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) 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, +} + +impl BufferReadMappingDetail { + pub(crate) fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data as *const u8, self.size) } + } +} + +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 { + 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 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>( + 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_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, + 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_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, + 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_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, + 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_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_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); + } +} + +pub(crate) fn texture_create_view( + texture: &TextureId, + desc: Option<&TextureViewDescriptor>, +) -> TextureViewId { + 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 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) { + 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..8c9fe3cb0 --- /dev/null +++ b/src/backend/web.rs @@ -0,0 +1,1213 @@ +use crate::{ + BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor, + CommandEncoderDescriptor, ComputePipelineDescriptor, PipelineLayoutDescriptor, + ProgrammableStageDescriptor, RenderPipelineDescriptor, SamplerDescriptor, TextureDescriptor, + TextureViewDescriptor, 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_desc = web_sys::GpuDeviceDescriptor::new(); + // TODO: label, extensions + let mut mapped_limits = web_sys::GpuLimits::new(); + mapped_limits.max_bind_groups(d.limits.max_bind_groups); + mapped_desc.limits(&mapped_limits); + adapter.request_device_with_descriptor(&mapped_desc) + } + None => adapter.request_device(), + }; + let js_value = wasm_bindgen_futures::JsFuture::from(device_promise) + .await + .expect("Unable to get device"); + let device_id = DeviceId::from(js_value); + let queue_id = device_id.default_queue(); + (device_id, queue_id) +} + +pub(crate) fn create_shader_module(device: &DeviceId, spv: &[u32]) -> ShaderModuleId { + let desc = web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(spv)); + // TODO: label + device.create_shader_module(&desc) +} + +pub(crate) fn create_bind_group_layout( + device: &DeviceId, + desc: &BindGroupLayoutDescriptor, +) -> BindGroupLayoutId { + use web_sys::GpuBindingType as bt; + + let mapped_bindings = desc + .bindings + .iter() + .map(|bind| { + let mapped_type = match bind.ty { + BindingType::UniformBuffer { .. } => bt::UniformBuffer, + BindingType::StorageBuffer { + readonly: false, .. + } => bt::StorageBuffer, + BindingType::StorageBuffer { readonly: true, .. } => bt::ReadonlyStorageBuffer, + BindingType::Sampler { comparison: false } => bt::Sampler, + BindingType::Sampler { .. } => bt::ComparisonSampler, + BindingType::SampledTexture { .. } => bt::SampledTexture, + BindingType::StorageTexture { readonly: true, .. } => bt::ReadonlyStorageTexture, + BindingType::StorageTexture { .. } => bt::WriteonlyStorageTexture, + }; + + let mut mapped_entry = web_sys::GpuBindGroupLayoutEntry::new( + bind.binding, + mapped_type, + bind.visibility.bits(), + ); + + match bind.ty { + BindingType::UniformBuffer { dynamic } + | BindingType::StorageBuffer { dynamic, .. } => { + mapped_entry.has_dynamic_offset(dynamic); + } + _ => {} + } + + if let BindingType::SampledTexture { multisampled, .. } = bind.ty { + mapped_entry.multisampled(multisampled); + } + + match bind.ty { + BindingType::SampledTexture { dimension, .. } + | BindingType::StorageTexture { dimension, .. } => { + mapped_entry.view_dimension(map_texture_view_dimension(dimension)); + } + _ => {} + } + + if let BindingType::StorageTexture { format, .. } = bind.ty { + mapped_entry.storage_texture_format(map_texture_format(format)); + } + + match bind.ty { + BindingType::SampledTexture { component_type, .. } + | BindingType::StorageTexture { component_type, .. } => { + mapped_entry.texture_component_type(map_texture_component_type(component_type)); + } + _ => {} + } + + mapped_entry + }) + .collect::(); + + let mut mapped_desc = web_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_bind_group_layout(&mapped_desc) +} + +pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) -> BindGroupId { + let mapped_entries = desc + .bindings + .iter() + .map(|binding| { + let mapped_resource = match binding.resource { + BindingResource::Buffer { + ref buffer, + ref range, + } => { + let mut mapped_buffer_binding = web_sys::GpuBufferBinding::new(&buffer.id); + mapped_buffer_binding.offset(range.start as f64); + mapped_buffer_binding.size((range.end - range.start) as f64); + JsValue::from(mapped_buffer_binding.clone()) + } + BindingResource::Sampler(ref sampler) => JsValue::from(sampler.id.clone()), + BindingResource::TextureView(ref texture_view) => { + JsValue::from(texture_view.id.clone()) + } + }; + + web_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource) + }) + .collect::(); + + let mut mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &desc.layout.id); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_bind_group(&mapped_desc) +} + +pub(crate) fn create_pipeline_layout( + device: &DeviceId, + desc: &PipelineLayoutDescriptor, +) -> PipelineLayoutId { + let temp_layouts = desc + .bind_group_layouts + .iter() + .map(|bgl| bgl.id.clone()) + .collect::(); + let mapped_desc = web_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts); + // TODO: label + device.create_pipeline_layout(&mapped_desc) +} + +fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTextureFormat { + 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_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 { + 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_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, + wgt::TextureDimension::D2 => web_sys::GpuTextureDimension::N2d, + wgt::TextureDimension::D3 => web_sys::GpuTextureDimension::N3d, + } +} + +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.bytes_per_row); + mapped.rows_per_image(view.rows_per_image); + 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_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, + 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, + } +} + +fn map_color(color: wgt::Color) -> web_sys::GpuColorDict { + web_sys::GpuColorDict::new(color.a, color.b, color.g, color.r) +} + +pub(crate) fn create_render_pipeline( + device: &DeviceId, + desc: &RenderPipelineDescriptor, +) -> RenderPipelineId { + use web_sys::GpuPrimitiveTopology as pt; + + let mapped_color_states = desc + .color_states + .iter() + .map(|color_state_desc| { + let mapped_format = map_texture_format(color_state_desc.format); + let mut mapped_color_state_desc = web_sys::GpuColorStateDescriptor::new(mapped_format); + mapped_color_state_desc + .alpha_blend(&map_blend_descriptor(&color_state_desc.alpha_blend)); + mapped_color_state_desc + .color_blend(&map_blend_descriptor(&color_state_desc.color_blend)); + mapped_color_state_desc.write_mask(color_state_desc.write_mask.bits()); + mapped_color_state_desc + }) + .collect::(); + + let mapped_primitive_topology = match desc.primitive_topology { + wgt::PrimitiveTopology::PointList => pt::PointList, + wgt::PrimitiveTopology::LineList => pt::LineList, + wgt::PrimitiveTopology::LineStrip => pt::LineStrip, + wgt::PrimitiveTopology::TriangleList => pt::TriangleList, + wgt::PrimitiveTopology::TriangleStrip => pt::TriangleStrip, + }; + + let mapped_vertex_stage = map_stage_descriptor(&desc.vertex_stage); + + let mut mapped_desc = web_sys::GpuRenderPipelineDescriptor::new( + &desc.layout.id, + &mapped_color_states, + mapped_primitive_topology, + &mapped_vertex_stage, + ); + + // TODO: label + + if let Some(ref frag) = desc.fragment_stage { + mapped_desc.fragment_stage(&map_stage_descriptor(frag)); + } + + if let Some(ref rasterization) = desc.rasterization_state { + mapped_desc.rasterization_state(&map_rasterization_state_descriptor(rasterization)); + } + + if let Some(ref depth_stencil) = desc.depth_stencil_state { + mapped_desc.depth_stencil_state(&map_depth_stencil_state_descriptor(depth_stencil)); + } + + mapped_desc.vertex_state(&map_vertex_state_descriptor(&desc)); + mapped_desc.sample_count(desc.sample_count); + mapped_desc.sample_mask(desc.sample_mask); + mapped_desc.alpha_to_coverage_enabled(desc.alpha_to_coverage_enabled); + + device.create_render_pipeline(&mapped_desc) +} + +pub(crate) fn create_compute_pipeline( + device: &DeviceId, + desc: &ComputePipelineDescriptor, +) -> ComputePipelineId { + let mapped_compute_stage = map_stage_descriptor(&desc.compute_stage); + let mapped_desc = + web_sys::GpuComputePipelineDescriptor::new(&desc.layout.id, &mapped_compute_stage); + // TODO: label + device.create_compute_pipeline(&mapped_desc) +} + +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 device_create_buffer_mapped<'a>( + device: &DeviceId, + desc: &BufferDescriptor, +) -> crate::CreateBufferMapped<'a> { + let mut mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + unsafe { + let pair = device.create_buffer_mapped(&mapped_desc); + let id = pair.get(0).into(); + let array_buffer = pair.get(1).into(); + // TODO: Use `Vec::from_raw_parts` once it's stable + let memory = vec![0; desc.size as usize].into_boxed_slice(); + let mapped_data = + std::slice::from_raw_parts_mut(Box::into_raw(memory) as *mut u8, desc.size as usize); + crate::CreateBufferMapped { + id, + mapped_data, + detail: CreateBufferMappedDetail { array_buffer }, + } + } +} + +pub type BufferDetail = (); + +pub(crate) fn device_create_buffer_mapped_finish( + create_buffer_mapped: crate::CreateBufferMapped<'_>, +) -> crate::Buffer { + unsafe { + // Convert the `mapped_data` slice back into a `Vec`. This should be + // safe because `mapped_data` is no longer accessible beyond this + // function. + let memory: Vec = Box::<[u8]>::from_raw(create_buffer_mapped.mapped_data).into(); + + // Create a view into the mapped `ArrayBuffer` that was provided by the + // browser + let mapped = js_sys::Uint8Array::new(&create_buffer_mapped.detail.array_buffer); + + // Convert `memory` into a temporary `Uint8Array` view. This should be + // safe as long as the backing wasm memory is not resized. + let memory_view = js_sys::Uint8Array::view(&memory[..]); + + // Finally copy into `mapped` and let `memory` drop + mapped.set(&memory_view, 0); + } + + buffer_unmap(&create_buffer_mapped.id); + + crate::Buffer { + id: create_buffer_mapped.id, + detail: (), + } +} + +pub(crate) fn buffer_unmap(buffer: &BufferId) { + buffer.unmap(); +} + +pub(crate) fn buffer_drop(_buffer: &BufferId) { + // Buffer is dropped automatically +} + +pub(crate) fn device_create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer { + let mut mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + crate::Buffer { + id: device.create_buffer(&mapped_desc), + detail: (), + } +} + +pub(crate) fn device_create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId { + let mut mapped_desc = web_sys::GpuTextureDescriptor::new( + map_texture_format(desc.format), + &map_extent_3d(desc.size), + desc.usage.bits(), + ); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + mapped_desc.dimension(map_texture_dimension(desc.dimension)); + mapped_desc.mip_level_count(desc.mip_level_count); + mapped_desc.sample_count(desc.sample_count); + device.create_texture(&mapped_desc) +} + +pub(crate) fn device_create_sampler(device: &DeviceId, desc: &SamplerDescriptor) -> SamplerId { + let mut mapped_desc = web_sys::GpuSamplerDescriptor::new(); + // TODO: label + mapped_desc.address_mode_u(map_address_mode(desc.address_mode_u)); + mapped_desc.address_mode_v(map_address_mode(desc.address_mode_v)); + mapped_desc.address_mode_w(map_address_mode(desc.address_mode_w)); + if let Some(compare) = map_compare_function(desc.compare) { + mapped_desc.compare(compare); + } + mapped_desc.lod_max_clamp(desc.lod_max_clamp); + mapped_desc.lod_min_clamp(desc.lod_min_clamp); + mapped_desc.mag_filter(map_filter_mode(desc.mag_filter)); + mapped_desc.min_filter(map_filter_mode(desc.min_filter)); + mapped_desc.mipmap_filter(map_filter_mode(desc.mipmap_filter)); + device.create_sampler_with_descriptor(&mapped_desc) +} + +pub(crate) fn create_command_encoder( + device: &DeviceId, + desc: &CommandEncoderDescriptor, +) -> CommandEncoderId { + let mut mapped_desc = web_sys::GpuCommandEncoderDescriptor::new(); + if let Some(label) = desc.label { + mapped_desc.label(label); + } + device.create_command_encoder_with_descriptor(&mapped_desc) +} + +pub(crate) fn command_encoder_copy_buffer_to_buffer( + command_encoder: &CommandEncoderId, + source: &crate::Buffer, + source_offset: wgt::BufferAddress, + destination: &crate::Buffer, + destination_offset: wgt::BufferAddress, + copy_size: wgt::BufferAddress, +) { + command_encoder.copy_buffer_to_buffer_with_f64_and_f64_and_f64( + &source.id, + source_offset as f64, + &destination.id, + destination_offset as f64, + copy_size as f64, + ); +} + +pub(crate) fn command_encoder_copy_buffer_to_texture( + command_encoder: &CommandEncoderId, + source: crate::BufferCopyView, + destination: crate::TextureCopyView, + copy_size: wgt::Extent3d, +) { + command_encoder.copy_buffer_to_texture_with_gpu_extent_3d_dict( + &map_buffer_copy_view(source), + &map_texture_copy_view(destination), + &map_extent_3d(copy_size), + ); +} + +pub(crate) fn command_encoder_copy_texture_to_buffer( + command_encoder: &CommandEncoderId, + source: crate::TextureCopyView, + destination: crate::BufferCopyView, + copy_size: wgt::Extent3d, +) { + command_encoder.copy_texture_to_buffer_with_gpu_extent_3d_dict( + &map_texture_copy_view(source), + &map_buffer_copy_view(destination), + &map_extent_3d(copy_size), + ); +} + +pub(crate) fn command_encoder_copy_texture_to_texture( + command_encoder: &CommandEncoderId, + source: crate::TextureCopyView, + destination: crate::TextureCopyView, + copy_size: wgt::Extent3d, +) { + command_encoder.copy_texture_to_texture_with_gpu_extent_3d_dict( + &map_texture_copy_view(source), + &map_texture_copy_view(destination), + &map_extent_3d(copy_size), + ); +} + +pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId { + let mut mapped_desc = web_sys::GpuComputePassDescriptor::new(); + if let Some(ref label) = command_encoder.label() { + mapped_desc.label(label); + } + command_encoder.begin_compute_pass_with_descriptor(&mapped_desc) +} + +pub(crate) fn compute_pass_set_pipeline( + compute_pass: &ComputePassId, + pipeline: &ComputePipelineId, +) { + compute_pass.set_pipeline(&pipeline); +} + +pub(crate) fn compute_pass_set_bind_group<'a>( + compute_pass: &ComputePassId, + index: u32, + bind_group: &BindGroupId, + offsets: &[wgt::DynamicOffset], +) { + compute_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( + index, + bind_group, + offsets, + 0f64, + offsets.len() as u32, + ); +} + +pub(crate) fn compute_pass_dispatch(compute_pass: &ComputePassId, x: u32, y: u32, z: u32) { + compute_pass.dispatch_with_y_and_z(x, y, z); +} + +pub(crate) fn compute_pass_dispatch_indirect( + compute_pass: &ComputePassId, + indirect_buffer: &BufferId, + indirect_offset: wgt::BufferAddress, +) { + compute_pass.dispatch_indirect_with_f64(indirect_buffer, indirect_offset as f64); +} + +pub(crate) fn compute_pass_end_pass(compute_pass: &ComputePassId) { + compute_pass.end_pass(); +} + +pub(crate) fn command_encoder_finish(command_encoder: &CommandEncoderId) -> CommandBufferId { + let mut mapped_desc = web_sys::GpuCommandBufferDescriptor::new(); + if let Some(ref label) = command_encoder.label() { + mapped_desc.label(label); + } + command_encoder.finish_with_descriptor(&mapped_desc) +} + +pub(crate) fn queue_submit(queue: &QueueId, command_buffers: &[crate::CommandBuffer]) { + let temp_command_buffers = command_buffers + .iter() + .map(|cb| &cb.id) + .collect::(); + + queue.submit(&temp_command_buffers); +} + +pub(crate) async fn buffer_map_read( + buffer: &crate::Buffer, + _start: wgt::BufferAddress, + _size: wgt::BufferAddress, +) -> Result { + let array_buffer_promise = buffer.id.map_read_async(); + let array_buffer: js_sys::ArrayBuffer = + wasm_bindgen_futures::JsFuture::from(array_buffer_promise) + .await + .expect("Unable to map buffer") + .into(); + let view = js_sys::Uint8Array::new(&array_buffer); + Ok(crate::BufferReadMapping { + detail: BufferReadMappingDetail { + buffer_id: buffer.id.clone(), + mapped: view.to_vec(), + }, + }) +} + +pub(crate) async fn buffer_map_write( + buffer: &crate::Buffer, + _start: wgt::BufferAddress, + _size: wgt::BufferAddress, +) -> Result { + let array_buffer_promise = buffer.id.map_write_async(); + let array_buffer: js_sys::ArrayBuffer = + wasm_bindgen_futures::JsFuture::from(array_buffer_promise) + .await + .expect("Unable to map buffer") + .into(); + let view = js_sys::Uint8Array::new(&array_buffer); + Ok(crate::BufferWriteMapping { + detail: BufferWriteMappingDetail { + buffer_id: buffer.id.clone(), + mapped: view.to_vec(), + }, + }) +} + +pub(crate) struct BufferReadMappingDetail { + pub(crate) buffer_id: BufferId, + mapped: Vec, +} + +impl BufferReadMappingDetail { + pub(crate) fn as_slice(&self) -> &[u8] { + &self.mapped[..] + } +} + +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 { + let handle = window.raw_window_handle(); + let canvas_attribute = match handle { + raw_window_handle::RawWindowHandle::Web(web_handle) => web_handle.id, + _ => panic!("expected valid handle for canvas"), + }; + let canvas_node: wasm_bindgen::JsValue = web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| { + doc.query_selector_all(&format!("[data-raw-handle=\"{}\"]", canvas_attribute)) + .ok() + }) + .and_then(|nodes| nodes.get(0)) + .expect("expected to find single canvas") + .into(); + let canvas_element: web_sys::HtmlCanvasElement = canvas_node.into(); + let context: wasm_bindgen::JsValue = match canvas_element.get_context("gpupresent") { + Ok(Some(ctx)) => ctx.into(), + _ => panic!("expected to get context from canvas"), + }; + context.into() +} + +pub(crate) fn device_create_swap_chain( + device: &DeviceId, + surface: &SurfaceId, + desc: &wgt::SwapChainDescriptor, +) -> SwapChainId { + let mut mapped = web_sys::GpuSwapChainDescriptor::new(device, map_texture_format(desc.format)); + mapped.usage(desc.usage.bits()); + surface.configure_swap_chain(&mapped) +} + +pub(crate) fn device_drop(_device: &DeviceId) { + // Device is dropped automatically +} + +pub(crate) fn swap_chain_get_next_texture( + swap_chain: &SwapChainId, +) -> Result { + // TODO: Should we pass a descriptor here? + // Or is the default view always correct? + Ok(crate::SwapChainOutput { + view: crate::TextureView { + id: swap_chain.get_current_texture().create_view(), + owned: false, + }, + detail: (), + }) +} + +pub(crate) type SwapChainOutputDetail = (); + +fn map_store_op(op: wgt::StoreOp) -> web_sys::GpuStoreOp { + match op { + wgt::StoreOp::Clear => web_sys::GpuStoreOp::Clear, + wgt::StoreOp::Store => web_sys::GpuStoreOp::Store, + } +} + +pub(crate) fn command_encoder_begin_render_pass<'a>( + command_encoder: &CommandEncoderId, + desc: &crate::RenderPassDescriptor<'a, '_>, +) -> RenderPassEncoderId { + let mapped_color_attachments = desc + .color_attachments + .iter() + .map(|ca| { + let mut mapped_color_attachment = web_sys::GpuRenderPassColorAttachmentDescriptor::new( + &ca.attachment.id, + &match ca.load_op { + wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(map_color(ca.clear_color)), + wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), + }, + ); + + if let Some(rt) = ca.resolve_target { + mapped_color_attachment.resolve_target(&rt.id); + } + + mapped_color_attachment.store_op(map_store_op(ca.store_op)); + + mapped_color_attachment + }) + .collect::(); + + let mut mapped_desc = web_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments); + + // TODO: label + + if let Some(dsa) = &desc.depth_stencil_attachment { + let mapped_depth_stencil_attachment = + web_sys::GpuRenderPassDepthStencilAttachmentDescriptor::new( + &dsa.attachment.id, + &match dsa.depth_load_op { + wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_depth), + wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), + }, + map_store_op(dsa.depth_store_op), + &match dsa.stencil_load_op { + wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_stencil), + wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load), + }, + map_store_op(dsa.stencil_store_op), + ); + + mapped_desc.depth_stencil_attachment(&mapped_depth_stencil_attachment); + } + + command_encoder.begin_render_pass(&mapped_desc) +} + +pub(crate) fn render_pass_set_pipeline( + render_pass: &RenderPassEncoderId, + pipeline: &RenderPipelineId, +) { + render_pass.set_pipeline(&pipeline); +} + +pub(crate) fn render_pass_set_blend_color(render_pass: &RenderPassEncoderId, color: wgt::Color) { + render_pass.set_blend_color_with_gpu_color_dict(&map_color(color)); +} + +pub(crate) fn render_pass_set_bind_group( + render_pass: &RenderPassEncoderId, + index: u32, + bind_group: &BindGroupId, + offsets: &[wgt::DynamicOffset], +) { + render_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length( + index, + bind_group, + offsets, + 0f64, + offsets.len() as u32, + ); +} + +pub(crate) fn render_pass_set_index_buffer<'a>( + render_pass: &RenderPassEncoderId, + buffer: &'a crate::Buffer, + offset: wgt::BufferAddress, + size: wgt::BufferAddress, +) { + render_pass.set_index_buffer_with_f64_and_f64(&buffer.id, offset as f64, size as f64); +} + +pub(crate) fn render_pass_set_vertex_buffer<'a>( + render_pass: &RenderPassEncoderId, + slot: u32, + buffer: &'a crate::Buffer, + offset: wgt::BufferAddress, + size: wgt::BufferAddress, +) { + render_pass.set_vertex_buffer_with_f64_and_f64(slot, &buffer.id, offset as f64, size as f64); +} + +pub(crate) fn render_pass_set_scissor_rect( + render_pass: &RenderPassEncoderId, + x: u32, + y: u32, + width: u32, + height: u32, +) { + render_pass.set_scissor_rect(x, y, width, height); +} + +pub(crate) fn render_pass_set_viewport( + render_pass: &RenderPassEncoderId, + x: f32, + y: f32, + width: f32, + height: f32, + min_depth: f32, + max_depth: f32, +) { + render_pass.set_viewport(x, y, width, height, min_depth, max_depth); +} + +pub(crate) fn render_pass_set_stencil_reference(render_pass: &RenderPassEncoderId, reference: u32) { + render_pass.set_stencil_reference(reference); +} + +pub(crate) fn render_pass_draw( + render_pass: &RenderPassEncoderId, + vertices: Range, + instances: Range, +) { + render_pass.draw_with_instance_count_and_first_vertex_and_first_instance( + vertices.end - vertices.start, + instances.end - instances.start, + vertices.start, + instances.start, + ) +} + +pub(crate) fn render_pass_draw_indexed( + render_pass: &RenderPassEncoderId, + indices: Range, + base_vertex: i32, + instances: Range, +) { + render_pass + .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance( + indices.end - indices.start, + instances.end - instances.start, + indices.start, + base_vertex, + instances.start, + ); +} + +pub(crate) fn render_pass_draw_indirect<'a>( + render_pass: &RenderPassEncoderId, + indirect_buffer: &'a crate::Buffer, + indirect_offset: wgt::BufferAddress, +) { + render_pass.draw_indirect_with_f64(&indirect_buffer.id, indirect_offset as f64); +} + +pub(crate) fn render_pass_draw_indexed_indirect<'a>( + render_pass: &RenderPassEncoderId, + indirect_buffer: &'a crate::Buffer, + indirect_offset: wgt::BufferAddress, +) { + render_pass.draw_indexed_indirect_with_f64(&indirect_buffer.id, indirect_offset as f64); +} + +pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) { + render_pass.end_pass(); +} + +pub(crate) fn texture_create_view( + texture: &TextureId, + desc: Option<&TextureViewDescriptor>, +) -> TextureViewId { + match desc { + Some(d) => { + let mut mapped_desc = web_sys::GpuTextureViewDescriptor::new(); + mapped_desc.array_layer_count(d.array_layer_count); + mapped_desc.aspect(map_texture_aspect(d.aspect)); + mapped_desc.base_array_layer(d.base_array_layer); + mapped_desc.base_mip_level(d.base_mip_level); + mapped_desc.dimension(map_texture_view_dimension(d.dimension)); + mapped_desc.format(map_texture_format(d.format)); + mapped_desc.mip_level_count(d.level_count); + // TODO: label + texture.create_view_with_descriptor(&mapped_desc) + } + None => texture.create_view(), + } +} + +pub(crate) fn texture_drop(_texture: &TextureId) { + // Texture is dropped automatically +} + +pub(crate) fn texture_view_drop(_texture_view: &TextureViewId) { + // Texture view is dropped automatically +} + +pub(crate) fn bind_group_drop(_bind_group: &BindGroupId) { + // Bind group is dropped automatically +} + +pub(crate) fn swap_chain_present(_swap_chain_output: &crate::SwapChainOutput) { + // Swapchain is presented automatically +} + +pub(crate) fn device_poll(_device: &DeviceId, _maintain: crate::Maintain) { + // Device is polled automatically +} diff --git a/src/lib.rs b/src/lib.rs index d247dcecd..af805e22c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,13 @@ //! 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}; +#[cfg(not(target_arch = "wasm32"))] pub use wgc::instance::{AdapterInfo, DeviceType}; pub use wgt::{ read_spirv, AddressMode, Backend, BackendBit, BlendDescriptor, BlendFactor, BlendOperation, @@ -37,7 +34,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 +52,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 +67,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 +84,7 @@ pub struct Texture { /// [`RenderPipeline`] or [`BindGroup`]. #[derive(Debug, PartialEq)] pub struct TextureView { - id: wgc::id::TextureViewId, + id: backend::TextureViewId, owned: bool, } @@ -98,7 +95,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 +104,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 +113,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 +124,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,12 +135,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) { - wgn::wgpu_bind_group_destroy(self.id); + backend::bind_group_drop(&self.id); } } @@ -154,7 +151,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 +159,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 +168,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 +184,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 +196,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 +205,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 +221,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,7 +459,7 @@ pub struct TextureDescriptor<'a> { #[derive(Debug)] pub struct SwapChainOutput { pub view: TextureView, - swap_chain_id: wgc::id::SwapChainId, + detail: backend::SwapChainOutputDetail, } /// A view of a buffer which can be used to copy to or from a texture. @@ -484,17 +481,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> { @@ -511,32 +497,25 @@ 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: 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::device_create_buffer_mapped_finish(self) } } @@ -544,7 +523,7 @@ 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::device_create_surface(window), } } @@ -558,6 +537,7 @@ impl Surface { impl Adapter { /// 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() @@ -574,26 +554,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,16 +566,16 @@ 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) } + #[cfg(not(target_arch = "wasm32"))] pub fn get_info(&self) -> AdapterInfo { wgn::adapter_get_info(self.id) } @@ -621,268 +584,57 @@ impl Adapter { 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::device_create_buffer(&self.id, desc) } /// Creates a new buffer and maps it into host-visible memory. @@ -891,40 +643,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::device_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,20 +662,8 @@ 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::device_create_texture(&self.id, desc), owned: true, } } @@ -955,24 +673,22 @@ impl Device { /// `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), + 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 { 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) { - wgn::wgpu_device_poll(self.id, true); - //TODO: make this work in general - #[cfg(feature = "metal-auto-capture")] - wgn::wgpu_device_destroy(self.id); + backend::device_drop(&self.id); } } @@ -980,9 +696,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,20 +704,18 @@ 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, - buffer_id: wgc::id::BufferId, + detail: backend::BufferWriteMappingDetail, } unsafe impl Send for BufferWriteMapping {} @@ -1011,13 +723,13 @@ 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); } } @@ -1034,38 +746,8 @@ 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)); - } - } - - wgn::wgpu_buffer_map_read_async( - self.id, - start, - size, - buffer_map_read_future_wrapper, - completion.to_raw() as _, - ); - - future + ) -> impl Future> + '_ { + backend::buffer_map_read(self, start, size) } /// Map the buffer for writing. The result is returned in a future. @@ -1076,49 +758,19 @@ 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_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 + ) -> 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); + backend::buffer_unmap(&self.id); } } impl Drop for Buffer { fn drop(&mut self) { - wgn::wgpu_buffer_destroy(self.id); + backend::buffer_drop(&self.id); } } @@ -1126,7 +778,7 @@ 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)), + id: backend::texture_create_view(&self.id, Some(desc)), owned: true, } } @@ -1134,7 +786,7 @@ impl Texture { /// 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_view(&self.id, None), owned: true, } } @@ -1143,7 +795,7 @@ impl Texture { impl Drop for Texture { fn drop(&mut self) { if self.owned { - wgn::wgpu_texture_destroy(self.id); + backend::texture_drop(&self.id); } } } @@ -1151,7 +803,7 @@ 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); } } } @@ -1160,7 +812,7 @@ 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 +823,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 +834,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,11 +848,11 @@ impl CommandEncoder { destination_offset: BufferAddress, copy_size: BufferAddress, ) { - wgn::wgpu_command_encoder_copy_buffer_to_buffer( - self.id, - source.id, + backend::command_encoder_copy_buffer_to_buffer( + &self.id, + source, source_offset, - destination.id, + destination, destination_offset, copy_size, ); @@ -1246,12 +865,7 @@ impl CommandEncoder { destination: TextureCopyView, copy_size: Extent3d, ) { - wgn::wgpu_command_encoder_copy_buffer_to_texture( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); + backend::command_encoder_copy_buffer_to_texture(&self.id, source, destination, copy_size); } /// Copy data from a texture to a buffer. @@ -1261,12 +875,7 @@ 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. @@ -1276,12 +885,7 @@ impl CommandEncoder { destination: TextureCopyView, copy_size: Extent3d, ) { - wgn::wgpu_command_encoder_copy_texture_to_texture( - self.id, - &source.into_native(), - &destination.into_native(), - copy_size, - ); + backend::command_encoder_copy_texture_to_texture(&self.id, source, destination, copy_size); } } @@ -1293,30 +897,18 @@ 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); - } + backend::render_pass_set_blend_color(&self.id, color) } /// Sets the active index buffer. @@ -1331,14 +923,7 @@ impl<'a> RenderPass<'a> { offset: BufferAddress, size: BufferAddress, ) { - unsafe { - wgn::wgpu_render_pass_set_index_buffer( - self.id.as_mut().unwrap(), - buffer.id, - offset, - size, - ); - } + backend::render_pass_set_index_buffer(&self.id, buffer, offset, size) } /// Assign a vertex buffer to a slot. @@ -1362,65 +947,35 @@ 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); - } + 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, - ); - } + 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); - } + backend::render_pass_set_stencil_reference(&self.id, 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. @@ -1428,16 +983,7 @@ impl<'a> RenderPass<'a> { /// The active index buffer can be set with [`RenderPass::set_index_buffer`], while the active /// vertex buffers can be set with [`RenderPass::set_vertex_buffer`]. pub fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range) { - 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`. @@ -1456,13 +1002,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, @@ -1488,22 +1028,14 @@ 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> { 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 +1048,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 +1069,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 +1084,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); } } } @@ -1606,28 +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 { - let output = wgn::wgpu_swap_chain_get_next_texture(self.id); - match output.view_id { - Some(id) => Ok(SwapChainOutput { - view: TextureView { id, owned: false }, - swap_chain_id: self.id, - }), - None => Err(TimeOut), - } - } -} - -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(), - } + backend::swap_chain_get_next_texture(&self.id) } }