summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wgpu-hal/src/auxil
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/wgpu-hal/src/auxil
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wgpu-hal/src/auxil')
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs270
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs105
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs261
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs5
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs42
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs94
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/mod.rs138
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/renderdoc.rs138
8 files changed, 1053 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs
new file mode 100644
index 0000000000..6af4b77bb3
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs
@@ -0,0 +1,270 @@
+use std::{ffi::OsString, os::windows::ffi::OsStringExt};
+use winapi::shared::dxgiformat;
+
+// Helper to convert DXGI adapter name to a normal string
+pub fn map_adapter_name(name: [u16; 128]) -> String {
+ let len = name.iter().take_while(|&&c| c != 0).count();
+ let name = OsString::from_wide(&name[..len]);
+ name.to_string_lossy().into_owned()
+}
+
+pub fn map_texture_format_failable(format: wgt::TextureFormat) -> Option<dxgiformat::DXGI_FORMAT> {
+ use wgt::TextureFormat as Tf;
+ use winapi::shared::dxgiformat::*;
+
+ Some(match format {
+ Tf::R8Unorm => DXGI_FORMAT_R8_UNORM,
+ Tf::R8Snorm => DXGI_FORMAT_R8_SNORM,
+ Tf::R8Uint => DXGI_FORMAT_R8_UINT,
+ Tf::R8Sint => DXGI_FORMAT_R8_SINT,
+ Tf::R16Uint => DXGI_FORMAT_R16_UINT,
+ Tf::R16Sint => DXGI_FORMAT_R16_SINT,
+ Tf::R16Unorm => DXGI_FORMAT_R16_UNORM,
+ Tf::R16Snorm => DXGI_FORMAT_R16_SNORM,
+ Tf::R16Float => DXGI_FORMAT_R16_FLOAT,
+ Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM,
+ Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM,
+ Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT,
+ Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT,
+ Tf::Rg16Unorm => DXGI_FORMAT_R16G16_UNORM,
+ Tf::Rg16Snorm => DXGI_FORMAT_R16G16_SNORM,
+ Tf::R32Uint => DXGI_FORMAT_R32_UINT,
+ Tf::R32Sint => DXGI_FORMAT_R32_SINT,
+ Tf::R32Float => DXGI_FORMAT_R32_FLOAT,
+ Tf::Rg16Uint => DXGI_FORMAT_R16G16_UINT,
+ Tf::Rg16Sint => DXGI_FORMAT_R16G16_SINT,
+ Tf::Rg16Float => DXGI_FORMAT_R16G16_FLOAT,
+ Tf::Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM,
+ Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
+ Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
+ Tf::Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM,
+ Tf::Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM,
+ Tf::Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT,
+ Tf::Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT,
+ Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
+ Tf::Rgb10a2Uint => DXGI_FORMAT_R10G10B10A2_UINT,
+ Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM,
+ Tf::Rg11b10Float => DXGI_FORMAT_R11G11B10_FLOAT,
+ Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT,
+ Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT,
+ Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT,
+ Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT,
+ Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT,
+ Tf::Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM,
+ Tf::Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM,
+ Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT,
+ Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT,
+ Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT,
+ Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT,
+ Tf::Stencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,
+ Tf::Depth16Unorm => DXGI_FORMAT_D16_UNORM,
+ Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT,
+ Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,
+ Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT,
+ Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
+ Tf::NV12 => DXGI_FORMAT_NV12,
+ Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM,
+ Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB,
+ Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM,
+ Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_UNORM_SRGB,
+ Tf::Bc3RgbaUnorm => DXGI_FORMAT_BC3_UNORM,
+ Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_UNORM_SRGB,
+ Tf::Bc4RUnorm => DXGI_FORMAT_BC4_UNORM,
+ Tf::Bc4RSnorm => DXGI_FORMAT_BC4_SNORM,
+ Tf::Bc5RgUnorm => DXGI_FORMAT_BC5_UNORM,
+ Tf::Bc5RgSnorm => DXGI_FORMAT_BC5_SNORM,
+ Tf::Bc6hRgbUfloat => DXGI_FORMAT_BC6H_UF16,
+ Tf::Bc6hRgbFloat => DXGI_FORMAT_BC6H_SF16,
+ Tf::Bc7RgbaUnorm => DXGI_FORMAT_BC7_UNORM,
+ Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_UNORM_SRGB,
+ Tf::Etc2Rgb8Unorm
+ | Tf::Etc2Rgb8UnormSrgb
+ | Tf::Etc2Rgb8A1Unorm
+ | Tf::Etc2Rgb8A1UnormSrgb
+ | Tf::Etc2Rgba8Unorm
+ | Tf::Etc2Rgba8UnormSrgb
+ | Tf::EacR11Unorm
+ | Tf::EacR11Snorm
+ | Tf::EacRg11Unorm
+ | Tf::EacRg11Snorm
+ | Tf::Astc {
+ block: _,
+ channel: _,
+ } => return None,
+ })
+}
+
+pub fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT {
+ match map_texture_format_failable(format) {
+ Some(f) => f,
+ None => unreachable!(),
+ }
+}
+
+// Note: DXGI doesn't allow sRGB format on the swapchain,
+// but creating RTV of swapchain buffers with sRGB works.
+pub fn map_texture_format_nosrgb(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT {
+ match format {
+ wgt::TextureFormat::Bgra8UnormSrgb => dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM,
+ wgt::TextureFormat::Rgba8UnormSrgb => dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM,
+ _ => map_texture_format(format),
+ }
+}
+
+// SRV and UAV can't use the depth or typeless formats
+// see https://microsoft.github.io/DirectX-Specs/d3d/PlanarDepthStencilDDISpec.html#view-creation
+pub fn map_texture_format_for_srv_uav(
+ format: wgt::TextureFormat,
+ aspect: crate::FormatAspects,
+) -> Option<dxgiformat::DXGI_FORMAT> {
+ Some(match (format, aspect) {
+ (wgt::TextureFormat::Depth16Unorm, crate::FormatAspects::DEPTH) => {
+ dxgiformat::DXGI_FORMAT_R16_UNORM
+ }
+ (wgt::TextureFormat::Depth32Float, crate::FormatAspects::DEPTH) => {
+ dxgiformat::DXGI_FORMAT_R32_FLOAT
+ }
+ (wgt::TextureFormat::Depth32FloatStencil8, crate::FormatAspects::DEPTH) => {
+ dxgiformat::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS
+ }
+ (
+ wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8,
+ crate::FormatAspects::DEPTH,
+ ) => dxgiformat::DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
+
+ (wgt::TextureFormat::Depth32FloatStencil8, crate::FormatAspects::STENCIL) => {
+ dxgiformat::DXGI_FORMAT_X32_TYPELESS_G8X24_UINT
+ }
+ (
+ wgt::TextureFormat::Stencil8 | wgt::TextureFormat::Depth24PlusStencil8,
+ crate::FormatAspects::STENCIL,
+ ) => dxgiformat::DXGI_FORMAT_X24_TYPELESS_G8_UINT,
+
+ (_, crate::FormatAspects::DEPTH)
+ | (_, crate::FormatAspects::STENCIL)
+ | (_, crate::FormatAspects::DEPTH_STENCIL) => return None,
+
+ _ => map_texture_format(format),
+ })
+}
+
+// see https://microsoft.github.io/DirectX-Specs/d3d/PlanarDepthStencilDDISpec.html#planar-layout-for-staging-from-buffer
+pub fn map_texture_format_for_copy(
+ format: wgt::TextureFormat,
+ aspect: crate::FormatAspects,
+) -> Option<dxgiformat::DXGI_FORMAT> {
+ Some(match (format, aspect) {
+ (wgt::TextureFormat::Depth16Unorm, crate::FormatAspects::DEPTH) => {
+ dxgiformat::DXGI_FORMAT_R16_UNORM
+ }
+ (
+ wgt::TextureFormat::Depth32Float | wgt::TextureFormat::Depth32FloatStencil8,
+ crate::FormatAspects::DEPTH,
+ ) => dxgiformat::DXGI_FORMAT_R32_FLOAT,
+
+ (
+ wgt::TextureFormat::Stencil8
+ | wgt::TextureFormat::Depth24PlusStencil8
+ | wgt::TextureFormat::Depth32FloatStencil8,
+ crate::FormatAspects::STENCIL,
+ ) => dxgiformat::DXGI_FORMAT_R8_UINT,
+
+ (format, crate::FormatAspects::COLOR) => map_texture_format(format),
+
+ _ => return None,
+ })
+}
+
+pub fn map_texture_format_for_resource(
+ format: wgt::TextureFormat,
+ usage: crate::TextureUses,
+ has_view_formats: bool,
+ casting_fully_typed_format_supported: bool,
+) -> dxgiformat::DXGI_FORMAT {
+ use wgt::TextureFormat as Tf;
+ use winapi::shared::dxgiformat::*;
+
+ if casting_fully_typed_format_supported {
+ map_texture_format(format)
+
+ // We might view this resource as srgb or non-srgb
+ } else if has_view_formats {
+ match format {
+ Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_TYPELESS,
+ Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_TYPELESS,
+ Tf::Bc1RgbaUnorm | Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_TYPELESS,
+ Tf::Bc2RgbaUnorm | Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_TYPELESS,
+ Tf::Bc3RgbaUnorm | Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_TYPELESS,
+ Tf::Bc7RgbaUnorm | Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_TYPELESS,
+ format => map_texture_format(format),
+ }
+
+ // We might view this resource as SRV/UAV but also as DSV
+ } else if format.is_depth_stencil_format()
+ && usage.intersects(
+ crate::TextureUses::RESOURCE
+ | crate::TextureUses::STORAGE_READ
+ | crate::TextureUses::STORAGE_READ_WRITE,
+ )
+ {
+ match format {
+ Tf::Depth16Unorm => DXGI_FORMAT_R16_TYPELESS,
+ Tf::Depth32Float => DXGI_FORMAT_R32_TYPELESS,
+ Tf::Depth32FloatStencil8 => DXGI_FORMAT_R32G8X24_TYPELESS,
+ Tf::Stencil8 | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => DXGI_FORMAT_R24G8_TYPELESS,
+ _ => unreachable!(),
+ }
+ } else {
+ map_texture_format(format)
+ }
+}
+
+pub fn map_index_format(format: wgt::IndexFormat) -> dxgiformat::DXGI_FORMAT {
+ match format {
+ wgt::IndexFormat::Uint16 => dxgiformat::DXGI_FORMAT_R16_UINT,
+ wgt::IndexFormat::Uint32 => dxgiformat::DXGI_FORMAT_R32_UINT,
+ }
+}
+
+pub fn map_vertex_format(format: wgt::VertexFormat) -> dxgiformat::DXGI_FORMAT {
+ use wgt::VertexFormat as Vf;
+ use winapi::shared::dxgiformat::*;
+
+ match format {
+ Vf::Unorm8x2 => DXGI_FORMAT_R8G8_UNORM,
+ Vf::Snorm8x2 => DXGI_FORMAT_R8G8_SNORM,
+ Vf::Uint8x2 => DXGI_FORMAT_R8G8_UINT,
+ Vf::Sint8x2 => DXGI_FORMAT_R8G8_SINT,
+ Vf::Unorm8x4 => DXGI_FORMAT_R8G8B8A8_UNORM,
+ Vf::Snorm8x4 => DXGI_FORMAT_R8G8B8A8_SNORM,
+ Vf::Uint8x4 => DXGI_FORMAT_R8G8B8A8_UINT,
+ Vf::Sint8x4 => DXGI_FORMAT_R8G8B8A8_SINT,
+ Vf::Unorm16x2 => DXGI_FORMAT_R16G16_UNORM,
+ Vf::Snorm16x2 => DXGI_FORMAT_R16G16_SNORM,
+ Vf::Uint16x2 => DXGI_FORMAT_R16G16_UINT,
+ Vf::Sint16x2 => DXGI_FORMAT_R16G16_SINT,
+ Vf::Float16x2 => DXGI_FORMAT_R16G16_FLOAT,
+ Vf::Unorm16x4 => DXGI_FORMAT_R16G16B16A16_UNORM,
+ Vf::Snorm16x4 => DXGI_FORMAT_R16G16B16A16_SNORM,
+ Vf::Uint16x4 => DXGI_FORMAT_R16G16B16A16_UINT,
+ Vf::Sint16x4 => DXGI_FORMAT_R16G16B16A16_SINT,
+ Vf::Float16x4 => DXGI_FORMAT_R16G16B16A16_FLOAT,
+ Vf::Uint32 => DXGI_FORMAT_R32_UINT,
+ Vf::Sint32 => DXGI_FORMAT_R32_SINT,
+ Vf::Float32 => DXGI_FORMAT_R32_FLOAT,
+ Vf::Uint32x2 => DXGI_FORMAT_R32G32_UINT,
+ Vf::Sint32x2 => DXGI_FORMAT_R32G32_SINT,
+ Vf::Float32x2 => DXGI_FORMAT_R32G32_FLOAT,
+ Vf::Uint32x3 => DXGI_FORMAT_R32G32B32_UINT,
+ Vf::Sint32x3 => DXGI_FORMAT_R32G32B32_SINT,
+ Vf::Float32x3 => DXGI_FORMAT_R32G32B32_FLOAT,
+ Vf::Uint32x4 => DXGI_FORMAT_R32G32B32A32_UINT,
+ Vf::Sint32x4 => DXGI_FORMAT_R32G32B32A32_SINT,
+ Vf::Float32x4 => DXGI_FORMAT_R32G32B32A32_FLOAT,
+ Vf::Float64 | Vf::Float64x2 | Vf::Float64x3 | Vf::Float64x4 => unimplemented!(),
+ }
+}
+
+pub fn map_acomposite_alpha_mode(_mode: wgt::CompositeAlphaMode) -> d3d12::AlphaMode {
+ d3d12::AlphaMode::Ignore
+}
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs
new file mode 100644
index 0000000000..70db8b2d0d
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs
@@ -0,0 +1,105 @@
+use std::{borrow::Cow, slice};
+
+use parking_lot::{lock_api::RawMutex, Mutex};
+use winapi::{
+ um::{errhandlingapi, winnt},
+ vc::excpt,
+};
+
+// This is a mutex as opposed to an atomic as we need to completely
+// lock everyone out until we have registered or unregistered the
+// exception handler, otherwise really nasty races could happen.
+//
+// By routing all the registration through these functions we can guarantee
+// there is either 1 or 0 exception handlers registered, not multiple.
+static EXCEPTION_HANDLER_COUNT: Mutex<usize> = Mutex::const_new(parking_lot::RawMutex::INIT, 0);
+
+pub fn register_exception_handler() {
+ let mut count_guard = EXCEPTION_HANDLER_COUNT.lock();
+ if *count_guard == 0 {
+ unsafe {
+ errhandlingapi::AddVectoredExceptionHandler(0, Some(output_debug_string_handler))
+ };
+ }
+ *count_guard += 1;
+}
+
+pub fn unregister_exception_handler() {
+ let mut count_guard = EXCEPTION_HANDLER_COUNT.lock();
+ if *count_guard == 1 {
+ unsafe {
+ errhandlingapi::RemoveVectoredExceptionHandler(output_debug_string_handler as *mut _)
+ };
+ }
+ *count_guard -= 1;
+}
+
+const MESSAGE_PREFIXES: &[(&str, log::Level)] = &[
+ ("CORRUPTION", log::Level::Error),
+ ("ERROR", log::Level::Error),
+ ("WARNING", log::Level::Warn),
+ ("INFO", log::Level::Info),
+ ("MESSAGE", log::Level::Debug),
+];
+
+unsafe extern "system" fn output_debug_string_handler(
+ exception_info: *mut winnt::EXCEPTION_POINTERS,
+) -> i32 {
+ // See https://stackoverflow.com/a/41480827
+ let record = unsafe { &*(*exception_info).ExceptionRecord };
+ if record.NumberParameters != 2 {
+ return excpt::EXCEPTION_CONTINUE_SEARCH;
+ }
+ let message = match record.ExceptionCode {
+ winnt::DBG_PRINTEXCEPTION_C => String::from_utf8_lossy(unsafe {
+ slice::from_raw_parts(
+ record.ExceptionInformation[1] as *const u8,
+ record.ExceptionInformation[0],
+ )
+ }),
+ winnt::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(unsafe {
+ slice::from_raw_parts(
+ record.ExceptionInformation[1] as *const u16,
+ record.ExceptionInformation[0],
+ )
+ })),
+ _ => return excpt::EXCEPTION_CONTINUE_SEARCH,
+ };
+
+ let message = match message.strip_prefix("D3D12 ") {
+ Some(msg) => msg
+ .trim_end_matches("\n\0")
+ .trim_end_matches("[ STATE_CREATION WARNING #0: UNKNOWN]"),
+ None => return excpt::EXCEPTION_CONTINUE_SEARCH,
+ };
+
+ let (message, level) = match MESSAGE_PREFIXES
+ .iter()
+ .find(|&&(prefix, _)| message.starts_with(prefix))
+ {
+ Some(&(prefix, level)) => (&message[prefix.len() + 2..], level),
+ None => (message, log::Level::Debug),
+ };
+
+ if level == log::Level::Warn && message.contains("#82") {
+ // This is are useless spammy warnings (#820, #821):
+ // "The application did not pass any clear value to resource creation"
+ return excpt::EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ if level == log::Level::Warn && message.contains("DRAW_EMPTY_SCISSOR_RECTANGLE") {
+ // This is normal, WebGPU allows passing empty scissor rectangles.
+ return excpt::EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ let _ = std::panic::catch_unwind(|| {
+ log::log!(level, "{}", message);
+ });
+
+ if cfg!(debug_assertions) && level == log::Level::Error {
+ // Set canary and continue
+ crate::VALIDATION_CANARY.add(message.to_string());
+ }
+
+ excpt::EXCEPTION_CONTINUE_EXECUTION
+}
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs
new file mode 100644
index 0000000000..38fdd17c89
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs
@@ -0,0 +1,261 @@
+use winapi::{
+ shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, winerror},
+ Interface,
+};
+
+use super::result::HResult as _;
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum DxgiFactoryType {
+ Factory2,
+ Factory4,
+ Factory6,
+}
+
+fn should_keep_adapter(adapter: &dxgi::IDXGIAdapter1) -> bool {
+ let mut desc = unsafe { std::mem::zeroed() };
+ unsafe { adapter.GetDesc1(&mut desc) };
+
+ // The Intel Haswell family of iGPUs had support for the D3D12 API but it was later
+ // removed due to a security vulnerability.
+ //
+ // We are explicitly filtering out all the devices in the family because we are now
+ // getting reports of device loss at a later time than at device creation time (`D3D12CreateDevice`).
+ //
+ // See https://www.intel.com/content/www/us/en/support/articles/000057520/graphics.html
+ // This list of device IDs is from https://dgpu-docs.intel.com/devices/hardware-table.html
+ let haswell_device_ids = [
+ 0x0422, 0x0426, 0x042A, 0x042B, 0x042E, 0x0C22, 0x0C26, 0x0C2A, 0x0C2B, 0x0C2E, 0x0A22,
+ 0x0A2A, 0x0A2B, 0x0D2A, 0x0D2B, 0x0D2E, 0x0A26, 0x0A2E, 0x0D22, 0x0D26, 0x0412, 0x0416,
+ 0x0D12, 0x041A, 0x041B, 0x0C12, 0x0C16, 0x0C1A, 0x0C1B, 0x0C1E, 0x0A12, 0x0A1A, 0x0A1B,
+ 0x0D16, 0x0D1A, 0x0D1B, 0x0D1E, 0x041E, 0x0A16, 0x0A1E, 0x0402, 0x0406, 0x040A, 0x040B,
+ 0x040E, 0x0C02, 0x0C06, 0x0C0A, 0x0C0B, 0x0C0E, 0x0A02, 0x0A06, 0x0A0A, 0x0A0B, 0x0A0E,
+ 0x0D02, 0x0D06, 0x0D0A, 0x0D0B, 0x0D0E,
+ ];
+ if desc.VendorId == 0x8086 && haswell_device_ids.contains(&desc.DeviceId) {
+ return false;
+ }
+
+ // If run completely headless, windows will show two different WARP adapters, one
+ // which is lying about being an integrated card. This is so that programs
+ // that ignore software adapters will actually run on headless/gpu-less machines.
+ //
+ // We don't want that and discorage that kind of filtering anyway, so we skip the integrated WARP.
+ if desc.VendorId == 5140 && (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) == 0 {
+ let adapter_name = super::conv::map_adapter_name(desc.Description);
+ if adapter_name.contains("Microsoft Basic Render Driver") {
+ return false;
+ }
+ }
+
+ true
+}
+
+pub fn enumerate_adapters(factory: d3d12::DxgiFactory) -> Vec<d3d12::DxgiAdapter> {
+ let mut adapters = Vec::with_capacity(8);
+
+ for cur_index in 0.. {
+ if let Some(factory6) = factory.as_factory6() {
+ profiling::scope!("IDXGIFactory6::EnumAdapterByGpuPreference");
+ // We're already at dxgi1.6, we can grab IDXGIAdapter4 directly
+ let mut adapter4 = d3d12::ComPtr::<dxgi1_6::IDXGIAdapter4>::null();
+ let hr = unsafe {
+ factory6.EnumAdapterByGpuPreference(
+ cur_index,
+ dxgi1_6::DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
+ &dxgi1_6::IDXGIAdapter4::uuidof(),
+ adapter4.mut_void(),
+ )
+ };
+
+ if hr == winerror::DXGI_ERROR_NOT_FOUND {
+ break;
+ }
+ if let Err(err) = hr.into_result() {
+ log::error!("Failed enumerating adapters: {}", err);
+ break;
+ }
+
+ if !should_keep_adapter(&adapter4) {
+ continue;
+ }
+
+ adapters.push(d3d12::DxgiAdapter::Adapter4(adapter4));
+ continue;
+ }
+
+ profiling::scope!("IDXGIFactory1::EnumAdapters1");
+ let mut adapter1 = d3d12::ComPtr::<dxgi::IDXGIAdapter1>::null();
+ let hr = unsafe { factory.EnumAdapters1(cur_index, adapter1.mut_self()) };
+
+ if hr == winerror::DXGI_ERROR_NOT_FOUND {
+ break;
+ }
+ if let Err(err) = hr.into_result() {
+ log::error!("Failed enumerating adapters: {}", err);
+ break;
+ }
+
+ if !should_keep_adapter(&adapter1) {
+ continue;
+ }
+
+ // Do the most aggressive casts first, skipping Adapter4 as we definitely don't have dxgi1_6.
+
+ // Adapter1 -> Adapter3
+ unsafe {
+ match adapter1.cast::<dxgi1_4::IDXGIAdapter3>().into_result() {
+ Ok(adapter3) => {
+ adapters.push(d3d12::DxgiAdapter::Adapter3(adapter3));
+ continue;
+ }
+ Err(err) => {
+ log::warn!("Failed casting Adapter1 to Adapter3: {}", err);
+ }
+ }
+ }
+
+ // Adapter1 -> Adapter2
+ unsafe {
+ match adapter1.cast::<dxgi1_2::IDXGIAdapter2>().into_result() {
+ Ok(adapter2) => {
+ adapters.push(d3d12::DxgiAdapter::Adapter2(adapter2));
+ continue;
+ }
+ Err(err) => {
+ log::warn!("Failed casting Adapter1 to Adapter2: {}", err);
+ }
+ }
+ }
+
+ adapters.push(d3d12::DxgiAdapter::Adapter1(adapter1));
+ }
+
+ adapters
+}
+
+/// Tries to create a IDXGIFactory6, then a IDXGIFactory4, then a IDXGIFactory2, then a IDXGIFactory1,
+/// returning the one that succeeds, or if the required_factory_type fails to be
+/// created.
+pub fn create_factory(
+ required_factory_type: DxgiFactoryType,
+ instance_flags: wgt::InstanceFlags,
+) -> Result<(d3d12::DxgiLib, d3d12::DxgiFactory), crate::InstanceError> {
+ let lib_dxgi = d3d12::DxgiLib::new().map_err(|e| {
+ crate::InstanceError::with_source(String::from("failed to load dxgi.dll"), e)
+ })?;
+
+ let mut factory_flags = d3d12::FactoryCreationFlags::empty();
+
+ if instance_flags.contains(wgt::InstanceFlags::VALIDATION) {
+ // The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to
+ // `CreateDXGIFactory2` if the debug interface is actually available. So
+ // we check for whether it exists first.
+ match lib_dxgi.get_debug_interface1() {
+ Ok(pair) => match pair.into_result() {
+ Ok(_debug_controller) => {
+ factory_flags |= d3d12::FactoryCreationFlags::DEBUG;
+ }
+ Err(err) => {
+ log::warn!("Unable to enable DXGI debug interface: {}", err);
+ }
+ },
+ Err(err) => {
+ log::warn!("Debug interface function for DXGI not found: {:?}", err);
+ }
+ }
+
+ // Intercept `OutputDebugString` calls
+ super::exception::register_exception_handler();
+ }
+
+ // Try to create IDXGIFactory4
+ let factory4 = match lib_dxgi.create_factory2(factory_flags) {
+ Ok(pair) => match pair.into_result() {
+ Ok(factory) => Some(factory),
+ // We hard error here as we _should have_ been able to make a factory4 but couldn't.
+ Err(err) => {
+ // err is a Cow<str>, not an Error implementor
+ return Err(crate::InstanceError::new(format!(
+ "failed to create IDXGIFactory4: {err:?}"
+ )));
+ }
+ },
+ // If we require factory4, hard error.
+ Err(err) if required_factory_type == DxgiFactoryType::Factory4 => {
+ return Err(crate::InstanceError::with_source(
+ String::from("IDXGIFactory1 creation function not found"),
+ err,
+ ));
+ }
+ // If we don't print it to warn as all win7 will hit this case.
+ Err(err) => {
+ log::warn!("IDXGIFactory1 creation function not found: {err:?}");
+ None
+ }
+ };
+
+ if let Some(factory4) = factory4 {
+ // Try to cast the IDXGIFactory4 into IDXGIFactory6
+ let factory6 = unsafe { factory4.cast::<dxgi1_6::IDXGIFactory6>().into_result() };
+ match factory6 {
+ Ok(factory6) => {
+ return Ok((lib_dxgi, d3d12::DxgiFactory::Factory6(factory6)));
+ }
+ // If we require factory6, hard error.
+ Err(err) if required_factory_type == DxgiFactoryType::Factory6 => {
+ // err is a Cow<str>, not an Error implementor
+ return Err(crate::InstanceError::new(format!(
+ "failed to cast IDXGIFactory4 to IDXGIFactory6: {err:?}"
+ )));
+ }
+ // If we don't print it to warn.
+ Err(err) => {
+ log::warn!("Failed to cast IDXGIFactory4 to IDXGIFactory6: {:?}", err);
+ return Ok((lib_dxgi, d3d12::DxgiFactory::Factory4(factory4)));
+ }
+ }
+ }
+
+ // Try to create IDXGIFactory1
+ let factory1 = match lib_dxgi.create_factory1() {
+ Ok(pair) => match pair.into_result() {
+ Ok(factory) => factory,
+ Err(err) => {
+ // err is a Cow<str>, not an Error implementor
+ return Err(crate::InstanceError::new(format!(
+ "failed to create IDXGIFactory1: {err:?}"
+ )));
+ }
+ },
+ // We always require at least factory1, so hard error
+ Err(err) => {
+ return Err(crate::InstanceError::with_source(
+ String::from("IDXGIFactory1 creation function not found"),
+ err,
+ ));
+ }
+ };
+
+ // Try to cast the IDXGIFactory1 into IDXGIFactory2
+ let factory2 = unsafe { factory1.cast::<dxgi1_2::IDXGIFactory2>().into_result() };
+ match factory2 {
+ Ok(factory2) => {
+ return Ok((lib_dxgi, d3d12::DxgiFactory::Factory2(factory2)));
+ }
+ // If we require factory2, hard error.
+ Err(err) if required_factory_type == DxgiFactoryType::Factory2 => {
+ // err is a Cow<str>, not an Error implementor
+ return Err(crate::InstanceError::new(format!(
+ "failed to cast IDXGIFactory1 to IDXGIFactory2: {err:?}"
+ )));
+ }
+ // If we don't print it to warn.
+ Err(err) => {
+ log::warn!("Failed to cast IDXGIFactory1 to IDXGIFactory2: {:?}", err);
+ }
+ }
+
+ // We tried to create 4 and 2, but only succeeded with 1.
+ Ok((lib_dxgi, d3d12::DxgiFactory::Factory1(factory1)))
+}
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs
new file mode 100644
index 0000000000..559969633c
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs
@@ -0,0 +1,5 @@
+pub mod conv;
+pub mod exception;
+pub mod factory;
+pub mod result;
+pub mod time;
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs
new file mode 100644
index 0000000000..db013d2dec
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs
@@ -0,0 +1,42 @@
+use std::borrow::Cow;
+
+use winapi::shared::winerror;
+
+pub(crate) trait HResult<O> {
+ fn into_result(self) -> Result<O, Cow<'static, str>>;
+ fn into_device_result(self, description: &str) -> Result<O, crate::DeviceError>;
+}
+impl HResult<()> for i32 {
+ fn into_result(self) -> Result<(), Cow<'static, str>> {
+ if self >= 0 {
+ return Ok(());
+ }
+ let description = match self {
+ winerror::E_UNEXPECTED => "unexpected",
+ winerror::E_NOTIMPL => "not implemented",
+ winerror::E_OUTOFMEMORY => "out of memory",
+ winerror::E_INVALIDARG => "invalid argument",
+ _ => return Err(Cow::Owned(format!("0x{:X}", self as u32))),
+ };
+ Err(Cow::Borrowed(description))
+ }
+ fn into_device_result(self, description: &str) -> Result<(), crate::DeviceError> {
+ self.into_result().map_err(|err| {
+ log::error!("{} failed: {}", description, err);
+ if self == winerror::E_OUTOFMEMORY {
+ crate::DeviceError::OutOfMemory
+ } else {
+ crate::DeviceError::Lost
+ }
+ })
+ }
+}
+
+impl<T> HResult<T> for (T, i32) {
+ fn into_result(self) -> Result<T, Cow<'static, str>> {
+ self.1.into_result().map(|()| self.0)
+ }
+ fn into_device_result(self, description: &str) -> Result<T, crate::DeviceError> {
+ self.1.into_device_result(description).map(|()| self.0)
+ }
+}
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs
new file mode 100644
index 0000000000..fd99c097d7
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs
@@ -0,0 +1,94 @@
+#![allow(dead_code)] // IPresentationManager is unused currently
+
+use std::mem;
+
+use winapi::um::{
+ profileapi::{QueryPerformanceCounter, QueryPerformanceFrequency},
+ winnt::LARGE_INTEGER,
+};
+
+pub enum PresentationTimer {
+ /// DXGI uses QueryPerformanceCounter
+ Dxgi {
+ /// How many ticks of QPC per second
+ frequency: u64,
+ },
+ /// IPresentationManager uses QueryInterruptTimePrecise
+ #[allow(non_snake_case)]
+ IPresentationManager {
+ fnQueryInterruptTimePrecise: unsafe extern "system" fn(*mut winapi::ctypes::c_ulonglong),
+ },
+}
+
+impl std::fmt::Debug for PresentationTimer {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match *self {
+ Self::Dxgi { frequency } => f
+ .debug_struct("DXGI")
+ .field("frequency", &frequency)
+ .finish(),
+ Self::IPresentationManager {
+ fnQueryInterruptTimePrecise,
+ } => f
+ .debug_struct("IPresentationManager")
+ .field(
+ "QueryInterruptTimePrecise",
+ &(fnQueryInterruptTimePrecise as usize),
+ )
+ .finish(),
+ }
+ }
+}
+
+impl PresentationTimer {
+ /// Create a presentation timer using QueryPerformanceFrequency (what DXGI uses for presentation times)
+ pub fn new_dxgi() -> Self {
+ let mut frequency: LARGE_INTEGER = unsafe { mem::zeroed() };
+ let success = unsafe { QueryPerformanceFrequency(&mut frequency) };
+ assert_ne!(success, 0);
+
+ Self::Dxgi {
+ frequency: unsafe { *frequency.QuadPart() } as u64,
+ }
+ }
+
+ /// Create a presentation timer using QueryInterruptTimePrecise (what IPresentationManager uses for presentation times)
+ ///
+ /// Panics if QueryInterruptTimePrecise isn't found (below Win10)
+ pub fn new_ipresentation_manager() -> Self {
+ // We need to load this explicitly, as QueryInterruptTimePrecise is only available on Windows 10+
+ //
+ // Docs say it's in kernel32.dll, but it's actually in kernelbase.dll.
+ let kernelbase =
+ libloading::os::windows::Library::open_already_loaded("kernelbase.dll").unwrap();
+ // No concerns about lifetimes here as kernelbase is always there.
+ let ptr = unsafe { kernelbase.get(b"QueryInterruptTimePrecise").unwrap() };
+ Self::IPresentationManager {
+ fnQueryInterruptTimePrecise: *ptr,
+ }
+ }
+
+ /// Gets the current time in nanoseconds.
+ pub fn get_timestamp_ns(&self) -> u128 {
+ // Always do u128 math _after_ hitting the timing function.
+ match *self {
+ PresentationTimer::Dxgi { frequency } => {
+ let mut counter: LARGE_INTEGER = unsafe { mem::zeroed() };
+ let success = unsafe { QueryPerformanceCounter(&mut counter) };
+ assert_ne!(success, 0);
+
+ // counter * (1_000_000_000 / freq) but re-ordered to make more precise
+ (unsafe { *counter.QuadPart() } as u128 * 1_000_000_000) / frequency as u128
+ }
+ PresentationTimer::IPresentationManager {
+ fnQueryInterruptTimePrecise,
+ } => {
+ let mut counter = 0;
+ unsafe { fnQueryInterruptTimePrecise(&mut counter) };
+
+ // QueryInterruptTimePrecise uses units of 100ns for its tick.
+ counter as u128 * 100
+ }
+ }
+ }
+}
diff --git a/third_party/rust/wgpu-hal/src/auxil/mod.rs b/third_party/rust/wgpu-hal/src/auxil/mod.rs
new file mode 100644
index 0000000000..f70a8bbe03
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/mod.rs
@@ -0,0 +1,138 @@
+#[cfg(dx12)]
+pub(super) mod dxgi;
+
+#[cfg(all(native, feature = "renderdoc"))]
+pub(super) mod renderdoc;
+
+pub mod db {
+ pub mod amd {
+ pub const VENDOR: u32 = 0x1002;
+ }
+ pub mod apple {
+ pub const VENDOR: u32 = 0x106B;
+ }
+ pub mod arm {
+ pub const VENDOR: u32 = 0x13B5;
+ }
+ pub mod broadcom {
+ pub const VENDOR: u32 = 0x14E4;
+ }
+ pub mod imgtec {
+ pub const VENDOR: u32 = 0x1010;
+ }
+ pub mod intel {
+ pub const VENDOR: u32 = 0x8086;
+ pub const DEVICE_KABY_LAKE_MASK: u32 = 0x5900;
+ pub const DEVICE_SKY_LAKE_MASK: u32 = 0x1900;
+ }
+ pub mod mesa {
+ // Mesa does not actually have a PCI vendor id.
+ //
+ // To match Vulkan, we use the VkVendorId for Mesa in the gles backend so that lavapipe (Vulkan) and
+ // llvmpipe (OpenGL) have the same vendor id.
+ pub const VENDOR: u32 = 0x10005;
+ }
+ pub mod nvidia {
+ pub const VENDOR: u32 = 0x10DE;
+ }
+ pub mod qualcomm {
+ pub const VENDOR: u32 = 0x5143;
+ }
+}
+
+/// Maximum binding size for the shaders that only support `i32` indexing.
+/// Interestingly, the index itself can't reach that high, because the minimum
+/// element size is 4 bytes, but the compiler toolchain still computes the
+/// offset at some intermediate point, internally, as i32.
+pub const MAX_I32_BINDING_SIZE: u32 = 1 << 31;
+
+pub fn map_naga_stage(stage: naga::ShaderStage) -> wgt::ShaderStages {
+ match stage {
+ naga::ShaderStage::Vertex => wgt::ShaderStages::VERTEX,
+ naga::ShaderStage::Fragment => wgt::ShaderStages::FRAGMENT,
+ naga::ShaderStage::Compute => wgt::ShaderStages::COMPUTE,
+ }
+}
+
+impl crate::CopyExtent {
+ pub fn map_extent_to_copy_size(extent: &wgt::Extent3d, dim: wgt::TextureDimension) -> Self {
+ Self {
+ width: extent.width,
+ height: extent.height,
+ depth: match dim {
+ wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => 1,
+ wgt::TextureDimension::D3 => extent.depth_or_array_layers,
+ },
+ }
+ }
+
+ pub fn min(&self, other: &Self) -> Self {
+ Self {
+ width: self.width.min(other.width),
+ height: self.height.min(other.height),
+ depth: self.depth.min(other.depth),
+ }
+ }
+
+ // Get the copy size at a specific mipmap level. This doesn't make most sense,
+ // since the copy extents are provided *for* a mipmap level to start with.
+ // But backends use `CopyExtent` more sparingly, and this piece is shared.
+ pub fn at_mip_level(&self, level: u32) -> Self {
+ Self {
+ width: (self.width >> level).max(1),
+ height: (self.height >> level).max(1),
+ depth: (self.depth >> level).max(1),
+ }
+ }
+}
+
+impl crate::TextureCopyBase {
+ pub fn max_copy_size(&self, full_size: &crate::CopyExtent) -> crate::CopyExtent {
+ let mip = full_size.at_mip_level(self.mip_level);
+ crate::CopyExtent {
+ width: mip.width - self.origin.x,
+ height: mip.height - self.origin.y,
+ depth: mip.depth - self.origin.z,
+ }
+ }
+}
+
+impl crate::BufferTextureCopy {
+ pub fn clamp_size_to_virtual(&mut self, full_size: &crate::CopyExtent) {
+ let max_size = self.texture_base.max_copy_size(full_size);
+ self.size = self.size.min(&max_size);
+ }
+}
+
+impl crate::TextureCopy {
+ pub fn clamp_size_to_virtual(
+ &mut self,
+ full_src_size: &crate::CopyExtent,
+ full_dst_size: &crate::CopyExtent,
+ ) {
+ let max_src_size = self.src_base.max_copy_size(full_src_size);
+ let max_dst_size = self.dst_base.max_copy_size(full_dst_size);
+ self.size = self.size.min(&max_src_size).min(&max_dst_size);
+ }
+}
+
+/// Construct a `CStr` from a byte slice, up to the first zero byte.
+///
+/// Return a `CStr` extending from the start of `bytes` up to and
+/// including the first zero byte. If there is no zero byte in
+/// `bytes`, return `None`.
+///
+/// This can be removed when `CStr::from_bytes_until_nul` is stabilized.
+/// ([#95027](https://github.com/rust-lang/rust/issues/95027))
+#[allow(dead_code)]
+pub(crate) fn cstr_from_bytes_until_nul(bytes: &[std::os::raw::c_char]) -> Option<&std::ffi::CStr> {
+ if bytes.contains(&0) {
+ // Safety for `CStr::from_ptr`:
+ // - We've ensured that the slice does contain a null terminator.
+ // - The range is valid to read, because the slice covers it.
+ // - The memory won't be changed, because the slice borrows it.
+ unsafe { Some(std::ffi::CStr::from_ptr(bytes.as_ptr())) }
+ } else {
+ None
+ }
+}
diff --git a/third_party/rust/wgpu-hal/src/auxil/renderdoc.rs b/third_party/rust/wgpu-hal/src/auxil/renderdoc.rs
new file mode 100644
index 0000000000..15b2c1039a
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/auxil/renderdoc.rs
@@ -0,0 +1,138 @@
+//! RenderDoc integration - <https://renderdoc.org/>
+
+use std::{ffi, os, ptr};
+
+/// The dynamically loaded RenderDoc API function table
+#[repr(C)]
+#[derive(Debug)]
+pub struct RenderDocApi {
+ api: renderdoc_sys::RENDERDOC_API_1_4_1,
+ lib: libloading::Library,
+}
+
+unsafe impl Send for RenderDocApi {}
+unsafe impl Sync for RenderDocApi {}
+
+/// RenderDoc API type
+#[derive(Debug)]
+pub enum RenderDoc {
+ /// RenderDoc functionality is available
+ Available {
+ /// RenderDoc API with function pointers
+ api: RenderDocApi,
+ },
+ /// RenderDoc functionality is _not_ available
+ NotAvailable {
+ /// A description why renderdoc functionality is not available
+ reason: String,
+ },
+}
+
+// TODO: replace with libloading API once supported
+#[cfg(unix)]
+const RTLD_NOLOAD: i32 = 0x4;
+
+impl RenderDoc {
+ pub unsafe fn new() -> Self {
+ type GetApiFn = unsafe extern "C" fn(version: u32, out: *mut *mut ffi::c_void) -> i32;
+
+ #[cfg(windows)]
+ let renderdoc_filename = "renderdoc.dll";
+ #[cfg(all(unix, not(target_os = "android")))]
+ let renderdoc_filename = "librenderdoc.so";
+ #[cfg(target_os = "android")]
+ let renderdoc_filename = "libVkLayer_GLES_RenderDoc.so";
+
+ #[cfg(unix)]
+ let renderdoc_result: Result<libloading::Library, libloading::Error> = unsafe {
+ libloading::os::unix::Library::open(
+ Some(renderdoc_filename),
+ libloading::os::unix::RTLD_NOW | RTLD_NOLOAD,
+ )
+ }
+ .map(|lib| lib.into());
+
+ #[cfg(windows)]
+ let renderdoc_result: Result<libloading::Library, libloading::Error> =
+ libloading::os::windows::Library::open_already_loaded(renderdoc_filename)
+ .map(|lib| lib.into());
+
+ let renderdoc_lib = match renderdoc_result {
+ Ok(lib) => lib,
+ Err(e) => {
+ return RenderDoc::NotAvailable {
+ reason: format!(
+ "Unable to load renderdoc library '{renderdoc_filename}': {e:?}"
+ ),
+ }
+ }
+ };
+
+ let get_api: libloading::Symbol<GetApiFn> =
+ match unsafe { renderdoc_lib.get(b"RENDERDOC_GetAPI\0") } {
+ Ok(api) => api,
+ Err(e) => {
+ return RenderDoc::NotAvailable {
+ reason: format!(
+ "Unable to get RENDERDOC_GetAPI from renderdoc library '{renderdoc_filename}': {e:?}"
+ ),
+ }
+ }
+ };
+ let mut obj = ptr::null_mut();
+ match unsafe { get_api(10401, &mut obj) } {
+ 1 => RenderDoc::Available {
+ api: RenderDocApi {
+ api: unsafe { *(obj as *mut renderdoc_sys::RENDERDOC_API_1_4_1) },
+ lib: renderdoc_lib,
+ },
+ },
+ return_value => RenderDoc::NotAvailable {
+ reason: format!(
+ "Unable to get API from renderdoc library '{renderdoc_filename}': {return_value}"
+ ),
+ },
+ }
+ }
+}
+
+impl Default for RenderDoc {
+ fn default() -> Self {
+ if !cfg!(debug_assertions) {
+ return RenderDoc::NotAvailable {
+ reason: "RenderDoc support is only enabled with 'debug_assertions'".into(),
+ };
+ }
+ unsafe { Self::new() }
+ }
+}
+/// A implementation specific handle
+pub type Handle = *mut os::raw::c_void;
+
+impl RenderDoc {
+ /// Start a RenderDoc frame capture
+ pub unsafe fn start_frame_capture(&self, device_handle: Handle, window_handle: Handle) -> bool {
+ match *self {
+ Self::Available { api: ref entry } => {
+ unsafe { entry.api.StartFrameCapture.unwrap()(device_handle, window_handle) };
+ true
+ }
+ Self::NotAvailable { ref reason } => {
+ log::warn!("Could not start RenderDoc frame capture: {}", reason);
+ false
+ }
+ }
+ }
+
+ /// End a RenderDoc frame capture
+ pub unsafe fn end_frame_capture(&self, device_handle: Handle, window_handle: Handle) {
+ match *self {
+ Self::Available { api: ref entry } => {
+ unsafe { entry.api.EndFrameCapture.unwrap()(device_handle, window_handle) };
+ }
+ Self::NotAvailable { ref reason } => {
+ log::warn!("Could not end RenderDoc frame capture: {}", reason)
+ }
+ };
+ }
+}