//! We try to use pipeline stream descriptors where possible, but this isn't allowed //! on some older windows 10 versions. Therefore, we also must have some logic to //! convert such descriptors to the "traditional" equivalent, //! `D3D12_GRAPHICS_PIPELINE_STATE_DESC`. //! //! Stream descriptors allow extending the pipeline, enabling more advanced features, //! including mesh shaders and multiview/view instancing. Using a stream descriptor //! is like using a vulkan descriptor with a `pNext` chain. It doesn't have direct //! benefits to all use cases, but allows new use cases. //! //! The code for pipeline stream descriptors is very complicated, and can have bad //! consequences if it is written incorrectly. It has been isolated to this file for //! that reason.
use core::{ffi::c_void, mem::ManuallyDrop, ptr::NonNull};
use alloc::vec::Vec; use windows::Win32::Graphics::Direct3D12::*; use windows::Win32::Graphics::Dxgi::Common::*; use windows_core::Interface;
usecrate::dx12::borrow_interface_temporarily;
// Wrapper newtypes for various pipeline subobjects which // use complicated or non-unique representations.
#[repr(transparent)] #[derive(Copy, Clone)] // Option<NonNull<c_void>> is guaranteed to have the same representation as a raw pointer. struct RootSignature(Option<NonNull<c_void>>);
/// Trait for types that can be used as subobjects in a pipeline state stream. /// /// Safety: /// - The type must be the correct alignment and size for the subobject it represents. /// - The type must map to exactly one `D3D12_PIPELINE_STATE_SUBOBJECT_TYPE` variant. /// - The variant must correctly represent the type's role in the pipeline state stream. /// - The type must be `Copy` to ensure safe duplication in the stream. /// - The type must be valid to memcpy into the pipeline state stream. unsafetrait RenderPipelineStreamObject: Copy { const SUBOBJECT_TYPE: D3D12_PIPELINE_STATE_SUBOBJECT_TYPE;
}
/// Implementaation of a pipeline state stream, which is a sequence of subobjects put into /// a byte array according to some basic alignment rules. /// /// Each subobject must start on an 8 byte boundary. Each subobject contains a 32 bit /// type identifier, followed by the actual subobject data, aligned as required by the /// subobject's structure. /// /// See <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_pipeline_state_stream_desc> /// for more information. pub(super) struct RenderPipelineStateStream<'a> {
bytes: Vec<u8>,
_marker: core::marker::PhantomData<&'a ()>,
}
impl<'a> RenderPipelineStateStream<'a> { fn new() -> Self { // Dynamic allocation is used here because the resulting stream can become very large. // We pre-allocate the size based on an estimate of the size of the struct plus some extra space // per member for tags and alignment padding. In practice this will always be too big, as not // all members will be used. let size_of_stream_desc = size_of::<RenderPipelineStateStreamDesc>(); let members = 20; // Approximate number of members we might push let capacity = size_of_stream_desc + members * 8; // Extra space for tags and alignment Self {
bytes: Vec::with_capacity(capacity),
_marker: core::marker::PhantomData,
}
}
/// Align the internal byte buffer to the given alignment, /// padding with zeros as necessary. fn align_to(&mutself, alignment: usize) { let aligned_length = self.bytes.len().next_multiple_of(alignment); self.bytes.resize(aligned_length, 0);
}
/// Adds a subobject to the pipeline state stream. fn add_object<T: RenderPipelineStreamObject>(&mutself, object: T) { // Ensure 8-byte alignment for the subobject start. self.align_to(8);
// Append the type tag (u32) let tag: u32 = T::SUBOBJECT_TYPE.0as u32; self.bytes.extend_from_slice(&tag.to_ne_bytes());
// Align the data to its natural alignment. self.align_to(align_of_val::<T>(&object));
// Append the data itself, as raw bytes let data_ptr: *const T = &object; let data_u8_ptr: *const u8 = data_ptr.cast::<u8>(); let data_size = size_of_val::<T>(&object); let slice = unsafe { core::slice::from_raw_parts::<u8>(data_u8_ptr, data_size) }; self.bytes.extend_from_slice(slice);
}
/// Creates a pipeline state object from the stream. /// /// Safety: /// - All unsafety invariants required by [`ID3D12Device2::CreatePipelineState`] must be upheld by the caller. pubunsafefn create_pipeline_state(
&mutself,
device: &ID3D12Device2,
) -> windows::core::Result<ID3D12PipelineState> { let stream_desc = D3D12_PIPELINE_STATE_STREAM_DESC {
SizeInBytes: self.bytes.len(),
pPipelineStateSubobjectStream: self.bytes.as_mut_ptr().cast(),
};
// Safety: lifetime on Self preserved the contents // of the stream. Other unsafety invariants are upheld by the caller. unsafe { device.CreatePipelineState(&stream_desc) }
}
}
// Importantly here, the ID3D12RootSignature _itself_ is the pointer we're // trying to serialize into the stream, not a pointer to the pointer. // // This is correct because as_raw() returns turns that smart object into the raw // pointer that _is_ the com object handle. let root_sig_pointer = self
.root_signature
.map(|a| NonNull::new(a.as_raw()).unwrap()); // Because the stream object borrows from self for its entire lifetime, // it is safe to store the pointer into it.
stream.add_object(RootSignature(root_sig_pointer));
// Tag at the beginning
assert_eq!(&stream.bytes[0..4], &1u32.to_ne_bytes()); // Data tucked in, aligned to the natural alignment of u16
assert_eq!(&stream.bytes[4..6], &42u16.to_ne_bytes()); // Padding to align the next subobject to an 8 byte boundary.
assert_eq!(&stream.bytes[6..8], &[0, 0]);
// Object 2: u32
// Tag at the beginning
assert_eq!(&stream.bytes[8..12], &2u32.to_ne_bytes()); // Data tucked in, aligned to the natural alignment of u32
assert_eq!(&stream.bytes[12..16], &84u32.to_ne_bytes());
// Object 3: u64
// Tag at the beginning
assert_eq!(&stream.bytes[16..20], &3u32.to_ne_bytes()); // Padding to align the u64 to an 8 byte boundary.
assert_eq!(&stream.bytes[20..24], &[0, 0, 0, 0]); // Data tucked in, aligned to the natural alignment of u64
assert_eq!(&stream.bytes[24..32], &168u64.to_ne_bytes());
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-08-25)
¤
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.