summaryrefslogtreecommitdiffstats
path: root/gfx/wgpu_bindings/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/wgpu_bindings/src/client.rs1016
-rw-r--r--gfx/wgpu_bindings/src/identity.rs214
-rw-r--r--gfx/wgpu_bindings/src/lib.rs114
-rw-r--r--gfx/wgpu_bindings/src/server.rs641
4 files changed, 1985 insertions, 0 deletions
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);
+ }
+}