/// Will error with crate::DeviceError::Unexpected if DXGI 1.3 is not available. pubfn create_factory_media(&self) -> Result<Dxgi::IDXGIFactoryMedia, crate::DeviceError> { // Calls windows::Win32::Graphics::Dxgi::CreateDXGIFactory1 on dxgi.dll type Fun = extern"system"fn(
riid: *const windows_core::GUID,
ppfactory: *mut *mut core::ffi::c_void,
) -> windows_core::HRESULT; let func: libloading::Symbol<Fun> = unsafe { self.lib.get(b"CreateDXGIFactory1\0") }?;
/// Create a temporary "owned" copy inside a [`mem::ManuallyDrop`] without increasing the refcount or /// moving away the source variable. /// /// This is a common pattern when needing to pass interface pointers ("borrows") into Windows /// structs. Moving/cloning ownership is impossible/inconvenient because: /// /// - The caller does _not_ assume ownership (and decrement the refcount at a later time); /// - Unnecessarily increasing and decrementing the refcount; /// - [`Drop`] destructors cannot run inside `union` structures (when the created structure is /// implicitly dropped after a call). /// /// See also <https://github.com/microsoft/windows-rs/pull/2361#discussion_r1150799401> and /// <https://github.com/microsoft/windows-rs/issues/2386>. /// /// # Safety /// Performs a [`mem::transmute_copy()`] on a refcounted [`Interface`] type. The returned /// [`mem::ManuallyDrop`] should _not_ be dropped. pubunsafefn borrow_interface_temporarily<I: Interface>(src: &I) -> mem::ManuallyDrop<Option<I>> { unsafe { mem::transmute_copy(src) }
}
implcrate::Api for Api { type Instance = Instance; type Surface = Surface; type Adapter = Adapter; type Device = Device;
type Queue = Queue; type CommandEncoder = CommandEncoder; type CommandBuffer = CommandBuffer;
type Buffer = Buffer; type Texture = Texture; type SurfaceTexture = Texture; type TextureView = TextureView; type Sampler = Sampler; type QuerySet = QuerySet; type Fence = Fence;
type BindGroupLayout = BindGroupLayout; type BindGroup = BindGroup; type PipelineLayout = PipelineLayout; type ShaderModule = ShaderModule; type RenderPipeline = RenderPipeline; type ComputePipeline = ComputePipeline; type PipelineCache = PipelineCache;
type AccelerationStructure = AccelerationStructure;
}
impl Instance { pubunsafefn create_surface_from_visual(&self, visual: *mut ffi::c_void) -> Surface { let visual = unsafe { DirectComposition::IDCompositionVisual::from_raw_borrowed(&visual) }
.expect("COM pointer should not be NULL");
Surface {
factory: self.factory.clone(),
factory_media: self.factory_media.clone(),
target: SurfaceTarget::Visual(visual.to_owned()),
supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None),
}
}
pubunsafefn create_surface_from_surface_handle(
&self,
surface_handle: *mut ffi::c_void,
) -> Surface { // TODO: We're not given ownership, so we shouldn't call HANDLE::free(). This puts an extra burden on the caller to keep it alive. // https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle could help us, even though DirectComposition is not in the list? // Or we make all these types owned, require an ownership transition, and replace SurfaceTargetUnsafe with SurfaceTarget. let surface_handle = Foundation::HANDLE(surface_handle);
Surface {
factory: self.factory.clone(),
factory_media: self.factory_media.clone(),
target: SurfaceTarget::SurfaceHandle(surface_handle),
supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None),
}
}
pubunsafefn create_surface_from_swap_chain_panel(
&self,
swap_chain_panel: *mut ffi::c_void,
) -> Surface { let swap_chain_panel = unsafe { types::ISwapChainPanelNative::from_raw_borrowed(&swap_chain_panel) }
.expect("COM pointer should not be NULL");
Surface {
factory: self.factory.clone(),
factory_media: self.factory_media.clone(),
target: SurfaceTarget::SwapChainPanel(swap_chain_panel.to_owned()),
supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None),
}
}
}
unsafeimpl Send for Instance {} unsafeimpl Sync for Instance {}
struct SwapChain { // TODO: Drop order frees the SWC before the raw image pointers...?
raw: Dxgi::IDXGISwapChain3, // need to associate raw image pointers with the swapchain so they can be properly released // when the swapchain is destroyed
resources: Vec<Direct3D12::ID3D12Resource>, /// Handle is freed in [`Self::release_resources()`]
waitable: Foundation::HANDLE,
acquired_count: usize,
present_mode: wgt::PresentMode,
format: wgt::TextureFormat,
size: wgt::Extent3d,
}
#[derive(Default)] struct Workarounds { // On WARP, temporary CPU descriptors are still used by the runtime // after we call `CopyDescriptors`.
avoid_cpu_descriptor_overwrites: bool,
}
pubstruct Adapter {
raw: DxgiAdapter,
device: Direct3D12::ID3D12Device,
library: Arc<D3D12Lib>,
private_caps: PrivateCapabilities,
presentation_timer: auxil::dxgi::time::PresentationTimer, // Note: this isn't used right now, but we'll need it later. #[allow(unused)]
workarounds: Workarounds,
dxc_container: Option<Arc<shader_compilation::DxcContainer>>,
}
unsafeimpl Send for Adapter {} unsafeimpl Sync for Adapter {}
#[derive(Clone, Copy)] enum RootElement {
Empty,
Constant,
SpecialConstantBuffer { /// The first vertex in an indirect draw call, _or_ the `x` of a compute dispatch.
first_vertex: i32, /// The first instance in an indirect draw call, _or_ the `y` of a compute dispatch.
first_instance: u32, /// Unused in an indirect draw call, _or_ the `z` of a compute dispatch.
other: u32,
}, /// Descriptor table.
Table(Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE), /// Descriptor for a buffer that has dynamic offset.
DynamicOffsetBuffer {
kind: BufferViewKind,
address: Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE,
},
}
/// If set, the end of the next render/compute pass will write a timestamp at /// the given pool & location.
end_of_pass_timer_query: Option<(Direct3D12::ID3D12QueryHeap, u32)>,
counters: Arc<wgt::HalCounters>,
}
unsafeimpl Send for CommandEncoder {} unsafeimpl Sync for CommandEncoder {}
unsafeimpl Send for PipelineLayoutSpecialConstants {} unsafeimpl Sync for PipelineLayoutSpecialConstants {}
#[derive(Debug)] pubstruct PipelineLayout {
shared: PipelineLayoutShared, // Storing for each associated bind group, which tables we created // in the root signature. This is required for binding descriptor sets.
bind_group_infos: ArrayVec<BindGroupInfo, { crate::MAX_BIND_GROUPS }>,
naga_options: naga::back::hlsl::Options,
}
implcrate::DynPipelineLayout for PipelineLayout {}
unsafefn configure(
&self,
device: &Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> { letmut flags = Dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; // We always set ALLOW_TEARING on the swapchain no matter // what kind of swapchain we want because ResizeBuffers // cannot change the swapchain's ALLOW_TEARING flag. // // This does not change the behavior of the swapchain, just // allow present calls to use tearing. ifself.supports_allow_tearing {
flags |= Dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
}
// While `configure`s contract ensures that no work on the GPU's main queues // are in flight, we still need to wait for the present queue to be idle. unsafe { device.wait_for_present_queue_idle() }?;
let non_srgb_format = auxil::dxgi::conv::map_texture_format_nosrgb(config.format);
// Nvidia recommends to use 1-2 more buffers than the maximum latency // https://developer.nvidia.com/blog/advanced-api-performance-swap-chains/ // For high latency extra buffers seems excessive, so go with a minimum of 3 and beyond that add 1. let swap_chain_buffer = (config.maximum_frame_latency + 1).min(16);
let swap_chain = matchself.swap_chain.write().take() { //Note: this path doesn't properly re-initialize all of the things
Some(sc) => { let raw = unsafe { sc.release_resources() }; let result = unsafe {
raw.ResizeBuffers(
swap_chain_buffer,
config.extent.width,
config.extent.height,
non_srgb_format,
flags,
)
}; iflet Err(err) = result {
log::error!("ResizeBuffers failed: {err}"); return Err(crate::SurfaceError::Other("window is in use"));
}
raw
}
None => { let desc = Dxgi::DXGI_SWAP_CHAIN_DESC1 {
AlphaMode: auxil::dxgi::conv::map_acomposite_alpha_mode(
config.composite_alpha_mode,
),
Width: config.extent.width,
Height: config.extent.height,
Format: non_srgb_format,
Stereo: false.into(),
SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
BufferUsage: Dxgi::DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount: swap_chain_buffer,
Scaling: Dxgi::DXGI_SCALING_STRETCH,
SwapEffect: Dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD,
Flags: flags.0as u32,
}; let swap_chain1 = matchself.target {
SurfaceTarget::Visual(_) | SurfaceTarget::SwapChainPanel(_) => {
profiling::scope!("IDXGIFactory2::CreateSwapChainForComposition"); unsafe { self.factory.CreateSwapChainForComposition(
&device.present_queue,
&desc,
None,
)
}
}
SurfaceTarget::SurfaceHandle(handle) => {
profiling::scope!( "IDXGIFactoryMedia::CreateSwapChainForCompositionSurfaceHandle"
); unsafe { self.factory_media
.as_ref()
.ok_or(crate::SurfaceError::Other("IDXGIFactoryMedia not found"))?
.CreateSwapChainForCompositionSurfaceHandle(
&device.present_queue,
handle,
&desc,
None,
)
}
}
SurfaceTarget::WndHandle(hwnd) => {
profiling::scope!("IDXGIFactory2::CreateSwapChainForHwnd"); unsafe { self.factory.CreateSwapChainForHwnd(
&device.present_queue,
hwnd,
&desc,
None,
None,
)
}
}
};
swap_chain1.cast::<Dxgi::IDXGISwapChain3>().map_err(|err| {
log::error!("Unable to cast swapchain: {err}"); crate::SurfaceError::Other("swapchain cast to version 3")
})?
}
};
letmut resources = Vec::with_capacity(swap_chain_buffer as usize); for i in0..swap_chain_buffer { let resource = unsafe { swap_chain.GetBuffer(i) }
.into_device_result("Failed to get swapchain buffer")?;
resources.push(resource);
}
unsafefn unconfigure(&self, device: &Device) { iflet Some(sc) = self.swap_chain.write().take() { unsafe { // While `unconfigure`s contract ensures that no work on the GPU's main queues // are in flight, we still need to wait for the present queue to be idle.
// The major failure mode of this function is device loss, // which if we have lost the device, we should just continue // cleaning up, without error. let _ = device.wait_for_present_queue_idle();
let base_index = unsafe { sc.raw.GetCurrentBackBufferIndex() } as usize; let index = (base_index + sc.acquired_count) % sc.resources.len();
sc.acquired_count += 1;
// Note the lack of synchronization here between the main Direct queue // and the dedicated presentation queue. This is automatically handled // by the D3D runtime by detecting uses of resources derived from the // swapchain. This automatic detection is why you cannot use a swapchain // as an UAV in D3D12.
let (interval, flags) = match sc.present_mode { // We only allow immediate if ALLOW_TEARING is valid.
wgt::PresentMode::Immediate => (0, Dxgi::DXGI_PRESENT_ALLOW_TEARING),
wgt::PresentMode::Mailbox => (0, Dxgi::DXGI_PRESENT::default()),
wgt::PresentMode::Fifo => (1, Dxgi::DXGI_PRESENT::default()),
m => unreachable!("Cannot make surface with present mode {m:?}"),
};
unsafefn get_timestamp_period(&self) -> f32 { let frequency = unsafe { self.raw.GetTimestampFrequency() }.expect("GetTimestampFrequency");
(1_000_000_000.0 / frequency as f64) as f32
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-22)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.