use log::{debug, warn, Level}; use windows::Win32::{
Foundation::E_OUTOFMEMORY,
Graphics::{Direct3D12::*, Dxgi::Common::DXGI_FORMAT},
};
#[cfg(feature = "public-winapi")] mod public_winapi { pubuse winapi::um::d3d12 as winapi_d3d12;
usesuper::*;
/// Trait similar to [`AsRef`]/[`AsMut`], pubtrait ToWinapi<T> { fn as_winapi(&self) -> *const T; fn as_winapi_mut(&mutself) -> *mut T;
}
/// [`windows`] types hold their pointer internally and provide drop semantics. As such this trait /// is usually implemented on the _pointer type_ (`*const`, `*mut`) of the [`winapi`] object so that /// a **borrow of** that pointer becomes a borrow of the [`windows`] type. pubtrait ToWindows<T> { fn as_windows(&self) -> &T;
}
/// [`ResourceCategory`] is used for supporting [`D3D12_RESOURCE_HEAP_TIER_1`]. /// [`ResourceCategory`] will be ignored if device supports [`D3D12_RESOURCE_HEAP_TIER_2`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pubenum ResourceCategory {
Buffer,
RtvDsvTexture,
OtherTexture,
}
#[derive(Clone, Debug)] pubstruct AllocationCreateDesc<'a> { /// Name of the allocation, for tracking and debugging purposes pub name: &'a str, /// Location where the memory allocation should be stored pub location: MemoryLocation,
/// Size of allocation, should be queried using [`ID3D12Device::GetResourceAllocationInfo()`] pub size: u64, /// Alignment of allocation, should be queried using [`ID3D12Device::GetResourceAllocationInfo()`] pub alignment: u64, /// Resource category based on resource dimension and flags. Can be created from a [`D3D12_RESOURCE_DESC`] /// using the helper into function. The resource category is ignored when Resource Heap Tier 2 or higher /// is supported. pub resource_category: ResourceCategory,
}
impl<'a> AllocationCreateDesc<'a> { /// Helper conversion function utilizing [`winapi`] types. /// /// This function is also available for [`windows::Win32::Graphics::Direct3D12`] /// types as [`from_d3d12_resource_desc()`][Self::from_d3d12_resource_desc()]. #[cfg(feature = "public-winapi")] pubfn from_winapi_d3d12_resource_desc(
device: *const winapi_d3d12::ID3D12Device,
desc: &winapi_d3d12::D3D12_RESOURCE_DESC,
name: &'a str,
location: MemoryLocation,
) -> Self { let device = device.as_windows(); // Raw structs are binary-compatible let desc = unsafe {
std::mem::transmute::<&winapi_d3d12::D3D12_RESOURCE_DESC, &D3D12_RESOURCE_DESC>(desc)
}; let allocation_info = unsafe { device.GetResourceAllocationInfo(0, std::slice::from_ref(desc)) }; let resource_category: ResourceCategory = desc.into();
/// Helper conversion function utilizing [`windows::Win32::Graphics::Direct3D12`] types. /// /// This function is also available for `winapi` types as `from_winapi_d3d12_resource_desc()` /// when the `public-winapi` feature is enabled. pubfn from_d3d12_resource_desc(
device: &ID3D12Device,
desc: &D3D12_RESOURCE_DESC,
name: &'a str,
location: MemoryLocation,
) -> Self { let allocation_info = unsafe { device.GetResourceAllocationInfo(0, std::slice::from_ref(desc)) }; let resource_category: ResourceCategory = desc.into();
#[derive(Clone, Debug)] pubenum ID3D12DeviceVersion { /// Basic device compatible with legacy barriers only, i.e. can only be used in conjunction /// with [`ResourceStateOrBarrierLayout::ResourceState`].
Device(ID3D12Device), /// Required for enhanced barrier support, i.e. when using /// [`ResourceStateOrBarrierLayout::BarrierLayout`].
Device10(ID3D12Device10), /// Required for castable formats support, implies use of enhanced barriers
Device12(ID3D12Device12),
}
impl std::ops::Deref for ID3D12DeviceVersion { type Target = ID3D12Device;
impl Drop for Resource { fn drop(&mutself) { ifself.resource.is_some() {
warn!("Dropping resource `{}` that was not freed. Call `Allocator::free_resource(resource)` instead.", self.name);
}
}
}
/// Returns the [`ID3D12Heap`] object that is backing this allocation. /// This heap object can be shared with multiple other allocations and shouldn't be freed (or allocated from) /// without this library, because that will lead to undefined behavior. /// /// # Safety /// The result of this function be safely passed into [`ID3D12Device::CreatePlacedResource()`]. /// It is exposed for this reason. Keep in mind to also pass [`Self::offset()`] along to it. pubunsafefn heap(&self) -> &ID3D12Heap {
&self.heap
}
/// Returns the offset of the allocation on the [`ID3D12Heap`]. /// When creating a placed resources, this offset needs to be supplied as well. pubfn offset(&self) -> u64 { self.offset
}
/// Returns the size of the allocation pubfn size(&self) -> u64 { self.size
}
let size = desc.size; let alignment = desc.alignment;
// Create a dedicated block for large memory allocations if size > memblock_size { let mem_block = MemoryBlock::new(
device,
size,
&self.heap_properties, self.heap_category, true,
)?;
let block_index = self.memory_blocks.iter().position(|block| block.is_none()); let block_index = match block_index {
Some(i) => { self.memory_blocks[i].replace(mem_block);
i
}
None => { self.memory_blocks.push(Some(mem_block)); self.memory_blocks.len() - 1
}
};
let mem_block = self.memory_blocks[block_index]
.as_mut()
.ok_or_else(|| AllocationError::Internal("Memory block must be Some".into()))?;
let mem_block = self.memory_blocks[new_block_index]
.as_mut()
.ok_or_else(|| AllocationError::Internal("Memory block must be Some".into()))?; let allocation = mem_block.sub_allocator.allocate(
size,
alignment,
allocation_type, 1,
desc.name,
backtrace,
); let (offset, chunk_id) = match allocation {
Err(AllocationError::OutOfMemory) => Err(AllocationError::Internal( "Allocation that must succeed failed. This is a bug in the allocator.".into(),
)),
a => a,
}?;
if mem_block.sub_allocator.is_empty() { if mem_block.sub_allocator.supports_general_allocations() { ifself.active_general_blocks > 1 { let block = self.memory_blocks[block_idx].take(); if block.is_none() { return Err(AllocationError::Internal( "Memory block must be Some.".into(),
));
} // Note that `block` will be destroyed on `drop` here
self.active_general_blocks -= 1;
}
} else { let block = self.memory_blocks[block_idx].take(); if block.is_none() { return Err(AllocationError::Internal( "Memory block must be Some.".into(),
));
} // Note that `block` will be destroyed on `drop` here
}
}
let mem_type = &mutself.memory_types[allocation.memory_type_index]; let mem_block = mem_type.memory_blocks[allocation.memory_block_index]
.as_mut()
.ok_or_else(|| AllocationError::Internal("Memory block must be Some.".into()))?;
// We always have one resource desc, hence we only have one mapping castable format array let num_castable_formats = desc.castable_formats.len() as u32; let num_castable_formats_array = &[num_castable_formats];
let castable_formats_array = &[desc.castable_formats.as_ptr()];
let (num_castable_formats_opt, castable_formats_opt) = if num_castable_formats > 0 {
(
Some(num_castable_formats_array.as_ptr()),
Some(castable_formats_array.as_ptr()),
)
} else {
(None, None)
};
/// Create a resource according to the provided parameters. /// Created resources should be freed at the end of their lifetime by calling [`Self::free_resource()`]. pubfn create_resource(&mutself, desc: &ResourceCreateDesc<'_>) -> Result<Resource> { match desc.resource_type {
ResourceType::Committed {
heap_properties,
heap_flags,
} => { letmut result: Option<ID3D12Resource> = None;
let clear_value: Option<*const D3D12_CLEAR_VALUE> =
desc.clear_value.map(|v| -> *const _ { v });
let resource = result.expect("Allocation succeeded but no resource was returned?"); let size = allocation.size();
Ok(Resource {
name: desc.name.into(),
allocation: Some(allocation),
resource: Some(resource),
size,
memory_location: desc.memory_location,
memory_type_index: None,
})
}
}
}
/// Free a resource and its memory. pubfn free_resource(&mutself, mut resource: Resource) -> Result<()> { // Explicitly drop the resource (which is backed by a refcounted COM object) // before freeing allocated memory. Windows-rs performs a Release() on drop(). let _ = resource
.resource
.take()
.expect("Resource was already freed.");
iflet Some(allocation) = resource.allocation.take() { self.free(allocation)
} else { // Dx12 CommittedResources do not have an application managed allocation. // We only have to update the tracked allocation count and memory usage. iflet Some(memory_type_index) = resource.memory_type_index { let memory_type = &mutself.memory_types[memory_type_index];
impl Drop for Allocator { fn drop(&mutself) { ifself.debug_settings.log_leaks_on_shutdown { self.report_memory_leaks(Level::Warn);
}
// Because Rust drop rules drop members in source-code order (that would be the // ID3D12Device before the ID3D12Heaps nested in these memory blocks), free // all remaining memory blocks manually first by dropping. for mem_type inself.memory_types.iter_mut() {
mem_type.memory_blocks.clear();
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-28)
¤
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.