diff options
Diffstat (limited to 'third_party/rust/wgpu-hal/src')
19 files changed, 508 insertions, 122 deletions
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs index db013d2dec..2ac4464568 100644 --- a/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs @@ -21,8 +21,26 @@ impl HResult<()> for i32 { Err(Cow::Borrowed(description)) } fn into_device_result(self, description: &str) -> Result<(), crate::DeviceError> { + #![allow(unreachable_code)] + self.into_result().map_err(|err| { log::error!("{} failed: {}", description, err); + + match self { + winerror::E_OUTOFMEMORY => { + #[cfg(feature = "oom_panic")] + panic!("{description} failed: Out of memory"); + } + winerror::DXGI_ERROR_DEVICE_RESET | winerror::DXGI_ERROR_DEVICE_REMOVED => { + #[cfg(feature = "device_lost_panic")] + panic!("{description} failed: Device lost ({err})"); + } + _ => { + #[cfg(feature = "internal_error_panic")] + panic!("{description} failed: {err}"); + } + } + if self == winerror::E_OUTOFMEMORY { crate::DeviceError::OutOfMemory } else { diff --git a/third_party/rust/wgpu-hal/src/dx12/adapter.rs b/third_party/rust/wgpu-hal/src/dx12/adapter.rs index f6027014d2..960e1790a9 100644 --- a/third_party/rust/wgpu-hal/src/dx12/adapter.rs +++ b/third_party/rust/wgpu-hal/src/dx12/adapter.rs @@ -242,6 +242,7 @@ impl super::Adapter { | wgt::Features::POLYGON_MODE_LINE | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | wgt::Features::TIMESTAMP_QUERY + | wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS | wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES | wgt::Features::TEXTURE_COMPRESSION_BC | wgt::Features::CLEAR_TEXTURE @@ -294,6 +295,22 @@ impl super::Adapter { bgra8unorm_storage_supported, ); + // we must be using DXC because uint64_t was added with Shader Model 6 + // and FXC only supports up to 5.1 + let int64_shader_ops_supported = dxc_container.is_some() && { + let mut features1: d3d12_ty::D3D12_FEATURE_DATA_D3D12_OPTIONS1 = + unsafe { mem::zeroed() }; + let hr = unsafe { + device.CheckFeatureSupport( + d3d12_ty::D3D12_FEATURE_D3D12_OPTIONS1, + &mut features1 as *mut _ as *mut _, + mem::size_of::<d3d12_ty::D3D12_FEATURE_DATA_D3D12_OPTIONS1>() as _, + ) + }; + hr == 0 && features1.Int64ShaderOps != 0 + }; + features.set(wgt::Features::SHADER_INT64, int64_shader_ops_supported); + // float32-filterable should always be available on d3d12 features.set(wgt::Features::FLOAT32_FILTERABLE, true); @@ -307,6 +324,12 @@ impl super::Adapter { downlevel.flags -= wgt::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW; + // See https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels#feature-level-support + let max_color_attachments = 8; + // TODO: determine this programmatically if possible. + // https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447 + let max_color_attachment_bytes_per_sample = 64; + Some(crate::ExposedAdapter { adapter: super::Adapter { raw: adapter, @@ -377,6 +400,8 @@ impl super::Adapter { d3d12_ty::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, min_storage_buffer_offset_alignment: 4, max_inter_stage_shader_components: base.max_inter_stage_shader_components, + max_color_attachments, + max_color_attachment_bytes_per_sample, max_compute_workgroup_storage_size: base.max_compute_workgroup_storage_size, //TODO? max_compute_invocations_per_workgroup: d3d12_ty::D3D12_CS_4_X_THREAD_GROUP_MAX_THREADS_PER_GROUP, diff --git a/third_party/rust/wgpu-hal/src/dx12/command.rs b/third_party/rust/wgpu-hal/src/dx12/command.rs index f527898d90..9d96d29cae 100644 --- a/third_party/rust/wgpu-hal/src/dx12/command.rs +++ b/third_party/rust/wgpu-hal/src/dx12/command.rs @@ -56,6 +56,13 @@ impl super::Temp { } } +impl Drop for super::CommandEncoder { + fn drop(&mut self) { + use crate::CommandEncoder; + unsafe { self.discard_encoding() } + } +} + impl super::CommandEncoder { unsafe fn begin_pass(&mut self, kind: super::PassKind, label: crate::Label) { let list = self.list.as_ref().unwrap(); diff --git a/third_party/rust/wgpu-hal/src/dx12/device.rs b/third_party/rust/wgpu-hal/src/dx12/device.rs index 2507c125f8..3603b033b8 100644 --- a/third_party/rust/wgpu-hal/src/dx12/device.rs +++ b/third_party/rust/wgpu-hal/src/dx12/device.rs @@ -663,11 +663,7 @@ impl crate::Device<super::Api> for super::Device { end_of_pass_timer_query: None, }) } - unsafe fn destroy_command_encoder(&self, encoder: super::CommandEncoder) { - if let Some(list) = encoder.list { - list.close(); - } - } + unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {} unsafe fn create_bind_group_layout( &self, diff --git a/third_party/rust/wgpu-hal/src/dx12/mod.rs b/third_party/rust/wgpu-hal/src/dx12/mod.rs index 053b880689..13b43f8aca 100644 --- a/third_party/rust/wgpu-hal/src/dx12/mod.rs +++ b/third_party/rust/wgpu-hal/src/dx12/mod.rs @@ -238,6 +238,9 @@ struct DeviceShared { heap_samplers: descriptor::GeneralHeap, } +unsafe impl Send for DeviceShared {} +unsafe impl Sync for DeviceShared {} + pub struct Device { raw: d3d12::Device, present_queue: d3d12::CommandQueue, diff --git a/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs b/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs index df040dba15..288fc24745 100644 --- a/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs +++ b/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs @@ -13,7 +13,7 @@ use crate::auxil::dxgi::result::HResult; pub(super) fn compile_fxc( device: &super::Device, - source: &String, + source: &str, source_name: &str, raw_ep: &std::ffi::CString, stage_bit: wgt::ShaderStages, @@ -211,7 +211,7 @@ mod dxc { Err(crate::PipelineError::Linkage( stage_bit, format!( - "DXC compile error: {:?}", + "DXC compile error: {}", get_error_string_from_dxc_result(&dxc_container.library, &e.0) .unwrap_or_default() ), diff --git a/third_party/rust/wgpu-hal/src/gles/adapter.rs b/third_party/rust/wgpu-hal/src/gles/adapter.rs index afa4023797..c09725e85f 100644 --- a/third_party/rust/wgpu-hal/src/gles/adapter.rs +++ b/third_party/rust/wgpu-hal/src/gles/adapter.rs @@ -4,6 +4,7 @@ use std::sync::{atomic::AtomicU8, Arc}; use wgt::AstcChannel; use crate::auxil::db; +use crate::gles::ShaderClearProgram; // https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html @@ -435,7 +436,8 @@ impl super::Adapter { let mut features = wgt::Features::empty() | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | wgt::Features::CLEAR_TEXTURE - | wgt::Features::PUSH_CONSTANTS; + | wgt::Features::PUSH_CONSTANTS + | wgt::Features::DEPTH32FLOAT_STENCIL8; features.set( wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO, extensions.contains("GL_EXT_texture_border_clamp") @@ -472,6 +474,7 @@ impl super::Adapter { features.set(wgt::Features::SHADER_UNUSED_VERTEX_OUTPUT, true); if extensions.contains("GL_ARB_timer_query") { features.set(wgt::Features::TIMESTAMP_QUERY, true); + features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true); features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true); } let gl_bcn_exts = [ @@ -652,6 +655,15 @@ impl super::Adapter { 0 }; + let max_color_attachments = unsafe { + gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS) + .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) + .min(crate::MAX_COLOR_ATTACHMENTS as i32) as u32 + }; + + // TODO: programmatically determine this. + let max_color_attachment_bytes_per_sample = 32; + let limits = wgt::Limits { max_texture_dimension_1d: max_texture_size, max_texture_dimension_2d: max_texture_size, @@ -719,9 +731,21 @@ impl super::Adapter { max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4, min_uniform_buffer_offset_alignment, min_storage_buffer_offset_alignment, - max_inter_stage_shader_components: unsafe { - gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) - } as u32, + max_inter_stage_shader_components: { + // MAX_VARYING_COMPONENTS may return 0, because it is deprecated since OpenGL 3.2 core, + // and an OpenGL Context with the core profile and with forward-compatibility=true, + // will make deprecated constants unavailable. + let max_varying_components = + unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32; + if max_varying_components == 0 { + // default value for max_inter_stage_shader_components + 60 + } else { + max_varying_components + } + }, + max_color_attachments, + max_color_attachment_bytes_per_sample, max_compute_workgroup_storage_size: if supports_work_group_params { (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32) } else { @@ -779,6 +803,7 @@ impl super::Adapter { } let downlevel_defaults = wgt::DownlevelLimits {}; + let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) }; // Drop the GL guard so we can move the context into AdapterShared // ( on Wasm the gl handle is just a ref so we tell clippy to allow @@ -797,6 +822,7 @@ impl super::Adapter { next_shader_id: Default::default(), program_cache: Default::default(), es: es_ver.is_some(), + max_msaa_samples: max_samples, }), }, info: Self::make_info(vendor, renderer), @@ -825,7 +851,14 @@ impl super::Adapter { let source = if es { format!("#version 300 es\nprecision lowp float;\n{source}") } else { - format!("#version 130\n{source}") + let version = gl.version(); + if version.major == 3 && version.minor == 0 { + // OpenGL 3.0 only supports this format + format!("#version 130\n{source}") + } else { + // OpenGL 3.1+ support this format + format!("#version 140\n{source}") + } }; let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader"); unsafe { gl.shader_source(shader, &source) }; @@ -846,7 +879,7 @@ impl super::Adapter { unsafe fn create_shader_clear_program( gl: &glow::Context, es: bool, - ) -> Option<(glow::Program, glow::UniformLocation)> { + ) -> Option<ShaderClearProgram> { let program = unsafe { gl.create_program() }.expect("Could not create shader program"); let vertex = unsafe { Self::compile_shader( @@ -882,7 +915,10 @@ impl super::Adapter { unsafe { gl.delete_shader(vertex) }; unsafe { gl.delete_shader(fragment) }; - Some((program, color_uniform_location)) + Some(ShaderClearProgram { + program, + color_uniform_location, + }) } } @@ -908,9 +944,18 @@ impl crate::Adapter<super::Api> for super::Adapter { // Compile the shader program we use for doing manual clears to work around Mesa fastclear // bug. - let (shader_clear_program, shader_clear_program_color_uniform_location) = unsafe { - Self::create_shader_clear_program(gl, self.shared.es) - .ok_or(crate::DeviceError::ResourceCreationFailed)? + let shader_clear_program = if self + .shared + .workarounds + .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR) + { + Some(unsafe { + Self::create_shader_clear_program(gl, self.shared.es) + .ok_or(crate::DeviceError::ResourceCreationFailed)? + }) + } else { + // If we don't need the workaround, don't waste time and resources compiling the clear program + None }; Ok(crate::OpenDevice { @@ -928,7 +973,6 @@ impl crate::Adapter<super::Api> for super::Adapter { copy_fbo: unsafe { gl.create_framebuffer() } .map_err(|_| crate::DeviceError::OutOfMemory)?, shader_clear_program, - shader_clear_program_color_uniform_location, zero_buffer, temp_query_results: Mutex::new(Vec::new()), draw_buffer_count: AtomicU8::new(1), @@ -945,12 +989,7 @@ impl crate::Adapter<super::Api> for super::Adapter { use wgt::TextureFormat as Tf; let sample_count = { - let max_samples = unsafe { - self.shared - .context - .lock() - .get_parameter_i32(glow::MAX_SAMPLES) - }; + let max_samples = self.shared.max_msaa_samples; if max_samples >= 16 { Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 diff --git a/third_party/rust/wgpu-hal/src/gles/command.rs b/third_party/rust/wgpu-hal/src/gles/command.rs index 926122e4ad..4385e2a31e 100644 --- a/third_party/rust/wgpu-hal/src/gles/command.rs +++ b/third_party/rust/wgpu-hal/src/gles/command.rs @@ -93,6 +93,13 @@ impl super::CommandBuffer { } } +impl Drop for super::CommandEncoder { + fn drop(&mut self) { + use crate::CommandEncoder; + unsafe { self.discard_encoding() } + } +} + impl super::CommandEncoder { fn rebind_stencil_func(&mut self) { fn make(s: &super::StencilSide, face: u32) -> C { diff --git a/third_party/rust/wgpu-hal/src/gles/device.rs b/third_party/rust/wgpu-hal/src/gles/device.rs index d0abe2c169..2678488cf8 100644 --- a/third_party/rust/wgpu-hal/src/gles/device.rs +++ b/third_party/rust/wgpu-hal/src/gles/device.rs @@ -1194,13 +1194,16 @@ impl crate::Device<super::Api> for super::Device { let sampler = desc.samplers[entry.resource_index as usize]; super::RawBinding::Sampler(sampler.raw) } - wgt::BindingType::Texture { .. } => { + wgt::BindingType::Texture { view_dimension, .. } => { let view = desc.textures[entry.resource_index as usize].view; if view.array_layers.start != 0 { log::error!("Unable to create a sampled texture binding for non-zero array layer.\n{}", "This is an implementation problem of wgpu-hal/gles backend.") } let (raw, target) = view.inner.as_native(); + + super::Texture::log_failing_target_heuristics(view_dimension, target); + super::RawBinding::Texture { raw, target, diff --git a/third_party/rust/wgpu-hal/src/gles/egl.rs b/third_party/rust/wgpu-hal/src/gles/egl.rs index aa985d8121..f4bfcf5487 100644 --- a/third_party/rust/wgpu-hal/src/gles/egl.rs +++ b/third_party/rust/wgpu-hal/src/gles/egl.rs @@ -1,7 +1,8 @@ use glow::HasContext; +use once_cell::sync::Lazy; use parking_lot::{Mutex, MutexGuard, RwLock}; -use std::{ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration}; +use std::{collections::HashMap, ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration}; /// The amount of time to wait while trying to obtain a lock to the adapter context const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1; @@ -50,16 +51,6 @@ type WlEglWindowResizeFun = unsafe extern "system" fn( type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void); -#[cfg(target_os = "android")] -extern "C" { - pub fn ANativeWindow_setBuffersGeometry( - window: *mut raw::c_void, - width: i32, - height: i32, - format: i32, - ) -> i32; -} - type EglLabel = *const raw::c_void; #[allow(clippy::upper_case_acronyms)] @@ -161,7 +152,7 @@ impl Drop for DisplayOwner { fn open_x_display() -> Option<DisplayOwner> { log::debug!("Loading X11 library to get the current display"); unsafe { - let library = libloading::Library::new("libX11.so").ok()?; + let library = find_library(&["libX11.so.6", "libX11.so"])?; let func: libloading::Symbol<XOpenDisplayFun> = library.get(b"XOpenDisplay").unwrap(); let result = func(ptr::null()); ptr::NonNull::new(result).map(|ptr| DisplayOwner { @@ -442,6 +433,45 @@ struct Inner { srgb_kind: SrgbFrameBufferKind, } +// Different calls to `eglGetPlatformDisplay` may return the same `Display`, making it a global +// state of all our `EglContext`s. This forces us to track the number of such context to prevent +// terminating the display if it's currently used by another `EglContext`. +static DISPLAYS_REFERENCE_COUNT: Lazy<Mutex<HashMap<usize, usize>>> = Lazy::new(Default::default); + +fn initialize_display( + egl: &EglInstance, + display: khronos_egl::Display, +) -> Result<(i32, i32), khronos_egl::Error> { + let mut guard = DISPLAYS_REFERENCE_COUNT.lock(); + *guard.entry(display.as_ptr() as usize).or_default() += 1; + + // We don't need to check the reference count here since according to the `eglInitialize` + // documentation, initializing an already initialized EGL display connection has no effect + // besides returning the version numbers. + egl.initialize(display) +} + +fn terminate_display( + egl: &EglInstance, + display: khronos_egl::Display, +) -> Result<(), khronos_egl::Error> { + let key = &(display.as_ptr() as usize); + let mut guard = DISPLAYS_REFERENCE_COUNT.lock(); + let count_ref = guard + .get_mut(key) + .expect("Attempted to decref a display before incref was called"); + + if *count_ref > 1 { + *count_ref -= 1; + + Ok(()) + } else { + guard.remove(key); + + egl.terminate(display) + } +} + impl Inner { fn create( flags: wgt::InstanceFlags, @@ -449,7 +479,7 @@ impl Inner { display: khronos_egl::Display, force_gles_minor_version: wgt::Gles3MinorVersion, ) -> Result<Self, crate::InstanceError> { - let version = egl.initialize(display).map_err(|e| { + let version = initialize_display(&egl, display).map_err(|e| { crate::InstanceError::with_source( String::from("failed to initialize EGL display connection"), e, @@ -618,7 +648,8 @@ impl Drop for Inner { { log::warn!("Error in destroy_context: {:?}", e); } - if let Err(e) = self.egl.instance.terminate(self.egl.display) { + + if let Err(e) = terminate_display(&self.egl.instance, self.egl.display) { log::warn!("Error in terminate: {:?}", e); } } @@ -783,11 +814,12 @@ impl crate::Instance<super::Api> for Instance { (display, Some(Rc::new(display_owner)), WindowKind::AngleX11) } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") { log::warn!("No windowing system present. Using surfaceless platform"); + #[allow(clippy::unnecessary_literal_unwrap)] // This is only a literal on Emscripten let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless"); let display = unsafe { egl.get_platform_display( EGL_PLATFORM_SURFACELESS_MESA, - std::ptr::null_mut(), + khronos_egl::DEFAULT_DISPLAY, &[khronos_egl::ATTRIB_NONE], ) } @@ -863,7 +895,12 @@ impl crate::Instance<super::Api> for Instance { .unwrap(); let ret = unsafe { - ANativeWindow_setBuffersGeometry(handle.a_native_window.as_ptr(), 0, 0, format) + ndk_sys::ANativeWindow_setBuffersGeometry( + handle.a_native_window.as_ptr() as *mut ndk_sys::ANativeWindow, + 0, + 0, + format, + ) }; if ret != 0 { diff --git a/third_party/rust/wgpu-hal/src/gles/mod.rs b/third_party/rust/wgpu-hal/src/gles/mod.rs index 646419c7fe..6f41f7c000 100644 --- a/third_party/rust/wgpu-hal/src/gles/mod.rs +++ b/third_party/rust/wgpu-hal/src/gles/mod.rs @@ -251,6 +251,11 @@ struct AdapterShared { next_shader_id: AtomicU32, program_cache: Mutex<ProgramCache>, es: bool, + + /// Result of `gl.get_parameter_i32(glow::MAX_SAMPLES)`. + /// Cached here so it doesn't need to be queried every time texture format capabilities are requested. + /// (this has been shown to be a significant enough overhead) + max_msaa_samples: i32, } pub struct Adapter { @@ -264,6 +269,11 @@ pub struct Device { render_doc: crate::auxil::renderdoc::RenderDoc, } +pub struct ShaderClearProgram { + pub program: glow::Program, + pub color_uniform_location: glow::UniformLocation, +} + pub struct Queue { shared: Arc<AdapterShared>, features: wgt::Features, @@ -271,9 +281,7 @@ pub struct Queue { copy_fbo: glow::Framebuffer, /// Shader program used to clear the screen for [`Workarounds::MESA_I915_SRGB_SHADER_CLEAR`] /// devices. - shader_clear_program: glow::Program, - /// The uniform location of the color uniform in the shader clear program - shader_clear_program_color_uniform_location: glow::UniformLocation, + shader_clear_program: Option<ShaderClearProgram>, /// Keep a reasonably large buffer filled with zeroes, so that we can implement `ClearBuffer` of /// zeroes by copying from it. zero_buffer: glow::Buffer, @@ -366,6 +374,8 @@ impl Texture { /// Returns the `target`, whether the image is 3d and whether the image is a cubemap. fn get_info_from_desc(desc: &TextureDescriptor) -> u32 { match desc.dimension { + // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are + // doing `TEXTURE_2D` instead wgt::TextureDimension::D1 => glow::TEXTURE_2D, wgt::TextureDimension::D2 => { // HACK: detect a cube map; forces cube compatible textures to be cube textures @@ -379,6 +389,43 @@ impl Texture { wgt::TextureDimension::D3 => glow::TEXTURE_3D, } } + + /// More information can be found in issues #1614 and #1574 + fn log_failing_target_heuristics(view_dimension: wgt::TextureViewDimension, target: u32) { + let expected_target = match view_dimension { + wgt::TextureViewDimension::D1 => glow::TEXTURE_2D, + wgt::TextureViewDimension::D2 => glow::TEXTURE_2D, + wgt::TextureViewDimension::D2Array => glow::TEXTURE_2D_ARRAY, + wgt::TextureViewDimension::Cube => glow::TEXTURE_CUBE_MAP, + wgt::TextureViewDimension::CubeArray => glow::TEXTURE_CUBE_MAP_ARRAY, + wgt::TextureViewDimension::D3 => glow::TEXTURE_3D, + }; + + if expected_target == target { + return; + } + + let buffer; + let got = match target { + glow::TEXTURE_2D => "D2", + glow::TEXTURE_2D_ARRAY => "D2Array", + glow::TEXTURE_CUBE_MAP => "Cube", + glow::TEXTURE_CUBE_MAP_ARRAY => "CubeArray", + glow::TEXTURE_3D => "D3", + target => { + buffer = target.to_string(); + &buffer + } + }; + + log::error!( + "wgpu-hal heuristics assumed that the view dimension will be equal to `{got}` rather than `{view_dimension:?}`.\n{}\n{}\n{}\n{}", + "`D2` textures with `depth_or_array_layers == 1` are assumed to have view dimension `D2`", + "`D2` textures with `depth_or_array_layers > 1` are assumed to have view dimension `D2Array`", + "`D2` textures with `depth_or_array_layers == 6` are assumed to have view dimension `Cube`", + "`D2` textures with `depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` are assumed to have view dimension `CubeArray`", + ); + } } #[derive(Clone, Debug)] diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs index 6ec553bd29..5db5af9a16 100644 --- a/third_party/rust/wgpu-hal/src/gles/queue.rs +++ b/third_party/rust/wgpu-hal/src/gles/queue.rs @@ -40,10 +40,14 @@ fn get_z_offset(target: u32, base: &crate::TextureCopyBase) -> u32 { impl super::Queue { /// Performs a manual shader clear, used as a workaround for a clearing bug on mesa unsafe fn perform_shader_clear(&self, gl: &glow::Context, draw_buffer: u32, color: [f32; 4]) { - unsafe { gl.use_program(Some(self.shader_clear_program)) }; + let shader_clear = self + .shader_clear_program + .as_ref() + .expect("shader_clear_program should always be set if the workaround is enabled"); + unsafe { gl.use_program(Some(shader_clear.program)) }; unsafe { gl.uniform_4_f32( - Some(&self.shader_clear_program_color_uniform_location), + Some(&shader_clear.color_uniform_location), color[0], color[1], color[2], diff --git a/third_party/rust/wgpu-hal/src/gles/wgl.rs b/third_party/rust/wgpu-hal/src/gles/wgl.rs index 6243430dc2..c9039090b7 100644 --- a/third_party/rust/wgpu-hal/src/gles/wgl.rs +++ b/third_party/rust/wgpu-hal/src/gles/wgl.rs @@ -160,6 +160,9 @@ struct Inner { context: WglContext, } +unsafe impl Send for Inner {} +unsafe impl Sync for Inner {} + pub struct Instance { srgb_capable: bool, inner: Arc<Mutex<Inner>>, diff --git a/third_party/rust/wgpu-hal/src/lib.rs b/third_party/rust/wgpu-hal/src/lib.rs index 5d8c6ddda8..f1794a4a89 100644 --- a/third_party/rust/wgpu-hal/src/lib.rs +++ b/third_party/rust/wgpu-hal/src/lib.rs @@ -16,6 +16,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow( + // this happens on the GL backend, where it is both thread safe and non-thread safe in the same code. + clippy::arc_with_non_send_sync, // for `if_then_panic` until it reaches stable unknown_lints, // We use loops for getting early-out of scope without closures. @@ -329,6 +331,9 @@ pub trait Device<A: Api>: WasmNotSendSync { unsafe fn create_sampler(&self, desc: &SamplerDescriptor) -> Result<A::Sampler, DeviceError>; unsafe fn destroy_sampler(&self, sampler: A::Sampler); + /// Create a fresh [`CommandEncoder`]. + /// + /// The new `CommandEncoder` is in the "closed" state. unsafe fn create_command_encoder( &self, desc: &CommandEncoderDescriptor<A>, @@ -429,19 +434,95 @@ pub trait Queue<A: Api>: WasmNotSendSync { unsafe fn get_timestamp_period(&self) -> f32; } -/// Encoder for commands in command buffers. -/// Serves as a parent for all the encoded command buffers. -/// Works in bursts of action: one or more command buffers are recorded, -/// then submitted to a queue, and then it needs to be `reset_all()`. +/// Encoder and allocation pool for `CommandBuffer`. +/// +/// The life cycle of a `CommandBuffer` is as follows: +/// +/// - Call [`Device::create_command_encoder`] to create a new +/// `CommandEncoder`, in the "closed" state. +/// +/// - Call `begin_encoding` on a closed `CommandEncoder` to begin +/// recording commands. This puts the `CommandEncoder` in the +/// "recording" state. +/// +/// - Call methods like `copy_buffer_to_buffer`, `begin_render_pass`, +/// etc. on a "recording" `CommandEncoder` to add commands to the +/// list. +/// +/// - Call `end_encoding` on a recording `CommandEncoder` to close the +/// encoder and construct a fresh `CommandBuffer` consisting of the +/// list of commands recorded up to that point. +/// +/// - Call `discard_encoding` on a recording `CommandEncoder` to drop +/// the commands recorded thus far and close the encoder. +/// +/// - Call `reset_all` on a closed `CommandEncoder`, passing all the +/// live `CommandBuffers` built from it. All the `CommandBuffer`s +/// are destroyed, and their resources are freed. +/// +/// # Safety +/// +/// - The `CommandEncoder` must be in the states described above to +/// make the given calls. +/// +/// - A `CommandBuffer` that has been submitted for execution on the +/// GPU must live until its execution is complete. +/// +/// - A `CommandBuffer` must not outlive the `CommandEncoder` that +/// built it. +/// +/// - A `CommandEncoder` must not outlive its `Device`. pub trait CommandEncoder<A: Api>: WasmNotSendSync + fmt::Debug { /// Begin encoding a new command buffer. + /// + /// This puts this `CommandEncoder` in the "recording" state. + /// + /// # Safety + /// + /// This `CommandEncoder` must be in the "closed" state. unsafe fn begin_encoding(&mut self, label: Label) -> Result<(), DeviceError>; - /// Discard currently recorded list, if any. + + /// Discard the command list under construction, if any. + /// + /// This puts this `CommandEncoder` in the "closed" state. + /// + /// # Safety + /// + /// This `CommandEncoder` must be in the "recording" state. unsafe fn discard_encoding(&mut self); + + /// Return a fresh [`CommandBuffer`] holding the recorded commands. + /// + /// The returned [`CommandBuffer`] holds all the commands recorded + /// on this `CommandEncoder` since the last call to + /// [`begin_encoding`]. + /// + /// This puts this `CommandEncoder` in the "closed" state. + /// + /// # Safety + /// + /// This `CommandEncoder` must be in the "recording" state. + /// + /// The returned [`CommandBuffer`] must not outlive this + /// `CommandEncoder`. Implementations are allowed to build + /// `CommandBuffer`s that depend on storage owned by this + /// `CommandEncoder`. + /// + /// [`CommandBuffer`]: Api::CommandBuffer + /// [`begin_encoding`]: CommandEncoder::begin_encoding unsafe fn end_encoding(&mut self) -> Result<A::CommandBuffer, DeviceError>; - /// Reclaims all resources that are allocated for this encoder. - /// Must get all of the produced command buffers back, - /// and they must not be used by GPU at this moment. + + /// Reclaim all resources belonging to this `CommandEncoder`. + /// + /// # Safety + /// + /// This `CommandEncoder` must be in the "closed" state. + /// + /// The `command_buffers` iterator must produce all the live + /// [`CommandBuffer`]s built using this `CommandEncoder` --- that + /// is, every extant `CommandBuffer` returned from `end_encoding`. + /// + /// [`CommandBuffer`]: Api::CommandBuffer unsafe fn reset_all<I>(&mut self, command_buffers: I) where I: Iterator<Item = A::CommandBuffer>; diff --git a/third_party/rust/wgpu-hal/src/metal/adapter.rs b/third_party/rust/wgpu-hal/src/metal/adapter.rs index a946ce5819..9ec777b0f0 100644 --- a/third_party/rust/wgpu-hal/src/metal/adapter.rs +++ b/third_party/rust/wgpu-hal/src/metal/adapter.rs @@ -731,6 +731,12 @@ impl super::PrivateCapabilities { } else { 4 }, + // Per https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf + max_color_attachment_bytes_per_sample: if device.supports_family(MTLGPUFamily::Apple4) { + 64 + } else { + 32 + }, max_varying_components: if device .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1) { @@ -833,7 +839,7 @@ impl super::PrivateCapabilities { self.indirect_draw_dispatch, ); features.set( - F::TIMESTAMP_QUERY, + F::TIMESTAMP_QUERY | F::TIMESTAMP_QUERY_INSIDE_ENCODERS, self.timestamp_query_support .contains(TimestampQuerySupport::STAGE_BOUNDARIES), ); @@ -872,6 +878,10 @@ impl super::PrivateCapabilities { { features.insert(F::STORAGE_RESOURCE_BINDING_ARRAY); } + features.set( + F::SHADER_INT64, + self.msl_version >= MTLLanguageVersion::V2_3, + ); features.set( F::ADDRESS_MODE_CLAMP_TO_BORDER, @@ -940,6 +950,10 @@ impl super::PrivateCapabilities { min_uniform_buffer_offset_alignment: self.buffer_alignment as u32, min_storage_buffer_offset_alignment: self.buffer_alignment as u32, max_inter_stage_shader_components: self.max_varying_components, + max_color_attachments: (self.max_color_render_targets as u32) + .min(crate::MAX_COLOR_ATTACHMENTS as u32), + max_color_attachment_bytes_per_sample: self.max_color_attachment_bytes_per_sample + as u32, max_compute_workgroup_storage_size: self.max_total_threadgroup_memory, max_compute_invocations_per_workgroup: self.max_threads_per_group, max_compute_workgroup_size_x: self.max_threads_per_group, diff --git a/third_party/rust/wgpu-hal/src/metal/mod.rs b/third_party/rust/wgpu-hal/src/metal/mod.rs index 298f60faac..62fbf3d49d 100644 --- a/third_party/rust/wgpu-hal/src/metal/mod.rs +++ b/third_party/rust/wgpu-hal/src/metal/mod.rs @@ -248,6 +248,7 @@ struct PrivateCapabilities { max_texture_layers: u64, max_fragment_input_components: u64, max_color_render_targets: u8, + max_color_attachment_bytes_per_sample: u8, max_varying_components: u32, max_threads_per_group: u32, max_total_threadgroup_memory: u32, diff --git a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs index 85e620d23c..83b3dfa8e5 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs @@ -189,7 +189,7 @@ impl PhysicalDeviceFeatures { //.shader_clip_distance(requested_features.contains(wgt::Features::SHADER_CLIP_DISTANCE)) //.shader_cull_distance(requested_features.contains(wgt::Features::SHADER_CULL_DISTANCE)) .shader_float64(requested_features.contains(wgt::Features::SHADER_F64)) - //.shader_int64(requested_features.contains(wgt::Features::SHADER_INT64)) + .shader_int64(requested_features.contains(wgt::Features::SHADER_INT64)) .shader_int16(requested_features.contains(wgt::Features::SHADER_I16)) //.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY)) .geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX)) @@ -369,6 +369,7 @@ impl PhysicalDeviceFeatures { | F::ADDRESS_MODE_CLAMP_TO_BORDER | F::ADDRESS_MODE_CLAMP_TO_ZERO | F::TIMESTAMP_QUERY + | F::TIMESTAMP_QUERY_INSIDE_ENCODERS | F::TIMESTAMP_QUERY_INSIDE_PASSES | F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | F::CLEAR_TEXTURE; @@ -468,7 +469,7 @@ impl PhysicalDeviceFeatures { //if self.core.shader_clip_distance != 0 { //if self.core.shader_cull_distance != 0 { features.set(F::SHADER_F64, self.core.shader_float64 != 0); - //if self.core.shader_int64 != 0 { + features.set(F::SHADER_INT64, self.core.shader_int64 != 0); features.set(F::SHADER_I16, self.core.shader_int16 != 0); //if caps.supports_extension(vk::KhrSamplerMirrorClampToEdgeFn::name()) { @@ -827,6 +828,11 @@ impl PhysicalDeviceCapabilities { u64::MAX }; + // TODO: programmatically determine this, if possible. It's unclear whether we can + // as of https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447. + // We could increase the limit when we aren't on a tiled GPU. + let max_color_attachment_bytes_per_sample = 32; + wgt::Limits { max_texture_dimension_1d: limits.max_image_dimension1_d, max_texture_dimension_2d: limits.max_image_dimension2_d, @@ -862,6 +868,10 @@ impl PhysicalDeviceCapabilities { max_inter_stage_shader_components: limits .max_vertex_output_components .min(limits.max_fragment_input_components), + max_color_attachments: limits + .max_color_attachments + .min(crate::MAX_COLOR_ATTACHMENTS as u32), + max_color_attachment_bytes_per_sample, max_compute_workgroup_storage_size: limits.max_compute_shared_memory_size, max_compute_invocations_per_workgroup: limits.max_compute_work_group_invocations, max_compute_workgroup_size_x: max_compute_workgroup_sizes[0], @@ -1444,6 +1454,10 @@ impl super::Adapter { capabilities.push(spv::Capability::RayQueryKHR); } + if features.contains(wgt::Features::SHADER_INT64) { + capabilities.push(spv::Capability::Int64); + } + let mut flags = spv::WriterFlags::empty(); flags.set( spv::WriterFlags::DEBUG, diff --git a/third_party/rust/wgpu-hal/src/vulkan/instance.rs b/third_party/rust/wgpu-hal/src/vulkan/instance.rs index c4ef573461..771938b0b0 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/instance.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/instance.rs @@ -6,6 +6,7 @@ use std::{ thread, }; +use arrayvec::ArrayVec; use ash::{ extensions::{ext, khr}, vk, @@ -34,11 +35,13 @@ unsafe extern "system" fn debug_utils_messenger_callback( // the debug range start and end appear in different command buffers. let khronos_validation_layer = std::ffi::CStr::from_bytes_with_nul(b"Khronos Validation Layer\0").unwrap(); - if user_data.validation_layer_description.as_ref() == khronos_validation_layer - && user_data.validation_layer_spec_version >= vk::make_api_version(0, 1, 3, 240) - && user_data.validation_layer_spec_version <= vk::make_api_version(0, 1, 3, 250) - { - return vk::FALSE; + if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() { + if layer_properties.layer_description.as_ref() == khronos_validation_layer + && layer_properties.layer_spec_version >= vk::make_api_version(0, 1, 3, 240) + && layer_properties.layer_spec_version <= vk::make_api_version(0, 1, 3, 250) + { + return vk::FALSE; + } } } @@ -211,6 +214,22 @@ impl super::Instance { &self.shared } + fn enumerate_instance_extension_properties( + entry: &ash::Entry, + layer_name: Option<&CStr>, + ) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> { + let instance_extensions = { + profiling::scope!("vkEnumerateInstanceExtensionProperties"); + entry.enumerate_instance_extension_properties(layer_name) + }; + instance_extensions.map_err(|e| { + crate::InstanceError::with_source( + String::from("enumerate_instance_extension_properties() failed"), + e, + ) + }) + } + /// Return the instance extension names wgpu would like to enable. /// /// Return a vector of the names of instance extensions actually available @@ -229,16 +248,7 @@ impl super::Instance { _instance_api_version: u32, flags: wgt::InstanceFlags, ) -> Result<Vec<&'static CStr>, crate::InstanceError> { - let instance_extensions = { - profiling::scope!("vkEnumerateInstanceExtensionProperties"); - entry.enumerate_instance_extension_properties(None) - }; - let instance_extensions = instance_extensions.map_err(|e| { - crate::InstanceError::with_source( - String::from("enumerate_instance_extension_properties() failed"), - e, - ) - })?; + let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?; // Check our extensions against the available extensions let mut extensions: Vec<&'static CStr> = Vec::new(); @@ -643,6 +653,31 @@ impl crate::Instance<super::Api> for super::Instance { .find(|inst_layer| cstr_from_bytes_until_nul(&inst_layer.layer_name) == Some(name)) } + let validation_layer_name = + CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap(); + let validation_layer_properties = find_layer(&instance_layers, validation_layer_name); + + // Determine if VK_EXT_validation_features is available, so we can enable + // GPU assisted validation and synchronization validation. + let validation_features_are_enabled = if validation_layer_properties.is_some() { + // Get the all the instance extension properties. + let exts = + Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?; + // Convert all the names of the extensions into an iterator of CStrs. + let mut ext_names = exts + .iter() + .filter_map(|ext| cstr_from_bytes_until_nul(&ext.extension_name)); + // Find the validation features extension. + ext_names.any(|ext_name| ext_name == vk::ExtValidationFeaturesFn::name()) + } else { + false + }; + + let should_enable_gpu_based_validation = desc + .flags + .intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION) + && validation_features_are_enabled; + let nv_optimus_layer = CStr::from_bytes_with_nul(b"VK_LAYER_NV_optimus\0").unwrap(); let has_nv_optimus = find_layer(&instance_layers, nv_optimus_layer).is_some(); @@ -651,52 +686,33 @@ impl crate::Instance<super::Api> for super::Instance { let mut layers: Vec<&'static CStr> = Vec::new(); + let has_debug_extension = extensions.contains(&ext::DebugUtils::name()); + let mut debug_user_data = has_debug_extension.then(|| { + // Put the callback data on the heap, to ensure it will never be + // moved. + Box::new(super::DebugUtilsMessengerUserData { + validation_layer_properties: None, + has_obs_layer, + }) + }); + // Request validation layer if asked. - let mut debug_utils = None; - if desc.flags.intersects(wgt::InstanceFlags::VALIDATION) { - let validation_layer_name = - CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap(); - if let Some(layer_properties) = find_layer(&instance_layers, validation_layer_name) { + if desc.flags.intersects(wgt::InstanceFlags::VALIDATION) + || should_enable_gpu_based_validation + { + if let Some(layer_properties) = validation_layer_properties { layers.push(validation_layer_name); - if extensions.contains(&ext::DebugUtils::name()) { - // Put the callback data on the heap, to ensure it will never be - // moved. - let callback_data = Box::new(super::DebugUtilsMessengerUserData { - validation_layer_description: cstr_from_bytes_until_nul( - &layer_properties.description, - ) - .unwrap() - .to_owned(), - validation_layer_spec_version: layer_properties.spec_version, - has_obs_layer, - }); - - // having ERROR unconditionally because Vk doesn't like empty flags - let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR; - if log::max_level() >= log::LevelFilter::Debug { - severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE; - } - if log::max_level() >= log::LevelFilter::Info { - severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO; - } - if log::max_level() >= log::LevelFilter::Warn { - severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING; - } - - let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL - | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION - | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE; - - let create_info = super::DebugUtilsCreateInfo { - severity, - message_type, - callback_data, - }; - - let vk_create_info = create_info.to_vk_create_info().build(); - - debug_utils = Some((create_info, vk_create_info)); + if let Some(debug_user_data) = debug_user_data.as_mut() { + debug_user_data.validation_layer_properties = + Some(super::ValidationLayerProperties { + layer_description: cstr_from_bytes_until_nul( + &layer_properties.description, + ) + .unwrap() + .to_owned(), + layer_spec_version: layer_properties.spec_version, + }); } } else { log::warn!( @@ -705,6 +721,35 @@ impl crate::Instance<super::Api> for super::Instance { ); } } + let mut debug_utils = if let Some(callback_data) = debug_user_data { + // having ERROR unconditionally because Vk doesn't like empty flags + let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR; + if log::max_level() >= log::LevelFilter::Debug { + severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE; + } + if log::max_level() >= log::LevelFilter::Info { + severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO; + } + if log::max_level() >= log::LevelFilter::Warn { + severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING; + } + + let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE; + + let create_info = super::DebugUtilsCreateInfo { + severity, + message_type, + callback_data, + }; + + let vk_create_info = create_info.to_vk_create_info().build(); + + Some((create_info, vk_create_info)) + } else { + None + }; #[cfg(target_os = "android")] let android_sdk_version = { @@ -756,6 +801,28 @@ impl crate::Instance<super::Api> for super::Instance { create_info = create_info.push_next(vk_create_info); } + // Enable explicit validation features if available + let mut validation_features; + let mut validation_feature_list: ArrayVec<_, 3>; + if validation_features_are_enabled { + validation_feature_list = ArrayVec::new(); + + // Always enable synchronization validation + validation_feature_list + .push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION); + + // Only enable GPU assisted validation if requested. + if should_enable_gpu_based_validation { + validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED); + validation_feature_list + .push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT); + } + + validation_features = vk::ValidationFeaturesEXT::builder() + .enabled_validation_features(&validation_feature_list); + create_info = create_info.push_next(&mut validation_features); + } + unsafe { profiling::scope!("vkCreateInstance"); entry.create_instance(&create_info, None) diff --git a/third_party/rust/wgpu-hal/src/vulkan/mod.rs b/third_party/rust/wgpu-hal/src/vulkan/mod.rs index 787ebd7267..1f922e83da 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/mod.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/mod.rs @@ -101,17 +101,25 @@ pub struct DebugUtilsCreateInfo { callback_data: Box<DebugUtilsMessengerUserData>, } +#[derive(Debug)] +/// The properties related to the validation layer needed for the +/// DebugUtilsMessenger for their workarounds +struct ValidationLayerProperties { + /// Validation layer description, from `vk::LayerProperties`. + layer_description: std::ffi::CString, + + /// Validation layer specification version, from `vk::LayerProperties`. + layer_spec_version: u32, +} + /// User data needed by `instance::debug_utils_messenger_callback`. /// /// When we create the [`vk::DebugUtilsMessengerEXT`], the `pUserData` /// pointer refers to one of these values. #[derive(Debug)] pub struct DebugUtilsMessengerUserData { - /// Validation layer description, from `vk::LayerProperties`. - validation_layer_description: std::ffi::CString, - - /// Validation layer specification version, from `vk::LayerProperties`. - validation_layer_spec_version: u32, + /// The properties related to the validation layer, if present + validation_layer_properties: Option<ValidationLayerProperties>, /// If the OBS layer is present. OBS never increments the version of their layer, /// so there's no reason to have the version. @@ -724,13 +732,25 @@ impl crate::Queue<Api> for Queue { impl From<vk::Result> for crate::DeviceError { fn from(result: vk::Result) -> Self { + #![allow(unreachable_code)] match result { vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { + #[cfg(feature = "oom_panic")] + panic!("Out of memory ({result:?})"); + Self::OutOfMemory } - vk::Result::ERROR_DEVICE_LOST => Self::Lost, + vk::Result::ERROR_DEVICE_LOST => { + #[cfg(feature = "device_lost_panic")] + panic!("Device lost"); + + Self::Lost + } _ => { - log::warn!("Unrecognized device error {:?}", result); + #[cfg(feature = "internal_error_panic")] + panic!("Internal error: {result:?}"); + + log::warn!("Unrecognized device error {result:?}"); Self::Lost } } |