/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use wgc::{device::DeviceError, id}; use wgc::{pipeline::CreateShaderModuleError, resource::BufferAccessError}; #[allow(unused_imports)] use wgh::Instance;
use std::borrow::Cow; #[allow(unused_imports)] use std::mem; #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))] use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd}; use std::os::raw::{c_char, c_void}; use std::ptr; use std::slice; use std::sync::atomic::{AtomicU32, Ordering};
#[allow(unused_imports)] use std::ffi::CString; use std::ffi::{c_long, c_ulong};
#[cfg(target_os = "windows")] use windows::Win32::{Foundation, Graphics::Direct3D12};
#[cfg(not(any(target_os = "macos", target_os = "ios")))] use ash::{khr, vk};
#[cfg(target_os = "macos")] use objc::{class, msg_send, sel, sel_impl};
/// We limit the size of buffer allocations for stability reason. /// We can reconsider this limit in the future. Note that some drivers (mesa for example), /// have issues when the size of a buffer, mapping or copy command does not fit into a /// signed 32 bits integer, so beyond a certain size, large allocations will need some form /// of driver allow/blocklist. pubconst MAX_BUFFER_SIZE: wgt::BufferAddress = 1u64 << 30u64; const MAX_BUFFER_SIZE_U32: u32 = MAX_BUFFER_SIZE as u32;
// Mesa has issues with height/depth that don't fit in a 16 bits signed integers. const MAX_TEXTURE_EXTENT: u32 = std::i16::MAX as u32; // We have to restrict the number of bindings for any given resource type so that // the sum of these limits multiplied by the number of shader stages fits // maxBindingsPerBindGroup (1000). This restriction is arbitrary and is likely to // change eventually. See github.com/gpuweb/gpuweb/pull/4484 // For now it's impractical for users to have very large numbers of bindings so this // limit should not be too restrictive until we add support for a bindless API. // Then we may have to ignore the spec or get it changed. const MAX_BINDINGS_PER_RESOURCE_TYPE: u32 = 64;
// hide wgc's global in private pubstruct Global {
global: wgc::global::Global, #[allow(dead_code)]
owner: *mut c_void,
}
impl std::ops::Deref for Global { type Target = wgc::global::Global; fn deref(&self) -> &Self::Target {
&self.global
}
}
#[no_mangle] pubextern"C"fn wgpu_server_new(owner: *mut c_void, use_dxc: bool) -> *mut Global {
log::info!("Initializing WGPU server"); let backends_pref = static_prefs::pref!("dom.webgpu.wgpu-backend").to_string(); let backends = if backends_pref.is_empty() { #[cfg(windows)]
{
wgt::Backends::DX12
} #[cfg(not(windows))]
{
wgt::Backends::PRIMARY
}
} else {
log::info!( "Selecting backends based on dom.webgpu.wgpu-backend pref: {:?}",
backends_pref
);
wgc::instance::parse_backends_from_comma_list(&backends_pref)
};
letmut instance_flags = wgt::InstanceFlags::from_build_config().with_env(); if !static_prefs::pref!("dom.webgpu.hal-labels") {
instance_flags.insert(wgt::InstanceFlags::DISCARD_HAL_LABELS);
}
let dx12_shader_compiler = if use_dxc {
wgt::Dx12Compiler::DynamicDxc {
dxc_path: "dxcompiler.dll".into(),
dxil_path: "dxil.dll".into(),
}
} else {
wgt::Dx12Compiler::Fxc
};
let global = wgc::global::Global::new( "wgpu",
&wgt::InstanceDescriptor {
backends,
flags: instance_flags,
dx12_shader_compiler,
gles_minor_version: wgt::Gles3MinorVersion::Automatic,
},
); let global = Global { global, owner }; 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] pubunsafeextern"C"fn wgpu_server_delete(global: *mut Global) {
log::info!("Terminating WGPU server"); let _ = Box::from_raw(global);
}
/// Request an adapter according to the specified options. /// /// Returns true if we successfully found an adapter. #[allow(unused_variables)] #[no_mangle] pubunsafeextern"C"fn wgpu_server_instance_request_adapter(
global: &Global,
desc: &wgc::instance::RequestAdapterOptions,
adapter_id: id::AdapterId,
adapter_luid: Option<&FfiLUID>, mut error_buf: ErrorBuffer,
) -> bool { // Prefer to use the dx12 backend, if one exists, and use the same DXGI adapter as WebRender. // If wgpu uses a different adapter than WebRender, textures created by // webgpu::ExternalTexture do not work with wgpu. #[cfg(target_os = "windows")] if adapter_luid.is_some() && !desc.force_fallback_adapter { iflet Some(instance) = global.global.instance_as_hal::<wgc::api::Dx12>() { for adapter in instance.enumerate_adapters(None) { let raw_adapter = adapter.adapter.raw_adapter(); let desc = unsafe { raw_adapter.GetDesc() }; iflet Ok(desc) = desc { if desc.AdapterLuid.LowPart == adapter_luid.unwrap().low_part
&& desc.AdapterLuid.HighPart == adapter_luid.unwrap().high_part
{
global.create_adapter_from_hal(
wgh::DynExposedAdapter::from(adapter),
Some(adapter_id),
); returntrue;
}
}
}
error_buf.init(ErrMsg {
message: "Failed to create adapter for dx12",
r#type: ErrorBufferType::Internal,
}); returnfalse;
}
}
let raw_instance = hal_adapter.shared_instance().raw_instance(); let raw_physical_device = hal_adapter.raw_physical_device();
let queue_family_index = raw_instance
.get_physical_device_queue_family_properties(raw_physical_device)
.into_iter()
.enumerate()
.find_map(|(queue_family_index, info)| { if info.queue_flags.contains(vk::QueueFlags::GRAPHICS) {
Some(queue_family_index as u32)
} else {
None
}
});
let queue_family_index = match queue_family_index {
None => { let msg =
CString::new(format!("Vulkan device has no graphics queue"))
.unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Some(queue_family_index) => queue_family_index,
};
let family_info = vk::DeviceQueueCreateInfo::default()
.queue_family_index(queue_family_index)
.queue_priorities(&[1.0]); let family_infos = [family_info];
let str_pointers = enabled_extensions
.iter()
.map(|&s| { // Safe because `enabled_extensions` entries have static lifetime.
s.as_ptr()
})
.collect::<Vec<_>>();
let pre_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&family_infos)
.enabled_extension_names(&str_pointers); let info = enabled_phd_features.add_to_device_create(pre_info);
let raw_device = match raw_instance.create_device(raw_physical_device, &info, None) {
Err(err) => { let msg =
CString::new(format!("create_device() failed: {:?}", err))
.unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Ok(raw_device) => raw_device,
};
impl DeviceLostClosure { fn call(self, reason: wgt::DeviceLostReason, message: String) { // Ensure message is structured as a null-terminated C string. It only // needs to live as long as the callback invocation. let message = std::ffi::CString::new(message).unwrap(); unsafe {
(self.callback)(self.user_data, reason as u8, message.as_ptr());
}
core::mem::forget(self);
}
}
impl Drop for DeviceLostClosure { fn drop(&mutself) { unsafe {
(self.cleanup_callback)(self.user_data);
}
}
}
impl ShaderModuleCompilationMessage { fn set_error(&mutself, error: &CreateShaderModuleError, source: &str) { // The WebGPU spec says that if the message doesn't point to a particular position in // the source, the line number, position, offset and lengths should be zero. let line_number; let line_pos; let utf16_offset; let utf16_length;
let location = match error {
CreateShaderModuleError::Parsing(e) => e.inner.location(source),
CreateShaderModuleError::Validation(e) => e.inner.location(source),
_ => None,
};
iflet Some(location) = location { let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum(); let start = location.offset as usize; let end = start + location.length as usize;
utf16_offset = len_utf16(&source[0..start]);
utf16_length = len_utf16(&source[start..end]);
line_number = location.line_number as u64; // Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it. let line_start = source[0..start].rfind('\n').map(|pos| pos + 1).unwrap_or(0);
line_pos = len_utf16(&source[line_start..start]) + 1;
} else {
line_number = 0;
line_pos = 0;
utf16_offset = 0;
utf16_length = 0;
}
/// A compilation message representation for the ffi boundary. /// the message is immediately copied into an equivalent C++ /// structure that owns its strings. #[repr(C)] #[derive(Clone)] pubstruct ShaderModuleCompilationMessage { pub line_number: u64, pub line_pos: u64, pub utf16_offset: u64, pub utf16_length: u64, pub message: nsString,
}
/// Creates a shader module and returns an object describing the errors if any. /// /// If there was no error, the returned pointer is nil. #[no_mangle] pubextern"C"fn wgpu_server_device_create_shader_module(
global: &Global,
self_id: id::DeviceId,
module_id: id::ShaderModuleId,
label: Option<&nsACString>,
code: &nsCString,
out_message: &mut ShaderModuleCompilationMessage, mut error_buf: ErrorBuffer,
) -> bool { let utf8_label = label.map(|utf16| utf16.to_string()); let label = utf8_label.as_ref().map(|s| Cow::from(&s[..]));
let source_str = code.to_utf8();
let source = wgc::pipeline::ShaderModuleSource::Wgsl(Cow::from(&source_str[..]));
let desc = wgc::pipeline::ShaderModuleDescriptor {
label,
runtime_checks: Default::default(),
};
let (_, error) = global.device_create_shader_module(self_id, &desc, source, Some(module_id));
// Per spec: "User agents should not include detailed compiler error messages or // shader text in the message text of validation errors arising here: these details // are accessible via getCompilationInfo()" let message = match &err {
CreateShaderModuleError::Parsing(_) => "Parsing error".to_string(),
CreateShaderModuleError::Validation(_) => "Shader validation error".to_string(),
CreateShaderModuleError::Device(device_err) => format!("{device_err:?}"),
_ => format!("{err:?}"),
};
/// The status code provided to the buffer mapping closure. /// /// This is very similar to `BufferAccessResult`, except that this is FFI-friendly. #[repr(C)] pubenum BufferMapAsyncStatus { /// The Buffer is successfully mapped, `get_mapped_range` can be called. /// /// All other variants of this enum represent failures to map the buffer.
Success, /// The buffer is already mapped. /// /// While this is treated as an error, it does not prevent mapped range from being accessed.
AlreadyMapped, /// Mapping was already requested.
MapAlreadyPending, /// An unknown error.
Error, /// The context is Lost.
ContextLost, /// The buffer is in an invalid state.
Invalid, /// The range isn't fully contained in the buffer.
InvalidRange, /// The range isn't properly aligned.
InvalidAlignment, /// Incompatible usage flags.
InvalidUsageFlags,
}
/// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `size` elements. #[no_mangle] pubunsafeextern"C"fn wgpu_server_buffer_get_mapped_range(
global: &Global,
buffer_id: id::BufferId,
start: wgt::BufferAddress,
size: wgt::BufferAddress, mut error_buf: ErrorBuffer,
) -> MappedBufferSlice { let result = global.buffer_get_mapped_range(buffer_id, start, Some(size));
#[no_mangle] pubextern"C"fn wgpu_server_buffer_unmap(
global: &Global,
buffer_id: id::BufferId, mut error_buf: ErrorBuffer,
) { iflet Err(e) = global.buffer_unmap(buffer_id) { match e { // NOTE: This is presumed by CTS test cases, and was even formally specified in the // WebGPU spec. previously, but this doesn't seem formally specified now. :confused: // // TODO: upstream this; see <https://bugzilla.mozilla.org/show_bug.cgi?id=1842297>.
BufferAccessError::InvalidResource(_) => (),
other => error_buf.init(other),
}
}
}
#[no_mangle] pubextern"C"fn wgpu_server_buffer_destroy(global: &Global, self_id: id::BufferId) { // Per spec, there is no need for the buffer or even device to be in a valid state, // even calling calling destroy multiple times is fine, so no error to push into // an error scope. let _ = global.buffer_destroy(self_id);
}
let device = hal_device.raw_device(); let physical_device = hal_device.raw_physical_device(); let instance = hal_device.shared_instance().raw_instance();
modifier_props.retain(|modifier_prop| { let support = is_dmabuf_supported(
instance,
physical_device,
vk::Format::R8G8B8A8_UNORM,
modifier_prop.drm_format_modifier,
usage_flags,
);
support
});
if modifier_props.is_empty() { let msg =
CString::new(format!("format not supported for dmabuf import")).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
let modifiers: Vec<u64> = modifier_props
.iter()
.map(|modifier_prop| modifier_prop.drm_format_modifier)
.collect();
let index = match index {
None => { let msg = CString::new(format!("Failed to get DEVICE_LOCAL memory index"))
.unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Some(index) => index,
};
let memory_allocate_info = vk::MemoryAllocateInfo::default()
.allocation_size(memory_req.size)
.memory_type_index(index as u32)
.push_next(&mut dedicated_memory_info)
.push_next(&mut export_memory_alloc_info);
let memory = match device.allocate_memory(&memory_allocate_info, None) {
Err(err) => { let msg =
CString::new(format!("allocate_memory() failed: {:?}", err)).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Ok(memory) => memory,
};
let result = device.bind_image_memory(image, memory, /* offset */ 0); if result.is_err() { let msg =
CString::new(format!("bind_image_memory() failed: {:?}", result)).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
*out_memory_size = memory_req.size;
let modifier_prop = modifier_props.iter().find(|prop| {
prop.drm_format_modifier == image_modifier_properties.drm_format_modifier
}); let modifier_prop = match modifier_prop {
None => { let msg = CString::new(format!("failed to find modifier_prop")).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Some(modifier_prop) => modifier_prop,
};
let plane_count = modifier_prop.drm_format_modifier_plane_count;
letmut layouts = Vec::new(); for i in0..plane_count { let flag = match i { 0 => vk::ImageAspectFlags::PLANE_0, 1 => vk::ImageAspectFlags::PLANE_1, 2 => vk::ImageAspectFlags::PLANE_2,
_ => unreachable!(),
}; let subresource = vk::ImageSubresource::default().aspect_mask(flag); let layout = device.get_image_subresource_layout(image, subresource);
layouts.push(layout);
}
#[allow(dead_code)] #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))] fn create_texture_with_external_texture_dmabuf(
&self,
device_id: id::DeviceId,
texture_id: id::TextureId,
desc: &wgc::resource::TextureDescriptor,
swap_chain_id: Option<SwapChainId>,
) -> bool { let ret = unsafe {
wgpu_server_ensure_external_texture_for_swap_chain( self.owner,
swap_chain_id.unwrap(),
device_id,
texture_id,
desc.size.width,
desc.size.height,
desc.format,
desc.usage,
)
}; if ret != true { let msg = CString::new(format!("Failed to create external texture")).unwrap(); unsafe {
gfx_critical_note(msg.as_ptr());
} returnfalse;
}
let handle = unsafe { wgpu_server_get_vk_image_handle(self.owner, texture_id) }; if handle.is_null() { let msg = CString::new(format!("Failed to get VkImageHandle")).unwrap(); unsafe {
gfx_critical_note(msg.as_ptr());
} returnfalse;
}
let vk_image_wrapper = unsafe { &*handle };
let fd = unsafe { wgpu_server_get_dma_buf_fd(self.owner, texture_id) }; if fd < 0 { let msg = CString::new(format!("Failed to get DMABuf fd")).unwrap(); unsafe {
gfx_critical_note(msg.as_ptr());
} returnfalse;
}
// Ensure to close file descriptor let owned_fd = unsafe { OwnedFd::from_raw_fd(fd as RawFd) };
let image_holder = unsafe { self.device_as_hal::<wgc::api::Vulkan, _, Option<VkImageHolder>>(
device_id,
|hal_device| { let hal_device = match hal_device {
None => { let msg = CString::new(format!("Vulkan device is invalid")).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Some(hal_device) => hal_device,
};
#[cfg(target_os = "macos")] fn create_texture_with_external_texture_iosurface(
&self,
device_id: id::DeviceId,
texture_id: id::TextureId,
desc: &wgc::resource::TextureDescriptor,
swap_chain_id: Option<SwapChainId>,
) -> bool { let ret = unsafe {
wgpu_server_ensure_external_texture_for_swap_chain( self.owner,
swap_chain_id.unwrap(),
device_id,
texture_id,
desc.size.width,
desc.size.height,
desc.format,
desc.usage,
)
}; if ret != true { let msg = CString::new(format!("Failed to create external texture")).unwrap(); unsafe {
gfx_critical_note(msg.as_ptr());
} returnfalse;
}
let io_surface_id = unsafe { wgpu_server_get_external_io_surface_id(self.owner, texture_id) }; if io_surface_id == 0 { let msg = CString::new(format!("Failed to get io surface id")).unwrap(); unsafe {
gfx_critical_note(msg.as_ptr());
} returnfalse;
}
let io_surface = io_surface::lookup(io_surface_id);
let desc_ref = &desc;
let raw = unsafe { self.device_as_hal::<wgc::api::Metal, _, Option<metal::Texture>>(
device_id,
|hal_device| { let hal_device = match hal_device {
None => { let msg = CString::new(format!("metal device is invalid")).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
Some(hal_device) => hal_device,
};
use metal::foreign_types::ForeignType as _; let device = hal_device.raw_device();
objc::rc::autoreleasepool(|| { let descriptor = metal::TextureDescriptor::new(); let usage = metal::MTLTextureUsage::RenderTarget
| metal::MTLTextureUsage::ShaderRead
| metal::MTLTextureUsage::PixelFormatView;
descriptor.set_texture_type(metal::MTLTextureType::D2);
descriptor.set_width(desc_ref.size.width as u64);
descriptor.set_height(desc_ref.size.height as u64);
descriptor.set_mipmap_level_count(desc_ref.mip_level_count as u64);
descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
descriptor.set_usage(usage);
descriptor.set_storage_mode(metal::MTLStorageMode::Private);
let raw_device = device.lock(); let raw_texture: metal::Texture = msg_send![*raw_device, newTextureWithDescriptor: descriptor
iosurface:io_surface.obj
plane:0];
if raw_texture.as_ptr().is_null() { let msg = CString::new(format!("Failed to create metal::Texture for swap chain")).unwrap();
gfx_critical_note(msg.as_ptr()); return None;
}
/// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `command_buffer_id_length` elements. #[no_mangle] pubunsafeextern"C"fn wgpu_server_queue_submit(
global: &Global,
self_id: id::QueueId,
command_buffer_ids: *const id::CommandBufferId,
command_buffer_id_length: usize, mut error_buf: ErrorBuffer,
) -> u64 { let command_buffers = slice::from_raw_parts(command_buffer_ids, command_buffer_id_length); let result = global.queue_submit(self_id, command_buffers);
match result {
Err((_index, err)) => {
error_buf.init(err); return0;
}
Ok(wrapped_index) => wrapped_index,
}
}
/// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `data_length` elements. #[no_mangle] pubunsafeextern"C"fn wgpu_server_queue_write_action(
global: &Global,
self_id: id::QueueId,
byte_buf: &ByteBuf,
data: *const u8,
data_length: usize, mut error_buf: ErrorBuffer,
) { let action: QueueWriteAction = bincode::deserialize(byte_buf.as_slice()).unwrap(); let data = slice::from_raw_parts(data, data_length); let result = match action {
QueueWriteAction::Buffer { dst, offset } => {
global.queue_write_buffer(self_id, dst, offset, data)
}
QueueWriteAction::Texture { dst, layout, size } => {
global.queue_write_texture(self_id, &dst, data, &layout, &size)
}
}; iflet Err(err) = result {
error_buf.init(err);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.