Lazy-load the dcomp library (#8216)

This commit is contained in:
Andy Leiserson 2025-09-15 11:48:28 -07:00 committed by GitHub
parent 04a3401638
commit aee8cd1650
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 84 additions and 12 deletions

1
Cargo.lock generated
View File

@ -5126,6 +5126,7 @@ dependencies = [
"naga",
"ndk-sys 0.6.0+11769913",
"objc",
"once_cell",
"ordered-float 5.0.0",
"parking_lot",
"portable-atomic",

View File

@ -136,6 +136,7 @@ dx12 = [
"dep:libloading",
"dep:log",
"dep:ordered-float",
"dep:once_cell",
"dep:parking_lot",
"dep:profiling",
"dep:range-alloc",
@ -258,6 +259,7 @@ windows-core = { workspace = true, optional = true }
bit-set = { workspace = true, optional = true }
range-alloc = { workspace = true, optional = true }
gpu-allocator = { workspace = true, optional = true }
once_cell = { workspace = true, optional = true }
# backend: GLES
glutin_wgl_sys = { workspace = true, optional = true }

View File

@ -17,7 +17,7 @@ use crate::{
self,
dxgi::{factory::DxgiAdapter, result::HResult},
},
dx12::{shader_compilation, SurfaceTarget},
dx12::{dcomp::DCompLib, shader_compilation, SurfaceTarget},
};
impl Drop for super::Adapter {
@ -55,6 +55,7 @@ impl super::Adapter {
pub(super) fn expose(
adapter: DxgiAdapter,
library: &Arc<D3D12Lib>,
dcomp_lib: &Arc<DCompLib>,
instance_flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
@ -559,6 +560,7 @@ impl super::Adapter {
raw: adapter,
device,
library: Arc::clone(library),
dcomp_lib: Arc::clone(dcomp_lib),
private_caps,
presentation_timer,
workarounds,
@ -726,6 +728,7 @@ impl crate::Adapter for super::Adapter {
memory_hints,
self.private_caps,
&self.library,
&self.dcomp_lib,
self.memory_budget_thresholds,
self.compiler_container.clone(),
self.options.clone(),

View File

@ -1,4 +1,65 @@
use alloc::sync::Arc;
use core::{ffi, ptr};
use once_cell::sync::Lazy;
use windows::Win32::{Foundation::HWND, Graphics::DirectComposition};
use windows_core::Interface;
use super::DynLib;
// Lazy-loaded DirectComposition library
#[derive(Debug)]
pub(crate) struct DCompLib {
lib: Lazy<Result<DynLib, crate::SurfaceError>>,
}
impl DCompLib {
pub(crate) fn new() -> Self {
Self {
lib: Lazy::new(|| unsafe {
DynLib::new("dcomp.dll").map_err(|err| {
log::error!("Error loading dcomp.dll: {err}");
crate::SurfaceError::Other("Error loading dcomp.dll")
})
}),
}
}
fn get_lib(&self) -> Result<&DynLib, crate::SurfaceError> {
match self.lib.as_ref() {
Ok(lib) => Ok(lib),
Err(err) => Err(err.clone()),
}
}
pub(crate) fn create_device(
&self,
) -> Result<DirectComposition::IDCompositionDevice, crate::SurfaceError> {
let lib = self.get_lib()?;
// Calls windows::Win32::Graphics::DirectComposition::DCompositionCreateDevice2 on dcomp.dll
type Fun = extern "system" fn(
pdxdevice: *mut ffi::c_void,
riid: *const windows_core::GUID,
ppdcompdevice: *mut *mut ffi::c_void,
) -> windows_core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { lib.get(c"DCompositionCreateDevice2".to_bytes()) }?;
let mut res: Option<DirectComposition::IDCompositionDevice> = None;
(func)(
ptr::null_mut(),
&DirectComposition::IDCompositionDevice::IID,
<*mut _>::cast(&mut res),
)
.map(|| res.unwrap())
.map_err(|err| {
log::error!("DirectComposition::DCompositionCreateDevice2 failed: {err}");
crate::SurfaceError::Other("DirectComposition::DCompositionCreateDevice2")
})
}
}
#[derive(Default)]
pub struct DCompState {
@ -10,10 +71,11 @@ impl DCompState {
/// If the device is already initialized, it will return the existing state.
pub unsafe fn get_or_init(
&mut self,
lib: &Arc<DCompLib>,
hwnd: &HWND,
) -> Result<&mut InnerState, crate::SurfaceError> {
if self.inner.is_none() {
self.inner = Some(unsafe { InnerState::init(hwnd) }?);
self.inner = Some(unsafe { InnerState::init(lib, hwnd) }?);
}
Ok(self.inner.as_mut().unwrap())
}
@ -28,14 +90,9 @@ pub struct InnerState {
impl InnerState {
/// Creates a DirectComposition device and a target for the given window handle.
pub unsafe fn init(hwnd: &HWND) -> Result<Self, crate::SurfaceError> {
pub unsafe fn init(lib: &Arc<DCompLib>, hwnd: &HWND) -> Result<Self, crate::SurfaceError> {
profiling::scope!("DCompState::init");
let dcomp_device: DirectComposition::IDCompositionDevice = {
unsafe { DirectComposition::DCompositionCreateDevice2(None) }.map_err(|err| {
log::error!("DirectComposition::DCompositionCreateDevice failed: {err}");
crate::SurfaceError::Other("DirectComposition::DCompositionCreateDevice")
})?
};
let dcomp_device = lib.create_device()?;
let target = unsafe { dcomp_device.CreateTargetForHwnd(*hwnd, false) }.map_err(|err| {
log::error!("IDCompositionDevice::CreateTargetForHwnd failed: {err}");

View File

@ -26,7 +26,7 @@ use crate::{
dxgi::{name::ObjectExt, result::HResult},
},
dx12::{
borrow_optional_interface_temporarily, shader_compilation, suballocation,
borrow_optional_interface_temporarily, shader_compilation, suballocation, DCompLib,
DynamicStorageBufferOffsets, Event, ShaderCacheKey, ShaderCacheValue,
},
AccelerationStructureEntries, TlasInstance,
@ -46,6 +46,7 @@ impl super::Device {
memory_hints: &wgt::MemoryHints,
private_caps: super::PrivateCapabilities,
library: &Arc<D3D12Lib>,
dcomp_lib: &Arc<DCompLib>,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
backend_options: wgt::Dx12BackendOptions,
@ -201,6 +202,7 @@ impl super::Device {
)),
options: backend_options,
library: Arc::clone(library),
dcomp_lib: Arc::clone(dcomp_lib),
#[cfg(feature = "renderdoc")]
render_doc: Default::default(),
null_rtv_handle,

View File

@ -12,7 +12,7 @@ use windows::{
use super::SurfaceTarget;
use crate::{
auxil,
dx12::{shader_compilation::CompilerContainer, D3D12Lib},
dx12::{shader_compilation::CompilerContainer, D3D12Lib, DCompLib},
};
impl crate::Instance for super::Instance {
@ -104,6 +104,7 @@ impl crate::Instance for super::Instance {
factory,
factory_media,
library: Arc::new(lib_main),
dcomp_lib: Arc::new(DCompLib::new()),
presentation_system: desc.backend_options.dx12.presentation_system,
_lib_dxgi: lib_dxgi,
supports_allow_tearing,
@ -158,6 +159,7 @@ impl crate::Instance for super::Instance {
super::Adapter::expose(
raw,
&self.library,
&self.dcomp_lib,
self.flags,
self.memory_budget_thresholds,
self.compiler_container.clone(),

View File

@ -101,6 +101,7 @@ use windows::{
},
};
use self::dcomp::DCompLib;
use crate::auxil::{
self,
dxgi::{
@ -460,6 +461,7 @@ pub struct Instance {
factory: DxgiFactory,
factory_media: Option<Dxgi::IDXGIFactoryMedia>,
library: Arc<D3D12Lib>,
dcomp_lib: Arc<DCompLib>,
supports_allow_tearing: bool,
presentation_system: wgt::Dx12SwapchainKind,
_lib_dxgi: DxgiLib,
@ -612,6 +614,7 @@ pub struct Adapter {
raw: DxgiAdapter,
device: Direct3D12::ID3D12Device,
library: Arc<D3D12Lib>,
dcomp_lib: Arc<DCompLib>,
private_caps: PrivateCapabilities,
presentation_timer: auxil::dxgi::time::PresentationTimer,
// Note: this isn't used right now, but we'll need it later.
@ -685,6 +688,7 @@ pub struct Device {
srv_uav_pool: Mutex<descriptor::CpuPool>,
// library
library: Arc<D3D12Lib>,
dcomp_lib: Arc<DCompLib>,
#[cfg(feature = "renderdoc")]
render_doc: auxil::renderdoc::RenderDoc,
null_rtv_handle: descriptor::Handle,
@ -1358,7 +1362,8 @@ impl crate::Surface for Surface {
dcomp_state,
} => {
let mut dcomp_state = dcomp_state.lock();
let dcomp_state = unsafe { dcomp_state.get_or_init(handle) }?;
let dcomp_state =
unsafe { dcomp_state.get_or_init(&device.dcomp_lib, handle) }?;
// Set the new swap chain as the content for the backing visual
// and commit the changes to the composition visual tree.
{