summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wgpu-hal/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/wgpu-hal/src')
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs18
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/adapter.rs25
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/command.rs7
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/device.rs6
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/mod.rs3
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs4
-rw-r--r--third_party/rust/wgpu-hal/src/gles/adapter.rs73
-rw-r--r--third_party/rust/wgpu-hal/src/gles/command.rs7
-rw-r--r--third_party/rust/wgpu-hal/src/gles/device.rs5
-rw-r--r--third_party/rust/wgpu-hal/src/gles/egl.rs69
-rw-r--r--third_party/rust/wgpu-hal/src/gles/mod.rs53
-rw-r--r--third_party/rust/wgpu-hal/src/gles/queue.rs8
-rw-r--r--third_party/rust/wgpu-hal/src/gles/wgl.rs3
-rw-r--r--third_party/rust/wgpu-hal/src/lib.rs97
-rw-r--r--third_party/rust/wgpu-hal/src/metal/adapter.rs16
-rw-r--r--third_party/rust/wgpu-hal/src/metal/mod.rs1
-rw-r--r--third_party/rust/wgpu-hal/src/vulkan/adapter.rs18
-rw-r--r--third_party/rust/wgpu-hal/src/vulkan/instance.rs183
-rw-r--r--third_party/rust/wgpu-hal/src/vulkan/mod.rs34
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
}
}