diff options
Diffstat (limited to 'gfx/wgpu_bindings')
-rw-r--r-- | gfx/wgpu_bindings/Cargo.toml | 30 | ||||
-rw-r--r-- | gfx/wgpu_bindings/cbindgen.toml | 73 | ||||
-rw-r--r-- | gfx/wgpu_bindings/moz.build | 23 | ||||
-rw-r--r-- | gfx/wgpu_bindings/src/client.rs | 1016 | ||||
-rw-r--r-- | gfx/wgpu_bindings/src/identity.rs | 214 | ||||
-rw-r--r-- | gfx/wgpu_bindings/src/lib.rs | 114 | ||||
-rw-r--r-- | gfx/wgpu_bindings/src/server.rs | 641 | ||||
-rw-r--r-- | gfx/wgpu_bindings/wgpu.h | 40 |
8 files changed, 2151 insertions, 0 deletions
diff --git a/gfx/wgpu_bindings/Cargo.toml b/gfx/wgpu_bindings/Cargo.toml new file mode 100644 index 0000000000..43f2287a71 --- /dev/null +++ b/gfx/wgpu_bindings/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wgpu_bindings" +version = "0.1.0" +authors = [ + "Dzmitry Malyshau <kvark@mozilla.com>", + "Joshua Groves <josh@joshgroves.com>", +] +edition = "2018" +license = "MPL-2.0" +publish = false + +[lib] + +[features] +default = [] + +[dependencies.wgc] +path = "../wgpu/wgpu-core" +package = "wgpu-core" +features = ["replay", "trace", "serial-pass"] + +[dependencies.wgt] +path = "../wgpu/wgpu-types" +package = "wgpu-types" + +[dependencies] +bincode = "1" +log = "0.4" +parking_lot = "0.11" +serde = "1" diff --git a/gfx/wgpu_bindings/cbindgen.toml b/gfx/wgpu_bindings/cbindgen.toml new file mode 100644 index 0000000000..eb745b1aa1 --- /dev/null +++ b/gfx/wgpu_bindings/cbindgen.toml @@ -0,0 +1,73 @@ +header = """/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */""" +autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. + * To generate this file: + * 1. Get the latest cbindgen using `cargo install --force cbindgen` + * a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release + * 2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate wgpu_bindings -o dom/webgpu/ffi/wgpu_ffi_generated.h` + */ + +struct WGPUByteBuf; +typedef uint64_t WGPUNonZeroU64; +typedef uint64_t WGPUOption_BufferSize; +typedef uint32_t WGPUOption_NonZeroU32; +typedef uint8_t WGPUOption_NonZeroU8; +typedef uint64_t WGPUOption_AdapterId; +typedef uint64_t WGPUOption_BufferId; +typedef uint64_t WGPUOption_PipelineLayoutId; +typedef uint64_t WGPUOption_BindGroupLayoutId; +typedef uint64_t WGPUOption_SamplerId; +typedef uint64_t WGPUOption_SurfaceId; +typedef uint64_t WGPUOption_TextureViewId; +""" +include_version = true +braces = "SameLine" +line_length = 100 +tab_width = 2 +language = "C" +style = "tag" + +[export] +prefix = "WGPU" +exclude = [ + "Option_AdapterId", "Option_BufferId", "Option_PipelineLayoutId", "Option_BindGroupLayoutId", + "Option_SamplerId", "Option_SurfaceId", "Option_TextureViewId", + "Option_BufferSize", "Option_NonZeroU32", "Option_NonZeroU8", +] + +[export.rename] +"BufferDescriptor_RawString" = "BufferDescriptor" +"CommandBufferDescriptor_RawString" = "CommandBufferDescriptor" +"CommandEncoderDescriptor_RawString" = "CommandEncoderDescriptor" +"DeviceDescriptor_RawString" = "DeviceDescriptor" +"TextureDescriptor_RawString" = "TextureDescriptor" +"SamplerDescriptor_RawString" = "SamplerDescriptor" + +[parse] +parse_deps = true +include = ["wgpu-core", "wgpu-types"] + +extra_bindings = ["wgpu-core", "wgpu-types"] + +[fn] +prefix = "WGPU_INLINE" +postfix = "WGPU_FUNC" +args = "Vertical" +rename_args = "GeckoCase" + +[struct] +derive_eq = true + +[enum] +prefix_with_name = true +derive_helper_methods = true +add_sentinel = true + +[macro_expansion] +bitflags = true + +[defines] +"target_os = windows" = "XP_WIN" +"target_os = macos" = "XP_MACOSX" +"target_os = android" = "ANDROID" diff --git a/gfx/wgpu_bindings/moz.build b/gfx/wgpu_bindings/moz.build new file mode 100644 index 0000000000..6f948a2eb2 --- /dev/null +++ b/gfx/wgpu_bindings/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla.webgpu.ffi += [ + "wgpu.h", +] + +UNIFIED_SOURCES += [] + +if CONFIG["COMPILE_ENVIRONMENT"]: + CbindgenHeader("wgpu_ffi_generated.h", inputs=["/gfx/wgpu_bindings", "/gfx/wgpu"]) + + EXPORTS.mozilla.webgpu.ffi += [ + "!wgpu_ffi_generated.h", + ] + + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" diff --git a/gfx/wgpu_bindings/src/client.rs b/gfx/wgpu_bindings/src/client.rs new file mode 100644 index 0000000000..dad76731df --- /dev/null +++ b/gfx/wgpu_bindings/src/client.rs @@ -0,0 +1,1016 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + cow_label, ByteBuf, CommandEncoderAction, DeviceAction, DropAction, ImplicitLayout, RawString, + TextureAction, +}; + +use wgc::{hub::IdentityManager, id}; +use wgt::Backend; + +pub use wgc::command::{compute_ffi::*, render_ffi::*}; + +use parking_lot::Mutex; + +use std::{ + borrow::Cow, + num::{NonZeroU32, NonZeroU8}, + ptr, slice, +}; + +fn make_byte_buf<T: serde::Serialize>(data: &T) -> ByteBuf { + let vec = bincode::serialize(data).unwrap(); + ByteBuf::from_vec(vec) +} + +#[repr(C)] +pub struct ShaderModuleDescriptor { + spirv_words: *const u32, + spirv_words_length: usize, + wgsl_chars: RawString, +} + +#[repr(C)] +pub struct ProgrammableStageDescriptor { + module: id::ShaderModuleId, + entry_point: RawString, +} + +impl ProgrammableStageDescriptor { + fn to_wgpu(&self) -> wgc::pipeline::ProgrammableStageDescriptor { + wgc::pipeline::ProgrammableStageDescriptor { + module: self.module, + entry_point: cow_label(&self.entry_point).unwrap(), + } + } +} + +#[repr(C)] +pub struct ComputePipelineDescriptor { + label: RawString, + layout: Option<id::PipelineLayoutId>, + compute_stage: ProgrammableStageDescriptor, +} + +#[repr(C)] +pub struct VertexBufferDescriptor { + stride: wgt::BufferAddress, + step_mode: wgt::InputStepMode, + attributes: *const wgt::VertexAttributeDescriptor, + attributes_length: usize, +} + +#[repr(C)] +pub struct VertexStateDescriptor { + index_format: wgt::IndexFormat, + vertex_buffers: *const VertexBufferDescriptor, + vertex_buffers_length: usize, +} + +#[repr(C)] +pub struct RenderPipelineDescriptor<'a> { + label: RawString, + layout: Option<id::PipelineLayoutId>, + vertex_stage: &'a ProgrammableStageDescriptor, + fragment_stage: Option<&'a ProgrammableStageDescriptor>, + primitive_topology: wgt::PrimitiveTopology, + rasterization_state: Option<&'a wgt::RasterizationStateDescriptor>, + color_states: *const wgt::ColorStateDescriptor, + color_states_length: usize, + depth_stencil_state: Option<&'a wgt::DepthStencilStateDescriptor>, + vertex_state: VertexStateDescriptor, + sample_count: u32, + sample_mask: u32, + alpha_to_coverage_enabled: bool, +} + +#[repr(C)] +pub enum RawTextureSampleType { + Float, + UnfilterableFloat, + Uint, + Sint, + Depth, +} + +#[repr(C)] +pub enum RawBindingType { + UniformBuffer, + StorageBuffer, + ReadonlyStorageBuffer, + Sampler, + ComparisonSampler, + SampledTexture, + ReadonlyStorageTexture, + WriteonlyStorageTexture, +} + +#[repr(C)] +pub struct BindGroupLayoutEntry<'a> { + binding: u32, + visibility: wgt::ShaderStage, + ty: RawBindingType, + has_dynamic_offset: bool, + min_binding_size: Option<wgt::BufferSize>, + view_dimension: Option<&'a wgt::TextureViewDimension>, + texture_sample_type: Option<&'a RawTextureSampleType>, + multisampled: bool, + storage_texture_format: Option<&'a wgt::TextureFormat>, +} + +#[repr(C)] +pub struct BindGroupLayoutDescriptor<'a> { + label: RawString, + entries: *const BindGroupLayoutEntry<'a>, + entries_length: usize, +} + +#[repr(C)] +#[derive(Debug)] +pub struct BindGroupEntry { + binding: u32, + buffer: Option<id::BufferId>, + offset: wgt::BufferAddress, + size: Option<wgt::BufferSize>, + sampler: Option<id::SamplerId>, + texture_view: Option<id::TextureViewId>, +} + +#[repr(C)] +pub struct BindGroupDescriptor { + label: RawString, + layout: id::BindGroupLayoutId, + entries: *const BindGroupEntry, + entries_length: usize, +} + +#[repr(C)] +pub struct PipelineLayoutDescriptor { + label: RawString, + bind_group_layouts: *const id::BindGroupLayoutId, + bind_group_layouts_length: usize, +} + +#[repr(C)] +pub struct SamplerDescriptor<'a> { + label: RawString, + address_modes: [wgt::AddressMode; 3], + mag_filter: wgt::FilterMode, + min_filter: wgt::FilterMode, + mipmap_filter: wgt::FilterMode, + lod_min_clamp: f32, + lod_max_clamp: f32, + compare: Option<&'a wgt::CompareFunction>, + anisotropy_clamp: Option<NonZeroU8>, +} + +#[repr(C)] +pub struct TextureViewDescriptor<'a> { + label: RawString, + format: Option<&'a wgt::TextureFormat>, + dimension: Option<&'a wgt::TextureViewDimension>, + aspect: wgt::TextureAspect, + base_mip_level: u32, + level_count: Option<NonZeroU32>, + base_array_layer: u32, + array_layer_count: Option<NonZeroU32>, +} + +#[derive(Debug, Default)] +struct IdentityHub { + adapters: IdentityManager, + devices: IdentityManager, + buffers: IdentityManager, + command_buffers: IdentityManager, + render_bundles: IdentityManager, + bind_group_layouts: IdentityManager, + pipeline_layouts: IdentityManager, + bind_groups: IdentityManager, + shader_modules: IdentityManager, + compute_pipelines: IdentityManager, + render_pipelines: IdentityManager, + textures: IdentityManager, + texture_views: IdentityManager, + samplers: IdentityManager, +} + +impl ImplicitLayout<'_> { + fn new(identities: &mut IdentityHub, backend: Backend) -> Self { + ImplicitLayout { + pipeline: identities.pipeline_layouts.alloc(backend), + bind_groups: Cow::Owned( + (0..wgc::MAX_BIND_GROUPS) + .map(|_| identities.bind_group_layouts.alloc(backend)) + .collect(), + ), + } + } +} + +#[derive(Debug, Default)] +struct Identities { + surfaces: IdentityManager, + vulkan: IdentityHub, + #[cfg(any(target_os = "ios", target_os = "macos"))] + metal: IdentityHub, + #[cfg(windows)] + dx12: IdentityHub, +} + +impl Identities { + fn select(&mut self, backend: Backend) -> &mut IdentityHub { + match backend { + Backend::Vulkan => &mut self.vulkan, + #[cfg(any(target_os = "ios", target_os = "macos"))] + Backend::Metal => &mut self.metal, + #[cfg(windows)] + Backend::Dx12 => &mut self.dx12, + _ => panic!("Unexpected backend: {:?}", backend), + } + } +} + +#[derive(Debug)] +pub struct Client { + identities: Mutex<Identities>, +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_drop_action(client: &mut Client, byte_buf: &ByteBuf) { + let mut cursor = std::io::Cursor::new(byte_buf.as_slice()); + let mut identities = client.identities.lock(); + while let Ok(action) = bincode::deserialize_from(&mut cursor) { + match action { + DropAction::Buffer(id) => identities.select(id.backend()).buffers.free(id), + DropAction::Texture(id) => identities.select(id.backend()).textures.free(id), + DropAction::Sampler(id) => identities.select(id.backend()).samplers.free(id), + DropAction::BindGroupLayout(id) => { + identities.select(id.backend()).bind_group_layouts.free(id) + } + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct Infrastructure { + pub client: *mut Client, + pub error: *const u8, +} + +#[no_mangle] +pub extern "C" fn wgpu_client_new() -> Infrastructure { + log::info!("Initializing WGPU client"); + let client = Box::new(Client { + identities: Mutex::new(Identities::default()), + }); + Infrastructure { + client: Box::into_raw(client), + error: ptr::null(), + } +} + +/// # Safety +/// +/// This function is unsafe because improper use may lead to memory +/// problems. For example, a double-free may occur if the function is called +/// twice on the same raw pointer. +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_delete(client: *mut Client) { + log::info!("Terminating WGPU client"); + let _client = Box::from_raw(client); +} + +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `id_length` elements. +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_make_adapter_ids( + client: &Client, + ids: *mut id::AdapterId, + id_length: usize, +) -> usize { + let mut identities = client.identities.lock(); + assert_ne!(id_length, 0); + let mut ids = slice::from_raw_parts_mut(ids, id_length).iter_mut(); + + *ids.next().unwrap() = identities.vulkan.adapters.alloc(Backend::Vulkan); + + #[cfg(any(target_os = "ios", target_os = "macos"))] + { + *ids.next().unwrap() = identities.metal.adapters.alloc(Backend::Metal); + } + #[cfg(windows)] + { + *ids.next().unwrap() = identities.dx12.adapters.alloc(Backend::Dx12); + } + + id_length - ids.len() +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_adapter_id(client: &Client, id: id::AdapterId) { + client + .identities + .lock() + .select(id.backend()) + .adapters + .free(id) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_make_device_id( + client: &Client, + adapter_id: id::AdapterId, +) -> id::DeviceId { + let backend = adapter_id.backend(); + client + .identities + .lock() + .select(backend) + .devices + .alloc(backend) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_device_id(client: &Client, id: id::DeviceId) { + client + .identities + .lock() + .select(id.backend()) + .devices + .free(id) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_make_buffer_id( + client: &Client, + device_id: id::DeviceId, +) -> id::BufferId { + let backend = device_id.backend(); + client + .identities + .lock() + .select(backend) + .buffers + .alloc(backend) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_create_buffer( + client: &Client, + device_id: id::DeviceId, + desc: &wgt::BufferDescriptor<RawString>, + bb: &mut ByteBuf, +) -> id::BufferId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .buffers + .alloc(backend); + + let action = DeviceAction::CreateBuffer(id, desc.map_label(cow_label)); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_buffer_id(client: &Client, id: id::BufferId) { + client + .identities + .lock() + .select(id.backend()) + .buffers + .free(id) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_create_texture( + client: &Client, + device_id: id::DeviceId, + desc: &wgt::TextureDescriptor<RawString>, + bb: &mut ByteBuf, +) -> id::TextureId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .textures + .alloc(backend); + + let action = DeviceAction::CreateTexture(id, desc.map_label(cow_label)); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_texture_id(client: &Client, id: id::TextureId) { + client + .identities + .lock() + .select(id.backend()) + .textures + .free(id) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_create_texture_view( + client: &Client, + device_id: id::DeviceId, + desc: &TextureViewDescriptor, + bb: &mut ByteBuf, +) -> id::TextureViewId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .texture_views + .alloc(backend); + + let wgpu_desc = wgc::resource::TextureViewDescriptor { + label: cow_label(&desc.label), + format: desc.format.cloned(), + dimension: desc.dimension.cloned(), + aspect: desc.aspect, + base_mip_level: desc.base_mip_level, + level_count: desc.level_count, + base_array_layer: desc.base_array_layer, + array_layer_count: desc.array_layer_count, + }; + + let action = TextureAction::CreateView(id, wgpu_desc); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_texture_view_id(client: &Client, id: id::TextureViewId) { + client + .identities + .lock() + .select(id.backend()) + .texture_views + .free(id) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_create_sampler( + client: &Client, + device_id: id::DeviceId, + desc: &SamplerDescriptor, + bb: &mut ByteBuf, +) -> id::SamplerId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .samplers + .alloc(backend); + + let wgpu_desc = wgc::resource::SamplerDescriptor { + label: cow_label(&desc.label), + address_modes: desc.address_modes, + mag_filter: desc.mag_filter, + min_filter: desc.min_filter, + mipmap_filter: desc.mipmap_filter, + lod_min_clamp: desc.lod_min_clamp, + lod_max_clamp: desc.lod_max_clamp, + compare: desc.compare.cloned(), + anisotropy_clamp: desc.anisotropy_clamp, + border_color: None, + }; + let action = DeviceAction::CreateSampler(id, wgpu_desc); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_sampler_id(client: &Client, id: id::SamplerId) { + client + .identities + .lock() + .select(id.backend()) + .samplers + .free(id) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_make_encoder_id( + client: &Client, + device_id: id::DeviceId, +) -> id::CommandEncoderId { + let backend = device_id.backend(); + client + .identities + .lock() + .select(backend) + .command_buffers + .alloc(backend) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_create_command_encoder( + client: &Client, + device_id: id::DeviceId, + desc: &wgt::CommandEncoderDescriptor<RawString>, + bb: &mut ByteBuf, +) -> id::CommandEncoderId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .command_buffers + .alloc(backend); + + let action = DeviceAction::CreateCommandEncoder(id, desc.map_label(cow_label)); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_encoder_id(client: &Client, id: id::CommandEncoderId) { + client + .identities + .lock() + .select(id.backend()) + .command_buffers + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_begin_compute_pass( + encoder_id: id::CommandEncoderId, + _desc: Option<&wgc::command::ComputePassDescriptor>, +) -> *mut wgc::command::ComputePass { + let pass = wgc::command::ComputePass::new(encoder_id); + Box::into_raw(Box::new(pass)) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_compute_pass_finish( + pass: *mut wgc::command::ComputePass, + output: &mut ByteBuf, +) { + let command = Box::from_raw(pass).into_command(); + *output = make_byte_buf(&command); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_compute_pass_destroy(pass: *mut wgc::command::ComputePass) { + let _ = Box::from_raw(pass); +} + +#[repr(C)] +pub struct RenderPassDescriptor { + pub color_attachments: *const wgc::command::ColorAttachmentDescriptor, + pub color_attachments_length: usize, + pub depth_stencil_attachment: *const wgc::command::DepthStencilAttachmentDescriptor, +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_begin_render_pass( + encoder_id: id::CommandEncoderId, + desc: &RenderPassDescriptor, +) -> *mut wgc::command::RenderPass { + let pass = wgc::command::RenderPass::new( + encoder_id, + wgc::command::RenderPassDescriptor { + color_attachments: Cow::Borrowed(slice::from_raw_parts( + desc.color_attachments, + desc.color_attachments_length, + )), + depth_stencil_attachment: desc.depth_stencil_attachment.as_ref(), + }, + ); + Box::into_raw(Box::new(pass)) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_render_pass_finish( + pass: *mut wgc::command::RenderPass, + output: &mut ByteBuf, +) { + let command = Box::from_raw(pass).into_command(); + *output = make_byte_buf(&command); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_render_pass_destroy(pass: *mut wgc::command::RenderPass) { + let _ = Box::from_raw(pass); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_create_bind_group_layout( + client: &Client, + device_id: id::DeviceId, + desc: &BindGroupLayoutDescriptor, + bb: &mut ByteBuf, +) -> id::BindGroupLayoutId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .bind_group_layouts + .alloc(backend); + + let mut entries = Vec::with_capacity(desc.entries_length); + for entry in slice::from_raw_parts(desc.entries, desc.entries_length) { + entries.push(wgt::BindGroupLayoutEntry { + binding: entry.binding, + visibility: entry.visibility, + count: None, + ty: match entry.ty { + RawBindingType::UniformBuffer => wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset: entry.has_dynamic_offset, + min_binding_size: entry.min_binding_size, + }, + RawBindingType::StorageBuffer => wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: entry.has_dynamic_offset, + min_binding_size: entry.min_binding_size, + }, + RawBindingType::ReadonlyStorageBuffer => wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: entry.has_dynamic_offset, + min_binding_size: entry.min_binding_size, + }, + RawBindingType::Sampler => wgt::BindingType::Sampler { + comparison: false, + filtering: false, + }, + RawBindingType::ComparisonSampler => wgt::BindingType::Sampler { + comparison: true, + filtering: false, + }, + RawBindingType::SampledTexture => wgt::BindingType::Texture { + //TODO: the spec has a bug here + view_dimension: *entry + .view_dimension + .unwrap_or(&wgt::TextureViewDimension::D2), + sample_type: match entry.texture_sample_type { + None | Some(RawTextureSampleType::Float) => { + wgt::TextureSampleType::Float { filterable: true } + } + Some(RawTextureSampleType::UnfilterableFloat) => { + wgt::TextureSampleType::Float { filterable: false } + } + Some(RawTextureSampleType::Uint) => wgt::TextureSampleType::Uint, + Some(RawTextureSampleType::Sint) => wgt::TextureSampleType::Sint, + Some(RawTextureSampleType::Depth) => wgt::TextureSampleType::Depth, + }, + multisampled: entry.multisampled, + }, + RawBindingType::ReadonlyStorageTexture => wgt::BindingType::StorageTexture { + access: wgt::StorageTextureAccess::ReadOnly, + view_dimension: *entry.view_dimension.unwrap(), + format: *entry.storage_texture_format.unwrap(), + }, + RawBindingType::WriteonlyStorageTexture => wgt::BindingType::StorageTexture { + access: wgt::StorageTextureAccess::WriteOnly, + view_dimension: *entry.view_dimension.unwrap(), + format: *entry.storage_texture_format.unwrap(), + }, + }, + }); + } + let wgpu_desc = wgc::binding_model::BindGroupLayoutDescriptor { + label: cow_label(&desc.label), + entries: Cow::Owned(entries), + }; + + let action = DeviceAction::CreateBindGroupLayout(id, wgpu_desc); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_bind_group_layout_id( + client: &Client, + id: id::BindGroupLayoutId, +) { + client + .identities + .lock() + .select(id.backend()) + .bind_group_layouts + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_create_pipeline_layout( + client: &Client, + device_id: id::DeviceId, + desc: &PipelineLayoutDescriptor, + bb: &mut ByteBuf, +) -> id::PipelineLayoutId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .pipeline_layouts + .alloc(backend); + + let wgpu_desc = wgc::binding_model::PipelineLayoutDescriptor { + label: cow_label(&desc.label), + bind_group_layouts: Cow::Borrowed(slice::from_raw_parts( + desc.bind_group_layouts, + desc.bind_group_layouts_length, + )), + push_constant_ranges: Cow::Borrowed(&[]), + }; + + let action = DeviceAction::CreatePipelineLayout(id, wgpu_desc); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_pipeline_layout_id(client: &Client, id: id::PipelineLayoutId) { + client + .identities + .lock() + .select(id.backend()) + .pipeline_layouts + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_create_bind_group( + client: &Client, + device_id: id::DeviceId, + desc: &BindGroupDescriptor, + bb: &mut ByteBuf, +) -> id::BindGroupId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .bind_groups + .alloc(backend); + + let mut entries = Vec::with_capacity(desc.entries_length); + for entry in slice::from_raw_parts(desc.entries, desc.entries_length) { + entries.push(wgc::binding_model::BindGroupEntry { + binding: entry.binding, + resource: if let Some(id) = entry.buffer { + wgc::binding_model::BindingResource::Buffer(wgc::binding_model::BufferBinding { + buffer_id: id, + offset: entry.offset, + size: entry.size, + }) + } else if let Some(id) = entry.sampler { + wgc::binding_model::BindingResource::Sampler(id) + } else if let Some(id) = entry.texture_view { + wgc::binding_model::BindingResource::TextureView(id) + } else { + panic!("Unexpected binding entry {:?}", entry); + }, + }); + } + let wgpu_desc = wgc::binding_model::BindGroupDescriptor { + label: cow_label(&desc.label), + layout: desc.layout, + entries: Cow::Owned(entries), + }; + + let action = DeviceAction::CreateBindGroup(id, wgpu_desc); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_bind_group_id(client: &Client, id: id::BindGroupId) { + client + .identities + .lock() + .select(id.backend()) + .bind_groups + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_create_shader_module( + client: &Client, + device_id: id::DeviceId, + desc: &ShaderModuleDescriptor, + bb: &mut ByteBuf, +) -> id::ShaderModuleId { + let backend = device_id.backend(); + let id = client + .identities + .lock() + .select(backend) + .shader_modules + .alloc(backend); + + assert!(!desc.spirv_words.is_null()); + let spv = Cow::Borrowed(if desc.spirv_words.is_null() { + &[][..] + } else { + slice::from_raw_parts(desc.spirv_words, desc.spirv_words_length) + }); + + let wgsl = cow_label(&desc.wgsl_chars).unwrap_or_default(); + + let action = DeviceAction::CreateShaderModule(id, spv, wgsl); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_shader_module_id(client: &Client, id: id::ShaderModuleId) { + client + .identities + .lock() + .select(id.backend()) + .shader_modules + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_create_compute_pipeline( + client: &Client, + device_id: id::DeviceId, + desc: &ComputePipelineDescriptor, + bb: &mut ByteBuf, + implicit_bind_group_layout_ids: *mut Option<id::BindGroupLayoutId>, +) -> id::ComputePipelineId { + let backend = device_id.backend(); + let mut identities = client.identities.lock(); + let id = identities.select(backend).compute_pipelines.alloc(backend); + + let wgpu_desc = wgc::pipeline::ComputePipelineDescriptor { + label: cow_label(&desc.label), + layout: desc.layout, + compute_stage: desc.compute_stage.to_wgpu(), + }; + + let implicit = match desc.layout { + Some(_) => None, + None => { + let implicit = ImplicitLayout::new(identities.select(backend), backend); + for (i, bgl_id) in implicit.bind_groups.iter().enumerate() { + *implicit_bind_group_layout_ids.add(i) = Some(*bgl_id); + } + Some(implicit) + } + }; + + let action = DeviceAction::CreateComputePipeline(id, wgpu_desc, implicit); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_compute_pipeline_id(client: &Client, id: id::ComputePipelineId) { + client + .identities + .lock() + .select(id.backend()) + .compute_pipelines + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_client_create_render_pipeline( + client: &Client, + device_id: id::DeviceId, + desc: &RenderPipelineDescriptor, + bb: &mut ByteBuf, + implicit_bind_group_layout_ids: *mut Option<id::BindGroupLayoutId>, +) -> id::RenderPipelineId { + let backend = device_id.backend(); + let mut identities = client.identities.lock(); + let id = identities.select(backend).render_pipelines.alloc(backend); + + let wgpu_desc = wgc::pipeline::RenderPipelineDescriptor { + label: cow_label(&desc.label), + layout: desc.layout, + vertex_stage: desc.vertex_stage.to_wgpu(), + fragment_stage: desc + .fragment_stage + .map(ProgrammableStageDescriptor::to_wgpu), + rasterization_state: desc.rasterization_state.cloned(), + primitive_topology: desc.primitive_topology, + color_states: Cow::Borrowed(slice::from_raw_parts( + desc.color_states, + desc.color_states_length, + )), + depth_stencil_state: desc.depth_stencil_state.cloned(), + vertex_state: wgc::pipeline::VertexStateDescriptor { + index_format: desc.vertex_state.index_format, + vertex_buffers: { + let vbufs = slice::from_raw_parts( + desc.vertex_state.vertex_buffers, + desc.vertex_state.vertex_buffers_length, + ); + let owned = vbufs + .iter() + .map(|vb| wgc::pipeline::VertexBufferDescriptor { + stride: vb.stride, + step_mode: vb.step_mode, + attributes: Cow::Borrowed(if vb.attributes.is_null() { + &[] + } else { + slice::from_raw_parts(vb.attributes, vb.attributes_length) + }), + }) + .collect(); + Cow::Owned(owned) + }, + }, + sample_count: desc.sample_count, + sample_mask: desc.sample_mask, + alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled, + }; + + let implicit = match desc.layout { + Some(_) => None, + None => { + let implicit = ImplicitLayout::new(identities.select(backend), backend); + for (i, bgl_id) in implicit.bind_groups.iter().enumerate() { + *implicit_bind_group_layout_ids.add(i) = Some(*bgl_id); + } + Some(implicit) + } + }; + + let action = DeviceAction::CreateRenderPipeline(id, wgpu_desc, implicit); + *bb = make_byte_buf(&action); + id +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_render_pipeline_id(client: &Client, id: id::RenderPipelineId) { + client + .identities + .lock() + .select(id.backend()) + .render_pipelines + .free(id) +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_copy_buffer_to_buffer( + src: id::BufferId, + src_offset: wgt::BufferAddress, + dst: id::BufferId, + dst_offset: wgt::BufferAddress, + size: wgt::BufferAddress, + bb: &mut ByteBuf, +) { + let action = CommandEncoderAction::CopyBufferToBuffer { + src, + src_offset, + dst, + dst_offset, + size, + }; + *bb = make_byte_buf(&action); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_copy_texture_to_buffer( + src: wgc::command::TextureCopyView, + dst: wgc::command::BufferCopyView, + size: wgt::Extent3d, + bb: &mut ByteBuf, +) { + let action = CommandEncoderAction::CopyTextureToBuffer { src, dst, size }; + *bb = make_byte_buf(&action); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_copy_buffer_to_texture( + src: wgc::command::BufferCopyView, + dst: wgc::command::TextureCopyView, + size: wgt::Extent3d, + bb: &mut ByteBuf, +) { + let action = CommandEncoderAction::CopyBufferToTexture { src, dst, size }; + *bb = make_byte_buf(&action); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_copy_texture_to_texture( + src: wgc::command::TextureCopyView, + dst: wgc::command::TextureCopyView, + size: wgt::Extent3d, + bb: &mut ByteBuf, +) { + let action = CommandEncoderAction::CopyTextureToTexture { src, dst, size }; + *bb = make_byte_buf(&action); +} diff --git a/gfx/wgpu_bindings/src/identity.rs b/gfx/wgpu_bindings/src/identity.rs new file mode 100644 index 0000000000..38c8930c76 --- /dev/null +++ b/gfx/wgpu_bindings/src/identity.rs @@ -0,0 +1,214 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use wgc::id; + +pub type FactoryParam = *mut std::ffi::c_void; + +#[derive(Debug)] +pub struct IdentityRecycler<I> { + fun: extern "C" fn(I, FactoryParam), + param: FactoryParam, + kind: &'static str, +} + +impl<I: id::TypedId + Clone + std::fmt::Debug> wgc::hub::IdentityHandler<I> + for IdentityRecycler<I> +{ + type Input = I; + fn process(&self, id: I, _backend: wgt::Backend) -> I { + log::debug!("process {} {:?}", self.kind, id); + //debug_assert_eq!(id.unzip().2, backend); + id + } + fn free(&self, id: I) { + log::debug!("free {} {:?}", self.kind, id); + (self.fun)(id, self.param); + } +} + +//TODO: remove this in favor of `DropAction` that could be sent over IPC. +#[repr(C)] +pub struct IdentityRecyclerFactory { + param: FactoryParam, + free_adapter: extern "C" fn(id::AdapterId, FactoryParam), + free_device: extern "C" fn(id::DeviceId, FactoryParam), + free_swap_chain: extern "C" fn(id::SwapChainId, FactoryParam), + free_pipeline_layout: extern "C" fn(id::PipelineLayoutId, FactoryParam), + free_shader_module: extern "C" fn(id::ShaderModuleId, FactoryParam), + free_bind_group_layout: extern "C" fn(id::BindGroupLayoutId, FactoryParam), + free_bind_group: extern "C" fn(id::BindGroupId, FactoryParam), + free_command_buffer: extern "C" fn(id::CommandBufferId, FactoryParam), + free_render_bundle: extern "C" fn(id::RenderBundleId, FactoryParam), + free_render_pipeline: extern "C" fn(id::RenderPipelineId, FactoryParam), + free_compute_pipeline: extern "C" fn(id::ComputePipelineId, FactoryParam), + free_buffer: extern "C" fn(id::BufferId, FactoryParam), + free_texture: extern "C" fn(id::TextureId, FactoryParam), + free_texture_view: extern "C" fn(id::TextureViewId, FactoryParam), + free_sampler: extern "C" fn(id::SamplerId, FactoryParam), + free_surface: extern "C" fn(id::SurfaceId, FactoryParam), +} + +impl wgc::hub::IdentityHandlerFactory<id::AdapterId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::AdapterId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_adapter, + param: self.param, + kind: "adapter", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::DeviceId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::DeviceId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_device, + param: self.param, + kind: "device", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::SwapChainId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::SwapChainId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_swap_chain, + param: self.param, + kind: "swap_chain", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::PipelineLayoutId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::PipelineLayoutId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_pipeline_layout, + param: self.param, + kind: "pipeline_layout", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::ShaderModuleId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::ShaderModuleId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_shader_module, + param: self.param, + kind: "shader_module", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::BindGroupLayoutId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::BindGroupLayoutId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_bind_group_layout, + param: self.param, + kind: "bind_group_layout", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::BindGroupId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::BindGroupId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_bind_group, + param: self.param, + kind: "bind_group", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::CommandBufferId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::CommandBufferId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_command_buffer, + param: self.param, + kind: "command_buffer", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::RenderBundleId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::RenderBundleId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_render_bundle, + param: self.param, + kind: "render_bundle", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::RenderPipelineId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::RenderPipelineId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_render_pipeline, + param: self.param, + kind: "render_pipeline", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::ComputePipelineId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::ComputePipelineId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_compute_pipeline, + param: self.param, + kind: "compute_pipeline", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::BufferId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::BufferId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_buffer, + param: self.param, + kind: "buffer", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::TextureId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::TextureId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_texture, + param: self.param, + kind: "texture", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::TextureViewId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::TextureViewId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_texture_view, + param: self.param, + kind: "texture_view", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::SamplerId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::SamplerId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_sampler, + param: self.param, + kind: "sampler", + } + } +} +impl wgc::hub::IdentityHandlerFactory<id::SurfaceId> for IdentityRecyclerFactory { + type Filter = IdentityRecycler<id::SurfaceId>; + fn spawn(&self, _min_index: u32) -> Self::Filter { + IdentityRecycler { + fun: self.free_surface, + param: self.param, + kind: "surface", + } + } +} + +impl wgc::hub::GlobalIdentityHandlerFactory for IdentityRecyclerFactory {} diff --git a/gfx/wgpu_bindings/src/lib.rs b/gfx/wgpu_bindings/src/lib.rs new file mode 100644 index 0000000000..05754fca96 --- /dev/null +++ b/gfx/wgpu_bindings/src/lib.rs @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use wgc::id; + +pub use wgc::command::{compute_ffi::*, render_ffi::*}; + +pub mod client; +pub mod identity; +pub mod server; + +pub use wgc::device::trace::Command as CommandEncoderAction; + +use std::{borrow::Cow, mem, slice}; + +type RawString = *const std::os::raw::c_char; + +//TODO: figure out why 'a and 'b have to be different here +//TODO: remove this +fn cow_label<'a, 'b>(raw: &'a RawString) -> Option<Cow<'b, str>> { + if raw.is_null() { + None + } else { + let cstr = unsafe { std::ffi::CStr::from_ptr(*raw) }; + cstr.to_str().ok().map(Cow::Borrowed) + } +} + +#[repr(C)] +pub struct ByteBuf { + data: *const u8, + len: usize, + capacity: usize, +} + +impl ByteBuf { + fn from_vec(vec: Vec<u8>) -> Self { + if vec.is_empty() { + ByteBuf { + data: std::ptr::null(), + len: 0, + capacity: 0, + } + } else { + let bb = ByteBuf { + data: vec.as_ptr(), + len: vec.len(), + capacity: vec.capacity(), + }; + mem::forget(vec); + bb + } + } + + unsafe fn as_slice(&self) -> &[u8] { + slice::from_raw_parts(self.data, self.len) + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +struct ImplicitLayout<'a> { + pipeline: id::PipelineLayoutId, + bind_groups: Cow<'a, [id::BindGroupLayoutId]>, +} + +#[derive(serde::Serialize, serde::Deserialize)] +enum DeviceAction<'a> { + CreateBuffer(id::BufferId, wgc::resource::BufferDescriptor<'a>), + CreateTexture(id::TextureId, wgc::resource::TextureDescriptor<'a>), + CreateSampler(id::SamplerId, wgc::resource::SamplerDescriptor<'a>), + CreateBindGroupLayout( + id::BindGroupLayoutId, + wgc::binding_model::BindGroupLayoutDescriptor<'a>, + ), + CreatePipelineLayout( + id::PipelineLayoutId, + wgc::binding_model::PipelineLayoutDescriptor<'a>, + ), + CreateBindGroup(id::BindGroupId, wgc::binding_model::BindGroupDescriptor<'a>), + CreateShaderModule(id::ShaderModuleId, Cow<'a, [u32]>, Cow<'a, str>), + CreateComputePipeline( + id::ComputePipelineId, + wgc::pipeline::ComputePipelineDescriptor<'a>, + Option<ImplicitLayout<'a>>, + ), + CreateRenderPipeline( + id::RenderPipelineId, + wgc::pipeline::RenderPipelineDescriptor<'a>, + Option<ImplicitLayout<'a>>, + ), + CreateRenderBundle( + id::RenderBundleId, + wgc::command::RenderBundleEncoderDescriptor<'a>, + wgc::command::BasePass<wgc::command::RenderCommand>, + ), + CreateCommandEncoder( + id::CommandEncoderId, + wgt::CommandEncoderDescriptor<wgc::Label<'a>>, + ), +} + +#[derive(serde::Serialize, serde::Deserialize)] +enum TextureAction<'a> { + CreateView(id::TextureViewId, wgc::resource::TextureViewDescriptor<'a>), +} + +#[derive(serde::Serialize, serde::Deserialize)] +enum DropAction { + Buffer(id::BufferId), + Texture(id::TextureId), + Sampler(id::SamplerId), + BindGroupLayout(id::BindGroupLayoutId), +} diff --git a/gfx/wgpu_bindings/src/server.rs b/gfx/wgpu_bindings/src/server.rs new file mode 100644 index 0000000000..6c4ab3dc0c --- /dev/null +++ b/gfx/wgpu_bindings/src/server.rs @@ -0,0 +1,641 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + cow_label, identity::IdentityRecyclerFactory, ByteBuf, CommandEncoderAction, DeviceAction, + DropAction, RawString, TextureAction, +}; + +use wgc::{gfx_select, id}; + +use std::{fmt::Display, os::raw::c_char, ptr, slice}; + +#[repr(C)] +pub struct ErrorBuffer { + string: *mut c_char, + capacity: usize, +} + +impl ErrorBuffer { + fn init(&mut self, error: impl Display) { + assert_ne!(self.capacity, 0); + let string = format!("{}", error); + let length = if string.len() >= self.capacity { + log::warn!( + "Error length {} reached capacity {}", + string.len(), + self.capacity + ); + self.capacity - 1 + } else { + string.len() + }; + unsafe { + ptr::copy_nonoverlapping(string.as_ptr(), self.string as *mut u8, length); + *self.string.add(length) = 0; + } + } +} + +// hide wgc's global in private +pub struct Global(wgc::hub::Global<IdentityRecyclerFactory>); + +impl std::ops::Deref for Global { + type Target = wgc::hub::Global<IdentityRecyclerFactory>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_new(factory: IdentityRecyclerFactory) -> *mut Global { + log::info!("Initializing WGPU server"); + let global = Global(wgc::hub::Global::new( + "wgpu", + factory, + wgt::BackendBit::PRIMARY, + )); + Box::into_raw(Box::new(global)) +} + +/// # Safety +/// +/// This function is unsafe because improper use may lead to memory +/// problems. For example, a double-free may occur if the function is called +/// twice on the same raw pointer. +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_delete(global: *mut Global) { + log::info!("Terminating WGPU server"); + let _ = Box::from_raw(global); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_poll_all_devices(global: &Global, force_wait: bool) { + global.poll_all_devices(force_wait).unwrap(); +} + +/// Request an adapter according to the specified options. +/// Provide the list of IDs to pick from. +/// +/// Returns the index in this list, or -1 if unable to pick. +/// +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `id_length` elements. +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_instance_request_adapter( + global: &Global, + desc: &wgc::instance::RequestAdapterOptions, + ids: *const id::AdapterId, + id_length: usize, + mut error_buf: ErrorBuffer, +) -> i8 { + let ids = slice::from_raw_parts(ids, id_length); + match global.request_adapter( + desc, + wgc::instance::AdapterInputs::IdSet(ids, |i| i.backend()), + ) { + Ok(id) => ids.iter().position(|&i| i == id).unwrap() as i8, + Err(e) => { + error_buf.init(e); + -1 + } + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_fill_default_limits(limits: &mut wgt::Limits) { + *limits = wgt::Limits::default(); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_adapter_request_device( + global: &Global, + self_id: id::AdapterId, + desc: &wgt::DeviceDescriptor<RawString>, + new_id: id::DeviceId, + mut error_buf: ErrorBuffer, +) { + let trace_string = std::env::var("WGPU_TRACE").ok(); + let trace_path = trace_string + .as_ref() + .map(|string| std::path::Path::new(string.as_str())); + let desc = desc.map_label(cow_label); + let (_, error) = + gfx_select!(self_id => global.adapter_request_device(self_id, &desc, trace_path, new_id)); + if let Some(err) = error { + error_buf.init(err); + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_adapter_drop(global: &Global, adapter_id: id::AdapterId) { + gfx_select!(adapter_id => global.adapter_drop(adapter_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_server_device_drop(global: &Global, self_id: id::DeviceId) { + gfx_select!(self_id => global.device_drop(self_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_server_device_create_buffer( + global: &Global, + self_id: id::DeviceId, + desc: &wgt::BufferDescriptor<RawString>, + new_id: id::BufferId, + mut error_buf: ErrorBuffer, +) { + let desc = desc.map_label(cow_label); + let (_, error) = gfx_select!(self_id => global.device_create_buffer(self_id, &desc, new_id)); + if let Some(err) = error { + error_buf.init(err); + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_buffer_map( + global: &Global, + buffer_id: id::BufferId, + start: wgt::BufferAddress, + size: wgt::BufferAddress, + operation: wgc::resource::BufferMapOperation, +) { + gfx_select!(buffer_id => global.buffer_map_async( + buffer_id, + start .. start + size, + operation + )) + .unwrap(); +} + +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `size` elements. +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_buffer_get_mapped_range( + global: &Global, + buffer_id: id::BufferId, + start: wgt::BufferAddress, + size: Option<wgt::BufferSize>, +) -> *mut u8 { + gfx_select!(buffer_id => global.buffer_get_mapped_range( + buffer_id, + start, + size + )) + .unwrap() +} + +#[no_mangle] +pub extern "C" fn wgpu_server_buffer_unmap(global: &Global, buffer_id: id::BufferId) { + gfx_select!(buffer_id => global.buffer_unmap(buffer_id)).unwrap(); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_buffer_drop(global: &Global, self_id: id::BufferId) { + gfx_select!(self_id => global.buffer_drop(self_id, false)); +} + +trait GlobalExt { + fn device_action<B: wgc::hub::GfxBackend>( + &self, + self_id: id::DeviceId, + action: DeviceAction, + error_buf: ErrorBuffer, + ) -> Vec<u8>; + fn texture_action<B: wgc::hub::GfxBackend>( + &self, + self_id: id::TextureId, + action: TextureAction, + error_buf: ErrorBuffer, + ); + fn command_encoder_action<B: wgc::hub::GfxBackend>( + &self, + self_id: id::CommandEncoderId, + action: CommandEncoderAction, + error_buf: ErrorBuffer, + ); +} + +impl GlobalExt for Global { + fn device_action<B: wgc::hub::GfxBackend>( + &self, + self_id: id::DeviceId, + action: DeviceAction, + mut error_buf: ErrorBuffer, + ) -> Vec<u8> { + let mut drop_actions = Vec::new(); + match action { + DeviceAction::CreateBuffer(id, desc) => { + let (_, error) = self.device_create_buffer::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreateTexture(id, desc) => { + let (_, error) = self.device_create_texture::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreateSampler(id, desc) => { + let (_, error) = self.device_create_sampler::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreateBindGroupLayout(id, desc) => { + let (_, error) = self.device_create_bind_group_layout::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreatePipelineLayout(id, desc) => { + let (_, error) = self.device_create_pipeline_layout::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreateBindGroup(id, desc) => { + let (_, error) = self.device_create_bind_group::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreateShaderModule(id, spirv, wgsl) => { + let desc = wgc::pipeline::ShaderModuleDescriptor { + label: None, //TODO + source: if spirv.is_empty() { + wgc::pipeline::ShaderModuleSource::Wgsl(wgsl) + } else { + wgc::pipeline::ShaderModuleSource::SpirV(spirv) + }, + }; + let (_, error) = self.device_create_shader_module::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + DeviceAction::CreateComputePipeline(id, desc, implicit) => { + let implicit_ids = implicit + .as_ref() + .map(|imp| wgc::device::ImplicitPipelineIds { + root_id: imp.pipeline, + group_ids: &imp.bind_groups, + }); + let (_, group_count, error) = + self.device_create_compute_pipeline::<B>(self_id, &desc, id, implicit_ids); + if let Some(err) = error { + error_buf.init(err); + } + if let Some(ref imp) = implicit { + for &bgl_id in imp.bind_groups[group_count as usize..].iter() { + bincode::serialize_into( + &mut drop_actions, + &DropAction::BindGroupLayout(bgl_id), + ) + .unwrap(); + } + } + } + DeviceAction::CreateRenderPipeline(id, desc, implicit) => { + let implicit_ids = implicit + .as_ref() + .map(|imp| wgc::device::ImplicitPipelineIds { + root_id: imp.pipeline, + group_ids: &imp.bind_groups, + }); + let (_, group_count, error) = + self.device_create_render_pipeline::<B>(self_id, &desc, id, implicit_ids); + if let Some(err) = error { + error_buf.init(err); + } + if let Some(ref imp) = implicit { + for &bgl_id in imp.bind_groups[group_count as usize..].iter() { + bincode::serialize_into( + &mut drop_actions, + &DropAction::BindGroupLayout(bgl_id), + ) + .unwrap(); + } + } + } + DeviceAction::CreateRenderBundle(_id, desc, _base) => { + wgc::command::RenderBundleEncoder::new(&desc, self_id, None).unwrap(); + } + DeviceAction::CreateCommandEncoder(id, desc) => { + let (_, error) = self.device_create_command_encoder::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + } + drop_actions + } + + fn texture_action<B: wgc::hub::GfxBackend>( + &self, + self_id: id::TextureId, + action: TextureAction, + mut error_buf: ErrorBuffer, + ) { + match action { + TextureAction::CreateView(id, desc) => { + let (_, error) = self.texture_create_view::<B>(self_id, &desc, id); + if let Some(err) = error { + error_buf.init(err); + } + } + } + } + + fn command_encoder_action<B: wgc::hub::GfxBackend>( + &self, + self_id: id::CommandEncoderId, + action: CommandEncoderAction, + mut error_buf: ErrorBuffer, + ) { + match action { + CommandEncoderAction::CopyBufferToBuffer { + src, + src_offset, + dst, + dst_offset, + size, + } => { + if let Err(err) = self.command_encoder_copy_buffer_to_buffer::<B>( + self_id, src, src_offset, dst, dst_offset, size, + ) { + error_buf.init(err); + } + } + CommandEncoderAction::CopyBufferToTexture { src, dst, size } => { + if let Err(err) = + self.command_encoder_copy_buffer_to_texture::<B>(self_id, &src, &dst, &size) + { + error_buf.init(err); + } + } + CommandEncoderAction::CopyTextureToBuffer { src, dst, size } => { + if let Err(err) = + self.command_encoder_copy_texture_to_buffer::<B>(self_id, &src, &dst, &size) + { + error_buf.init(err); + } + } + CommandEncoderAction::CopyTextureToTexture { src, dst, size } => { + if let Err(err) = + self.command_encoder_copy_texture_to_texture::<B>(self_id, &src, &dst, &size) + { + error_buf.init(err); + } + } + CommandEncoderAction::RunComputePass { base } => { + if let Err(err) = + self.command_encoder_run_compute_pass_impl::<B>(self_id, base.as_ref()) + { + error_buf.init(err); + } + } + CommandEncoderAction::RunRenderPass { + base, + target_colors, + target_depth_stencil, + } => { + if let Err(err) = self.command_encoder_run_render_pass_impl::<B>( + self_id, + base.as_ref(), + &target_colors, + target_depth_stencil.as_ref(), + ) { + error_buf.init(err); + } + } + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_device_action( + global: &Global, + self_id: id::DeviceId, + byte_buf: &ByteBuf, + drop_byte_buf: &mut ByteBuf, + error_buf: ErrorBuffer, +) { + let action = bincode::deserialize(byte_buf.as_slice()).unwrap(); + let drop_actions = gfx_select!(self_id => global.device_action(self_id, action, error_buf)); + *drop_byte_buf = ByteBuf::from_vec(drop_actions); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_texture_action( + global: &Global, + self_id: id::TextureId, + byte_buf: &ByteBuf, + error_buf: ErrorBuffer, +) { + let action = bincode::deserialize(byte_buf.as_slice()).unwrap(); + gfx_select!(self_id => global.texture_action(self_id, action, error_buf)); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_command_encoder_action( + global: &Global, + self_id: id::CommandEncoderId, + byte_buf: &ByteBuf, + error_buf: ErrorBuffer, +) { + let action = bincode::deserialize(byte_buf.as_slice()).unwrap(); + gfx_select!(self_id => global.command_encoder_action(self_id, action, error_buf)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_device_create_encoder( + global: &Global, + self_id: id::DeviceId, + desc: &wgt::CommandEncoderDescriptor<RawString>, + new_id: id::CommandEncoderId, + mut error_buf: ErrorBuffer, +) { + let desc = desc.map_label(cow_label); + let (_, error) = + gfx_select!(self_id => global.device_create_command_encoder(self_id, &desc, new_id)); + if let Some(err) = error { + error_buf.init(err); + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_encoder_finish( + global: &Global, + self_id: id::CommandEncoderId, + desc: &wgt::CommandBufferDescriptor<RawString>, + mut error_buf: ErrorBuffer, +) { + let desc = desc.map_label(cow_label); + let (_, error) = gfx_select!(self_id => global.command_encoder_finish(self_id, &desc)); + if let Some(err) = error { + error_buf.init(err); + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_encoder_drop(global: &Global, self_id: id::CommandEncoderId) { + gfx_select!(self_id => global.command_encoder_drop(self_id)); +} + +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `byte_length` elements. +#[no_mangle] +pub extern "C" fn wgpu_server_command_buffer_drop(global: &Global, self_id: id::CommandBufferId) { + gfx_select!(self_id => global.command_buffer_drop(self_id)); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_encoder_copy_texture_to_buffer( + global: &Global, + self_id: id::CommandEncoderId, + source: &wgc::command::TextureCopyView, + destination: &wgc::command::BufferCopyView, + size: &wgt::Extent3d, +) { + gfx_select!(self_id => global.command_encoder_copy_texture_to_buffer(self_id, source, destination, size)).unwrap(); +} + +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `command_buffer_id_length` elements. +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_queue_submit( + global: &Global, + self_id: id::QueueId, + command_buffer_ids: *const id::CommandBufferId, + command_buffer_id_length: usize, +) { + let command_buffers = slice::from_raw_parts(command_buffer_ids, command_buffer_id_length); + gfx_select!(self_id => global.queue_submit(self_id, command_buffers)).unwrap(); +} + +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `data_length` elements. +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_queue_write_buffer( + global: &Global, + self_id: id::QueueId, + buffer_id: id::BufferId, + buffer_offset: wgt::BufferAddress, + data: *const u8, + data_length: usize, +) { + let data = slice::from_raw_parts(data, data_length); + gfx_select!(self_id => global.queue_write_buffer(self_id, buffer_id, buffer_offset, data)) + .unwrap(); +} + +/// # Safety +/// +/// This function is unsafe as there is no guarantee that the given pointer is +/// valid for `data_length` elements. +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_queue_write_texture( + global: &Global, + self_id: id::QueueId, + destination: &wgc::command::TextureCopyView, + data: *const u8, + data_length: usize, + layout: &wgt::TextureDataLayout, + extent: &wgt::Extent3d, +) { + let data = slice::from_raw_parts(data, data_length); + gfx_select!(self_id => global.queue_write_texture(self_id, destination, data, layout, extent)) + .unwrap(); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_bind_group_layout_drop( + global: &Global, + self_id: id::BindGroupLayoutId, +) { + gfx_select!(self_id => global.bind_group_layout_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_pipeline_layout_drop(global: &Global, self_id: id::PipelineLayoutId) { + gfx_select!(self_id => global.pipeline_layout_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_bind_group_drop(global: &Global, self_id: id::BindGroupId) { + gfx_select!(self_id => global.bind_group_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_shader_module_drop(global: &Global, self_id: id::ShaderModuleId) { + gfx_select!(self_id => global.shader_module_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_compute_pipeline_drop( + global: &Global, + self_id: id::ComputePipelineId, +) { + gfx_select!(self_id => global.compute_pipeline_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_render_pipeline_drop(global: &Global, self_id: id::RenderPipelineId) { + gfx_select!(self_id => global.render_pipeline_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_texture_drop(global: &Global, self_id: id::TextureId) { + gfx_select!(self_id => global.texture_drop(self_id, false)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_texture_view_drop(global: &Global, self_id: id::TextureViewId) { + gfx_select!(self_id => global.texture_view_drop(self_id)).unwrap(); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_sampler_drop(global: &Global, self_id: id::SamplerId) { + gfx_select!(self_id => global.sampler_drop(self_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_compute_pipeline_get_bind_group_layout( + global: &Global, + self_id: id::ComputePipelineId, + index: u32, + assign_id: id::BindGroupLayoutId, + mut error_buf: ErrorBuffer, +) { + let (_, error) = gfx_select!(self_id => global.compute_pipeline_get_bind_group_layout(self_id, index, assign_id)); + if let Some(err) = error { + error_buf.init(err); + } +} + +#[no_mangle] +pub extern "C" fn wgpu_server_render_pipeline_get_bind_group_layout( + global: &Global, + self_id: id::RenderPipelineId, + index: u32, + assign_id: id::BindGroupLayoutId, + mut error_buf: ErrorBuffer, +) { + let (_, error) = gfx_select!(self_id => global.render_pipeline_get_bind_group_layout(self_id, index, assign_id)); + if let Some(err) = error { + error_buf.init(err); + } +} diff --git a/gfx/wgpu_bindings/wgpu.h b/gfx/wgpu_bindings/wgpu.h new file mode 100644 index 0000000000..b81603738e --- /dev/null +++ b/gfx/wgpu_bindings/wgpu.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WGPU_h +#define WGPU_h + +// Prelude of types necessary before including wgpu_ffi_generated.h +namespace mozilla { +namespace ipc { +class ByteBuf; +} // namespace ipc +namespace webgpu { +namespace ffi { + +#define WGPU_INLINE +#define WGPU_FUNC + +extern "C" { +#include "wgpu_ffi_generated.h" +} + +#undef WGPU_INLINE +#undef WGPU_FUNC + +} // namespace ffi + +inline ffi::WGPUByteBuf* ToFFI(ipc::ByteBuf* x) { + return reinterpret_cast<ffi::WGPUByteBuf*>(x); +} +inline const ffi::WGPUByteBuf* ToFFI(const ipc::ByteBuf* x) { + return reinterpret_cast<const ffi::WGPUByteBuf*>(x); +} + +} // namespace webgpu +} // namespace mozilla + +#endif // WGPU_h |