mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
[rs] Add web backend
This commit is contained in:
committed by
Josh Groves
parent
f984eb1997
commit
75ec7155f1
@@ -23,13 +23,13 @@ default = []
|
||||
# Make Vulkan backend available on platforms where it is by default not, e.g. macOS
|
||||
vulkan = ["wgn/vulkan-portability"]
|
||||
|
||||
[dependencies.wgn]
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgn]
|
||||
package = "wgpu-native"
|
||||
version = "0.5"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "49dbe08f37f8396cff0d6672667a48116ec487f5"
|
||||
|
||||
[dependencies.wgc]
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc]
|
||||
package = "wgpu-core"
|
||||
version = "0.5"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
@@ -49,11 +49,10 @@ parking_lot = "0.10"
|
||||
|
||||
[dev-dependencies]
|
||||
cgmath = "0.17"
|
||||
env_logger = "0.7"
|
||||
glsl-to-spirv = "0.1"
|
||||
#glsl-to-spirv = "0.1"
|
||||
log = "0.4"
|
||||
png = "0.15"
|
||||
winit = "0.22"
|
||||
winit = { version = "0.22", features = ["web-sys"] }
|
||||
rand = "0.7.2"
|
||||
bytemuck = "1"
|
||||
futures = "0.3"
|
||||
@@ -76,3 +75,93 @@ test = true
|
||||
#gfx-backend-dx11 = { version = "0.5.0", path = "../gfx/src/backend/dx11" }
|
||||
#gfx-descriptor = { version = "0.1.0", path = "../gfx-extras/gfx-descriptor" }
|
||||
#gfx-memory = { version = "0.1.0", path = "../gfx-extras/gfx-memory" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
env_logger = "0.7"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen = "0.2.59"
|
||||
web-sys = { version = "0.3.36", features = [
|
||||
"Document",
|
||||
"Navigator",
|
||||
"Node",
|
||||
"NodeList",
|
||||
"Gpu",
|
||||
"GpuAdapter",
|
||||
"GpuBindGroup",
|
||||
"GpuBindGroupBinding",
|
||||
"GpuBindGroupDescriptor",
|
||||
"GpuBindGroupLayout",
|
||||
"GpuBindGroupLayoutBinding",
|
||||
"GpuBindGroupLayoutDescriptor",
|
||||
"GpuBlendDescriptor",
|
||||
"GpuBlendFactor",
|
||||
"GpuBlendOperation",
|
||||
"GpuBindingType",
|
||||
"GpuBuffer",
|
||||
"GpuBufferBinding",
|
||||
"GpuBufferDescriptor",
|
||||
"GpuCanvasContext",
|
||||
"GpuColorDict",
|
||||
"GpuColorStateDescriptor",
|
||||
"GpuCommandBuffer",
|
||||
"GpuCommandBufferDescriptor",
|
||||
"GpuCommandEncoder",
|
||||
"GpuCommandEncoderDescriptor",
|
||||
"GpuCompareFunction",
|
||||
"GpuComputePassDescriptor",
|
||||
"GpuComputePassEncoder",
|
||||
"GpuComputePipeline",
|
||||
"GpuComputePipelineDescriptor",
|
||||
"GpuCullMode",
|
||||
"GpuDepthStencilStateDescriptor",
|
||||
"GpuDevice",
|
||||
"GpuDeviceDescriptor",
|
||||
"GpuExtent3dDict",
|
||||
"GpuFrontFace",
|
||||
"GpuIndexFormat",
|
||||
"GpuInputStepMode",
|
||||
"GpuLimits",
|
||||
"GpuLoadOp",
|
||||
"GpuPipelineLayout",
|
||||
"GpuPipelineLayoutDescriptor",
|
||||
"GpuPowerPreference",
|
||||
"GpuPrimitiveTopology",
|
||||
"GpuProgrammableStageDescriptor",
|
||||
"GpuQueue",
|
||||
"GpuRasterizationStateDescriptor",
|
||||
"GpuRenderPassColorAttachmentDescriptor",
|
||||
"GpuRenderPassDepthStencilAttachmentDescriptor",
|
||||
"GpuRenderPassDescriptor",
|
||||
"GpuRenderPassEncoder",
|
||||
"GpuRenderPipeline",
|
||||
"GpuRenderPipelineDescriptor",
|
||||
"GpuRequestAdapterOptions",
|
||||
"GpuSampler",
|
||||
"GpuShaderModule",
|
||||
"GpuShaderModuleDescriptor",
|
||||
"GpuStencilOperation",
|
||||
"GpuStencilStateFaceDescriptor",
|
||||
"GpuStoreOp",
|
||||
"GpuSwapChain",
|
||||
"GpuSwapChainDescriptor",
|
||||
"GpuTexture",
|
||||
"GpuTextureDescriptor",
|
||||
"GpuTextureDimension",
|
||||
"GpuTextureFormat",
|
||||
"GpuTextureViewDimension",
|
||||
"GpuTextureView",
|
||||
"GpuVertexAttributeDescriptor",
|
||||
"GpuVertexBufferLayoutDescriptor",
|
||||
"GpuVertexFormat",
|
||||
"GpuVertexStateDescriptor",
|
||||
"GpuVertexAttributeDescriptor",
|
||||
"HtmlCanvasElement",
|
||||
"Window",
|
||||
]}
|
||||
js-sys = "0.3.36"
|
||||
wasm-bindgen-futures = "0.4.9"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
console_log = "0.1.2"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<u32> {
|
||||
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<E: Example>(title: &str) {
|
||||
use winit::{
|
||||
event,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
};
|
||||
async fn run_async<E: Example>(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,7 @@ async fn run_async<E: Example>(title: &str) {
|
||||
|
||||
let mut sc_desc = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
@@ -185,7 +141,36 @@ async fn run_async<E: Example>(title: &str) {
|
||||
}
|
||||
|
||||
pub fn run<E: Example>(title: &str) {
|
||||
futures::executor::block_on(run_async::<E>(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::<E>(event_loop, window));
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
// On wasm, append the canvas to the document body
|
||||
web_sys::window()
|
||||
.and_then(|win| win.document())
|
||||
.and_then(|doc| doc.body())
|
||||
.and_then(|body| {
|
||||
body.append_child(&web_sys::Element::from(window.canvas()))
|
||||
.ok()
|
||||
})
|
||||
.expect("couldn't append canvas to document body");
|
||||
wasm_bindgen_futures::spawn_local(run_async::<E>(event_loop, window));
|
||||
}
|
||||
}
|
||||
|
||||
// This allows treating the framework as a standalone example,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{convert::TryInto as _, str::FromStr};
|
||||
use std::{convert::TryInto, str::FromStr};
|
||||
|
||||
async fn run() {
|
||||
let numbers = if std::env::args().len() == 1 {
|
||||
let numbers = if std::env::args().len() <= 1 {
|
||||
let default = vec![1, 2, 3, 4];
|
||||
log::info!("No numbers were provided, defaulting to {:?}", default);
|
||||
default
|
||||
@@ -124,9 +124,20 @@ async fn execute_gpu(numbers: Vec<u32>) -> Vec<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))]
|
||||
pub fn wasm_main() {
|
||||
console_log::init().expect("could not initialize log");
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
wasm_bindgen_futures::spawn_local(run());
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
futures::executor::block_on(run());
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
}),
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: &[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
@@ -84,7 +84,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
|
||||
let mut sc_desc = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
@@ -93,7 +93,8 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
// TODO: ControlFlow::Poll;
|
||||
*control_flow = ControlFlow::Wait;
|
||||
match event {
|
||||
Event::MainEventsCleared => window.request_redraw(),
|
||||
Event::WindowEvent {
|
||||
@@ -136,9 +137,34 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))]
|
||||
pub fn wasm_main() {
|
||||
main();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||
env_logger::init();
|
||||
futures::executor::block_on(run(event_loop, window));
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
env_logger::init();
|
||||
futures::executor::block_on(run(event_loop, window));
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
// On wasm, append the canvas to the document body
|
||||
web_sys::window()
|
||||
.and_then(|win| win.document())
|
||||
.and_then(|doc| doc.body())
|
||||
.and_then(|body| {
|
||||
body.append_child(&web_sys::Element::from(window.canvas()))
|
||||
.ok()
|
||||
})
|
||||
.expect("couldn't append canvas to document body");
|
||||
wasm_bindgen_futures::spawn_local(run(event_loop, window));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<wgpu::CommandBuffer>) {
|
||||
println!("Press left/right arrow keys to change sample_count.");
|
||||
log::info!("Press left/right arrow keys to change sample_count.");
|
||||
let sample_count = 4;
|
||||
|
||||
let vs_bytes =
|
||||
framework::load_glsl(include_str!("shader.vert"), framework::ShaderStage::Vertex);
|
||||
let fs_bytes = framework::load_glsl(
|
||||
include_str!("shader.frag"),
|
||||
framework::ShaderStage::Fragment,
|
||||
);
|
||||
let vs_module = device.create_shader_module(&vs_bytes);
|
||||
let fs_module = device.create_shader_module(&fs_bytes);
|
||||
let vs_bytes = include_bytes!("shader.vert.spv");
|
||||
let fs_bytes = include_bytes!("shader.frag.spv");
|
||||
let vs_module = device
|
||||
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
|
||||
let fs_module = device
|
||||
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[],
|
||||
@@ -270,6 +268,12 @@ impl framework::Example for Example {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(start))]
|
||||
pub fn wasm_main() {
|
||||
main();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
framework::run::<Example>("msaa-line");
|
||||
}
|
||||
|
||||
BIN
wgpu/examples/msaa-line/shader.frag.spv
Normal file
BIN
wgpu/examples/msaa-line/shader.frag.spv
Normal file
Binary file not shown.
BIN
wgpu/examples/msaa-line/shader.vert.spv
Normal file
BIN
wgpu/examples/msaa-line/shader.vert.spv
Normal file
Binary file not shown.
@@ -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;
|
||||
|
||||
671
wgpu/src/backend/native.rs
Normal file
671
wgpu/src/backend/native.rs
Normal file
@@ -0,0 +1,671 @@
|
||||
use wgn;
|
||||
|
||||
use crate::{
|
||||
backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource,
|
||||
BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor,
|
||||
PipelineLayoutDescriptor, RenderPipelineDescriptor, TextureDescriptor, TextureViewDimension,
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use smallvec::SmallVec;
|
||||
use std::{ffi::CString, future::Future, ops::Range, ptr, slice};
|
||||
|
||||
pub type AdapterId = wgc::id::AdapterId;
|
||||
pub type DeviceId = wgc::id::DeviceId;
|
||||
pub type QueueId = wgc::id::QueueId;
|
||||
pub type ShaderModuleId = wgc::id::ShaderModuleId;
|
||||
pub type BindGroupLayoutId = wgc::id::BindGroupLayoutId;
|
||||
pub type BindGroupId = wgc::id::BindGroupId;
|
||||
pub type TextureViewId = wgc::id::TextureViewId;
|
||||
pub type SamplerId = wgc::id::SamplerId;
|
||||
pub type BufferId = wgc::id::BufferId;
|
||||
pub type TextureId = wgc::id::TextureId;
|
||||
pub type PipelineLayoutId = wgc::id::PipelineLayoutId;
|
||||
pub type RenderPipelineId = wgc::id::RenderPipelineId;
|
||||
pub type ComputePipelineId = wgc::id::ComputePipelineId;
|
||||
pub type CommandEncoderId = wgc::id::CommandEncoderId;
|
||||
pub type ComputePassId = wgc::id::ComputePassId;
|
||||
pub type CommandBufferId = wgc::id::CommandBufferId;
|
||||
pub type SurfaceId = wgc::id::SurfaceId;
|
||||
pub type SwapChainId = wgc::id::SwapChainId;
|
||||
pub type RenderPassEncoderId = wgc::id::RenderPassId;
|
||||
|
||||
pub(crate) async fn request_adapter(
|
||||
options: &crate::RequestAdapterOptions<'_>,
|
||||
backends: wgt::BackendBit,
|
||||
) -> Option<AdapterId> {
|
||||
unsafe extern "C" fn adapter_callback(
|
||||
id: Option<wgc::id::AdapterId>,
|
||||
user_data: *mut std::ffi::c_void,
|
||||
) {
|
||||
*(user_data as *mut Option<wgc::id::AdapterId>) = 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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
wgn::wgpu_device_create_render_pipeline(
|
||||
*device,
|
||||
&pipe::RenderPipelineDescriptor {
|
||||
layout: desc.layout.id,
|
||||
vertex_stage,
|
||||
fragment_stage: fragment_stage
|
||||
.as_ref()
|
||||
.map_or(ptr::null(), |fs| fs as *const _),
|
||||
rasterization_state: desc
|
||||
.rasterization_state
|
||||
.as_ref()
|
||||
.map_or(ptr::null(), |p| p as *const _),
|
||||
primitive_topology: desc.primitive_topology,
|
||||
color_states: temp_color_states.as_ptr(),
|
||||
color_states_length: temp_color_states.len(),
|
||||
depth_stencil_state: desc
|
||||
.depth_stencil_state
|
||||
.as_ref()
|
||||
.map_or(ptr::null(), |p| p as *const _),
|
||||
vertex_state: pipe::VertexStateDescriptor {
|
||||
index_format: desc.vertex_state.index_format,
|
||||
vertex_buffers: temp_vertex_buffers.as_ptr(),
|
||||
vertex_buffers_length: temp_vertex_buffers.len(),
|
||||
},
|
||||
sample_count: desc.sample_count,
|
||||
sample_mask: desc.sample_mask,
|
||||
alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_compute_pipeline(
|
||||
device: &DeviceId,
|
||||
desc: &ComputePipelineDescriptor,
|
||||
) -> ComputePipelineId {
|
||||
use wgc::pipeline as pipe;
|
||||
|
||||
let entry_point = CString::new(desc.compute_stage.entry_point).unwrap();
|
||||
|
||||
wgn::wgpu_device_create_compute_pipeline(
|
||||
*device,
|
||||
&pipe::ComputePipelineDescriptor {
|
||||
layout: desc.layout.id,
|
||||
compute_stage: pipe::ProgrammableStageDescriptor {
|
||||
module: desc.compute_stage.module.id,
|
||||
entry_point: entry_point.as_ptr(),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) type CreateBufferMappedDetail = BufferDetail;
|
||||
|
||||
pub(crate) fn create_buffer_mapped<'a>(
|
||||
device: &DeviceId,
|
||||
desc: &BufferDescriptor,
|
||||
) -> crate::CreateBufferMapped<'a> {
|
||||
let owned_label = OwnedLabel::new(desc.label.as_deref());
|
||||
let mut data_ptr: *mut u8 = std::ptr::null_mut();
|
||||
unsafe {
|
||||
let id = wgn::wgpu_device_create_buffer_mapped(
|
||||
*device,
|
||||
&wgt::BufferDescriptor {
|
||||
label: owned_label.as_ptr(),
|
||||
size: desc.size,
|
||||
usage: desc.usage,
|
||||
},
|
||||
&mut data_ptr as *mut *mut u8,
|
||||
);
|
||||
let mapped_data = std::slice::from_raw_parts_mut(data_ptr as *mut u8, desc.size as usize);
|
||||
crate::CreateBufferMapped {
|
||||
id,
|
||||
mapped_data,
|
||||
detail: CreateBufferMappedDetail { device_id: *device },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq)]
|
||||
pub(crate) struct BufferDetail {
|
||||
/// On native we need to track the device in order to later destroy the
|
||||
/// buffer.
|
||||
device_id: DeviceId,
|
||||
}
|
||||
|
||||
pub(crate) fn create_buffer_mapped_finish(
|
||||
create_buffer_mapped: crate::CreateBufferMapped<'_>,
|
||||
) -> crate::Buffer {
|
||||
buffer_unmap(&create_buffer_mapped.id);
|
||||
crate::Buffer {
|
||||
id: create_buffer_mapped.id,
|
||||
detail: BufferDetail {
|
||||
device_id: create_buffer_mapped.detail.device_id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_unmap(buffer: &BufferId) {
|
||||
wgn::wgpu_buffer_unmap(*buffer);
|
||||
}
|
||||
|
||||
pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer {
|
||||
let owned_label = OwnedLabel::new(desc.label.as_deref());
|
||||
crate::Buffer {
|
||||
id: wgn::wgpu_device_create_buffer(
|
||||
*device,
|
||||
&wgt::BufferDescriptor {
|
||||
label: owned_label.as_ptr(),
|
||||
size: desc.size,
|
||||
usage: desc.usage,
|
||||
},
|
||||
),
|
||||
detail: BufferDetail { device_id: *device },
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId {
|
||||
let owned_label = OwnedLabel::new(desc.label.as_deref());
|
||||
wgn::wgpu_device_create_texture(
|
||||
*device,
|
||||
&wgt::TextureDescriptor {
|
||||
label: owned_label.as_ptr(),
|
||||
size: desc.size,
|
||||
mip_level_count: desc.mip_level_count,
|
||||
sample_count: desc.sample_count,
|
||||
dimension: desc.dimension,
|
||||
format: desc.format,
|
||||
usage: desc.usage,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_command_encoder(
|
||||
device: &DeviceId,
|
||||
desc: &CommandEncoderDescriptor,
|
||||
) -> CommandEncoderId {
|
||||
let owned_label = OwnedLabel::new(desc.label.as_deref());
|
||||
wgn::wgpu_device_create_command_encoder(
|
||||
*device,
|
||||
Some(&wgt::CommandEncoderDescriptor {
|
||||
label: owned_label.as_ptr(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn copy_buffer_to_buffer(
|
||||
command_encoder: &CommandEncoderId,
|
||||
source: &crate::Buffer,
|
||||
source_offset: wgt::BufferAddress,
|
||||
destination: &crate::Buffer,
|
||||
destination_offset: wgt::BufferAddress,
|
||||
copy_size: wgt::BufferAddress,
|
||||
) {
|
||||
wgn::wgpu_command_encoder_copy_buffer_to_buffer(
|
||||
*command_encoder,
|
||||
source.id,
|
||||
source_offset,
|
||||
destination.id,
|
||||
destination_offset,
|
||||
copy_size,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId {
|
||||
unsafe { wgn::wgpu_command_encoder_begin_compute_pass(*command_encoder, None) }
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_set_pipeline(
|
||||
compute_pass: &ComputePassId,
|
||||
pipeline: &ComputePipelineId,
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_compute_pass_set_pipeline(compute_pass.as_mut().unwrap(), *pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_set_bind_group<'a>(
|
||||
compute_pass: &ComputePassId,
|
||||
index: u32,
|
||||
bind_group: &BindGroupId,
|
||||
offsets: &[wgt::DynamicOffset],
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_compute_pass_set_bind_group(
|
||||
compute_pass.as_mut().unwrap(),
|
||||
index,
|
||||
*bind_group,
|
||||
offsets.as_ptr(),
|
||||
offsets.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_dispatch(compute_pass: &ComputePassId, x: u32, y: u32, z: u32) {
|
||||
unsafe {
|
||||
wgn::wgpu_compute_pass_dispatch(compute_pass.as_mut().unwrap(), x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_dispatch_indirect(
|
||||
compute_pass: &ComputePassId,
|
||||
indirect_buffer: &BufferId,
|
||||
indirect_offset: wgt::BufferAddress,
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_compute_pass_dispatch_indirect(
|
||||
compute_pass.as_mut().unwrap(),
|
||||
*indirect_buffer,
|
||||
indirect_offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_end_pass(compute_pass: &ComputePassId) {
|
||||
unsafe {
|
||||
wgn::wgpu_compute_pass_end_pass(*compute_pass);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn command_encoder_finish(command_encoder: &CommandEncoderId) -> CommandBufferId {
|
||||
wgn::wgpu_command_encoder_finish(*command_encoder, None)
|
||||
}
|
||||
|
||||
pub(crate) fn queue_submit(queue: &QueueId, command_buffers: &[crate::CommandBuffer]) {
|
||||
let temp_command_buffers = command_buffers
|
||||
.iter()
|
||||
.map(|cb| cb.id)
|
||||
.collect::<SmallVec<[_; 4]>>();
|
||||
|
||||
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<Output = Result<crate::BufferReadMapping, crate::BufferAsyncErr>> {
|
||||
let (future, completion) = native_gpu_future::new_gpu_future(buffer.id, size);
|
||||
|
||||
extern "C" fn buffer_map_read_future_wrapper(
|
||||
status: wgc::resource::BufferMapAsyncStatus,
|
||||
data: *const u8,
|
||||
user_data: *mut u8,
|
||||
) {
|
||||
let completion =
|
||||
unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) };
|
||||
let (buffer_id, size) = completion.get_buffer_info();
|
||||
|
||||
if let wgc::resource::BufferMapAsyncStatus::Success = status {
|
||||
completion.complete(Ok(crate::BufferReadMapping {
|
||||
detail: BufferReadMappingDetail {
|
||||
data,
|
||||
size: size as usize,
|
||||
buffer_id,
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
completion.complete(Err(crate::BufferAsyncErr));
|
||||
}
|
||||
}
|
||||
|
||||
wgn::wgpu_buffer_map_read_async(
|
||||
buffer.id,
|
||||
start,
|
||||
size,
|
||||
buffer_map_read_future_wrapper,
|
||||
completion.to_raw() as _,
|
||||
);
|
||||
|
||||
future
|
||||
}
|
||||
|
||||
pub(crate) struct BufferReadMappingDetail {
|
||||
data: *const u8,
|
||||
size: usize,
|
||||
pub(crate) buffer_id: BufferId,
|
||||
}
|
||||
|
||||
impl BufferReadMappingDetail {
|
||||
pub(crate) fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.data as *const u8, self.size) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_surface<W: raw_window_handle::HasRawWindowHandle>(window: &W) -> SurfaceId {
|
||||
wgn::wgpu_create_surface(window.raw_window_handle())
|
||||
}
|
||||
|
||||
pub(crate) fn device_create_swap_chain(
|
||||
device: &DeviceId,
|
||||
surface: &SurfaceId,
|
||||
desc: &wgt::SwapChainDescriptor,
|
||||
) -> SwapChainId {
|
||||
wgn::wgpu_device_create_swap_chain(*device, *surface, desc)
|
||||
}
|
||||
|
||||
pub(crate) fn swap_chain_get_next_texture(swap_chain: &SwapChainId) -> Option<TextureViewId> {
|
||||
wgn::wgpu_swap_chain_get_next_texture(*swap_chain).view_id
|
||||
}
|
||||
|
||||
pub(crate) fn command_encoder_begin_render_pass<'a>(
|
||||
command_encoder: &CommandEncoderId,
|
||||
desc: &crate::RenderPassDescriptor<'a, '_>,
|
||||
) -> RenderPassEncoderId {
|
||||
let colors = desc
|
||||
.color_attachments
|
||||
.iter()
|
||||
.map(|ca| wgc::command::RenderPassColorAttachmentDescriptor {
|
||||
attachment: ca.attachment.id,
|
||||
resolve_target: ca.resolve_target.map(|rt| rt.id),
|
||||
load_op: ca.load_op,
|
||||
store_op: ca.store_op,
|
||||
clear_color: ca.clear_color,
|
||||
})
|
||||
.collect::<ArrayVec<[_; 4]>>();
|
||||
|
||||
let depth_stencil = desc.depth_stencil_attachment.as_ref().map(|dsa| {
|
||||
wgc::command::RenderPassDepthStencilAttachmentDescriptor {
|
||||
attachment: dsa.attachment.id,
|
||||
depth_load_op: dsa.depth_load_op,
|
||||
depth_store_op: dsa.depth_store_op,
|
||||
clear_depth: dsa.clear_depth,
|
||||
stencil_load_op: dsa.stencil_load_op,
|
||||
stencil_store_op: dsa.stencil_store_op,
|
||||
clear_stencil: dsa.clear_stencil,
|
||||
}
|
||||
});
|
||||
|
||||
unsafe {
|
||||
wgn::wgpu_command_encoder_begin_render_pass(
|
||||
*command_encoder,
|
||||
&wgc::command::RenderPassDescriptor {
|
||||
color_attachments: colors.as_ptr(),
|
||||
color_attachments_length: colors.len(),
|
||||
depth_stencil_attachment: depth_stencil.as_ref(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_set_pipeline(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
pipeline: &RenderPipelineId,
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_render_pass_set_pipeline(render_pass.as_mut().unwrap(), *pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_set_bind_group(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
index: u32,
|
||||
bind_group: &BindGroupId,
|
||||
offsets: &[wgt::DynamicOffset],
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_render_pass_set_bind_group(
|
||||
render_pass.as_mut().unwrap(),
|
||||
index,
|
||||
*bind_group,
|
||||
offsets.as_ptr(),
|
||||
offsets.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_set_vertex_buffer<'a>(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
slot: u32,
|
||||
buffer: &'a crate::Buffer,
|
||||
offset: wgt::BufferAddress,
|
||||
size: wgt::BufferAddress,
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_render_pass_set_vertex_buffer(
|
||||
render_pass.as_mut().unwrap(),
|
||||
slot,
|
||||
buffer.id,
|
||||
offset,
|
||||
size,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_draw(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
vertices: Range<u32>,
|
||||
instances: Range<u32>,
|
||||
) {
|
||||
unsafe {
|
||||
wgn::wgpu_render_pass_draw(
|
||||
render_pass.as_mut().unwrap(),
|
||||
vertices.end - vertices.start,
|
||||
instances.end - instances.start,
|
||||
vertices.start,
|
||||
instances.start,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) {
|
||||
unsafe {
|
||||
wgn::wgpu_render_pass_end_pass(*render_pass);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn texture_create_default_view(texture: &TextureId) -> TextureViewId {
|
||||
wgn::wgpu_texture_create_view(*texture, None)
|
||||
}
|
||||
|
||||
pub(crate) fn swap_chain_present(swap_chain: &SwapChainId) {
|
||||
wgn::wgpu_swap_chain_present(*swap_chain);
|
||||
}
|
||||
|
||||
pub(crate) fn device_poll(device: &DeviceId, maintain: crate::Maintain) {
|
||||
wgn::wgpu_device_poll(
|
||||
*device,
|
||||
match maintain {
|
||||
crate::Maintain::Poll => false,
|
||||
crate::Maintain::Wait => true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
struct OwnedLabel(Option<CString>);
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
922
wgpu/src/backend/web.rs
Normal file
922
wgpu/src/backend/web.rs
Normal file
@@ -0,0 +1,922 @@
|
||||
use crate::{
|
||||
BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor,
|
||||
CommandEncoderDescriptor, ComputePipelineDescriptor, PipelineLayoutDescriptor,
|
||||
ProgrammableStageDescriptor, RenderPipelineDescriptor, TextureDescriptor, TextureViewDimension,
|
||||
};
|
||||
|
||||
use std::ops::Range;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub type AdapterId = web_sys::GpuAdapter;
|
||||
pub type DeviceId = web_sys::GpuDevice;
|
||||
pub type QueueId = web_sys::GpuQueue;
|
||||
pub type ShaderModuleId = web_sys::GpuShaderModule;
|
||||
pub type BindGroupLayoutId = web_sys::GpuBindGroupLayout;
|
||||
pub type BindGroupId = web_sys::GpuBindGroup;
|
||||
pub type TextureViewId = web_sys::GpuTextureView;
|
||||
pub type SamplerId = web_sys::GpuSampler;
|
||||
pub type BufferId = web_sys::GpuBuffer;
|
||||
pub type TextureId = web_sys::GpuTexture;
|
||||
pub type PipelineLayoutId = web_sys::GpuPipelineLayout;
|
||||
pub type RenderPipelineId = web_sys::GpuRenderPipeline;
|
||||
pub type ComputePipelineId = web_sys::GpuComputePipeline;
|
||||
pub type CommandEncoderId = web_sys::GpuCommandEncoder;
|
||||
pub type ComputePassId = web_sys::GpuComputePassEncoder;
|
||||
pub type CommandBufferId = web_sys::GpuCommandBuffer;
|
||||
pub type SurfaceId = web_sys::GpuCanvasContext;
|
||||
pub type SwapChainId = web_sys::GpuSwapChain;
|
||||
pub type RenderPassEncoderId = web_sys::GpuRenderPassEncoder;
|
||||
|
||||
fn gpu() -> web_sys::Gpu {
|
||||
web_sys::window().unwrap().navigator().gpu()
|
||||
}
|
||||
|
||||
pub(crate) async fn request_adapter(
|
||||
options: &crate::RequestAdapterOptions<'_>,
|
||||
backends: wgt::BackendBit,
|
||||
) -> Option<AdapterId> {
|
||||
if !backends.contains(wgt::BackendBit::BROWSER_WEBGPU) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut mapped_options = web_sys::GpuRequestAdapterOptions::new();
|
||||
let mapped_power_preference = match options.power_preference {
|
||||
wgt::PowerPreference::LowPower => web_sys::GpuPowerPreference::LowPower,
|
||||
wgt::PowerPreference::HighPerformance | wgt::PowerPreference::Default => {
|
||||
web_sys::GpuPowerPreference::HighPerformance
|
||||
}
|
||||
};
|
||||
mapped_options.power_preference(mapped_power_preference);
|
||||
let adapter_promise = gpu().request_adapter_with_options(&mapped_options);
|
||||
Some(
|
||||
wasm_bindgen_futures::JsFuture::from(adapter_promise)
|
||||
.await
|
||||
.expect("Unable to get adapter")
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn request_device_and_queue(
|
||||
adapter: &AdapterId,
|
||||
desc: Option<&wgt::DeviceDescriptor>,
|
||||
) -> (DeviceId, QueueId) {
|
||||
let device_promise = match desc {
|
||||
Some(d) => {
|
||||
let mut mapped_descriptor = web_sys::GpuDeviceDescriptor::new();
|
||||
// TODO: Extensions
|
||||
let mut mapped_limits = web_sys::GpuLimits::new();
|
||||
mapped_limits.max_bind_groups(d.limits.max_bind_groups);
|
||||
// TODO: Other fields
|
||||
mapped_descriptor.limits(&mapped_limits);
|
||||
adapter.request_device_with_descriptor(&mapped_descriptor)
|
||||
}
|
||||
None => adapter.request_device(),
|
||||
};
|
||||
let js_value = wasm_bindgen_futures::JsFuture::from(device_promise)
|
||||
.await
|
||||
.expect("Unable to get device");
|
||||
let device_id = DeviceId::from(js_value);
|
||||
let queue_id = device_id.default_queue();
|
||||
(device_id, queue_id)
|
||||
}
|
||||
|
||||
pub(crate) fn create_shader_module(device: &DeviceId, spv: &[u32]) -> ShaderModuleId {
|
||||
let desc = web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(spv));
|
||||
device.create_shader_module(&desc)
|
||||
}
|
||||
|
||||
pub(crate) fn create_bind_group_layout(
|
||||
device: &DeviceId,
|
||||
desc: &BindGroupLayoutDescriptor,
|
||||
) -> BindGroupLayoutId {
|
||||
use web_sys::GpuBindingType as bt;
|
||||
use web_sys::GpuTextureViewDimension as tvd;
|
||||
|
||||
let mapped_bindings = desc
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|bind| {
|
||||
let mapped_type = match bind.ty {
|
||||
BindingType::UniformBuffer { .. } => bt::UniformBuffer,
|
||||
BindingType::StorageBuffer {
|
||||
readonly: false, ..
|
||||
} => bt::StorageBuffer,
|
||||
BindingType::StorageBuffer { readonly: true, .. } => bt::ReadonlyStorageBuffer,
|
||||
BindingType::Sampler { comparison: false } => bt::Sampler,
|
||||
BindingType::Sampler { .. } => unimplemented!(), // TODO: bt::ComparisonSampler,
|
||||
BindingType::SampledTexture { .. } => bt::SampledTexture,
|
||||
BindingType::StorageTexture { readonly: true, .. } => {
|
||||
unimplemented!() // TODO: bt::ReadonlyStorageTexture
|
||||
}
|
||||
BindingType::StorageTexture { .. } => {
|
||||
unimplemented!() // TODO: bt::WriteonlyStorageTexture
|
||||
}
|
||||
};
|
||||
|
||||
let mapped_dynamic = match bind.ty {
|
||||
BindingType::UniformBuffer { dynamic }
|
||||
| BindingType::StorageBuffer { dynamic, .. } => dynamic,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mapped_multisampled = match bind.ty {
|
||||
BindingType::SampledTexture { multisampled, .. } => multisampled,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mapped_view_dimension = match bind.ty {
|
||||
BindingType::SampledTexture { dimension, .. }
|
||||
| BindingType::StorageTexture { dimension, .. } => match dimension {
|
||||
TextureViewDimension::D1 => tvd::N1d,
|
||||
TextureViewDimension::D2 => tvd::N2d,
|
||||
TextureViewDimension::D2Array => tvd::N2dArray,
|
||||
TextureViewDimension::Cube => tvd::Cube,
|
||||
TextureViewDimension::CubeArray => tvd::CubeArray,
|
||||
TextureViewDimension::D3 => tvd::N3d,
|
||||
},
|
||||
_ => tvd::N2d,
|
||||
};
|
||||
|
||||
let mut mapped_binding = web_sys::GpuBindGroupLayoutBinding::new(
|
||||
bind.binding,
|
||||
mapped_type,
|
||||
bind.visibility.bits(),
|
||||
);
|
||||
mapped_binding.has_dynamic_offset(mapped_dynamic);
|
||||
mapped_binding.multisampled(mapped_multisampled);
|
||||
mapped_binding.texture_dimension(mapped_view_dimension);
|
||||
|
||||
// TODO: Texture component type, storage texture format
|
||||
|
||||
mapped_binding
|
||||
})
|
||||
.collect::<js_sys::Array>();
|
||||
|
||||
let mapped_desc = web_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings);
|
||||
device.create_bind_group_layout(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn create_bind_group(device: &DeviceId, desc: &BindGroupDescriptor) -> BindGroupId {
|
||||
let mapped_bindings = desc
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|binding| {
|
||||
let mapped_resource = match binding.resource {
|
||||
BindingResource::Buffer {
|
||||
ref buffer,
|
||||
ref range,
|
||||
} => {
|
||||
let mut mapped_buffer_binding = web_sys::GpuBufferBinding::new(&buffer.id);
|
||||
mapped_buffer_binding.offset(range.start as f64);
|
||||
mapped_buffer_binding.size((range.end - range.start) as f64);
|
||||
JsValue::from(mapped_buffer_binding.clone())
|
||||
}
|
||||
BindingResource::Sampler(ref sampler) => JsValue::from(sampler.id.clone()),
|
||||
BindingResource::TextureView(ref texture_view) => {
|
||||
JsValue::from(texture_view.id.clone())
|
||||
}
|
||||
};
|
||||
|
||||
web_sys::GpuBindGroupBinding::new(binding.binding, &mapped_resource)
|
||||
})
|
||||
.collect::<js_sys::Array>();
|
||||
|
||||
let mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_bindings, &desc.layout.id);
|
||||
device.create_bind_group(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn create_pipeline_layout(
|
||||
device: &DeviceId,
|
||||
desc: &PipelineLayoutDescriptor,
|
||||
) -> PipelineLayoutId {
|
||||
//TODO: avoid allocation here
|
||||
let temp_layouts = desc
|
||||
.bind_group_layouts
|
||||
.iter()
|
||||
.map(|bgl| bgl.id.clone())
|
||||
.collect::<js_sys::Array>();
|
||||
let mapped_desc = web_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts);
|
||||
device.create_pipeline_layout(&mapped_desc)
|
||||
}
|
||||
|
||||
fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTextureFormat {
|
||||
use web_sys::GpuTextureFormat as tf;
|
||||
use wgt::TextureFormat;
|
||||
match texture_format {
|
||||
TextureFormat::R8Unorm => tf::R8unorm,
|
||||
TextureFormat::R8Snorm => tf::R8snorm,
|
||||
TextureFormat::R8Uint => tf::R8uint,
|
||||
TextureFormat::R8Sint => tf::R8sint,
|
||||
TextureFormat::R16Uint => tf::R16uint,
|
||||
TextureFormat::R16Sint => tf::R16sint,
|
||||
TextureFormat::R16Float => tf::R16float,
|
||||
TextureFormat::Rg8Unorm => tf::Rg8unorm,
|
||||
TextureFormat::Rg8Snorm => tf::Rg8snorm,
|
||||
TextureFormat::Rg8Uint => tf::Rg8uint,
|
||||
TextureFormat::Rg8Sint => tf::Rg8sint,
|
||||
TextureFormat::R32Uint => tf::R32uint,
|
||||
TextureFormat::R32Sint => tf::R32sint,
|
||||
TextureFormat::R32Float => tf::R32float,
|
||||
TextureFormat::Rg16Uint => tf::Rg16uint,
|
||||
TextureFormat::Rg16Sint => tf::Rg16sint,
|
||||
TextureFormat::Rg16Float => tf::Rg16float,
|
||||
TextureFormat::Rgba8Unorm => tf::Rgba8unorm,
|
||||
TextureFormat::Rgba8UnormSrgb => tf::Rgba8unormSrgb,
|
||||
TextureFormat::Rgba8Snorm => tf::Rgba8snorm,
|
||||
TextureFormat::Rgba8Uint => tf::Rgba8uint,
|
||||
TextureFormat::Rgba8Sint => tf::Rgba8sint,
|
||||
TextureFormat::Bgra8Unorm => tf::Bgra8unorm,
|
||||
TextureFormat::Bgra8UnormSrgb => tf::Bgra8unormSrgb,
|
||||
TextureFormat::Rgb10a2Unorm => tf::Rgb10a2unorm,
|
||||
TextureFormat::Rg11b10Float => tf::Rg11b10float,
|
||||
TextureFormat::Rg32Uint => tf::Rg32uint,
|
||||
TextureFormat::Rg32Sint => tf::Rg32sint,
|
||||
TextureFormat::Rg32Float => tf::Rg32float,
|
||||
TextureFormat::Rgba16Uint => tf::Rgba16uint,
|
||||
TextureFormat::Rgba16Sint => tf::Rgba16sint,
|
||||
TextureFormat::Rgba16Float => tf::Rgba16float,
|
||||
TextureFormat::Rgba32Uint => tf::Rgba32uint,
|
||||
TextureFormat::Rgba32Sint => tf::Rgba32sint,
|
||||
TextureFormat::Rgba32Float => tf::Rgba32float,
|
||||
TextureFormat::Depth32Float => tf::Depth32float,
|
||||
TextureFormat::Depth24Plus => tf::Depth24plus,
|
||||
TextureFormat::Depth24PlusStencil8 => tf::Depth24plusStencil8,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_stage_descriptor(
|
||||
desc: &ProgrammableStageDescriptor,
|
||||
) -> web_sys::GpuProgrammableStageDescriptor {
|
||||
web_sys::GpuProgrammableStageDescriptor::new(desc.entry_point, &desc.module.id)
|
||||
}
|
||||
|
||||
fn map_cull_mode(cull_mode: wgt::CullMode) -> web_sys::GpuCullMode {
|
||||
use web_sys::GpuCullMode as cm;
|
||||
use wgt::CullMode;
|
||||
match cull_mode {
|
||||
CullMode::None => cm::None,
|
||||
CullMode::Front => cm::Front,
|
||||
CullMode::Back => cm::Back,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_front_face(front_face: wgt::FrontFace) -> web_sys::GpuFrontFace {
|
||||
use web_sys::GpuFrontFace as ff;
|
||||
use wgt::FrontFace;
|
||||
match front_face {
|
||||
FrontFace::Ccw => ff::Ccw,
|
||||
FrontFace::Cw => ff::Cw,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_rasterization_state_descriptor(
|
||||
desc: &wgt::RasterizationStateDescriptor,
|
||||
) -> web_sys::GpuRasterizationStateDescriptor {
|
||||
let mut mapped = web_sys::GpuRasterizationStateDescriptor::new();
|
||||
mapped.cull_mode(map_cull_mode(desc.cull_mode));
|
||||
mapped.depth_bias(desc.depth_bias);
|
||||
mapped.depth_bias_clamp(desc.depth_bias_clamp);
|
||||
mapped.depth_bias_slope_scale(desc.depth_bias_slope_scale);
|
||||
mapped.front_face(map_front_face(desc.front_face));
|
||||
mapped
|
||||
}
|
||||
|
||||
fn map_compare_function(compare_fn: wgt::CompareFunction) -> Option<web_sys::GpuCompareFunction> {
|
||||
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::<js_sys::Array>();
|
||||
|
||||
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::<js_sys::Array>();
|
||||
|
||||
let mut mapped = web_sys::GpuVertexStateDescriptor::new();
|
||||
mapped.index_format(map_index_format(desc.vertex_state.index_format));
|
||||
mapped.vertex_buffers(&mapped_vertex_buffers);
|
||||
mapped
|
||||
}
|
||||
|
||||
fn map_extent_3d(extent: wgt::Extent3d) -> web_sys::GpuExtent3dDict {
|
||||
web_sys::GpuExtent3dDict::new(extent.depth, extent.height, extent.width)
|
||||
}
|
||||
|
||||
fn map_texture_dimension(texture_dimension: wgt::TextureDimension) -> web_sys::GpuTextureDimension {
|
||||
match texture_dimension {
|
||||
wgt::TextureDimension::D1 => web_sys::GpuTextureDimension::N1d,
|
||||
wgt::TextureDimension::D2 => web_sys::GpuTextureDimension::N2d,
|
||||
wgt::TextureDimension::D3 => web_sys::GpuTextureDimension::N3d,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_render_pipeline(
|
||||
device: &DeviceId,
|
||||
desc: &RenderPipelineDescriptor,
|
||||
) -> RenderPipelineId {
|
||||
use web_sys::GpuPrimitiveTopology as pt;
|
||||
|
||||
let mapped_color_states = desc
|
||||
.color_states
|
||||
.iter()
|
||||
.map(|color_state_desc| {
|
||||
let mapped_format = map_texture_format(color_state_desc.format);
|
||||
let mut mapped_color_state_desc = web_sys::GpuColorStateDescriptor::new(mapped_format);
|
||||
mapped_color_state_desc
|
||||
.alpha_blend(&map_blend_descriptor(&color_state_desc.alpha_blend));
|
||||
mapped_color_state_desc
|
||||
.color_blend(&map_blend_descriptor(&color_state_desc.color_blend));
|
||||
mapped_color_state_desc.write_mask(color_state_desc.write_mask.bits());
|
||||
mapped_color_state_desc
|
||||
})
|
||||
.collect::<js_sys::Array>();
|
||||
|
||||
let mapped_primitive_topology = match desc.primitive_topology {
|
||||
wgt::PrimitiveTopology::PointList => pt::PointList,
|
||||
wgt::PrimitiveTopology::LineList => pt::LineList,
|
||||
wgt::PrimitiveTopology::LineStrip => pt::LineStrip,
|
||||
wgt::PrimitiveTopology::TriangleList => pt::TriangleList,
|
||||
wgt::PrimitiveTopology::TriangleStrip => pt::TriangleStrip,
|
||||
};
|
||||
|
||||
let mapped_vertex_stage = map_stage_descriptor(&desc.vertex_stage);
|
||||
|
||||
let mut mapped_desc = web_sys::GpuRenderPipelineDescriptor::new(
|
||||
&desc.layout.id,
|
||||
&mapped_color_states,
|
||||
mapped_primitive_topology,
|
||||
&mapped_vertex_stage,
|
||||
);
|
||||
|
||||
if let Some(ref frag) = desc.fragment_stage {
|
||||
mapped_desc.fragment_stage(&map_stage_descriptor(frag));
|
||||
}
|
||||
|
||||
if let Some(ref rasterization) = desc.rasterization_state {
|
||||
mapped_desc.rasterization_state(&map_rasterization_state_descriptor(rasterization));
|
||||
}
|
||||
|
||||
if let Some(ref depth_stencil) = desc.depth_stencil_state {
|
||||
mapped_desc.depth_stencil_state(&map_depth_stencil_state_descriptor(depth_stencil));
|
||||
}
|
||||
|
||||
mapped_desc.vertex_state(&map_vertex_state_descriptor(&desc));
|
||||
mapped_desc.sample_count(desc.sample_count);
|
||||
mapped_desc.sample_mask(desc.sample_mask);
|
||||
mapped_desc.alpha_to_coverage_enabled(desc.alpha_to_coverage_enabled);
|
||||
|
||||
device.create_render_pipeline(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn create_compute_pipeline(
|
||||
device: &DeviceId,
|
||||
desc: &ComputePipelineDescriptor,
|
||||
) -> ComputePipelineId {
|
||||
let mapped_compute_stage = map_stage_descriptor(&desc.compute_stage);
|
||||
let mapped_desc =
|
||||
web_sys::GpuComputePipelineDescriptor::new(&desc.layout.id, &mapped_compute_stage);
|
||||
|
||||
device.create_compute_pipeline(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) struct CreateBufferMappedDetail {
|
||||
/// On wasm we need to allocate our own temporary storage for `data`. Later
|
||||
/// we copy this temporary storage into the `Uint8Array` which was returned
|
||||
/// by the browser originally.
|
||||
array_buffer: js_sys::ArrayBuffer,
|
||||
}
|
||||
|
||||
pub(crate) fn create_buffer_mapped<'a>(
|
||||
device: &DeviceId,
|
||||
desc: &BufferDescriptor,
|
||||
) -> crate::CreateBufferMapped<'a> {
|
||||
let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits());
|
||||
unsafe {
|
||||
let pair = device.create_buffer_mapped(&mapped_desc);
|
||||
let id = pair.get(0).into();
|
||||
let array_buffer = pair.get(1).into();
|
||||
// TODO: Use `Vec::from_raw_parts` once it's stable
|
||||
let memory = vec![0; desc.size as usize].into_boxed_slice();
|
||||
let mapped_data =
|
||||
std::slice::from_raw_parts_mut(Box::into_raw(memory) as *mut u8, desc.size as usize);
|
||||
crate::CreateBufferMapped {
|
||||
id,
|
||||
mapped_data,
|
||||
detail: CreateBufferMappedDetail { array_buffer },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type BufferDetail = ();
|
||||
|
||||
pub(crate) fn create_buffer_mapped_finish(
|
||||
create_buffer_mapped: crate::CreateBufferMapped<'_>,
|
||||
) -> crate::Buffer {
|
||||
unsafe {
|
||||
// Convert the `mapped_data` slice back into a `Vec`. This should be
|
||||
// safe because `mapped_data` is no longer accessible beyond this
|
||||
// function.
|
||||
let memory: Vec<u8> = Box::<[u8]>::from_raw(create_buffer_mapped.mapped_data).into();
|
||||
|
||||
// Create a view into the mapped `ArrayBuffer` that was provided by the
|
||||
// browser
|
||||
let mapped = js_sys::Uint8Array::new(&create_buffer_mapped.detail.array_buffer);
|
||||
|
||||
// Convert `memory` into a temporary `Uint8Array` view. This should be
|
||||
// safe as long as the backing wasm memory is not resized.
|
||||
let memory_view = js_sys::Uint8Array::view(&memory[..]);
|
||||
|
||||
// Finally copy into `mapped` and let `memory` drop
|
||||
mapped.set(&memory_view, 0);
|
||||
}
|
||||
|
||||
buffer_unmap(&create_buffer_mapped.id);
|
||||
|
||||
crate::Buffer {
|
||||
id: create_buffer_mapped.id,
|
||||
detail: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_unmap(buffer: &BufferId) {
|
||||
buffer.unmap();
|
||||
}
|
||||
|
||||
pub(crate) fn create_buffer(device: &DeviceId, desc: &BufferDescriptor) -> crate::Buffer {
|
||||
let mapped_desc = web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits());
|
||||
crate::Buffer {
|
||||
id: device.create_buffer(&mapped_desc),
|
||||
detail: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_texture(device: &DeviceId, desc: &TextureDescriptor) -> TextureId {
|
||||
let extent = map_extent_3d(desc.size);
|
||||
let mut mapped_desc = web_sys::GpuTextureDescriptor::new(
|
||||
map_texture_format(desc.format),
|
||||
&extent,
|
||||
desc.usage.bits(),
|
||||
);
|
||||
mapped_desc.array_layer_count(desc.array_layer_count);
|
||||
mapped_desc.dimension(map_texture_dimension(desc.dimension));
|
||||
mapped_desc.mip_level_count(desc.mip_level_count);
|
||||
mapped_desc.sample_count(desc.sample_count);
|
||||
mapped_desc.array_layer_count(desc.array_layer_count);
|
||||
device.create_texture(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn create_command_encoder(
|
||||
device: &DeviceId,
|
||||
_desc: &CommandEncoderDescriptor,
|
||||
) -> CommandEncoderId {
|
||||
let mapped_desc = web_sys::GpuCommandEncoderDescriptor::new();
|
||||
device.create_command_encoder_with_descriptor(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn copy_buffer_to_buffer(
|
||||
command_encoder: &CommandEncoderId,
|
||||
source: &crate::Buffer,
|
||||
source_offset: wgt::BufferAddress,
|
||||
destination: &crate::Buffer,
|
||||
destination_offset: wgt::BufferAddress,
|
||||
copy_size: wgt::BufferAddress,
|
||||
) {
|
||||
command_encoder.copy_buffer_to_buffer_with_f64_and_f64_and_f64(
|
||||
&source.id,
|
||||
source_offset as f64,
|
||||
&destination.id,
|
||||
destination_offset as f64,
|
||||
copy_size as f64,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn begin_compute_pass(command_encoder: &CommandEncoderId) -> ComputePassId {
|
||||
let mapped_desc = web_sys::GpuComputePassDescriptor::new();
|
||||
command_encoder.begin_compute_pass_with_descriptor(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_set_pipeline(
|
||||
compute_pass: &ComputePassId,
|
||||
pipeline: &ComputePipelineId,
|
||||
) {
|
||||
compute_pass.set_pipeline(&pipeline);
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_set_bind_group<'a>(
|
||||
compute_pass: &ComputePassId,
|
||||
index: u32,
|
||||
bind_group: &BindGroupId,
|
||||
offsets: &[wgt::DynamicOffset],
|
||||
) {
|
||||
compute_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length(
|
||||
index,
|
||||
bind_group,
|
||||
// TODO: `offsets` currently requires `&mut` so we have to clone it
|
||||
// here, but this should be fixed upstream in web-sys in the future
|
||||
&mut offsets.to_vec(),
|
||||
0f64,
|
||||
offsets.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_dispatch(compute_pass: &ComputePassId, x: u32, y: u32, z: u32) {
|
||||
compute_pass.dispatch_with_y_and_z(x, y, z);
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_dispatch_indirect(
|
||||
compute_pass: &ComputePassId,
|
||||
indirect_buffer: &BufferId,
|
||||
indirect_offset: wgt::BufferAddress,
|
||||
) {
|
||||
compute_pass.dispatch_indirect_with_f64(indirect_buffer, indirect_offset as f64);
|
||||
}
|
||||
|
||||
pub(crate) fn compute_pass_end_pass(compute_pass: &ComputePassId) {
|
||||
compute_pass.end_pass();
|
||||
}
|
||||
|
||||
pub(crate) fn command_encoder_finish(command_encoder: &CommandEncoderId) -> CommandBufferId {
|
||||
let mapped_desc = web_sys::GpuCommandBufferDescriptor::new();
|
||||
command_encoder.finish_with_descriptor(&mapped_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn queue_submit(queue: &QueueId, command_buffers: &[crate::CommandBuffer]) {
|
||||
let temp_command_buffers = command_buffers
|
||||
.iter()
|
||||
.map(|cb| &cb.id)
|
||||
.collect::<js_sys::Array>();
|
||||
|
||||
queue.submit(&temp_command_buffers);
|
||||
}
|
||||
|
||||
pub(crate) async fn buffer_map_read(
|
||||
buffer: &crate::Buffer,
|
||||
_start: wgt::BufferAddress,
|
||||
_size: wgt::BufferAddress,
|
||||
) -> Result<crate::BufferReadMapping, crate::BufferAsyncErr> {
|
||||
let array_buffer_promise = buffer.id.map_read_async();
|
||||
let array_buffer: js_sys::ArrayBuffer =
|
||||
wasm_bindgen_futures::JsFuture::from(array_buffer_promise)
|
||||
.await
|
||||
.expect("Unable to map buffer")
|
||||
.into();
|
||||
let view = js_sys::Uint8Array::new(&array_buffer);
|
||||
Ok(crate::BufferReadMapping {
|
||||
detail: BufferReadMappingDetail {
|
||||
buffer_id: buffer.id.clone(),
|
||||
mapped: view.to_vec(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) struct BufferReadMappingDetail {
|
||||
pub(crate) buffer_id: BufferId,
|
||||
mapped: Vec<u8>,
|
||||
}
|
||||
|
||||
impl BufferReadMappingDetail {
|
||||
pub(crate) fn as_slice(&self) -> &[u8] {
|
||||
&self.mapped[..]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_surface<W: raw_window_handle::HasRawWindowHandle>(window: &W) -> SurfaceId {
|
||||
let handle = window.raw_window_handle();
|
||||
let canvas_attribute = match handle {
|
||||
raw_window_handle::RawWindowHandle::Web(web_handle) => web_handle.id,
|
||||
_ => panic!("expected valid handle for canvas"),
|
||||
};
|
||||
let canvas_node: wasm_bindgen::JsValue = web_sys::window()
|
||||
.and_then(|win| win.document())
|
||||
.and_then(|doc| {
|
||||
doc.query_selector_all(&format!("[data-raw-handle=\"{}\"]", canvas_attribute))
|
||||
.ok()
|
||||
})
|
||||
.and_then(|nodes| nodes.get(0))
|
||||
.expect("expected to find single canvas")
|
||||
.into();
|
||||
let canvas_element: web_sys::HtmlCanvasElement = canvas_node.into();
|
||||
let context: wasm_bindgen::JsValue = match canvas_element.get_context("gpupresent") {
|
||||
Ok(Some(ctx)) => ctx.into(),
|
||||
_ => panic!("expected to get context from canvas"),
|
||||
};
|
||||
context.into()
|
||||
}
|
||||
|
||||
pub(crate) fn device_create_swap_chain(
|
||||
device: &DeviceId,
|
||||
surface: &SurfaceId,
|
||||
desc: &wgt::SwapChainDescriptor,
|
||||
) -> SwapChainId {
|
||||
let mut mapped = web_sys::GpuSwapChainDescriptor::new(device, map_texture_format(desc.format));
|
||||
mapped.usage(desc.usage.bits());
|
||||
surface.configure_swap_chain(&mapped)
|
||||
}
|
||||
|
||||
pub(crate) fn swap_chain_get_next_texture(swap_chain: &SwapChainId) -> Option<TextureViewId> {
|
||||
// TODO: Should we pass a descriptor here?
|
||||
// Or is the default view always correct?
|
||||
Some(swap_chain.get_current_texture().create_view())
|
||||
}
|
||||
|
||||
fn map_store_op(op: wgt::StoreOp) -> web_sys::GpuStoreOp {
|
||||
match op {
|
||||
wgt::StoreOp::Clear => web_sys::GpuStoreOp::Clear,
|
||||
wgt::StoreOp::Store => web_sys::GpuStoreOp::Store,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn command_encoder_begin_render_pass<'a>(
|
||||
command_encoder: &CommandEncoderId,
|
||||
desc: &crate::RenderPassDescriptor<'a, '_>,
|
||||
) -> RenderPassEncoderId {
|
||||
let mapped_color_attachments = desc
|
||||
.color_attachments
|
||||
.iter()
|
||||
.map(|ca| {
|
||||
let mut mapped_color_attachment = web_sys::GpuRenderPassColorAttachmentDescriptor::new(
|
||||
&ca.attachment.id,
|
||||
&match ca.load_op {
|
||||
wgt::LoadOp::Clear => {
|
||||
let color = ca.clear_color;
|
||||
let mapped_color =
|
||||
web_sys::GpuColorDict::new(color.a, color.b, color.g, color.r);
|
||||
wasm_bindgen::JsValue::from(mapped_color)
|
||||
}
|
||||
wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load),
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(rt) = ca.resolve_target {
|
||||
mapped_color_attachment.resolve_target(&rt.id);
|
||||
}
|
||||
|
||||
mapped_color_attachment.store_op(map_store_op(ca.store_op));
|
||||
|
||||
mapped_color_attachment
|
||||
})
|
||||
.collect::<js_sys::Array>();
|
||||
|
||||
let mut mapped = web_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments);
|
||||
|
||||
if let Some(dsa) = &desc.depth_stencil_attachment {
|
||||
let mapped_depth_stencil_attachment =
|
||||
web_sys::GpuRenderPassDepthStencilAttachmentDescriptor::new(
|
||||
&dsa.attachment.id,
|
||||
&match dsa.depth_load_op {
|
||||
wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_depth),
|
||||
wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load),
|
||||
},
|
||||
map_store_op(dsa.depth_store_op),
|
||||
&match dsa.stencil_load_op {
|
||||
wgt::LoadOp::Clear => wasm_bindgen::JsValue::from(dsa.clear_stencil),
|
||||
wgt::LoadOp::Load => wasm_bindgen::JsValue::from(web_sys::GpuLoadOp::Load),
|
||||
},
|
||||
map_store_op(dsa.stencil_store_op),
|
||||
);
|
||||
|
||||
mapped.depth_stencil_attachment(&mapped_depth_stencil_attachment);
|
||||
}
|
||||
|
||||
command_encoder.begin_render_pass(&mapped)
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_set_pipeline(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
pipeline: &RenderPipelineId,
|
||||
) {
|
||||
render_pass.set_pipeline(&pipeline);
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_set_bind_group(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
index: u32,
|
||||
bind_group: &BindGroupId,
|
||||
offsets: &[wgt::DynamicOffset],
|
||||
) {
|
||||
render_pass.set_bind_group_with_u32_array_and_f64_and_dynamic_offsets_data_length(
|
||||
index,
|
||||
bind_group,
|
||||
// TODO: `offsets` currently requires `&mut` so we have to clone it
|
||||
// here, but this should be fixed upstream in web-sys in the future
|
||||
&mut offsets.to_vec(),
|
||||
0f64,
|
||||
offsets.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_set_vertex_buffer<'a>(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
slot: u32,
|
||||
buffer: &'a crate::Buffer,
|
||||
offset: wgt::BufferAddress,
|
||||
_size: wgt::BufferAddress,
|
||||
) {
|
||||
render_pass.set_vertex_buffer_with_f64(
|
||||
slot,
|
||||
&buffer.id,
|
||||
offset as f64,
|
||||
// TODO: size,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_draw(
|
||||
render_pass: &RenderPassEncoderId,
|
||||
vertices: Range<u32>,
|
||||
instances: Range<u32>,
|
||||
) {
|
||||
render_pass.draw(
|
||||
vertices.end - vertices.start,
|
||||
instances.end - instances.start,
|
||||
vertices.start,
|
||||
instances.start,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn render_pass_end_pass(render_pass: &RenderPassEncoderId) {
|
||||
render_pass.end_pass();
|
||||
}
|
||||
|
||||
pub(crate) fn texture_create_default_view(texture: &TextureId) -> TextureViewId {
|
||||
texture.create_view()
|
||||
}
|
||||
|
||||
pub(crate) fn swap_chain_present(_swap_chain: &SwapChainId) {
|
||||
// Swapchain is presented automatically
|
||||
}
|
||||
|
||||
pub(crate) fn device_poll(_device: &DeviceId, _maintain: crate::Maintain) {
|
||||
// Device is polled automatically
|
||||
}
|
||||
943
wgpu/src/lib.rs
943
wgpu/src/lib.rs
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user