Source code

Revision control

Copy as Markdown

Other Tools

/* 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, error::HasErrorBufferType, wgpu_string, AdapterInformation, ByteBuf,
CommandEncoderAction, DeviceAction, DropAction, ImageDataLayout, ImplicitLayout,
QueueWriteAction, RawString, TextureAction,
};
use crate::SwapChainId;
use wgc::{id, identity::IdentityManager};
use wgt::{Backend, TextureFormat};
pub use wgc::command::{compute_ffi::*, render_ffi::*};
use wgc::id::markers;
use parking_lot::Mutex;
use nsstring::{nsACString, nsString};
use std::{borrow::Cow, ptr};
// we can't call `from_raw_parts` unconditionally because the caller
// may not even have a valid pointer (e.g. NULL) if the `length` is zero.
fn make_slice<'a, T>(pointer: *const T, length: usize) -> &'a [T] {
if length == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(pointer, length) }
}
}
fn make_byte_buf<T: serde::Serialize>(data: &T) -> ByteBuf {
let vec = bincode::serialize(data).unwrap();
ByteBuf::from_vec(vec)
}
#[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),
constants: Cow::Owned(std::collections::HashMap::new()),
}
}
}
#[repr(C)]
pub struct ComputePipelineDescriptor<'a> {
label: Option<&'a nsACString>,
layout: Option<id::PipelineLayoutId>,
stage: ProgrammableStageDescriptor,
}
#[repr(C)]
pub struct VertexBufferLayout {
array_stride: wgt::BufferAddress,
step_mode: wgt::VertexStepMode,
attributes: *const wgt::VertexAttribute,
attributes_length: usize,
}
#[repr(C)]
pub struct VertexState {
stage: ProgrammableStageDescriptor,
buffers: *const VertexBufferLayout,
buffers_length: usize,
}
impl VertexState {
fn to_wgpu(&self) -> wgc::pipeline::VertexState {
let buffer_layouts = make_slice(self.buffers, self.buffers_length)
.iter()
.map(|vb| wgc::pipeline::VertexBufferLayout {
array_stride: vb.array_stride,
step_mode: vb.step_mode,
attributes: Cow::Borrowed(make_slice(vb.attributes, vb.attributes_length)),
})
.collect();
wgc::pipeline::VertexState {
stage: self.stage.to_wgpu(),
buffers: Cow::Owned(buffer_layouts),
}
}
}
#[repr(C)]
pub struct ColorTargetState<'a> {
format: wgt::TextureFormat,
blend: Option<&'a wgt::BlendState>,
write_mask: wgt::ColorWrites,
}
#[repr(C)]
pub struct FragmentState<'a> {
stage: ProgrammableStageDescriptor,
targets: *const ColorTargetState<'a>,
targets_length: usize,
}
impl FragmentState<'_> {
fn to_wgpu(&self) -> wgc::pipeline::FragmentState {
let color_targets = make_slice(self.targets, self.targets_length)
.iter()
.map(|ct| {
Some(wgt::ColorTargetState {
format: ct.format,
blend: ct.blend.cloned(),
write_mask: ct.write_mask,
})
})
.collect();
wgc::pipeline::FragmentState {
stage: self.stage.to_wgpu(),
targets: Cow::Owned(color_targets),
}
}
}
#[repr(C)]
pub struct PrimitiveState<'a> {
topology: wgt::PrimitiveTopology,
strip_index_format: Option<&'a wgt::IndexFormat>,
front_face: wgt::FrontFace,
cull_mode: Option<&'a wgt::Face>,
polygon_mode: wgt::PolygonMode,
unclipped_depth: bool,
}
impl PrimitiveState<'_> {
fn to_wgpu(&self) -> wgt::PrimitiveState {
wgt::PrimitiveState {
topology: self.topology,
strip_index_format: self.strip_index_format.cloned(),
front_face: self.front_face.clone(),
cull_mode: self.cull_mode.cloned(),
polygon_mode: self.polygon_mode,
unclipped_depth: self.unclipped_depth,
conservative: false,
}
}
}
#[repr(C)]
pub struct RenderPipelineDescriptor<'a> {
label: Option<&'a nsACString>,
layout: Option<id::PipelineLayoutId>,
vertex: &'a VertexState,
primitive: PrimitiveState<'a>,
fragment: Option<&'a FragmentState<'a>>,
depth_stencil: Option<&'a wgt::DepthStencilState>,
multisample: wgt::MultisampleState,
}
#[repr(C)]
pub enum RawTextureSampleType {
Float,
UnfilterableFloat,
Uint,
Sint,
Depth,
}
#[repr(C)]
pub enum RawBindingType {
UniformBuffer,
StorageBuffer,
ReadonlyStorageBuffer,
Sampler,
SampledTexture,
ReadonlyStorageTexture,
WriteonlyStorageTexture,
ReadWriteStorageTexture,
}
#[repr(C)]
pub struct BindGroupLayoutEntry<'a> {
binding: u32,
visibility: wgt::ShaderStages,
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>,
sampler_filter: bool,
sampler_compare: bool,
}
#[repr(C)]
pub struct BindGroupLayoutDescriptor<'a> {
label: Option<&'a nsACString>,
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<'a> {
label: Option<&'a nsACString>,
layout: id::BindGroupLayoutId,
entries: *const BindGroupEntry,
entries_length: usize,
}
#[repr(C)]
pub struct PipelineLayoutDescriptor<'a> {
label: Option<&'a nsACString>,
bind_group_layouts: *const id::BindGroupLayoutId,
bind_group_layouts_length: usize,
}
#[repr(C)]
pub struct SamplerDescriptor<'a> {
label: Option<&'a nsACString>,
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>,
max_anisotropy: u16,
}
#[repr(C)]
pub struct TextureViewDescriptor<'a> {
label: Option<&'a nsACString>,
format: Option<&'a wgt::TextureFormat>,
dimension: Option<&'a wgt::TextureViewDimension>,
aspect: wgt::TextureAspect,
base_mip_level: u32,
mip_level_count: Option<&'a u32>,
base_array_layer: u32,
array_layer_count: Option<&'a u32>,
}
#[repr(C)]
pub struct RenderBundleEncoderDescriptor<'a> {
label: Option<&'a nsACString>,
color_formats: *const wgt::TextureFormat,
color_formats_length: usize,
depth_stencil_format: Option<&'a wgt::TextureFormat>,
depth_read_only: bool,
stencil_read_only: bool,
sample_count: u32,
}
#[derive(Debug)]
struct IdentityHub {
adapters: IdentityManager<markers::Adapter>,
devices: IdentityManager<markers::Device>,
buffers: IdentityManager<markers::Buffer>,
command_buffers: IdentityManager<markers::CommandBuffer>,
render_bundles: IdentityManager<markers::RenderBundle>,
bind_group_layouts: IdentityManager<markers::BindGroupLayout>,
pipeline_layouts: IdentityManager<markers::PipelineLayout>,
bind_groups: IdentityManager<markers::BindGroup>,
shader_modules: IdentityManager<markers::ShaderModule>,
compute_pipelines: IdentityManager<markers::ComputePipeline>,
render_pipelines: IdentityManager<markers::RenderPipeline>,
textures: IdentityManager<markers::Texture>,
texture_views: IdentityManager<markers::TextureView>,
samplers: IdentityManager<markers::Sampler>,
}
impl Default for IdentityHub {
fn default() -> Self {
IdentityHub {
adapters: IdentityManager::new(),
devices: IdentityManager::new(),
buffers: IdentityManager::new(),
command_buffers: IdentityManager::new(),
render_bundles: IdentityManager::new(),
bind_group_layouts: IdentityManager::new(),
pipeline_layouts: IdentityManager::new(),
bind_groups: IdentityManager::new(),
shader_modules: IdentityManager::new(),
compute_pipelines: IdentityManager::new(),
render_pipelines: IdentityManager::new(),
textures: IdentityManager::new(),
texture_views: IdentityManager::new(),
samplers: IdentityManager::new(),
}
}
}
impl ImplicitLayout<'_> {
fn new(identities: &mut IdentityHub, backend: Backend) -> Self {
ImplicitLayout {
pipeline: identities.pipeline_layouts.process(backend),
bind_groups: Cow::Owned(
(0..8) // hal::MAX_BIND_GROUPS
.map(|_| Some(identities.bind_group_layouts.process(backend)))
.collect(),
),
}
}
}
#[derive(Debug, Default)]
struct Identities {
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::Adapter(id) => identities.select(id.backend()).adapters.free(id),
DropAction::Device(id) => identities.select(id.backend()).devices.free(id),
DropAction::ShaderModule(id) => identities.select(id.backend()).shader_modules.free(id),
DropAction::PipelineLayout(id) => {
identities.select(id.backend()).pipeline_layouts.free(id)
}
DropAction::BindGroupLayout(id) => {
identities.select(id.backend()).bind_group_layouts.free(id)
}
DropAction::BindGroup(id) => identities.select(id.backend()).bind_groups.free(id),
DropAction::CommandBuffer(id) => {
identities.select(id.backend()).command_buffers.free(id)
}
DropAction::RenderBundle(id) => identities.select(id.backend()).render_bundles.free(id),
DropAction::RenderPipeline(id) => {
identities.select(id.backend()).render_pipelines.free(id)
}
DropAction::ComputePipeline(id) => {
identities.select(id.backend()).compute_pipelines.free(id)
}
DropAction::Buffer(id) => identities.select(id.backend()).buffers.free(id),
DropAction::Texture(id) => identities.select(id.backend()).textures.free(id),
DropAction::TextureView(id) => identities.select(id.backend()).texture_views.free(id),
DropAction::Sampler(id) => identities.select(id.backend()).samplers.free(id),
}
}
}
#[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)
}
#[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 identities = client.identities.lock();
assert_ne!(id_length, 0);
let mut ids = std::slice::from_raw_parts_mut(ids, id_length).iter_mut();
*ids.next().unwrap() = identities.vulkan.adapters.process(Backend::Vulkan);
#[cfg(any(target_os = "ios", target_os = "macos"))]
{
*ids.next().unwrap() = identities.metal.adapters.process(Backend::Metal);
}
#[cfg(windows)]
{
*ids.next().unwrap() = identities.dx12.adapters.process(Backend::Dx12);
}
id_length - ids.len()
}
#[no_mangle]
pub extern "C" fn wgpu_client_fill_default_limits(limits: &mut wgt::Limits) {
*limits = wgt::Limits::default();
}
#[no_mangle]
pub extern "C" fn wgpu_client_adapter_extract_info(
byte_buf: &ByteBuf,
info: &mut AdapterInformation<nsString>,
) {
let AdapterInformation {
backend,
device_type,
device,
driver_info,
driver,
features,
id,
limits,
name,
vendor,
} = bincode::deserialize::<AdapterInformation<String>>(unsafe { byte_buf.as_slice() }).unwrap();
let nss = |s: &str| {
let mut ns_string = nsString::new();
ns_string.assign_str(s);
ns_string
};
*info = AdapterInformation {
backend,
device_type,
device,
driver_info: nss(&driver_info),
driver: nss(&driver),
features,
id,
limits,
name: nss(&name),
vendor,
};
}
#[no_mangle]
pub extern "C" fn wgpu_client_serialize_device_descriptor(
desc: &wgt::DeviceDescriptor<Option<&nsACString>>,
bb: &mut ByteBuf,
) {
let label = wgpu_string(desc.label);
*bb = make_byte_buf(&desc.map_label(|_| label));
}
#[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
.process(backend)
}
#[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
.process(backend)
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_buffer_id(
client: &Client,
id: id::BufferId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(backend)
.buffers
.free(id)
}
#[no_mangle]
pub extern "C" fn wgpu_client_create_texture(
client: &Client,
device_id: id::DeviceId,
desc: &wgt::TextureDescriptor<Option<&nsACString>, crate::FfiSlice<TextureFormat>>,
swap_chain_id: Option<&SwapChainId>,
bb: &mut ByteBuf,
) -> id::TextureId {
let label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.textures
.process(backend);
let view_formats = unsafe { desc.view_formats.as_slice() }.to_vec();
let action = DeviceAction::CreateTexture(
id,
desc.map_label_and_view_formats(|_| label, |_| view_formats),
swap_chain_id.copied(),
);
*bb = make_byte_buf(&action);
id
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_texture_id(
client: &Client,
id: id::TextureId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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 label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.texture_views
.process(backend);
let wgpu_desc = wgc::resource::TextureViewDescriptor {
label,
format: desc.format.cloned(),
dimension: desc.dimension.cloned(),
range: wgt::ImageSubresourceRange {
aspect: desc.aspect,
base_mip_level: desc.base_mip_level,
mip_level_count: desc.mip_level_count.map(|ptr| *ptr),
base_array_layer: desc.base_array_layer,
array_layer_count: desc.array_layer_count.map(|ptr| *ptr),
},
};
let action = TextureAction::CreateView(id, wgpu_desc);
*bb = make_byte_buf(&action);
id
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_texture_view_id(
client: &Client,
id: id::TextureViewId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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 label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.samplers
.process(backend);
let wgpu_desc = wgc::resource::SamplerDescriptor {
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.max_anisotropy,
border_color: None,
};
let action = DeviceAction::CreateSampler(id, wgpu_desc);
*bb = make_byte_buf(&action);
id
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_sampler_id(
client: &Client,
id: id::SamplerId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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
.process(backend)
.into_command_encoder_id()
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_command_encoder_id(
client: &Client,
id: id::CommandEncoderId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(backend)
.command_buffers
.free(id.into_command_buffer_id())
}
#[no_mangle]
pub extern "C" fn wgpu_client_create_command_encoder(
client: &Client,
device_id: id::DeviceId,
desc: &wgt::CommandEncoderDescriptor<Option<&nsACString>>,
bb: &mut ByteBuf,
) -> id::CommandEncoderId {
let label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.command_buffers
.process(backend)
.into_command_encoder_id();
let action = DeviceAction::CreateCommandEncoder(id, desc.map_label(|_| label));
*bb = make_byte_buf(&action);
id
}
#[no_mangle]
pub extern "C" fn wgpu_device_create_render_bundle_encoder(
device_id: id::DeviceId,
desc: &RenderBundleEncoderDescriptor,
bb: &mut ByteBuf,
) -> *mut wgc::command::RenderBundleEncoder {
let label = wgpu_string(desc.label);
let color_formats: Vec<_> = make_slice(desc.color_formats, desc.color_formats_length)
.iter()
.map(|format| Some(format.clone()))
.collect();
let descriptor = wgc::command::RenderBundleEncoderDescriptor {
label,
color_formats: Cow::Owned(color_formats),
depth_stencil: desc
.depth_stencil_format
.map(|&format| wgt::RenderBundleDepthStencil {
format,
depth_read_only: desc.depth_read_only,
stencil_read_only: desc.stencil_read_only,
}),
sample_count: desc.sample_count,
multiview: None,
};
match wgc::command::RenderBundleEncoder::new(&descriptor, device_id, None) {
Ok(encoder) => Box::into_raw(Box::new(encoder)),
Err(e) => {
let message = format!("Error in `Device::create_render_bundle_encoder`: {}", e);
let action = DeviceAction::Error {
message,
r#type: e.error_type(),
};
*bb = make_byte_buf(&action);
ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_encoder_destroy(
pass: *mut wgc::command::RenderBundleEncoder,
) {
// The RB encoder is just a boxed Rust struct, it doesn't have any API primitives
// associated with it right now, but in the future it will.
let _ = Box::from_raw(pass);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_client_create_render_bundle(
client: &Client,
encoder: *mut wgc::command::RenderBundleEncoder,
device_id: id::DeviceId,
desc: &wgt::RenderBundleDescriptor<Option<&nsACString>>,
bb: &mut ByteBuf,
) -> id::RenderBundleId {
let label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.render_bundles
.process(backend);
let action =
DeviceAction::CreateRenderBundle(id, *Box::from_raw(encoder), desc.map_label(|_| label));
*bb = make_byte_buf(&action);
id
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_client_create_render_bundle_error(
client: &Client,
device_id: id::DeviceId,
label: Option<&nsACString>,
bb: &mut ByteBuf,
) -> id::RenderBundleId {
let label = wgpu_string(label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.render_bundles
.process(backend);
let action = DeviceAction::CreateRenderBundleError(id, label);
*bb = make_byte_buf(&action);
id
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_render_bundle_id(
client: &Client,
id: id::RenderBundleId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(backend)
.render_bundles
.free(id)
}
#[repr(C)]
pub struct ComputePassDescriptor<'a> {
pub label: Option<&'a nsACString>,
pub timestamp_writes: Option<&'a ComputePassTimestampWrites<'a>>,
}
#[repr(C)]
pub struct ComputePassTimestampWrites<'a> {
pub query_set: id::QuerySetId,
pub beginning_of_pass_write_index: Option<&'a u32>,
pub end_of_pass_write_index: Option<&'a u32>,
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_begin_compute_pass(
desc: &ComputePassDescriptor,
) -> *mut crate::command::RecordedComputePass {
let &ComputePassDescriptor {
label,
timestamp_writes,
} = desc;
let label = wgpu_string(label);
let timestamp_writes = timestamp_writes.map(|tsw| {
let &ComputePassTimestampWrites {
query_set,
beginning_of_pass_write_index,
end_of_pass_write_index,
} = tsw;
let beginning_of_pass_write_index = beginning_of_pass_write_index.cloned();
let end_of_pass_write_index = end_of_pass_write_index.cloned();
wgc::command::ComputePassTimestampWrites {
query_set,
beginning_of_pass_write_index,
end_of_pass_write_index,
}
});
let timestamp_writes = timestamp_writes.as_ref();
let pass = crate::command::RecordedComputePass::new(&wgc::command::ComputePassDescriptor {
label,
timestamp_writes,
});
Box::into_raw(Box::new(pass))
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_finish(
pass: *mut crate::command::RecordedComputePass,
output: &mut ByteBuf,
) {
let command = Box::from_raw(pass);
*output = make_byte_buf(&command);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_destroy(pass: *mut crate::command::RecordedComputePass) {
let _ = Box::from_raw(pass);
}
#[repr(C)]
pub struct RenderPassDescriptor<'a> {
pub label: Option<&'a nsACString>,
pub color_attachments: *const wgc::command::RenderPassColorAttachment,
pub color_attachments_length: usize,
pub depth_stencil_attachment: *const wgc::command::RenderPassDepthStencilAttachment,
pub timestamp_writes: Option<&'a RenderPassTimestampWrites<'a>>,
pub occlusion_query_set: Option<wgc::id::QuerySetId>,
}
#[repr(C)]
pub struct RenderPassTimestampWrites<'a> {
pub query_set: wgc::id::QuerySetId,
pub beginning_of_pass_write_index: Option<&'a u32>,
pub end_of_pass_write_index: Option<&'a u32>,
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_begin_render_pass(
desc: &RenderPassDescriptor,
) -> *mut crate::command::RecordedRenderPass {
let &RenderPassDescriptor {
label,
color_attachments,
color_attachments_length,
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
} = desc;
let label = wgpu_string(label);
let timestamp_writes = timestamp_writes.map(|tsw| {
let &RenderPassTimestampWrites {
query_set,
beginning_of_pass_write_index,
end_of_pass_write_index,
} = tsw;
let beginning_of_pass_write_index = beginning_of_pass_write_index.cloned();
let end_of_pass_write_index = end_of_pass_write_index.cloned();
wgc::command::RenderPassTimestampWrites {
query_set,
beginning_of_pass_write_index,
end_of_pass_write_index,
}
});
let timestamp_writes = timestamp_writes.as_ref();
let color_attachments: Vec<_> = make_slice(color_attachments, color_attachments_length)
.iter()
.map(|format| Some(format.clone()))
.collect();
let pass = crate::command::RecordedRenderPass::new(&wgc::command::RenderPassDescriptor {
label,
color_attachments: Cow::Owned(color_attachments),
depth_stencil_attachment: depth_stencil_attachment.as_ref(),
timestamp_writes,
occlusion_query_set,
});
Box::into_raw(Box::new(pass))
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_finish(
pass: *mut crate::command::RecordedRenderPass,
output: &mut ByteBuf,
) {
let command = Box::from_raw(pass);
*output = make_byte_buf(&command);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_destroy(pass: *mut crate::command::RecordedRenderPass) {
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 label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.bind_group_layouts
.process(backend);
let mut entries = Vec::with_capacity(desc.entries_length);
for entry in make_slice(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(if entry.sampler_compare {
wgt::SamplerBindingType::Comparison
} else if entry.sampler_filter {
wgt::SamplerBindingType::Filtering
} else {
wgt::SamplerBindingType::NonFiltering
}),
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(),
},
RawBindingType::ReadWriteStorageTexture => wgt::BindingType::StorageTexture {
access: wgt::StorageTextureAccess::ReadWrite,
view_dimension: *entry.view_dimension.unwrap(),
format: *entry.storage_texture_format.unwrap(),
},
},
});
}
let wgpu_desc = wgc::binding_model::BindGroupLayoutDescriptor {
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_free_bind_group_layout_id(
client: &Client,
id: id::BindGroupLayoutId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(backend)
.bind_group_layouts
.free(id)
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_client_render_pipeline_get_bind_group_layout(
client: &Client,
pipeline_id: id::RenderPipelineId,
index: u32,
bb: &mut ByteBuf,
) -> id::BindGroupLayoutId {
let backend = pipeline_id.backend();
let bgl_id = client
.identities
.lock()
.select(backend)
.bind_group_layouts
.process(backend);
let action = DeviceAction::RenderPipelineGetBindGroupLayout(pipeline_id, index, bgl_id);
*bb = make_byte_buf(&action);
bgl_id
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_client_compute_pipeline_get_bind_group_layout(
client: &Client,
pipeline_id: id::ComputePipelineId,
index: u32,
bb: &mut ByteBuf,
) -> id::BindGroupLayoutId {
let backend = pipeline_id.backend();
let bgl_id = client
.identities
.lock()
.select(backend)
.bind_group_layouts
.process(backend);
let action = DeviceAction::ComputePipelineGetBindGroupLayout(pipeline_id, index, bgl_id);
*bb = make_byte_buf(&action);
bgl_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 label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.pipeline_layouts
.process(backend);
let wgpu_desc = wgc::binding_model::PipelineLayoutDescriptor {
label,
bind_group_layouts: Cow::Borrowed(make_slice(
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_free_pipeline_layout_id(
client: &Client,
id: id::PipelineLayoutId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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 label = wgpu_string(desc.label);
let backend = device_id.backend();
let id = client
.identities
.lock()
.select(backend)
.bind_groups
.process(backend);
let mut entries = Vec::with_capacity(desc.entries_length);
for entry in make_slice(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,
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_free_bind_group_id(
client: &Client,
id: id::BindGroupId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(backend)
.bind_groups
.free(id)
}
#[no_mangle]
pub extern "C" fn wgpu_client_make_shader_module_id(
client: &Client,
device_id: id::DeviceId,
) -> id::ShaderModuleId {
let backend = device_id.backend();
client
.identities
.lock()
.select(backend)
.shader_modules
.process(backend)
}
#[no_mangle]
pub extern "C" fn wgpu_client_free_shader_module_id(
client: &Client,
id: id::ShaderModuleId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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_pipeline_layout_id: *mut Option<id::PipelineLayoutId>,
implicit_bind_group_layout_ids: *mut Option<id::BindGroupLayoutId>,
) -> id::ComputePipelineId {
let label = wgpu_string(desc.label);
let backend = device_id.backend();
let mut identities = client.identities.lock();
let id = identities
.select(backend)
.compute_pipelines
.process(backend);
let wgpu_desc = wgc::pipeline::ComputePipelineDescriptor {
label,
layout: desc.layout,
stage: desc.stage.to_wgpu(),
};
let implicit = match desc.layout {
Some(_) => None,
None => {
let implicit = ImplicitLayout::new(identities.select(backend), backend);
ptr::write(implicit_pipeline_layout_id, Some(implicit.pipeline));
for (i, bgl_id) in implicit.bind_groups.iter().enumerate() {
*implicit_bind_group_layout_ids.add(i) = *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_free_compute_pipeline_id(
client: &Client,
id: id::ComputePipelineId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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_pipeline_layout_id: *mut Option<id::PipelineLayoutId>,
implicit_bind_group_layout_ids: *mut Option<id::BindGroupLayoutId>,
) -> id::RenderPipelineId {
let label = wgpu_string(desc.label);
let backend = device_id.backend();
let mut identities = client.identities.lock();
let id = identities.select(backend).render_pipelines.process(backend);
let wgpu_desc = wgc::pipeline::RenderPipelineDescriptor {
label,
layout: desc.layout,
vertex: desc.vertex.to_wgpu(),
fragment: desc.fragment.map(FragmentState::to_wgpu),
primitive: desc.primitive.to_wgpu(),
depth_stencil: desc.depth_stencil.cloned(),
multisample: desc.multisample.clone(),
multiview: None,
};
let implicit = match desc.layout {
Some(_) => None,
None => {
let implicit = ImplicitLayout::new(identities.select(backend), backend);
ptr::write(implicit_pipeline_layout_id, Some(implicit.pipeline));
for (i, bgl_id) in implicit.bind_groups.iter().enumerate() {
*implicit_bind_group_layout_ids.add(i) = *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_free_render_pipeline_id(
client: &Client,
id: id::RenderPipelineId,
) {
let backend = id.backend();
client
.identities
.lock()
.select(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::ImageCopyTexture,
dst_buffer: wgc::id::BufferId,
dst_layout: &ImageDataLayout,
size: wgt::Extent3d,
bb: &mut ByteBuf,
) {
let action = CommandEncoderAction::CopyTextureToBuffer {
src,
dst: wgc::command::ImageCopyBuffer {
buffer: dst_buffer,
layout: dst_layout.into_wgt(),
},
size,
};
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_copy_buffer_to_texture(
src_buffer: wgc::id::BufferId,
src_layout: &ImageDataLayout,
dst: wgc::command::ImageCopyTexture,
size: wgt::Extent3d,
bb: &mut ByteBuf,
) {
let action = CommandEncoderAction::CopyBufferToTexture {
src: wgc::command::ImageCopyBuffer {
buffer: src_buffer,
layout: src_layout.into_wgt(),
},
dst,
size,
};
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_copy_texture_to_texture(
src: wgc::command::ImageCopyTexture,
dst: wgc::command::ImageCopyTexture,
size: wgt::Extent3d,
bb: &mut ByteBuf,
) {
let action = CommandEncoderAction::CopyTextureToTexture { src, dst, size };
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_clear_buffer(
dst: wgc::id::BufferId,
offset: u64,
size: Option<&u64>,
bb: &mut ByteBuf,
) {
let action = CommandEncoderAction::ClearBuffer {
dst,
offset,
size: size.cloned(),
};
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub extern "C" fn wgpu_command_encoder_push_debug_group(marker: &nsACString, bb: &mut ByteBuf) {
let string = marker.to_string();
let action = CommandEncoderAction::PushDebugGroup(string);
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_pop_debug_group(bb: &mut ByteBuf) {
let action = CommandEncoderAction::PopDebugGroup;
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_command_encoder_insert_debug_marker(
marker: &nsACString,
bb: &mut ByteBuf,
) {
let string = marker.to_string();
let action = CommandEncoderAction::InsertDebugMarker(string);
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_queue_write_buffer(
dst: id::BufferId,
offset: wgt::BufferAddress,
bb: &mut ByteBuf,
) {
let action = QueueWriteAction::Buffer { dst, offset };
*bb = make_byte_buf(&action);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_queue_write_texture(
dst: wgt::ImageCopyTexture<id::TextureId>,
layout: ImageDataLayout,
size: wgt::Extent3d,
bb: &mut ByteBuf,
) {
let layout = layout.into_wgt();
let action = QueueWriteAction::Texture { dst, layout, size };
*bb = make_byte_buf(&action);
}
/// Returns the block size or zero if the format has multiple aspects (for example depth+stencil).
#[no_mangle]
pub extern "C" fn wgpu_texture_format_block_size_single_aspect(format: wgt::TextureFormat) -> u32 {
format.block_copy_size(None).unwrap_or(0)
}
#[no_mangle]
pub extern "C" fn wgpu_client_use_external_texture_in_swapChain(
device_id: id::DeviceId,
format: wgt::TextureFormat,
) -> bool {
if device_id.backend() != wgt::Backend::Dx12 {
return false;
}
let supported = match format {
wgt::TextureFormat::Bgra8Unorm => true,
_ => false,
};
supported
}