diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 93583303d5..2aa0e43f0f 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -54,10 +54,10 @@ hal = { path = "../wgpu-hal", package = "wgpu-hal", features = ["metal"] } #Note: could also enable "vulkan" for Vulkan Portability [target.'cfg(all(not(target_arch = "wasm32"), unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies] -hal = { path = "../wgpu-hal", package = "wgpu-hal", features = ["vulkan", "gles"] } +hal = { path = "../wgpu-hal", package = "wgpu-hal", features = ["vulkan", "gles", "renderdoc"] } [target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies] -hal = { path = "../wgpu-hal", package = "wgpu-hal", features = ["vulkan", "dx12"] } +hal = { path = "../wgpu-hal", package = "wgpu-hal", features = ["vulkan", "dx12", "renderdoc"] } [build-dependencies] cfg_aliases = "0.1" diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index a38f8c8753..d554353ad5 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -2268,7 +2268,7 @@ impl Device { } hal::PipelineError::EntryPoint(stage) => { pipeline::CreateRenderPipelineError::Internal { - stage: hal::util::map_naga_stage(stage), + stage: hal::auxil::map_naga_stage(stage), error: EP_FAILURE.to_string(), } } diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index dd968b33fe..63e81dc2b0 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -14,9 +14,10 @@ license = "MIT OR Apache-2.0" [features] default = [] metal = ["naga/msl-out", "block", "foreign-types"] -vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "inplace_it", "renderdoc-sys"] +vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "inplace_it"] gles = ["naga/glsl-out", "glow", "egl", "libloading"] dx12 = ["naga/hlsl-out", "native", "bit-set", "range-alloc", "winapi/d3d12", "winapi/d3d12shader", "winapi/d3d12sdklayers", "winapi/dxgi1_6"] +renderdoc = ["libloading", "renderdoc-sys"] [dependencies] bitflags = "1.0" @@ -29,17 +30,21 @@ wgt = { package = "wgpu-types", path = "../wgpu-types" } arrayvec = "0.7" fxhash = "0.2.1" log = "0.4" +renderdoc-sys = { version = "0.7.1", optional = true } + # backend: Metal block = { version = "0.1", optional = true } foreign-types = { version = "0.3", optional = true } + # backend: Vulkan ash = { version = "0.32", optional = true } gpu-alloc = { version = "0.4", optional = true } gpu-descriptor = { version = "0.1", optional = true } inplace_it = { version ="0.3.3", optional = true } -renderdoc-sys = { version = "0.7.1", optional = true } + # backend: Gles glow = { git = "https://github.com/grovesNL/glow", rev = "0864897a28bbdd43f89f4fd8fdd4ed781b719f8a", optional = true } + # backend: Dx12 bit-set = { version = "0.5", optional = true } native = { package = "d3d12", version = "0.4", features = ["libloading"], optional = true } @@ -47,6 +52,7 @@ range-alloc = { version = "0.1", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] egl = { package = "khronos-egl", version = "4.1", features = ["dynamic"], optional = true } +#Note: it's only unused on Apple platforms libloading = { version = "0.7", optional = true } [target.'cfg(windows)'.dependencies] diff --git a/wgpu-hal/src/util.rs b/wgpu-hal/src/auxil/mod.rs similarity index 89% rename from wgpu-hal/src/util.rs rename to wgpu-hal/src/auxil/mod.rs index 0f76810cf1..bf4470f8ec 100644 --- a/wgpu-hal/src/util.rs +++ b/wgpu-hal/src/auxil/mod.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "renderdoc")] +pub(super) mod renderdoc; + pub mod db { pub mod intel { pub const VENDOR: u32 = 0x8086; diff --git a/wgpu-hal/src/auxil/renderdoc.rs b/wgpu-hal/src/auxil/renderdoc.rs new file mode 100644 index 0000000000..cbe3c1908d --- /dev/null +++ b/wgpu-hal/src/auxil/renderdoc.rs @@ -0,0 +1,122 @@ +//! RenderDoc integration - + +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, + }, +} + +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"; + + let renderdoc_lib = match libloading::Library::new(renderdoc_filename) { + Ok(lib) => lib, + Err(e) => { + return RenderDoc::NotAvailable { + reason: format!( + "Unable to load renderdoc library '{}': {:?}", + renderdoc_filename, e + ), + } + } + }; + + let get_api: libloading::Symbol = match 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 get_api(10401, &mut obj) { + 1 => RenderDoc::Available { + api: RenderDocApi { + api: *(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 } => { + 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 } => { + entry.api.EndFrameCapture.unwrap()(device_handle, window_handle); + } + Self::NotAvailable { ref reason } => { + log::warn!("Could not end RenderDoc frame capture: {}", reason) + } + }; + } +} diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index bd475e7182..2543932e69 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -147,6 +147,8 @@ impl super::Device { native::DescriptorHeapType::Sampler, )), library: Arc::clone(library), + #[cfg(feature = "renderdoc")] + render_doc: Default::default(), }) } @@ -522,7 +524,7 @@ impl super::Device { ) -> Result { use naga::back::hlsl; - let stage_bit = crate::util::map_naga_stage(naga_stage); + let stage_bit = crate::auxil::map_naga_stage(naga_stage); let module = &stage.module.naga.module; //TODO: reuse the writer let mut source = String::new(); @@ -1664,7 +1666,18 @@ impl crate::Device for super::Device { } unsafe fn start_capture(&self) -> bool { + #[cfg(feature = "renderdoc")] + { + self.render_doc + .start_frame_capture(self.raw.as_mut_ptr() as *mut _, ptr::null_mut()) + } + #[cfg(not(feature = "renderdoc"))] false } - unsafe fn stop_capture(&self) {} + + unsafe fn stop_capture(&self) { + #[cfg(feature = "renderdoc")] + self.render_doc + .end_frame_capture(self.raw.as_mut_ptr() as *mut _, ptr::null_mut()) + } } diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 8dea5cc9e4..7e134d8b7e 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -212,6 +212,8 @@ pub struct Device { sampler_pool: Mutex, // library library: Arc, + #[cfg(feature = "renderdoc")] + render_doc: crate::auxil::renderdoc::RenderDoc, } unsafe impl Send for Device {} diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 153755d8f9..2b68227f16 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -353,6 +353,8 @@ impl crate::Adapter for super::Adapter { device: super::Device { shared: Arc::clone(&self.shared), main_vao, + #[cfg(feature = "renderdoc")] + render_doc: Default::default(), }, queue: super::Queue { shared: Arc::clone(&self.shared), diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index f04cb4fe28..926e9ae168 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -1,7 +1,7 @@ use super::conv; -use crate::util::map_naga_stage; +use crate::auxil::map_naga_stage; use glow::HasContext; -use std::{convert::TryInto, iter, ptr::NonNull, sync::Arc}; +use std::{convert::TryInto, iter, ptr, sync::Arc}; type ShaderStage<'a> = ( naga::ShaderStage, @@ -378,7 +378,7 @@ impl crate::Device for super::Device { gl.bind_buffer(buffer.target, None); Ok(crate::BufferMapping { - ptr: NonNull::new(ptr).ok_or(crate::DeviceError::Lost)?, + ptr: ptr::NonNull::new(ptr).ok_or(crate::DeviceError::Lost)?, is_coherent, }) } @@ -1017,7 +1017,18 @@ impl crate::Device for super::Device { } unsafe fn start_capture(&self) -> bool { + #[cfg(feature = "renderdoc")] + { + //Note: it doesn't look like the device pointer is used by RD + self.render_doc + .start_frame_capture(ptr::null_mut(), ptr::null_mut()) + } + #[cfg(not(feature = "renderdoc"))] false } - unsafe fn stop_capture(&self) {} + unsafe fn stop_capture(&self) { + #[cfg(feature = "renderdoc")] + self.render_doc + .end_frame_capture(ptr::null_mut(), ptr::null_mut()) + } } diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index cd324cc8a6..59903324f8 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -158,6 +158,8 @@ pub struct Adapter { pub struct Device { shared: Arc, main_vao: glow::VertexArray, + #[cfg(feature = "renderdoc")] + render_doc: crate::auxil::renderdoc::RenderDoc, } pub struct Queue { diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index e79ab9b8d3..80737c8d72 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -56,7 +56,7 @@ mod metal; #[cfg(feature = "vulkan")] mod vulkan; -pub mod util; +pub mod auxil; pub mod api { #[cfg(feature = "dx12")] pub use super::dx12::Api as Dx12; diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index 358de98e5f..0e68ceac2b 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -5,7 +5,7 @@ use std::{ }; use super::conv; -use crate::util::map_naga_stage; +use crate::auxil::map_naga_stage; type DeviceResult = Result; diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index a7ba07f7cd..9ab56c1ddd 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -618,7 +618,7 @@ impl super::Instance { let (available_features, downlevel_flags) = phd_features.to_wgpu(&phd_capabilities); { - use crate::util::db; + use crate::auxil::db; // see https://github.com/gfx-rs/gfx/issues/1930 let _is_windows_intel_dual_src_bug = cfg!(windows) && phd_capabilities.properties.vendor_id == db::intel::VENDOR @@ -897,6 +897,8 @@ impl crate::Adapter for super::Adapter { desc_allocator: Mutex::new(desc_allocator), valid_ash_memory_types, naga_options, + #[cfg(feature = "renderdoc")] + render_doc: Default::default(), }; Ok(crate::OpenDevice { device, queue }) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 152c717293..0b4bb129b1 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1501,9 +1501,23 @@ impl crate::Device for super::Device { } unsafe fn start_capture(&self) -> bool { + #[cfg(feature = "renderdoc")] + { + self.render_doc.start_frame_capture( + ash::vk::Handle::as_raw(self.shared.raw.handle()) as *mut _, + ptr::null_mut(), + ) + } + #[cfg(not(feature = "renderdoc"))] false } - unsafe fn stop_capture(&self) {} + unsafe fn stop_capture(&self) { + #[cfg(feature = "renderdoc")] + self.render_doc.end_frame_capture( + ash::vk::Handle::as_raw(self.shared.raw.handle()) as *mut _, + ptr::null_mut(), + ) + } } impl From for crate::DeviceError { diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 39261edbed..763917844a 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -626,7 +626,7 @@ impl crate::Surface for super::Surface { }; // special case for Intel Vulkan returning bizzare values (ugh) - if sc.device.vendor_id == crate::util::db::intel::VENDOR && index > 0x100 { + if sc.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 { return Err(crate::SurfaceError::Outdated); } diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index e29c37f83b..8171dc3ce4 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -231,6 +231,8 @@ pub struct Device { Mutex>, valid_ash_memory_types: u32, naga_options: naga::back::spv::Options, + #[cfg(feature = "renderdoc")] + render_doc: crate::auxil::renderdoc::RenderDoc, } pub struct Queue {