use crate::{ context::{ObjectId, Unused}, AdapterInfo, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BufferBinding, BufferDescriptor, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, DownlevelCapabilities, Features, Label, Limits, LoadOp, MapMode, Operations, PipelineLayoutDescriptor, RenderBundleEncoderDescriptor, RenderPipelineDescriptor, SamplerDescriptor, ShaderModuleDescriptor, ShaderModuleDescriptorSpirV, ShaderSource, SurfaceStatus, TextureDescriptor, TextureViewDescriptor, UncapturedErrorHandler, }; use arrayvec::ArrayVec; use parking_lot::Mutex; use smallvec::SmallVec; use std::{ any::Any, borrow::Cow::{Borrowed, Owned}, error::Error, fmt, future::{ready, Ready}, ops::Range, slice, sync::Arc, }; use wgc::command::{bundle_ffi::*, compute_ffi::*, render_ffi::*}; use wgc::id::TypedId; use wgt::{WasmNotSend, WasmNotSync}; const LABEL: &str = "label"; pub struct Context(wgc::global::Global); impl Drop for Context { fn drop(&mut self) { //nothing } } impl fmt::Debug for Context { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context").field("type", &"Native").finish() } } impl Context { pub unsafe fn from_hal_instance(hal_instance: A::Instance) -> Self { Self(unsafe { wgc::global::Global::from_hal_instance::( "wgpu", wgc::identity::IdentityManagerFactory, hal_instance, ) }) } /// # Safety /// /// - The raw instance handle returned must not be manually destroyed. pub unsafe fn instance_as_hal(&self) -> Option<&A::Instance> { unsafe { self.0.instance_as_hal::() } } pub unsafe fn from_core_instance(core_instance: wgc::instance::Instance) -> Self { Self(unsafe { wgc::global::Global::from_instance(wgc::identity::IdentityManagerFactory, core_instance) }) } pub(crate) fn global(&self) -> &wgc::global::Global { &self.0 } pub fn enumerate_adapters(&self, backends: wgt::Backends) -> Vec { self.0 .enumerate_adapters(wgc::instance::AdapterInputs::Mask(backends, |_| ())) } pub unsafe fn create_adapter_from_hal( &self, hal_adapter: hal::ExposedAdapter, ) -> wgc::id::AdapterId { unsafe { self.0.create_adapter_from_hal(hal_adapter, ()) } } pub unsafe fn adapter_as_hal< A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Adapter>) -> R, R, >( &self, adapter: wgc::id::AdapterId, hal_adapter_callback: F, ) -> R { unsafe { self.0 .adapter_as_hal::(adapter, hal_adapter_callback) } } pub unsafe fn create_device_from_hal( &self, adapter: &wgc::id::AdapterId, hal_device: hal::OpenDevice, desc: &crate::DeviceDescriptor, trace_dir: Option<&std::path::Path>, ) -> Result<(Device, Queue), crate::RequestDeviceError> { let global = &self.0; let (device_id, error) = unsafe { global.create_device_from_hal( *adapter, hal_device, &desc.map_label(|l| l.map(Borrowed)), trace_dir, (), ) }; if let Some(err) = error { self.handle_error_fatal(err, "Adapter::create_device_from_hal"); } let error_sink = Arc::new(Mutex::new(ErrorSinkRaw::new())); let device = Device { id: device_id, error_sink: error_sink.clone(), features: desc.features, }; let queue = Queue { id: device_id, error_sink, }; Ok((device, queue)) } pub unsafe fn create_texture_from_hal( &self, hal_texture: A::Texture, device: &Device, desc: &TextureDescriptor, ) -> Texture { let descriptor = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec()); let global = &self.0; let (id, error) = unsafe { global.create_texture_from_hal::(hal_texture, device.id, &descriptor, ()) }; if let Some(cause) = error { self.handle_error( &device.error_sink, cause, LABEL, desc.label, "Device::create_texture_from_hal", ); } Texture { id, error_sink: Arc::clone(&device.error_sink), } } pub unsafe fn create_buffer_from_hal( &self, hal_buffer: A::Buffer, device: &Device, desc: &BufferDescriptor, ) -> (wgc::id::BufferId, Buffer) { let global = &self.0; let (id, error) = unsafe { global.create_buffer_from_hal::( hal_buffer, device.id, &desc.map_label(|l| l.map(Borrowed)), (), ) }; if let Some(cause) = error { self.handle_error( &device.error_sink, cause, LABEL, desc.label, "Device::create_buffer_from_hal", ); } ( id, Buffer { error_sink: Arc::clone(&device.error_sink), }, ) } pub unsafe fn device_as_hal) -> R, R>( &self, device: &Device, hal_device_callback: F, ) -> R { unsafe { self.0 .device_as_hal::(device.id, hal_device_callback) } } pub unsafe fn surface_as_hal_mut< A: wgc::hal_api::HalApi, F: FnOnce(Option<&mut A::Surface>) -> R, R, >( &self, surface: &Surface, hal_surface_callback: F, ) -> R { unsafe { self.0 .surface_as_hal_mut::(surface.id, hal_surface_callback) } } pub unsafe fn texture_as_hal)>( &self, texture: &Texture, hal_texture_callback: F, ) { unsafe { self.0 .texture_as_hal::(texture.id, hal_texture_callback) } } pub fn generate_report(&self) -> wgc::global::GlobalReport { self.0.generate_report() } #[cfg(any(target_os = "ios", target_os = "macos"))] pub unsafe fn create_surface_from_core_animation_layer( &self, layer: *mut std::ffi::c_void, ) -> Surface { let id = unsafe { self.0.instance_create_surface_metal(layer, ()) }; Surface { id, configured_device: Mutex::default(), } } #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] pub fn instance_create_surface_from_canvas( &self, canvas: web_sys::HtmlCanvasElement, ) -> Result { let id = self .0 .create_surface_webgl_canvas(canvas, ()) .map_err(|hal::InstanceError| crate::CreateSurfaceError {})?; Ok(Surface { id, configured_device: Mutex::default(), }) } #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] pub fn instance_create_surface_from_offscreen_canvas( &self, canvas: web_sys::OffscreenCanvas, ) -> Result { let id = self .0 .create_surface_webgl_offscreen_canvas(canvas, ()) .map_err(|hal::InstanceError| crate::CreateSurfaceError {})?; Ok(Surface { id, configured_device: Mutex::default(), }) } #[cfg(target_os = "windows")] pub unsafe fn create_surface_from_visual(&self, visual: *mut std::ffi::c_void) -> Surface { let id = unsafe { self.0.instance_create_surface_from_visual(visual, ()) }; Surface { id, configured_device: Mutex::default(), } } #[cfg(target_os = "windows")] pub unsafe fn create_surface_from_surface_handle( &self, surface_handle: *mut std::ffi::c_void, ) -> Surface { let id = unsafe { self.0 .instance_create_surface_from_surface_handle(surface_handle, ()) }; Surface { id, configured_device: Mutex::default(), } } fn handle_error( &self, sink_mutex: &Mutex, cause: impl Error + WasmNotSend + WasmNotSync + 'static, label_key: &'static str, label: Label, string: &'static str, ) { let error = wgc::error::ContextError { string, cause: Box::new(cause), label: label.unwrap_or_default().to_string(), label_key, }; let mut sink = sink_mutex.lock(); let mut source_opt: Option<&(dyn Error + 'static)> = Some(&error); while let Some(source) = source_opt { if let Some(wgc::device::DeviceError::OutOfMemory) = source.downcast_ref::() { return sink.handle_error(crate::Error::OutOfMemory { source: Box::new(error), }); } source_opt = source.source(); } // Otherwise, it is a validation error sink.handle_error(crate::Error::Validation { description: self.format_error(&error), source: Box::new(error), }); } fn handle_error_nolabel( &self, sink_mutex: &Mutex, cause: impl Error + WasmNotSend + WasmNotSync + 'static, string: &'static str, ) { self.handle_error(sink_mutex, cause, "", None, string) } #[track_caller] fn handle_error_fatal( &self, cause: impl Error + WasmNotSend + WasmNotSync + 'static, operation: &'static str, ) -> ! { panic!("Error in {operation}: {f}", f = self.format_error(&cause)); } fn format_error(&self, err: &(impl Error + 'static)) -> String { let global = self.global(); let mut err_descs = vec![]; let mut err_str = String::new(); wgc::error::format_pretty_any(&mut err_str, global, err); err_descs.push(err_str); let mut source_opt = err.source(); while let Some(source) = source_opt { let mut source_str = String::new(); wgc::error::format_pretty_any(&mut source_str, global, source); err_descs.push(source_str); source_opt = source.source(); } format!("Validation Error\n\nCaused by:\n{}", err_descs.join("")) } } fn map_buffer_copy_view(view: crate::ImageCopyBuffer) -> wgc::command::ImageCopyBuffer { wgc::command::ImageCopyBuffer { buffer: view.buffer.id.into(), layout: view.layout, } } fn map_texture_copy_view(view: crate::ImageCopyTexture) -> wgc::command::ImageCopyTexture { wgc::command::ImageCopyTexture { texture: view.texture.id.into(), mip_level: view.mip_level, origin: view.origin, aspect: view.aspect, } } #[cfg_attr( any(not(target_arch = "wasm32"), target_os = "emscripten"), allow(unused) )] fn map_texture_tagged_copy_view( view: crate::ImageCopyTextureTagged, ) -> wgc::command::ImageCopyTextureTagged { wgc::command::ImageCopyTextureTagged { texture: view.texture.id.into(), mip_level: view.mip_level, origin: view.origin, aspect: view.aspect, color_space: view.color_space, premultiplied_alpha: view.premultiplied_alpha, } } fn map_pass_channel( ops: Option<&Operations>, ) -> wgc::command::PassChannel { match ops { Some(&Operations { load: LoadOp::Clear(clear_value), store, }) => wgc::command::PassChannel { load_op: wgc::command::LoadOp::Clear, store_op: if store { wgc::command::StoreOp::Store } else { wgc::command::StoreOp::Discard }, clear_value, read_only: false, }, Some(&Operations { load: LoadOp::Load, store, }) => wgc::command::PassChannel { load_op: wgc::command::LoadOp::Load, store_op: if store { wgc::command::StoreOp::Store } else { wgc::command::StoreOp::Discard }, clear_value: V::default(), read_only: false, }, None => wgc::command::PassChannel { load_op: wgc::command::LoadOp::Load, store_op: wgc::command::StoreOp::Store, clear_value: V::default(), read_only: true, }, } } #[derive(Debug)] pub struct Surface { id: wgc::id::SurfaceId, /// Configured device is needed to know which backend /// code to execute when acquiring a new frame. configured_device: Mutex>, } impl Surface { // Not used on every platform #[allow(dead_code)] pub fn id(&self) -> wgc::id::SurfaceId { self.id } } #[derive(Debug)] pub struct Device { id: wgc::id::DeviceId, error_sink: ErrorSink, features: Features, } impl Device { // Not used on every platform #[allow(dead_code)] pub fn id(&self) -> wgc::id::DeviceId { self.id } } #[derive(Debug)] pub struct Buffer { error_sink: ErrorSink, } #[derive(Debug)] pub struct Texture { id: wgc::id::TextureId, error_sink: ErrorSink, } impl Texture { // Not used on every platform #[allow(dead_code)] pub fn id(&self) -> wgc::id::TextureId { self.id } } #[derive(Debug)] pub struct Queue { id: wgc::id::QueueId, error_sink: ErrorSink, } impl Queue { // Not used on every platform #[allow(dead_code)] pub fn id(&self) -> wgc::id::QueueId { self.id } } #[derive(Debug)] pub struct CommandEncoder { error_sink: ErrorSink, open: bool, } impl crate::Context for Context { type AdapterId = wgc::id::AdapterId; type AdapterData = (); type DeviceId = wgc::id::DeviceId; type DeviceData = Device; type QueueId = wgc::id::QueueId; type QueueData = Queue; type ShaderModuleId = wgc::id::ShaderModuleId; type ShaderModuleData = (); type BindGroupLayoutId = wgc::id::BindGroupLayoutId; type BindGroupLayoutData = (); type BindGroupId = wgc::id::BindGroupId; type BindGroupData = (); type TextureViewId = wgc::id::TextureViewId; type TextureViewData = (); type SamplerId = wgc::id::SamplerId; type SamplerData = (); type BufferId = wgc::id::BufferId; type BufferData = Buffer; type TextureId = wgc::id::TextureId; type TextureData = Texture; type QuerySetId = wgc::id::QuerySetId; type QuerySetData = (); type PipelineLayoutId = wgc::id::PipelineLayoutId; type PipelineLayoutData = (); type RenderPipelineId = wgc::id::RenderPipelineId; type RenderPipelineData = (); type ComputePipelineId = wgc::id::ComputePipelineId; type ComputePipelineData = (); type CommandEncoderId = wgc::id::CommandEncoderId; type CommandEncoderData = CommandEncoder; type ComputePassId = Unused; type ComputePassData = wgc::command::ComputePass; type RenderPassId = Unused; type RenderPassData = wgc::command::RenderPass; type CommandBufferId = wgc::id::CommandBufferId; type CommandBufferData = (); type RenderBundleEncoderId = Unused; type RenderBundleEncoderData = wgc::command::RenderBundleEncoder; type RenderBundleId = wgc::id::RenderBundleId; type RenderBundleData = (); type SurfaceId = wgc::id::SurfaceId; type SurfaceData = Surface; type SurfaceOutputDetail = SurfaceOutputDetail; type SubmissionIndex = Unused; type SubmissionIndexData = wgc::device::queue::WrappedSubmissionIndex; type RequestAdapterFuture = Ready>; #[allow(clippy::type_complexity)] type RequestDeviceFuture = Ready< Result< ( Self::DeviceId, Self::DeviceData, Self::QueueId, Self::QueueData, ), crate::RequestDeviceError, >, >; type PopErrorScopeFuture = Ready>; fn init(instance_desc: wgt::InstanceDescriptor) -> Self { Self(wgc::global::Global::new( "wgpu", wgc::identity::IdentityManagerFactory, instance_desc, )) } fn instance_create_surface( &self, display_handle: raw_window_handle::RawDisplayHandle, window_handle: raw_window_handle::RawWindowHandle, ) -> Result<(Self::SurfaceId, Self::SurfaceData), crate::CreateSurfaceError> { let id = self .0 .instance_create_surface(display_handle, window_handle, ()); Ok(( id, Surface { id, configured_device: Mutex::new(None), }, )) } fn instance_request_adapter( &self, options: &crate::RequestAdapterOptions, ) -> Self::RequestAdapterFuture { let id = self.0.request_adapter( &wgc::instance::RequestAdapterOptions { power_preference: options.power_preference, force_fallback_adapter: options.force_fallback_adapter, compatible_surface: options.compatible_surface.map(|surface| surface.id.into()), }, wgc::instance::AdapterInputs::Mask(wgt::Backends::all(), |_| ()), ); ready(id.ok().map(|id| (id, ()))) } fn adapter_request_device( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, desc: &crate::DeviceDescriptor, trace_dir: Option<&std::path::Path>, ) -> Self::RequestDeviceFuture { let global = &self.0; let (device_id, error) = wgc::gfx_select!(*adapter => global.adapter_request_device( *adapter, &desc.map_label(|l| l.map(Borrowed)), trace_dir, () )); if let Some(err) = error { log::error!("Error in Adapter::request_device: {}", err); return ready(Err(crate::RequestDeviceError)); } let error_sink = Arc::new(Mutex::new(ErrorSinkRaw::new())); let device = Device { id: device_id, error_sink: error_sink.clone(), features: desc.features, }; let queue = Queue { id: device_id, error_sink, }; ready(Ok((device_id, device, device_id, queue))) } fn instance_poll_all_devices(&self, force_wait: bool) -> bool { let global = &self.0; match global.poll_all_devices(force_wait) { Ok(all_queue_empty) => all_queue_empty, Err(err) => self.handle_error_fatal(err, "Device::poll"), } } fn adapter_is_surface_supported( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, surface: &Self::SurfaceId, _surface_data: &Self::SurfaceData, ) -> bool { let global = &self.0; match wgc::gfx_select!(adapter => global.adapter_is_surface_supported(*adapter, *surface)) { Ok(result) => result, Err(err) => self.handle_error_fatal(err, "Adapter::is_surface_supported"), } } fn adapter_features( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, ) -> Features { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_features(*adapter)) { Ok(features) => features, Err(err) => self.handle_error_fatal(err, "Adapter::features"), } } fn adapter_limits( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, ) -> Limits { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_limits(*adapter)) { Ok(limits) => limits, Err(err) => self.handle_error_fatal(err, "Adapter::limits"), } } fn adapter_downlevel_capabilities( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, ) -> DownlevelCapabilities { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_downlevel_capabilities(*adapter)) { Ok(downlevel) => downlevel, Err(err) => self.handle_error_fatal(err, "Adapter::downlevel_properties"), } } fn adapter_get_info( &self, adapter: &wgc::id::AdapterId, _adapter_data: &Self::AdapterData, ) -> AdapterInfo { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_get_info(*adapter)) { Ok(info) => info, Err(err) => self.handle_error_fatal(err, "Adapter::get_info"), } } fn adapter_get_texture_format_features( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, format: wgt::TextureFormat, ) -> wgt::TextureFormatFeatures { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_get_texture_format_features(*adapter, format)) { Ok(info) => info, Err(err) => self.handle_error_fatal(err, "Adapter::get_texture_format_features"), } } fn adapter_get_presentation_timestamp( &self, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, ) -> wgt::PresentationTimestamp { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_get_presentation_timestamp(*adapter)) { Ok(timestamp) => timestamp, Err(err) => self.handle_error_fatal(err, "Adapter::correlate_presentation_timestamp"), } } fn surface_get_capabilities( &self, surface: &Self::SurfaceId, _surface_data: &Self::SurfaceData, adapter: &Self::AdapterId, _adapter_data: &Self::AdapterData, ) -> wgt::SurfaceCapabilities { let global = &self.0; match wgc::gfx_select!(adapter => global.surface_get_capabilities(*surface, *adapter)) { Ok(caps) => caps, Err(wgc::instance::GetSurfaceSupportError::Unsupported) => { wgt::SurfaceCapabilities::default() } Err(err) => self.handle_error_fatal(err, "Surface::get_supported_formats"), } } fn surface_configure( &self, surface: &Self::SurfaceId, surface_data: &Self::SurfaceData, device: &Self::DeviceId, _device_data: &Self::DeviceData, config: &crate::SurfaceConfiguration, ) { let global = &self.0; let error = wgc::gfx_select!(device => global.surface_configure(*surface, *device, config)); if let Some(e) = error { self.handle_error_fatal(e, "Surface::configure"); } else { *surface_data.configured_device.lock() = Some(*device); } } fn surface_get_current_texture( &self, surface: &Self::SurfaceId, surface_data: &Self::SurfaceData, ) -> ( Option, Option, SurfaceStatus, Self::SurfaceOutputDetail, ) { let global = &self.0; let device_id = surface_data .configured_device .lock() .expect("Surface was not configured?"); match wgc::gfx_select!( device_id => global.surface_get_current_texture(*surface, ()) ) { Ok(wgc::present::SurfaceOutput { status, texture_id }) => { let (id, data) = { ( texture_id, texture_id.map(|id| Texture { id, error_sink: Arc::new(Mutex::new(ErrorSinkRaw::new())), }), ) }; ( id, data, status, SurfaceOutputDetail { surface_id: *surface, }, ) } Err(err) => self.handle_error_fatal(err, "Surface::get_current_texture_view"), } } fn surface_present(&self, texture: &Self::TextureId, detail: &Self::SurfaceOutputDetail) { let global = &self.0; match wgc::gfx_select!(texture => global.surface_present(detail.surface_id)) { Ok(_status) => (), Err(err) => self.handle_error_fatal(err, "Surface::present"), } } fn surface_texture_discard( &self, texture: &Self::TextureId, detail: &Self::SurfaceOutputDetail, ) { let global = &self.0; match wgc::gfx_select!(texture => global.surface_texture_discard(detail.surface_id)) { Ok(_status) => (), Err(err) => self.handle_error_fatal(err, "Surface::discard_texture"), } } fn device_features( &self, device: &Self::DeviceId, _device_data: &Self::DeviceData, ) -> Features { let global = &self.0; match wgc::gfx_select!(device => global.device_features(*device)) { Ok(features) => features, Err(err) => self.handle_error_fatal(err, "Device::features"), } } fn device_limits(&self, device: &Self::DeviceId, _device_data: &Self::DeviceData) -> Limits { let global = &self.0; match wgc::gfx_select!(device => global.device_limits(*device)) { Ok(limits) => limits, Err(err) => self.handle_error_fatal(err, "Device::limits"), } } fn device_downlevel_properties( &self, device: &Self::DeviceId, _device_data: &Self::DeviceData, ) -> DownlevelCapabilities { let global = &self.0; match wgc::gfx_select!(device => global.device_downlevel_properties(*device)) { Ok(limits) => limits, Err(err) => self.handle_error_fatal(err, "Device::downlevel_properties"), } } #[cfg_attr( not(any( feature = "spirv", feature = "glsl", feature = "wgsl", feature = "naga" )), allow(unreachable_code, unused_variables) )] fn device_create_shader_module( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: ShaderModuleDescriptor, shader_bound_checks: wgt::ShaderBoundChecks, ) -> (Self::ShaderModuleId, Self::ShaderModuleData) { let global = &self.0; let descriptor = wgc::pipeline::ShaderModuleDescriptor { label: desc.label.map(Borrowed), shader_bound_checks, }; let source = match desc.source { #[cfg(feature = "spirv")] ShaderSource::SpirV(ref spv) => { // Parse the given shader code and store its representation. let options = naga::front::spv::Options { adjust_coordinate_space: false, // we require NDC_Y_UP feature strict_capabilities: true, block_ctx_dump_prefix: None, }; let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options); let module = parser.parse().unwrap(); wgc::pipeline::ShaderModuleSource::Naga(Owned(module)) } #[cfg(feature = "glsl")] ShaderSource::Glsl { ref shader, stage, ref defines, } => { // Parse the given shader code and store its representation. let options = naga::front::glsl::Options { stage, defines: defines.clone(), }; let mut parser = naga::front::glsl::Frontend::default(); let module = parser.parse(&options, shader).unwrap(); wgc::pipeline::ShaderModuleSource::Naga(Owned(module)) } #[cfg(feature = "wgsl")] ShaderSource::Wgsl(ref code) => wgc::pipeline::ShaderModuleSource::Wgsl(Borrowed(code)), #[cfg(feature = "naga")] ShaderSource::Naga(module) => wgc::pipeline::ShaderModuleSource::Naga(module), ShaderSource::Dummy(_) => panic!("found `ShaderSource::Dummy`"), }; let (id, error) = wgc::gfx_select!( device => global.device_create_shader_module(*device, &descriptor, source, ()) ); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_shader_module", ); } (id, ()) } unsafe fn device_create_shader_module_spirv( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &ShaderModuleDescriptorSpirV, ) -> (Self::ShaderModuleId, Self::ShaderModuleData) { let global = &self.0; let descriptor = wgc::pipeline::ShaderModuleDescriptor { label: desc.label.map(Borrowed), // Doesn't matter the value since spirv shaders aren't mutated to include // runtime checks shader_bound_checks: unsafe { wgt::ShaderBoundChecks::unchecked() }, }; let (id, error) = wgc::gfx_select!( device => global.device_create_shader_module_spirv(*device, &descriptor, Borrowed(&desc.source), ()) ); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_shader_module_spirv", ); } (id, ()) } fn device_create_bind_group_layout( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &BindGroupLayoutDescriptor, ) -> (Self::BindGroupLayoutId, Self::BindGroupLayoutData) { let global = &self.0; let descriptor = wgc::binding_model::BindGroupLayoutDescriptor { label: desc.label.map(Borrowed), entries: Borrowed(desc.entries), }; let (id, error) = wgc::gfx_select!( device => global.device_create_bind_group_layout(*device, &descriptor, ()) ); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_bind_group_layout", ); } (id, ()) } fn device_create_bind_group( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &BindGroupDescriptor, ) -> (Self::BindGroupId, Self::BindGroupData) { use wgc::binding_model as bm; let mut arrayed_texture_views = Vec::::new(); let mut arrayed_samplers = Vec::::new(); if device_data .features .contains(Features::TEXTURE_BINDING_ARRAY) { // gather all the array view IDs first for entry in desc.entries.iter() { if let BindingResource::TextureViewArray(array) = entry.resource { arrayed_texture_views.extend(array.iter().map(|view| &view.id)); } if let BindingResource::SamplerArray(array) = entry.resource { arrayed_samplers.extend(array.iter().map(|sampler| &sampler.id)); } } } let mut remaining_arrayed_texture_views = &arrayed_texture_views[..]; let mut remaining_arrayed_samplers = &arrayed_samplers[..]; let mut arrayed_buffer_bindings = Vec::new(); if device_data .features .contains(Features::BUFFER_BINDING_ARRAY) { // gather all the buffers first for entry in desc.entries.iter() { if let BindingResource::BufferArray(array) = entry.resource { arrayed_buffer_bindings.extend(array.iter().map(|binding| bm::BufferBinding { buffer_id: binding.buffer.id.into(), offset: binding.offset, size: binding.size, })); } } } let mut remaining_arrayed_buffer_bindings = &arrayed_buffer_bindings[..]; let entries = desc .entries .iter() .map(|entry| bm::BindGroupEntry { binding: entry.binding, resource: match entry.resource { BindingResource::Buffer(BufferBinding { buffer, offset, size, }) => bm::BindingResource::Buffer(bm::BufferBinding { buffer_id: buffer.id.into(), offset, size, }), BindingResource::BufferArray(array) => { let slice = &remaining_arrayed_buffer_bindings[..array.len()]; remaining_arrayed_buffer_bindings = &remaining_arrayed_buffer_bindings[array.len()..]; bm::BindingResource::BufferArray(Borrowed(slice)) } BindingResource::Sampler(sampler) => { bm::BindingResource::Sampler(sampler.id.into()) } BindingResource::SamplerArray(array) => { let samplers = remaining_arrayed_samplers[..array.len()] .iter() .map(|id| ::from(*id)) .collect::>(); remaining_arrayed_samplers = &remaining_arrayed_samplers[array.len()..]; bm::BindingResource::SamplerArray(Owned(samplers)) } BindingResource::TextureView(texture_view) => { bm::BindingResource::TextureView(texture_view.id.into()) } BindingResource::TextureViewArray(array) => { let views = remaining_arrayed_texture_views[..array.len()] .iter() .map(|id| ::from(*id)) .collect::>(); remaining_arrayed_texture_views = &remaining_arrayed_texture_views[array.len()..]; bm::BindingResource::TextureViewArray(Owned(views)) } }, }) .collect::>(); let descriptor = bm::BindGroupDescriptor { label: desc.label.as_ref().map(|label| Borrowed(&label[..])), layout: desc.layout.id.into(), entries: Borrowed(&entries), }; let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_bind_group( *device, &descriptor, () )); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_bind_group", ); } (id, ()) } fn device_create_pipeline_layout( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &PipelineLayoutDescriptor, ) -> (Self::PipelineLayoutId, Self::PipelineLayoutData) { // Limit is always less or equal to hal::MAX_BIND_GROUPS, so this is always right // Guards following ArrayVec assert!( desc.bind_group_layouts.len() <= wgc::MAX_BIND_GROUPS, "Bind group layout count {} exceeds device bind group limit {}", desc.bind_group_layouts.len(), wgc::MAX_BIND_GROUPS ); let temp_layouts = desc .bind_group_layouts .iter() .map(|bgl| bgl.id.into()) .collect::>(); let descriptor = wgc::binding_model::PipelineLayoutDescriptor { label: desc.label.map(Borrowed), bind_group_layouts: Borrowed(&temp_layouts), push_constant_ranges: Borrowed(desc.push_constant_ranges), }; let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_pipeline_layout( *device, &descriptor, () )); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_pipeline_layout", ); } (id, ()) } fn device_create_render_pipeline( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &RenderPipelineDescriptor, ) -> (Self::RenderPipelineId, Self::RenderPipelineData) { use wgc::pipeline as pipe; let vertex_buffers: ArrayVec<_, { wgc::MAX_VERTEX_BUFFERS }> = desc .vertex .buffers .iter() .map(|vbuf| pipe::VertexBufferLayout { array_stride: vbuf.array_stride, step_mode: vbuf.step_mode, attributes: Borrowed(vbuf.attributes), }) .collect(); let implicit_pipeline_ids = match desc.layout { Some(_) => None, None => Some(wgc::device::ImplicitPipelineIds { root_id: (), group_ids: &[(); wgc::MAX_BIND_GROUPS], }), }; let descriptor = pipe::RenderPipelineDescriptor { label: desc.label.map(Borrowed), layout: desc.layout.map(|l| l.id.into()), vertex: pipe::VertexState { stage: pipe::ProgrammableStageDescriptor { module: desc.vertex.module.id.into(), entry_point: Borrowed(desc.vertex.entry_point), }, buffers: Borrowed(&vertex_buffers), }, primitive: desc.primitive, depth_stencil: desc.depth_stencil.clone(), multisample: desc.multisample, fragment: desc.fragment.as_ref().map(|frag| pipe::FragmentState { stage: pipe::ProgrammableStageDescriptor { module: frag.module.id.into(), entry_point: Borrowed(frag.entry_point), }, targets: Borrowed(frag.targets), }), multiview: desc.multiview, }; let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_render_pipeline( *device, &descriptor, (), implicit_pipeline_ids )); if let Some(cause) = error { if let wgc::pipeline::CreateRenderPipelineError::Internal { stage, ref error } = cause { log::error!("Shader translation error for stage {:?}: {}", stage, error); log::error!("Please report it to https://github.com/gfx-rs/naga"); } self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_render_pipeline", ); } (id, ()) } fn device_create_compute_pipeline( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &ComputePipelineDescriptor, ) -> (Self::ComputePipelineId, Self::ComputePipelineData) { use wgc::pipeline as pipe; let implicit_pipeline_ids = match desc.layout { Some(_) => None, None => Some(wgc::device::ImplicitPipelineIds { root_id: (), group_ids: &[(); wgc::MAX_BIND_GROUPS], }), }; let descriptor = pipe::ComputePipelineDescriptor { label: desc.label.map(Borrowed), layout: desc.layout.map(|l| l.id.into()), stage: pipe::ProgrammableStageDescriptor { module: desc.module.id.into(), entry_point: Borrowed(desc.entry_point), }, }; let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_compute_pipeline( *device, &descriptor, (), implicit_pipeline_ids )); if let Some(cause) = error { if let wgc::pipeline::CreateComputePipelineError::Internal(ref error) = cause { log::warn!( "Shader translation error for stage {:?}: {}", wgt::ShaderStages::COMPUTE, error ); log::warn!("Please report it to https://github.com/gfx-rs/naga"); } self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_compute_pipeline", ); } (id, ()) } fn device_create_buffer( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &crate::BufferDescriptor<'_>, ) -> (Self::BufferId, Self::BufferData) { let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_buffer( *device, &desc.map_label(|l| l.map(Borrowed)), () )); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_buffer", ); } ( id, Buffer { error_sink: Arc::clone(&device_data.error_sink), }, ) } fn device_create_texture( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &TextureDescriptor, ) -> (Self::TextureId, Self::TextureData) { let wgt_desc = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec()); let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_texture( *device, &wgt_desc, () )); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_texture", ); } ( id, Texture { id, error_sink: Arc::clone(&device_data.error_sink), }, ) } fn device_create_sampler( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &SamplerDescriptor, ) -> (Self::SamplerId, Self::SamplerData) { let descriptor = wgc::resource::SamplerDescriptor { label: desc.label.map(Borrowed), address_modes: [ desc.address_mode_u, desc.address_mode_v, desc.address_mode_w, ], 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, anisotropy_clamp: desc.anisotropy_clamp, border_color: desc.border_color, }; let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_sampler( *device, &descriptor, () )); if let Some(cause) = error { self.handle_error( &device_data.error_sink, cause, LABEL, desc.label, "Device::create_sampler", ); } (id, ()) } fn device_create_query_set( &self, device: &Self::DeviceId, device_data: &Self::DeviceData, desc: &wgt::QuerySetDescriptor