Vendor wgpu-hal 29.0.4 and i-slint-renderer-skia 1.17.1, unmodified

The Android develop view reads its frame back through memory (TD-1)
because wgpu's Vulkan swapchain never pre-rotates, and a portrait window
on this tablet's landscape panel then tears. The fix is a small patch to
each of these two crates, and this commit is only the ground it lands on:
both are byte-for-byte the crates.io sources the lockfile already
resolved, so the commits that follow are the patch and nothing else.

third_party/ is excluded from the workspace, or every path dependency
under the root would become a member and `--workspace` would test and
lint upstream code as ours. The README says how to carry the patches
across a Slint or wgpu bump, which matters because a stale version here
does not fail the build — cargo just warns and uses the unpatched crate.
This commit is contained in:
2026-09-25 04:18:39 -04:00
parent b5ae5c2be1
commit dc1add9dbb
112 changed files with 60304 additions and 4 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+449
View File
@@ -0,0 +1,449 @@
use windows::Win32::Graphics::{Direct3D, Direct3D12, Dxgi};
pub fn map_buffer_usage_to_resource_flags(
usage: wgt::BufferUses,
) -> Direct3D12::D3D12_RESOURCE_FLAGS {
let mut flags = Direct3D12::D3D12_RESOURCE_FLAG_NONE;
if usage.contains(wgt::BufferUses::STORAGE_READ_WRITE)
|| usage.contains(wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY)
{
flags |= Direct3D12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
flags
}
pub fn map_buffer_descriptor(
desc: &crate::BufferDescriptor<'_>,
) -> Direct3D12::D3D12_RESOURCE_DESC {
Direct3D12::D3D12_RESOURCE_DESC {
Dimension: Direct3D12::D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: 0,
Width: desc.size,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: Dxgi::Common::DXGI_FORMAT_UNKNOWN,
SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: Direct3D12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: map_buffer_usage_to_resource_flags(desc.usage),
}
}
pub fn map_texture_dimension(dim: wgt::TextureDimension) -> Direct3D12::D3D12_RESOURCE_DIMENSION {
match dim {
wgt::TextureDimension::D1 => Direct3D12::D3D12_RESOURCE_DIMENSION_TEXTURE1D,
wgt::TextureDimension::D2 => Direct3D12::D3D12_RESOURCE_DIMENSION_TEXTURE2D,
wgt::TextureDimension::D3 => Direct3D12::D3D12_RESOURCE_DIMENSION_TEXTURE3D,
}
}
pub fn map_texture_usage_to_resource_flags(
usage: wgt::TextureUses,
) -> Direct3D12::D3D12_RESOURCE_FLAGS {
let mut flags = Direct3D12::D3D12_RESOURCE_FLAG_NONE;
if usage.contains(wgt::TextureUses::COLOR_TARGET) {
flags |= Direct3D12::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
}
if usage
.intersects(wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::DEPTH_STENCIL_WRITE)
{
flags |= Direct3D12::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
if !usage.contains(wgt::TextureUses::RESOURCE) {
flags |= Direct3D12::D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
}
}
if usage.intersects(
wgt::TextureUses::STORAGE_READ_ONLY
| wgt::TextureUses::STORAGE_WRITE_ONLY
| wgt::TextureUses::STORAGE_READ_WRITE,
) {
flags |= Direct3D12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
flags
}
pub fn map_address_mode(mode: wgt::AddressMode) -> Direct3D12::D3D12_TEXTURE_ADDRESS_MODE {
use wgt::AddressMode as Am;
match mode {
Am::Repeat => Direct3D12::D3D12_TEXTURE_ADDRESS_MODE_WRAP,
Am::MirrorRepeat => Direct3D12::D3D12_TEXTURE_ADDRESS_MODE_MIRROR,
Am::ClampToEdge => Direct3D12::D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
Am::ClampToBorder => Direct3D12::D3D12_TEXTURE_ADDRESS_MODE_BORDER,
//Am::MirrorClamp => Direct3D12::D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE,
}
}
pub fn map_filter_mode(mode: wgt::FilterMode) -> Direct3D12::D3D12_FILTER_TYPE {
match mode {
wgt::FilterMode::Nearest => Direct3D12::D3D12_FILTER_TYPE_POINT,
wgt::FilterMode::Linear => Direct3D12::D3D12_FILTER_TYPE_LINEAR,
}
}
pub fn map_mipmap_filter_mode(mode: wgt::MipmapFilterMode) -> Direct3D12::D3D12_FILTER_TYPE {
match mode {
wgt::MipmapFilterMode::Nearest => Direct3D12::D3D12_FILTER_TYPE_POINT,
wgt::MipmapFilterMode::Linear => Direct3D12::D3D12_FILTER_TYPE_LINEAR,
}
}
pub fn map_comparison(func: wgt::CompareFunction) -> Direct3D12::D3D12_COMPARISON_FUNC {
use wgt::CompareFunction as Cf;
match func {
Cf::Never => Direct3D12::D3D12_COMPARISON_FUNC_NEVER,
Cf::Less => Direct3D12::D3D12_COMPARISON_FUNC_LESS,
Cf::LessEqual => Direct3D12::D3D12_COMPARISON_FUNC_LESS_EQUAL,
Cf::Equal => Direct3D12::D3D12_COMPARISON_FUNC_EQUAL,
Cf::GreaterEqual => Direct3D12::D3D12_COMPARISON_FUNC_GREATER_EQUAL,
Cf::Greater => Direct3D12::D3D12_COMPARISON_FUNC_GREATER,
Cf::NotEqual => Direct3D12::D3D12_COMPARISON_FUNC_NOT_EQUAL,
Cf::Always => Direct3D12::D3D12_COMPARISON_FUNC_ALWAYS,
}
}
pub fn map_border_color(border_color: Option<wgt::SamplerBorderColor>) -> [f32; 4] {
use wgt::SamplerBorderColor as Sbc;
match border_color {
Some(Sbc::TransparentBlack) | Some(Sbc::Zero) | None => [0.0; 4],
Some(Sbc::OpaqueBlack) => [0.0, 0.0, 0.0, 1.0],
Some(Sbc::OpaqueWhite) => [1.0; 4],
}
}
pub fn map_visibility(visibility: wgt::ShaderStages) -> Direct3D12::D3D12_SHADER_VISIBILITY {
match visibility {
wgt::ShaderStages::VERTEX => Direct3D12::D3D12_SHADER_VISIBILITY_VERTEX,
wgt::ShaderStages::FRAGMENT => Direct3D12::D3D12_SHADER_VISIBILITY_PIXEL,
_ => Direct3D12::D3D12_SHADER_VISIBILITY_ALL,
}
}
pub fn map_binding_type(ty: &wgt::BindingType) -> Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE {
use wgt::BindingType as Bt;
match *ty {
Bt::Sampler { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,
Bt::Buffer {
ty: wgt::BufferBindingType::Uniform,
..
} => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
Bt::Buffer {
ty: wgt::BufferBindingType::Storage { read_only: true },
..
}
| Bt::Texture { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
Bt::Buffer {
ty: wgt::BufferBindingType::Storage { read_only: false },
..
}
| Bt::StorageTexture { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
Bt::AccelerationStructure { .. } => Direct3D12::D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
// External textures require multiple bindings and therefore cannot
// be mapped to a single descriptor range type. They must be handled
// separately by the caller.
Bt::ExternalTexture => unreachable!("External textures must be handled separately"),
}
}
pub fn map_buffer_usage_to_state(usage: wgt::BufferUses) -> Direct3D12::D3D12_RESOURCE_STATES {
use wgt::BufferUses as Bu;
let mut state = Direct3D12::D3D12_RESOURCE_STATE_COMMON;
if usage.intersects(Bu::COPY_SRC) {
state |= Direct3D12::D3D12_RESOURCE_STATE_COPY_SOURCE;
}
if usage.intersects(Bu::COPY_DST) {
state |= Direct3D12::D3D12_RESOURCE_STATE_COPY_DEST;
}
if usage.intersects(Bu::INDEX) {
state |= Direct3D12::D3D12_RESOURCE_STATE_INDEX_BUFFER;
}
if usage.intersects(Bu::VERTEX | Bu::UNIFORM) {
state |= Direct3D12::D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
}
if usage.intersects(Bu::STORAGE_READ_WRITE) {
state |= Direct3D12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
} else if usage.intersects(Bu::STORAGE_READ_ONLY) {
state |= Direct3D12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
| Direct3D12::D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
}
if usage.intersects(Bu::INDIRECT) {
state |= Direct3D12::D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
}
if usage.intersects(Bu::ACCELERATION_STRUCTURE_QUERY) {
state |= Direct3D12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
}
state
}
pub fn map_texture_usage_to_state(usage: wgt::TextureUses) -> Direct3D12::D3D12_RESOURCE_STATES {
use wgt::TextureUses as Tu;
let mut state = Direct3D12::D3D12_RESOURCE_STATE_COMMON;
//Note: `RESOLVE_SOURCE` and `RESOLVE_DEST` are not used here
//Note: `PRESENT` is the same as `COMMON`
if usage == wgt::TextureUses::UNINITIALIZED {
return state;
}
if usage.intersects(Tu::COPY_SRC) {
state |= Direct3D12::D3D12_RESOURCE_STATE_COPY_SOURCE;
}
if usage.intersects(Tu::COPY_DST) {
state |= Direct3D12::D3D12_RESOURCE_STATE_COPY_DEST;
}
if usage.intersects(Tu::RESOURCE) {
state |= Direct3D12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
| Direct3D12::D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
}
if usage.intersects(Tu::COLOR_TARGET) {
state |= Direct3D12::D3D12_RESOURCE_STATE_RENDER_TARGET;
}
if usage.intersects(Tu::DEPTH_STENCIL_READ) {
state |= Direct3D12::D3D12_RESOURCE_STATE_DEPTH_READ;
}
if usage.intersects(Tu::DEPTH_STENCIL_WRITE) {
state |= Direct3D12::D3D12_RESOURCE_STATE_DEPTH_WRITE;
}
if usage.intersects(Tu::STORAGE_READ_ONLY | Tu::STORAGE_WRITE_ONLY | Tu::STORAGE_READ_WRITE) {
state |= Direct3D12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
}
state
}
pub fn map_topology(
topology: wgt::PrimitiveTopology,
) -> (
Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE,
Direct3D::D3D_PRIMITIVE_TOPOLOGY,
) {
match topology {
wgt::PrimitiveTopology::PointList => (
Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT,
Direct3D::D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
),
wgt::PrimitiveTopology::LineList => (
Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,
Direct3D::D3D_PRIMITIVE_TOPOLOGY_LINELIST,
),
wgt::PrimitiveTopology::LineStrip => (
Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,
Direct3D::D3D_PRIMITIVE_TOPOLOGY_LINESTRIP,
),
wgt::PrimitiveTopology::TriangleList => (
Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
),
wgt::PrimitiveTopology::TriangleStrip => (
Direct3D12::D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
),
}
}
pub fn map_polygon_mode(mode: wgt::PolygonMode) -> Direct3D12::D3D12_FILL_MODE {
match mode {
wgt::PolygonMode::Fill => Direct3D12::D3D12_FILL_MODE_SOLID,
wgt::PolygonMode::Line => Direct3D12::D3D12_FILL_MODE_WIREFRAME,
wgt::PolygonMode::Point => panic!(
"{:?} is not enabled for this backend",
wgt::Features::POLYGON_MODE_POINT
),
}
}
/// D3D12 doesn't support passing factors ending in `_COLOR` for alpha blending
/// (see <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_render_target_blend_desc>).
/// Therefore this function takes an additional `is_alpha` argument
/// which if set will return an equivalent `_ALPHA` factor.
fn map_blend_factor(factor: wgt::BlendFactor, is_alpha: bool) -> Direct3D12::D3D12_BLEND {
use wgt::BlendFactor as Bf;
match factor {
Bf::Zero => Direct3D12::D3D12_BLEND_ZERO,
Bf::One => Direct3D12::D3D12_BLEND_ONE,
Bf::Src if is_alpha => Direct3D12::D3D12_BLEND_SRC_ALPHA,
Bf::Src => Direct3D12::D3D12_BLEND_SRC_COLOR,
Bf::OneMinusSrc if is_alpha => Direct3D12::D3D12_BLEND_INV_SRC_ALPHA,
Bf::OneMinusSrc => Direct3D12::D3D12_BLEND_INV_SRC_COLOR,
Bf::Dst if is_alpha => Direct3D12::D3D12_BLEND_DEST_ALPHA,
Bf::Dst => Direct3D12::D3D12_BLEND_DEST_COLOR,
Bf::OneMinusDst if is_alpha => Direct3D12::D3D12_BLEND_INV_DEST_ALPHA,
Bf::OneMinusDst => Direct3D12::D3D12_BLEND_INV_DEST_COLOR,
Bf::SrcAlpha => Direct3D12::D3D12_BLEND_SRC_ALPHA,
Bf::OneMinusSrcAlpha => Direct3D12::D3D12_BLEND_INV_SRC_ALPHA,
Bf::DstAlpha => Direct3D12::D3D12_BLEND_DEST_ALPHA,
Bf::OneMinusDstAlpha => Direct3D12::D3D12_BLEND_INV_DEST_ALPHA,
Bf::Constant => Direct3D12::D3D12_BLEND_BLEND_FACTOR,
Bf::OneMinusConstant => Direct3D12::D3D12_BLEND_INV_BLEND_FACTOR,
Bf::SrcAlphaSaturated => Direct3D12::D3D12_BLEND_SRC_ALPHA_SAT,
Bf::Src1 if is_alpha => Direct3D12::D3D12_BLEND_SRC1_ALPHA,
Bf::Src1 => Direct3D12::D3D12_BLEND_SRC1_COLOR,
Bf::OneMinusSrc1 if is_alpha => Direct3D12::D3D12_BLEND_INV_SRC1_ALPHA,
Bf::OneMinusSrc1 => Direct3D12::D3D12_BLEND_INV_SRC1_COLOR,
Bf::Src1Alpha => Direct3D12::D3D12_BLEND_SRC1_ALPHA,
Bf::OneMinusSrc1Alpha => Direct3D12::D3D12_BLEND_INV_SRC1_ALPHA,
}
}
fn map_blend_component(
component: &wgt::BlendComponent,
is_alpha: bool,
) -> (
Direct3D12::D3D12_BLEND_OP,
Direct3D12::D3D12_BLEND,
Direct3D12::D3D12_BLEND,
) {
let raw_op = match component.operation {
wgt::BlendOperation::Add => Direct3D12::D3D12_BLEND_OP_ADD,
wgt::BlendOperation::Subtract => Direct3D12::D3D12_BLEND_OP_SUBTRACT,
wgt::BlendOperation::ReverseSubtract => Direct3D12::D3D12_BLEND_OP_REV_SUBTRACT,
wgt::BlendOperation::Min => Direct3D12::D3D12_BLEND_OP_MIN,
wgt::BlendOperation::Max => Direct3D12::D3D12_BLEND_OP_MAX,
};
let raw_src = map_blend_factor(component.src_factor, is_alpha);
let raw_dst = map_blend_factor(component.dst_factor, is_alpha);
(raw_op, raw_src, raw_dst)
}
pub fn map_render_targets(
color_targets: &[Option<wgt::ColorTargetState>],
) -> [Direct3D12::D3D12_RENDER_TARGET_BLEND_DESC;
Direct3D12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize] {
let dummy_target = Direct3D12::D3D12_RENDER_TARGET_BLEND_DESC {
BlendEnable: false.into(),
LogicOpEnable: false.into(),
SrcBlend: Direct3D12::D3D12_BLEND_ZERO,
DestBlend: Direct3D12::D3D12_BLEND_ZERO,
BlendOp: Direct3D12::D3D12_BLEND_OP_ADD,
SrcBlendAlpha: Direct3D12::D3D12_BLEND_ZERO,
DestBlendAlpha: Direct3D12::D3D12_BLEND_ZERO,
BlendOpAlpha: Direct3D12::D3D12_BLEND_OP_ADD,
LogicOp: Direct3D12::D3D12_LOGIC_OP_CLEAR,
RenderTargetWriteMask: 0,
};
let mut raw_targets =
[dummy_target; Direct3D12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize];
for (raw, ct) in raw_targets.iter_mut().zip(color_targets.iter()) {
if let Some(ct) = ct.as_ref() {
raw.RenderTargetWriteMask = ct.write_mask.bits() as u8;
if let Some(ref blend) = ct.blend {
let (color_op, color_src, color_dst) = map_blend_component(&blend.color, false);
let (alpha_op, alpha_src, alpha_dst) = map_blend_component(&blend.alpha, true);
raw.BlendEnable = true.into();
raw.BlendOp = color_op;
raw.SrcBlend = color_src;
raw.DestBlend = color_dst;
raw.BlendOpAlpha = alpha_op;
raw.SrcBlendAlpha = alpha_src;
raw.DestBlendAlpha = alpha_dst;
}
}
}
raw_targets
}
fn map_stencil_op(op: wgt::StencilOperation) -> Direct3D12::D3D12_STENCIL_OP {
use wgt::StencilOperation as So;
match op {
So::Keep => Direct3D12::D3D12_STENCIL_OP_KEEP,
So::Zero => Direct3D12::D3D12_STENCIL_OP_ZERO,
So::Replace => Direct3D12::D3D12_STENCIL_OP_REPLACE,
So::IncrementClamp => Direct3D12::D3D12_STENCIL_OP_INCR_SAT,
So::IncrementWrap => Direct3D12::D3D12_STENCIL_OP_INCR,
So::DecrementClamp => Direct3D12::D3D12_STENCIL_OP_DECR_SAT,
So::DecrementWrap => Direct3D12::D3D12_STENCIL_OP_DECR,
So::Invert => Direct3D12::D3D12_STENCIL_OP_INVERT,
}
}
fn map_stencil_face(face: &wgt::StencilFaceState) -> Direct3D12::D3D12_DEPTH_STENCILOP_DESC {
Direct3D12::D3D12_DEPTH_STENCILOP_DESC {
StencilFailOp: map_stencil_op(face.fail_op),
StencilDepthFailOp: map_stencil_op(face.depth_fail_op),
StencilPassOp: map_stencil_op(face.pass_op),
StencilFunc: map_comparison(face.compare),
}
}
pub fn map_depth_stencil(ds: &wgt::DepthStencilState) -> Direct3D12::D3D12_DEPTH_STENCIL_DESC {
Direct3D12::D3D12_DEPTH_STENCIL_DESC {
DepthEnable: ds.is_depth_enabled().into(),
DepthWriteMask: if ds.depth_write_enabled.unwrap_or_default() {
Direct3D12::D3D12_DEPTH_WRITE_MASK_ALL
} else {
Direct3D12::D3D12_DEPTH_WRITE_MASK_ZERO
},
DepthFunc: map_comparison(ds.depth_compare.unwrap_or_default()),
StencilEnable: ds.stencil.is_enabled().into(),
StencilReadMask: ds.stencil.read_mask as u8,
StencilWriteMask: ds.stencil.write_mask as u8,
FrontFace: map_stencil_face(&ds.stencil.front),
BackFace: map_stencil_face(&ds.stencil.back),
}
}
pub(crate) fn map_acceleration_structure_build_flags(
flags: wgt::AccelerationStructureFlags,
mode: Option<crate::AccelerationStructureBuildMode>,
) -> Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS {
let mut d3d_flags = Default::default();
if flags.contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION) {
d3d_flags |=
Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_ALLOW_COMPACTION;
}
if flags.contains(wgt::AccelerationStructureFlags::ALLOW_UPDATE) {
d3d_flags |= Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_ALLOW_UPDATE;
}
if flags.contains(wgt::AccelerationStructureFlags::LOW_MEMORY) {
d3d_flags |= Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_MINIMIZE_MEMORY;
}
if flags.contains(wgt::AccelerationStructureFlags::PREFER_FAST_BUILD) {
d3d_flags |=
Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_BUILD;
}
if flags.contains(wgt::AccelerationStructureFlags::PREFER_FAST_TRACE) {
d3d_flags |=
Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;
}
if let Some(crate::AccelerationStructureBuildMode::Update) = mode {
d3d_flags |= Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PERFORM_UPDATE
}
d3d_flags
}
pub(crate) fn map_acceleration_structure_geometry_flags(
flags: wgt::AccelerationStructureGeometryFlags,
) -> Direct3D12::D3D12_RAYTRACING_GEOMETRY_FLAGS {
let mut d3d_flags = Default::default();
if flags.contains(wgt::AccelerationStructureGeometryFlags::OPAQUE) {
d3d_flags |= Direct3D12::D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE;
}
if flags.contains(wgt::AccelerationStructureGeometryFlags::NO_DUPLICATE_ANY_HIT_INVOCATION) {
d3d_flags |= Direct3D12::D3D12_RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANYHIT_INVOCATION;
}
d3d_flags
}
pub(crate) fn map_acceleration_structure_copy_mode(
mode: wgt::AccelerationStructureCopy,
) -> Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE {
match mode {
wgt::AccelerationStructureCopy::Clone => {
Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_CLONE
}
wgt::AccelerationStructureCopy::Compact => {
Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_COMPACT
}
}
}
+120
View File
@@ -0,0 +1,120 @@
use alloc::sync::Arc;
use core::{ffi, ptr};
use once_cell::sync::Lazy;
use windows::{
core::Interface as _,
Win32::{Foundation::HWND, Graphics::DirectComposition},
};
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 {
inner: Option<InnerState>,
}
impl DCompState {
/// This will create a DirectComposition device and a target for the window handle if not already initialized.
/// 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(lib, hwnd) }?);
}
Ok(self.inner.as_mut().unwrap())
}
}
pub struct InnerState {
pub visual: DirectComposition::IDCompositionVisual,
pub device: DirectComposition::IDCompositionDevice,
// Must be kept alive but is otherwise unused after initialization.
pub _target: DirectComposition::IDCompositionTarget,
}
impl InnerState {
/// Creates a DirectComposition device and a target for the given window handle.
pub unsafe fn init(lib: &Arc<DCompLib>, hwnd: &HWND) -> Result<Self, crate::SurfaceError> {
profiling::scope!("DCompState::init");
let dcomp_device = lib.create_device()?;
let target = unsafe { dcomp_device.CreateTargetForHwnd(*hwnd, false) }.map_err(|err| {
log::error!("IDCompositionDevice::CreateTargetForHwnd failed: {err}");
crate::SurfaceError::Other("IDCompositionDevice::CreateTargetForHwnd")
})?;
let visual = unsafe { dcomp_device.CreateVisual() }.map_err(|err| {
log::error!("IDCompositionDevice::CreateVisual failed: {err}");
crate::SurfaceError::Other("IDCompositionDevice::CreateVisual")
})?;
unsafe { target.SetRoot(&visual) }.map_err(|err| {
log::error!("IDCompositionTarget::SetRoot failed: {err}");
crate::SurfaceError::Other("IDCompositionTarget::SetRoot")
})?;
Ok(InnerState {
visual,
device: dcomp_device,
_target: target,
})
}
}
+329
View File
@@ -0,0 +1,329 @@
use alloc::vec::Vec;
use core::fmt;
use bit_set::BitSet;
use parking_lot::Mutex;
use range_alloc::RangeAllocator;
use windows::Win32::Graphics::Direct3D12;
use crate::auxil::dxgi::result::HResult as _;
const HEAP_SIZE_FIXED: usize = 64;
#[derive(Copy, Clone)]
pub(super) struct DualHandle {
cpu: Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE,
pub gpu: Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE,
/// How large the block allocated to this handle is.
count: u64,
}
impl fmt::Debug for DualHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DualHandle")
.field("cpu", &self.cpu.ptr)
.field("gpu", &self.gpu.ptr)
.field("count", &self.count)
.finish()
}
}
type DescriptorIndex = u64;
pub(super) struct GeneralHeap {
pub raw: Direct3D12::ID3D12DescriptorHeap,
ty: Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE,
handle_size: u64,
total_handles: u64,
start: DualHandle,
ranges: Mutex<RangeAllocator<DescriptorIndex>>,
}
impl GeneralHeap {
pub(super) fn new(
device: &Direct3D12::ID3D12Device,
ty: Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE,
total_handles: u64,
) -> Result<Self, crate::DeviceError> {
let raw = {
profiling::scope!("ID3D12Device::CreateDescriptorHeap");
let desc = Direct3D12::D3D12_DESCRIPTOR_HEAP_DESC {
Type: ty,
NumDescriptors: total_handles as u32,
Flags: Direct3D12::D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
NodeMask: 0,
};
unsafe { device.CreateDescriptorHeap::<Direct3D12::ID3D12DescriptorHeap>(&desc) }
.into_device_result("Descriptor heap creation")?
};
let start = DualHandle {
cpu: unsafe { raw.GetCPUDescriptorHandleForHeapStart() },
gpu: unsafe { raw.GetGPUDescriptorHandleForHeapStart() },
count: 0,
};
Ok(Self {
raw,
ty,
handle_size: unsafe { device.GetDescriptorHandleIncrementSize(ty) } as u64,
total_handles,
start,
ranges: Mutex::new(RangeAllocator::new(0..total_handles)),
})
}
pub(super) fn at(&self, index: DescriptorIndex, count: u64) -> DualHandle {
assert!(index < self.total_handles);
DualHandle {
cpu: self.cpu_descriptor_at(index),
gpu: self.gpu_descriptor_at(index),
count,
}
}
fn cpu_descriptor_at(&self, index: u64) -> Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE {
Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: self.start.cpu.ptr + (self.handle_size * index) as usize,
}
}
fn gpu_descriptor_at(&self, index: u64) -> Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE {
Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE {
ptr: self.start.gpu.ptr + self.handle_size * index,
}
}
pub(super) fn allocate_slice(&self, count: u64) -> Result<DescriptorIndex, crate::DeviceError> {
let range = self.ranges.lock().allocate_range(count).map_err(|err| {
log::error!("Unable to allocate descriptors: {err:?}");
crate::DeviceError::OutOfMemory
})?;
Ok(range.start)
}
/// Free handles previously given out by this `DescriptorHeapSlice`.
/// Do not use this with handles not given out by this `DescriptorHeapSlice`.
pub(crate) fn free_slice(&self, handle: DualHandle) {
let start = (handle.gpu.ptr - self.start.gpu.ptr) / self.handle_size;
self.ranges.lock().free_range(start..start + handle.count);
}
}
/// Fixed-size free-list allocator for CPU descriptors.
struct FixedSizeHeap {
_raw: Direct3D12::ID3D12DescriptorHeap,
/// Bit flag representation of available handles in the heap.
///
/// 0 - Occupied
/// 1 - free
availability: u64,
handle_size: usize,
start: Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE,
}
impl FixedSizeHeap {
fn new(
device: &Direct3D12::ID3D12Device,
ty: Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE,
) -> Result<Self, crate::DeviceError> {
let desc = Direct3D12::D3D12_DESCRIPTOR_HEAP_DESC {
Type: ty,
NumDescriptors: HEAP_SIZE_FIXED as u32,
Flags: Direct3D12::D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
};
let heap =
unsafe { device.CreateDescriptorHeap::<Direct3D12::ID3D12DescriptorHeap>(&desc) }
.into_device_result("Descriptor heap creation")?;
Ok(Self {
handle_size: unsafe { device.GetDescriptorHandleIncrementSize(ty) } as usize,
availability: !0, // all free!
start: unsafe { heap.GetCPUDescriptorHandleForHeapStart() },
_raw: heap,
})
}
fn alloc_handle(
&mut self,
) -> Result<Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE, crate::DeviceError> {
// Find first free slot.
let slot = self.availability.trailing_zeros() as usize;
if slot >= HEAP_SIZE_FIXED {
log::error!("Failed to allocate a handle form a fixed size heap");
return Err(crate::DeviceError::OutOfMemory);
}
// Set the slot as occupied.
self.availability ^= 1 << slot;
Ok(Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: self.start.ptr + self.handle_size * slot,
})
}
fn free_handle(&mut self, handle: Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE) {
let slot = (handle.ptr - self.start.ptr) / self.handle_size;
assert!(slot < HEAP_SIZE_FIXED);
assert_eq!(self.availability & (1 << slot), 0);
self.availability ^= 1 << slot;
}
fn is_full(&self) -> bool {
self.availability == 0
}
}
#[derive(Clone, Copy)]
pub(super) struct Handle {
pub raw: Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE,
heap_index: usize,
}
impl fmt::Debug for Handle {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Handle")
.field("ptr", &self.raw.ptr)
.field("heap_index", &self.heap_index)
.finish()
}
}
pub(super) struct CpuPool {
device: Direct3D12::ID3D12Device,
ty: Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE,
heaps: Vec<FixedSizeHeap>,
available_heap_indices: BitSet,
}
impl CpuPool {
pub(super) fn new(
device: Direct3D12::ID3D12Device,
ty: Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE,
) -> Self {
Self {
device,
ty,
heaps: Vec::new(),
available_heap_indices: BitSet::new(),
}
}
pub(super) fn alloc_handle(&mut self) -> Result<Handle, crate::DeviceError> {
let heap_index = self
.available_heap_indices
.iter()
.next()
.unwrap_or(self.heaps.len());
// Allocate a new heap
if heap_index == self.heaps.len() {
self.heaps.push(FixedSizeHeap::new(&self.device, self.ty)?);
self.available_heap_indices.insert(heap_index);
}
let heap = &mut self.heaps[heap_index];
let handle = Handle {
raw: heap.alloc_handle()?,
heap_index,
};
if heap.is_full() {
self.available_heap_indices.remove(heap_index);
}
Ok(handle)
}
pub(super) fn free_handle(&mut self, handle: Handle) {
self.heaps[handle.heap_index].free_handle(handle.raw);
self.available_heap_indices.insert(handle.heap_index);
}
}
pub(super) struct CpuHeapInner {
pub _raw: Direct3D12::ID3D12DescriptorHeap,
pub stage: Vec<Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE>,
}
pub(super) struct CpuHeap {
pub inner: Mutex<CpuHeapInner>,
start: Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE,
handle_size: u32,
total: u32,
}
unsafe impl Send for CpuHeap {}
unsafe impl Sync for CpuHeap {}
impl CpuHeap {
pub(super) fn new(
device: &Direct3D12::ID3D12Device,
ty: Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE,
total: u32,
) -> Result<Self, crate::DeviceError> {
let handle_size = unsafe { device.GetDescriptorHandleIncrementSize(ty) };
let desc = Direct3D12::D3D12_DESCRIPTOR_HEAP_DESC {
Type: ty,
NumDescriptors: total,
Flags: Direct3D12::D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
};
let raw = unsafe { device.CreateDescriptorHeap::<Direct3D12::ID3D12DescriptorHeap>(&desc) }
.into_device_result("CPU descriptor heap creation")?;
let start = unsafe { raw.GetCPUDescriptorHandleForHeapStart() };
Ok(Self {
inner: Mutex::new(CpuHeapInner {
_raw: raw,
stage: Vec::new(),
}),
start,
handle_size,
total,
})
}
pub(super) fn at(&self, index: u32) -> Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE {
debug_assert!(
index < self.total,
"Index ({index}) out of bounds {total}",
total = self.total
);
Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: self.start.ptr + (self.handle_size * index) as usize,
}
}
}
impl fmt::Debug for CpuHeap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CpuHeap")
.field("start", &self.start.ptr)
.field("handle_size", &self.handle_size)
.field("total", &self.total)
.finish()
}
}
pub(super) unsafe fn upload(
device: &Direct3D12::ID3D12Device,
src: &CpuHeapInner,
dst: &GeneralHeap,
dummy_copy_counts: &[u32],
) -> Result<DualHandle, crate::DeviceError> {
let count = src.stage.len() as u32;
let index = dst.allocate_slice(count as u64)?;
unsafe {
device.CopyDescriptors(
1,
&dst.cpu_descriptor_at(index),
Some(&count),
count,
src.stage.as_ptr(),
Some(dummy_copy_counts.as_ptr()),
dst.ty,
)
};
Ok(dst.at(index, count as u64))
}
File diff suppressed because it is too large Load Diff
+176
View File
@@ -0,0 +1,176 @@
use alloc::sync::Arc;
use core::ops::Deref;
use windows::core::Interface as _;
use windows::Win32::Graphics::{Direct3D, Direct3D12};
use super::D3D12Lib;
use crate::auxil::dxgi::factory::DxgiAdapter;
/// Abstraction over D3D12 device creation.
///
/// Supports two paths:
/// - **Independent**: Uses `ID3D12DeviceFactory` from the Agility SDK's Independent Devices API.
/// - **Legacy**: Uses the traditional `D3D12CreateDevice` export.
pub(super) enum DeviceFactory {
/// Uses `ID3D12DeviceFactory` from the Independent Devices API.
Independent(Direct3D12::ID3D12DeviceFactory),
/// Uses the traditional `D3D12CreateDevice` export.
Legacy,
}
impl DeviceFactory {
/// Create a new `DeviceFactory`.
///
/// If `agility_sdk` is `Some`, attempts to set up the Independent Devices API path.
/// On failure, the behavior depends on
/// [`on_load_failure`](wgt::Dx12AgilitySDKLoadFailure):
/// - [`Fallback`](wgt::Dx12AgilitySDKLoadFailure::Fallback): logs a warning and
/// returns `Ok(Legacy)`.
/// - [`Error`](wgt::Dx12AgilitySDKLoadFailure::Error): returns an `Err`.
pub(super) fn new(
lib: &D3D12Lib,
agility_sdk: Option<&wgt::Dx12AgilitySDK>,
) -> Result<Self, crate::InstanceError> {
let Some(agility_sdk) = agility_sdk else {
log::debug!("No D3D12 Agility SDK configuration provided; using system D3D12 runtime");
return Ok(Self::Legacy);
};
match Self::try_create_independent(lib, agility_sdk) {
Ok(factory) => {
log::debug!(
"Using D3D12 Agility SDK v{} from '{}'",
agility_sdk.sdk_version,
agility_sdk.sdk_path
);
Ok(Self::Independent(factory))
}
Err(err) => {
let message = format!(
"Failed to initialize D3D12 Agility SDK (v{} at '{}'): {err}",
agility_sdk.sdk_version, agility_sdk.sdk_path
);
match agility_sdk.on_load_failure {
wgt::Dx12AgilitySDKLoadFailure::Fallback => {
log::warn!("{message}; falling back to system D3D12 runtime");
Ok(Self::Legacy)
}
wgt::Dx12AgilitySDKLoadFailure::Error => {
Err(crate::InstanceError::new(message))
}
}
}
}
}
fn try_create_independent(
lib: &D3D12Lib,
agility_sdk: &wgt::Dx12AgilitySDK,
) -> Result<Direct3D12::ID3D12DeviceFactory, DeviceFactoryError> {
// Step 1: Get ID3D12SDKConfiguration1 via D3D12GetInterface
let sdk_config: Direct3D12::ID3D12SDKConfiguration1 = lib
.get_interface(&Direct3D12::CLSID_D3D12SDKConfiguration)
.map_err(DeviceFactoryError::GetInterface)?;
// Step 2: Create device factory with the specified SDK version and path
let sdk_path = std::ffi::CString::new(agility_sdk.sdk_path.as_bytes())
.map_err(|_| DeviceFactoryError::InvalidPath)?;
let factory: Direct3D12::ID3D12DeviceFactory = unsafe {
sdk_config.CreateDeviceFactory(
agility_sdk.sdk_version,
windows::core::PCSTR(sdk_path.as_ptr().cast::<u8>()),
)
}
.map_err(DeviceFactoryError::CreateDeviceFactory)?;
Ok(factory)
}
/// Enable the D3D12 debug layer and optionally GPU-based validation.
///
/// - **Legacy**: configures debug globally via `D3D12GetDebugInterface`.
/// - **Independent**: uses `GetConfigurationInterface` to get an
/// `ID3D12Debug` scoped to the factory.
pub(super) fn enable_debug_layer(&self, lib: &D3D12Lib, flags: wgt::InstanceFlags) {
if !flags
.intersects(wgt::InstanceFlags::VALIDATION | wgt::InstanceFlags::GPU_BASED_VALIDATION)
{
return;
}
let debug_controller = match self {
Self::Independent(factory) => {
match unsafe {
factory.GetConfigurationInterface::<Direct3D12::ID3D12Debug>(
&Direct3D12::CLSID_D3D12Debug,
)
} {
Ok(debug) => debug,
Err(err) => {
log::warn!("Failed to get debug interface from device factory: {err}");
return;
}
}
}
Self::Legacy => match lib.debug_interface() {
Ok(Some(debug)) => debug,
Ok(None) => return,
Err(err) => {
log::warn!("Failed to get debug interface: {err}");
return;
}
},
};
if flags.intersects(wgt::InstanceFlags::VALIDATION) {
unsafe { debug_controller.EnableDebugLayer() }
}
if flags.intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION) {
if let Ok(debug1) = debug_controller.cast::<Direct3D12::ID3D12Debug1>() {
unsafe { debug1.SetEnableGPUBasedValidation(true) }
} else {
log::warn!("Failed to enable GPU-based validation");
}
}
}
/// Create a D3D12 device using the appropriate method.
pub(super) fn create_device(
&self,
lib: &Arc<D3D12Lib>,
adapter: &DxgiAdapter,
feature_level: Direct3D::D3D_FEATURE_LEVEL,
) -> Result<Direct3D12::ID3D12Device, super::CreateDeviceError> {
match self {
Self::Independent(factory) => {
let mut result__: Option<Direct3D12::ID3D12Device> = None;
unsafe { factory.CreateDevice(adapter.deref(), feature_level, &mut result__) }
.map_err(|e| super::CreateDeviceError::D3D12CreateDevice(e.into()))?;
result__.ok_or(super::CreateDeviceError::RetDeviceIsNull)
}
Self::Legacy => lib.create_device(adapter, feature_level),
}
}
}
impl core::fmt::Debug for DeviceFactory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Independent(_) => write!(f, "DeviceFactory::Independent"),
Self::Legacy => write!(f, "DeviceFactory::Legacy"),
}
}
}
#[derive(Debug, thiserror::Error)]
enum DeviceFactoryError {
#[error("failed to get ID3D12SDKConfiguration1: {0}")]
GetInterface(super::GetInterfaceError),
#[error("SDK path contains null bytes")]
InvalidPath,
#[error("CreateDeviceFactory failed: {0}")]
CreateDeviceFactory(windows::core::Error),
}
+188
View File
@@ -0,0 +1,188 @@
use alloc::{string::String, sync::Arc, vec::Vec};
use parking_lot::RwLock;
use windows::Win32::{Foundation, Graphics::Dxgi};
use super::SurfaceTarget;
use crate::{
auxil,
dx12::{
device_creation::DeviceFactory, shader_compilation::CompilerContainer, D3D12Lib, DCompLib,
},
};
impl crate::Instance for super::Instance {
type A = super::Api;
unsafe fn init(desc: &crate::InstanceDescriptor<'_>) -> Result<Self, crate::InstanceError> {
profiling::scope!("Init DX12 Backend");
let lib_main = D3D12Lib::new().map_err(|e| {
crate::InstanceError::with_source(String::from("failed to load d3d12.dll"), e)
})?;
// Create DeviceFactory first so we know which debug path to use
let device_factory =
DeviceFactory::new(&lib_main, desc.backend_options.dx12.agility_sdk.as_ref())?;
device_factory.enable_debug_layer(&lib_main, desc.flags);
let (lib_dxgi, factory) = auxil::dxgi::factory::create_factory(desc.flags)?;
// Create IDXGIFactoryMedia
let factory_media = lib_dxgi.create_factory_media().ok();
let mut supports_allow_tearing = false;
if let Some(factory5) = factory.as_factory5() {
let mut allow_tearing = Foundation::FALSE;
let hr = unsafe {
factory5.CheckFeatureSupport(
Dxgi::DXGI_FEATURE_PRESENT_ALLOW_TEARING,
<*mut _>::cast(&mut allow_tearing),
size_of_val(&allow_tearing) as u32,
)
};
match hr {
Err(err) => log::warn!("Unable to check for tearing support: {err}"),
Ok(()) => supports_allow_tearing = true,
}
}
// Initialize the shader compiler
let compiler_container = match desc.backend_options.dx12.shader_compiler.clone() {
wgt::Dx12Compiler::DynamicDxc { dxc_path } => {
CompilerContainer::new_dynamic_dxc(dxc_path.into()).map_err(|e| {
crate::InstanceError::with_source(String::from("Failed to load dynamic DXC"), e)
})?
}
wgt::Dx12Compiler::StaticDxc => CompilerContainer::new_static_dxc().map_err(|e| {
crate::InstanceError::with_source(String::from("Failed to load static DXC"), e)
})?,
wgt::Dx12Compiler::Fxc => CompilerContainer::new_fxc().map_err(|e| {
crate::InstanceError::with_source(String::from("Failed to load FXC"), e)
})?,
wgt::Dx12Compiler::Auto => {
if cfg!(feature = "static-dxc") {
// Prefer static DXC if its compiled in
CompilerContainer::new_static_dxc().map_err(|e| {
crate::InstanceError::with_source(
String::from("Failed to load static DXC"),
e,
)
})?
} else {
// Try to load dynamic DXC
let dynamic = CompilerContainer::new_dynamic_dxc("dxcompiler.dll".into());
match dynamic {
Ok(v) => v,
Err(super::shader_compilation::GetContainerError::FailedToLoad(..)) => {
// If it can't be found load FXC
CompilerContainer::new_fxc().map_err(|e| {
crate::InstanceError::with_source(
String::from("Failed to load FXC"),
e,
)
})?
}
Err(e) => {
// If another error occurs when loading static DXC return that error
return Err(crate::InstanceError::with_source(
String::from("Failed to load dynamic DXC"),
e,
));
}
}
}
}
};
match compiler_container {
CompilerContainer::DynamicDxc(..) => {
log::debug!("Using dynamic DXC for shader compilation")
}
CompilerContainer::StaticDxc(..) => {
log::debug!("Using static DXC for shader compilation")
}
CompilerContainer::Fxc(..) => {
log::debug!("Using FXC for shader compilation")
}
}
Ok(Self {
// The call to create_factory will only succeed if we get a factory4, so this is safe.
factory,
factory_media,
library: Arc::new(lib_main),
device_factory: Arc::new(device_factory),
dcomp_lib: Arc::new(DCompLib::new()),
presentation_system: desc.backend_options.dx12.presentation_system,
_lib_dxgi: lib_dxgi,
supports_allow_tearing,
flags: desc.flags,
memory_budget_thresholds: desc.memory_budget_thresholds,
compiler_container: Arc::new(compiler_container),
options: desc.backend_options.dx12.clone(),
telemetry: desc.telemetry,
})
}
unsafe fn create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<super::Surface, crate::InstanceError> {
assert!(matches!(
display_handle,
raw_window_handle::RawDisplayHandle::Windows(_)
));
match window_handle {
raw_window_handle::RawWindowHandle::Win32(handle) => {
// https://github.com/rust-windowing/raw-window-handle/issues/171
let handle = Foundation::HWND(handle.hwnd.get() as *mut _);
let target = match self.presentation_system {
wgt::Dx12SwapchainKind::DxgiFromHwnd => SurfaceTarget::WndHandle(handle),
wgt::Dx12SwapchainKind::DxgiFromVisual => SurfaceTarget::VisualFromWndHandle {
handle,
dcomp_state: Default::default(),
},
};
Ok(super::Surface {
factory: self.factory.clone(),
factory_media: self.factory_media.clone(),
target,
supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None),
options: self.options.clone(),
})
}
_ => Err(crate::InstanceError::new(format!(
"window handle {window_handle:?} is not a Win32 handle"
))),
}
}
unsafe fn enumerate_adapters(
&self,
_surface_hint: Option<&super::Surface>,
) -> Vec<crate::ExposedAdapter<super::Api>> {
let adapters = auxil::dxgi::factory::enumerate_adapters(self.factory.clone());
adapters
.into_iter()
.filter_map(|raw| {
super::Adapter::expose(
raw,
&self.library,
&self.device_factory,
&self.dcomp_lib,
self.flags,
self.memory_budget_thresholds,
self.compiler_container.clone(),
self.options.clone(),
self.telemetry,
)
})
.collect()
}
}
File diff suppressed because it is too large Load Diff
+344
View File
@@ -0,0 +1,344 @@
//! 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;
use crate::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>>);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct VertexShader(D3D12_SHADER_BYTECODE);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct PixelShader(D3D12_SHADER_BYTECODE);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct MeshShader(D3D12_SHADER_BYTECODE);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct TaskShader(D3D12_SHADER_BYTECODE);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct SampleMask(u32);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct NodeMask(u32);
/// 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.
unsafe trait RenderPipelineStreamObject: Copy {
const SUBOBJECT_TYPE: D3D12_PIPELINE_STATE_SUBOBJECT_TYPE;
}
macro_rules! implement_stream_object {
(unsafe $ty:ty => $variant:expr) => {
unsafe impl RenderPipelineStreamObject for $ty {
const SUBOBJECT_TYPE: D3D12_PIPELINE_STATE_SUBOBJECT_TYPE = $variant;
}
};
}
implement_stream_object! { unsafe RootSignature => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE }
implement_stream_object! { unsafe VertexShader => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS }
implement_stream_object! { unsafe PixelShader => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS }
implement_stream_object! { unsafe MeshShader => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS }
implement_stream_object! { unsafe TaskShader => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS }
implement_stream_object! { unsafe D3D12_BLEND_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND }
implement_stream_object! { unsafe SampleMask => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK }
implement_stream_object! { unsafe D3D12_RASTERIZER_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER }
implement_stream_object! { unsafe D3D12_DEPTH_STENCIL_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL }
implement_stream_object! { unsafe D3D12_PRIMITIVE_TOPOLOGY_TYPE => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY }
implement_stream_object! { unsafe D3D12_RT_FORMAT_ARRAY => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS }
implement_stream_object! { unsafe DXGI_FORMAT => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT }
implement_stream_object! { unsafe DXGI_SAMPLE_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC }
implement_stream_object! { unsafe NodeMask => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK }
implement_stream_object! { unsafe D3D12_CACHED_PIPELINE_STATE => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO }
implement_stream_object! { unsafe D3D12_PIPELINE_STATE_FLAGS => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS }
implement_stream_object! { unsafe D3D12_INPUT_LAYOUT_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT }
implement_stream_object! { unsafe D3D12_INDEX_BUFFER_STRIP_CUT_VALUE => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE }
implement_stream_object! { unsafe D3D12_STREAM_OUTPUT_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT }
implement_stream_object! { unsafe D3D12_VIEW_INSTANCING_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING }
/// 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(&mut self, 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>(&mut self, 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.0 as 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.
pub unsafe fn create_pipeline_state(
&mut self,
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) }
}
}
#[repr(C)]
#[derive(Debug)]
pub struct RenderPipelineStateStreamDesc<'a> {
pub root_signature: Option<&'a ID3D12RootSignature>,
pub pixel_shader: D3D12_SHADER_BYTECODE,
pub blend_state: D3D12_BLEND_DESC,
pub sample_mask: u32,
pub rasterizer_state: D3D12_RASTERIZER_DESC,
pub depth_stencil_state: D3D12_DEPTH_STENCIL_DESC,
pub primitive_topology_type: D3D12_PRIMITIVE_TOPOLOGY_TYPE,
pub rtv_formats: D3D12_RT_FORMAT_ARRAY,
pub dsv_format: DXGI_FORMAT,
pub sample_desc: DXGI_SAMPLE_DESC,
pub node_mask: u32,
pub cached_pso: D3D12_CACHED_PIPELINE_STATE,
pub flags: D3D12_PIPELINE_STATE_FLAGS,
pub view_instancing: Option<D3D12_VIEW_INSTANCING_DESC>,
// Vertex pipeline specific
pub vertex_shader: D3D12_SHADER_BYTECODE,
pub input_layout: D3D12_INPUT_LAYOUT_DESC,
pub index_buffer_strip_cut_value: D3D12_INDEX_BUFFER_STRIP_CUT_VALUE,
pub stream_output: D3D12_STREAM_OUTPUT_DESC,
// Mesh pipeline specific
pub task_shader: D3D12_SHADER_BYTECODE,
pub mesh_shader: D3D12_SHADER_BYTECODE,
}
impl RenderPipelineStateStreamDesc<'_> {
pub fn to_stream(&self) -> RenderPipelineStateStream<'_> {
let mut stream = RenderPipelineStateStream::new();
// 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));
stream.add_object(self.blend_state);
stream.add_object(SampleMask(self.sample_mask));
stream.add_object(self.rasterizer_state);
stream.add_object(self.depth_stencil_state);
stream.add_object(self.primitive_topology_type);
if self.rtv_formats.NumRenderTargets != 0 {
stream.add_object(self.rtv_formats);
}
if self.dsv_format != DXGI_FORMAT_UNKNOWN {
stream.add_object(self.dsv_format);
}
stream.add_object(self.sample_desc);
if self.node_mask != 0 {
stream.add_object(NodeMask(self.node_mask));
}
if !self.cached_pso.pCachedBlob.is_null() {
stream.add_object(self.cached_pso);
}
stream.add_object(self.flags);
if let Some(view_instancing) = self.view_instancing {
stream.add_object(view_instancing);
}
if !self.pixel_shader.pShaderBytecode.is_null() {
stream.add_object(PixelShader(self.pixel_shader));
}
if !self.vertex_shader.pShaderBytecode.is_null() {
stream.add_object(VertexShader(self.vertex_shader));
stream.add_object(self.input_layout);
stream.add_object(self.index_buffer_strip_cut_value);
stream.add_object(self.stream_output);
}
if !self.task_shader.pShaderBytecode.is_null() {
stream.add_object(TaskShader(self.task_shader));
}
if !self.mesh_shader.pShaderBytecode.is_null() {
stream.add_object(MeshShader(self.mesh_shader));
}
stream
}
/// Returns a traditional D3D12_GRAPHICS_PIPELINE_STATE_DESC.
///
/// Safety:
/// - This returned struct must not outlive self.
pub unsafe fn to_graphics_pipeline_descriptor(&self) -> D3D12_GRAPHICS_PIPELINE_STATE_DESC {
D3D12_GRAPHICS_PIPELINE_STATE_DESC {
pRootSignature: if let Some(rsig) = self.root_signature {
unsafe { borrow_interface_temporarily(rsig) }
} else {
ManuallyDrop::new(None)
},
VS: self.vertex_shader,
PS: self.pixel_shader,
DS: D3D12_SHADER_BYTECODE::default(),
HS: D3D12_SHADER_BYTECODE::default(),
GS: D3D12_SHADER_BYTECODE::default(),
StreamOutput: self.stream_output,
BlendState: self.blend_state,
SampleMask: self.sample_mask,
RasterizerState: self.rasterizer_state,
DepthStencilState: self.depth_stencil_state,
InputLayout: self.input_layout,
IBStripCutValue: self.index_buffer_strip_cut_value,
PrimitiveTopologyType: self.primitive_topology_type,
NumRenderTargets: self.rtv_formats.NumRenderTargets,
RTVFormats: self.rtv_formats.RTFormats,
DSVFormat: self.dsv_format,
SampleDesc: self.sample_desc,
NodeMask: self.node_mask,
CachedPSO: self.cached_pso,
Flags: self.flags,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wrappers() {
assert_eq!(size_of::<RootSignature>(), size_of::<ID3D12RootSignature>());
assert_eq!(
align_of::<RootSignature>(),
align_of::<ID3D12RootSignature>()
)
}
implement_stream_object!(unsafe u16 => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE(1));
implement_stream_object!(unsafe u32 => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE(2));
implement_stream_object!(unsafe u64 => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE(3));
#[test]
fn stream() {
let mut stream = RenderPipelineStateStream::new();
stream.add_object(42u16);
stream.add_object(84u32);
stream.add_object(168u64);
assert_eq!(stream.bytes.len(), 32);
// Object 1: u16
// 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());
}
}
+252
View File
@@ -0,0 +1,252 @@
//! Sampler management for DX12.
//!
//! Nearly identical to the Vulkan sampler cache, with added descriptor heap management.
use alloc::vec::Vec;
use hashbrown::{hash_map::Entry, HashMap};
use ordered_float::OrderedFloat;
use parking_lot::Mutex;
use windows::Win32::Graphics::Direct3D12::*;
use crate::dx12::HResult;
/// The index of a sampler in the global sampler heap.
///
/// This is a type-safe, transparent wrapper around a u32.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct SamplerIndex(u32);
/// [`D3D12_SAMPLER_DESC`] is not hashable, so we wrap it in a newtype that is.
///
/// We use [`OrderedFloat`] to allow for floating point values to be compared and
/// hashed in a defined way.
#[derive(Debug, Copy, Clone)]
struct HashableSamplerDesc(D3D12_SAMPLER_DESC);
impl PartialEq for HashableSamplerDesc {
fn eq(&self, other: &Self) -> bool {
self.0.Filter == other.0.Filter
&& self.0.AddressU == other.0.AddressU
&& self.0.AddressV == other.0.AddressV
&& self.0.AddressW == other.0.AddressW
&& OrderedFloat(self.0.MipLODBias) == OrderedFloat(other.0.MipLODBias)
&& self.0.MaxAnisotropy == other.0.MaxAnisotropy
&& self.0.ComparisonFunc == other.0.ComparisonFunc
&& self.0.BorderColor.map(OrderedFloat) == other.0.BorderColor.map(OrderedFloat)
&& OrderedFloat(self.0.MinLOD) == OrderedFloat(other.0.MinLOD)
&& OrderedFloat(self.0.MaxLOD) == OrderedFloat(other.0.MaxLOD)
}
}
impl Eq for HashableSamplerDesc {}
impl core::hash::Hash for HashableSamplerDesc {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.Filter.0.hash(state);
self.0.AddressU.0.hash(state);
self.0.AddressV.0.hash(state);
self.0.AddressW.0.hash(state);
OrderedFloat(self.0.MipLODBias).hash(state);
self.0.MaxAnisotropy.hash(state);
self.0.ComparisonFunc.0.hash(state);
self.0.BorderColor.map(OrderedFloat).hash(state);
OrderedFloat(self.0.MinLOD).hash(state);
OrderedFloat(self.0.MaxLOD).hash(state);
}
}
/// Entry in the sampler cache.
struct CacheEntry {
index: SamplerIndex,
ref_count: u32,
}
/// Container for the mutable management state of the sampler heap.
///
/// We have this separated, using interior mutability, to allow for the outside world
/// to access the heap directly without needing to take the lock.
pub(crate) struct SamplerHeapState {
/// Mapping from the sampler description to the index within the heap and the refcount.
mapping: HashMap<HashableSamplerDesc, CacheEntry>,
/// List of free sampler indices.
freelist: Vec<SamplerIndex>,
}
/// Global sampler heap for the device.
///
/// As D3D12 only allows 2048 samplers to be in a single heap, we need to cache
/// samplers aggressively and refer to them in shaders by index.
pub(crate) struct SamplerHeap {
/// Mutable management state of the sampler heap.
state: Mutex<SamplerHeapState>,
/// The heap itself.
heap: ID3D12DescriptorHeap,
/// The CPU-side handle to the first descriptor in the heap.
///
/// Both the CPU and GPU handles point to the same descriptor, just in
/// different contexts.
heap_cpu_start_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
/// The GPU-side handle to the first descriptor in the heap.
///
/// Both the CPU and GPU handles point to the same descriptor, just in
/// different contexts.
heap_gpu_start_handle: D3D12_GPU_DESCRIPTOR_HANDLE,
/// This is the device-specific size of sampler descriptors.
descriptor_stride: u32,
}
impl SamplerHeap {
pub fn new(
device: &ID3D12Device,
private_caps: &super::PrivateCapabilities,
) -> Result<Self, crate::DeviceError> {
profiling::scope!("SamplerHeap::new");
// WARP can report this as 2M or more. We clamp it to 64k to be safe.
const SAMPLER_HEAP_SIZE_CLAMP: u32 = 64 * 1024;
let max_unique_samplers = private_caps
.max_sampler_descriptor_heap_size
.min(SAMPLER_HEAP_SIZE_CLAMP);
let desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
NumDescriptors: max_unique_samplers,
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
NodeMask: 0,
};
let heap = unsafe { device.CreateDescriptorHeap::<ID3D12DescriptorHeap>(&desc) }
.into_device_result("Failed to create global GPU-Visible Sampler Descriptor Heap")?;
let heap_cpu_start_handle = unsafe { heap.GetCPUDescriptorHandleForHeapStart() };
let heap_gpu_start_handle = unsafe { heap.GetGPUDescriptorHandleForHeapStart() };
let descriptor_stride =
unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) };
Ok(Self {
state: Mutex::new(SamplerHeapState {
mapping: HashMap::new(),
// Reverse so that samplers get allocated starting from zero.
freelist: (0..max_unique_samplers).map(SamplerIndex).rev().collect(),
}),
heap,
heap_cpu_start_handle,
heap_gpu_start_handle,
descriptor_stride,
})
}
/// Returns a reference to the raw descriptor heap.
pub fn heap(&self) -> &ID3D12DescriptorHeap {
&self.heap
}
/// Returns a reference the handle to be bound to the descriptor table.
pub fn gpu_descriptor_table(&self) -> D3D12_GPU_DESCRIPTOR_HANDLE {
self.heap_gpu_start_handle
}
/// Add a sampler with the given description to the heap.
///
/// If the sampler already exists, the refcount is incremented and the existing index is returned.
///
/// If the sampler does not exist, a new sampler is created and the index is returned.
///
/// If the heap is full, an error is returned.
pub fn create_sampler(
&self,
device: &ID3D12Device,
desc: D3D12_SAMPLER_DESC,
) -> Result<SamplerIndex, crate::DeviceError> {
profiling::scope!("SamplerHeap::create_sampler");
let hashable_desc = HashableSamplerDesc(desc);
// Eagarly dereference the lock to allow split borrows.
let state = &mut *self.state.lock();
// Lookup the sampler in the mapping.
match state.mapping.entry(hashable_desc) {
Entry::Occupied(occupied_entry) => {
// We have found a match, so increment the refcount and return the index.
let entry = occupied_entry.into_mut();
entry.ref_count += 1;
Ok(entry.index)
}
Entry::Vacant(vacant_entry) => {
// We need to create a new sampler.
// Try to get a new index from the freelist.
let Some(index) = state.freelist.pop() else {
// If the freelist is empty, we have hit the maximum number of samplers.
log::error!("There is no more room in the global sampler heap for more unique samplers. Your device supports a maximum of {} unique samplers.", state.mapping.len());
return Err(crate::DeviceError::OutOfMemory);
};
// Compute the CPU side handle for the new sampler.
let handle = D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: self.heap_cpu_start_handle.ptr
+ self.descriptor_stride as usize * index.0 as usize,
};
unsafe {
device.CreateSampler(&desc, handle);
}
// Insert the new sampler into the mapping.
vacant_entry.insert(CacheEntry {
index,
ref_count: 1,
});
Ok(index)
}
}
}
/// Decrement the refcount of the sampler with the given description.
///
/// If the refcount reaches zero, the sampler is destroyed and the index is returned to the freelist.
///
/// The provided index is checked against the index of the sampler with the given description, ensuring
/// that there isn't a clerical error from the caller.
pub fn destroy_sampler(&self, desc: D3D12_SAMPLER_DESC, provided_index: SamplerIndex) {
profiling::scope!("SamplerHeap::destroy_sampler");
// Eagarly dereference the lock to allow split borrows.
let state = &mut *self.state.lock();
// Get the index of the sampler to destroy.
let Entry::Occupied(mut hash_map_entry) = state.mapping.entry(HashableSamplerDesc(desc))
else {
log::error!(
"Tried to destroy a sampler that doesn't exist. Sampler description: {desc:#?}"
);
return;
};
let cache_entry = hash_map_entry.get_mut();
// Ensure that the provided index matches the index of the sampler to destroy.
assert_eq!(
cache_entry.index, provided_index,
"Mismatched sampler index, this is an implementation bug"
);
// Decrement the refcount of the sampler.
cache_entry.ref_count -= 1;
// If we are the last reference, remove the sampler from the mapping and return the index to the freelist.
//
// As samplers only exist as descriptors in the heap, there is nothing needed to be done to destroy the sampler.
if cache_entry.ref_count == 0 {
state.freelist.push(cache_entry.index);
hash_map_entry.remove();
}
}
}
@@ -0,0 +1,447 @@
use alloc::{string::String, vec::Vec};
use core::ffi::CStr;
use std::path::PathBuf;
use crate::auxil::dxgi::result::HResult;
use thiserror::Error;
use windows::{
core::{Interface, PCSTR, PCWSTR},
Win32::Graphics::Direct3D::{Dxc, Fxc, ID3DBlob, D3D_SHADER_MACRO},
};
pub(super) enum CompilerContainer {
Fxc(CompilerFxc),
DynamicDxc(CompilerDynamicDxc),
#[cfg_attr(not(static_dxc), allow(unused))]
StaticDxc(CompilerStaticDxc),
}
pub(super) struct CompilerFxc {
fxc: FxcLib,
}
pub(super) struct CompilerDynamicDxc {
max_shader_model: wgt::DxcShaderModel,
compiler: Dxc::IDxcCompiler3,
// Has to be held onto for the lifetime of the device otherwise shaders will fail to compile.
// Only needed when using dynamic linking.
_dxc: DxcLib,
}
pub(super) struct CompilerStaticDxc {
max_shader_model: wgt::DxcShaderModel,
compiler: Dxc::IDxcCompiler3,
}
#[derive(Debug, Error)]
pub(super) enum GetContainerError {
#[error(transparent)]
Device(#[from] crate::DeviceError),
#[error("Failed to load {0}: {1}")]
FailedToLoad(&'static str, libloading::Error),
}
impl CompilerContainer {
pub(super) fn new_fxc() -> Result<Self, GetContainerError> {
FxcLib::new_dynamic().map(|fxc| Self::Fxc(CompilerFxc { fxc }))
}
pub(super) fn new_dynamic_dxc(dxc_path: PathBuf) -> Result<Self, GetContainerError> {
let dxc = DxcLib::new_dynamic(dxc_path)
.map_err(|e| GetContainerError::FailedToLoad("dxcompiler.dll", e))?;
let compiler = dxc.create_instance::<Dxc::IDxcCompiler3>()?;
let (mut major, mut minor) = (1, 0);
// DXC 1.0 didn't support this. If the cast fails assume it is DXC 1.0.
if let Ok(version_info) = compiler.cast::<Dxc::IDxcVersionInfo>() {
unsafe {
version_info.GetVersion(&mut major, &mut minor).unwrap();
}
}
Ok(Self::DynamicDxc(CompilerDynamicDxc {
max_shader_model: wgt::DxcShaderModel::from_dxc_version(major, minor),
compiler,
_dxc: dxc,
}))
}
/// Creates a [`CompilerContainer`] that delegates to the statically-linked version of DXC.
pub(super) fn new_static_dxc() -> Result<CompilerContainer, crate::DeviceError> {
#[cfg(static_dxc)]
{
unsafe {
let compiler = dxc_create_instance::<Dxc::IDxcCompiler3>(|clsid, iid, ppv| {
windows_core::HRESULT(mach_dxcompiler_rs::DxcCreateInstance(
clsid.cast(),
iid.cast(),
ppv,
))
})?;
Ok(CompilerContainer::StaticDxc(CompilerStaticDxc {
max_shader_model: wgt::DxcShaderModel::V6_7,
compiler,
}))
}
}
#[cfg(not(static_dxc))]
{
panic!("Attempted to create a static DXC shader compiler, but the static-dxc feature was not enabled")
}
}
pub(super) fn max_shader_model(&self) -> Option<wgt::DxcShaderModel> {
match self {
CompilerContainer::Fxc(..) => None,
CompilerContainer::DynamicDxc(CompilerDynamicDxc {
max_shader_model, ..
})
| CompilerContainer::StaticDxc(CompilerStaticDxc {
max_shader_model, ..
}) => Some(max_shader_model.clone()),
}
}
pub(super) fn compile(
&self,
device: &super::Device,
source: &str,
source_name: Option<&CStr>,
raw_ep: &str,
stage_bit: wgt::ShaderStages,
full_stage: &str,
) -> Result<super::CompiledShader, crate::PipelineError> {
match self {
CompilerContainer::Fxc(CompilerFxc { fxc }) => compile_fxc(
device,
source,
source_name,
raw_ep,
stage_bit,
full_stage,
fxc,
),
CompilerContainer::DynamicDxc(CompilerDynamicDxc { compiler, .. })
| CompilerContainer::StaticDxc(CompilerStaticDxc { compiler, .. }) => compile_dxc(
device,
source,
source_name,
raw_ep,
stage_bit,
full_stage,
compiler,
),
}
}
}
type D3DCompileFn = unsafe extern "system" fn(
psrcdata: *const core::ffi::c_void,
srcdatasize: usize,
psourcename: PCSTR,
pdefines: *const D3D_SHADER_MACRO,
pinclude: *mut core::ffi::c_void,
pentrypoint: PCSTR,
ptarget: PCSTR,
flags1: u32,
flags2: u32,
ppcode: *mut *mut core::ffi::c_void,
pperrormsgs: *mut *mut core::ffi::c_void,
) -> windows_core::HRESULT;
#[derive(Debug)]
struct FxcLib {
// `d3dcompile_fn` points into `_lib`, so `_lib` must be held for as long
// as we want to keep compiling shaders with FXC.
_lib: crate::dx12::DynLib,
d3dcompile_fn: D3DCompileFn,
}
impl FxcLib {
const PATH: &str = "d3dcompiler_47.dll";
fn new_dynamic() -> Result<Self, GetContainerError> {
unsafe {
let lib = crate::dx12::DynLib::new(Self::PATH)
.map_err(|e| GetContainerError::FailedToLoad(FxcLib::PATH, e))?;
let d3dcompile_fn: D3DCompileFn = *lib.get::<D3DCompileFn>(c"D3DCompile".to_bytes())?;
Ok(Self {
_lib: lib,
d3dcompile_fn,
})
}
}
#[allow(clippy::too_many_arguments)]
fn compile(
&self,
source: &str,
source_name: Option<&CStr>,
raw_ep: &str,
full_stage: &str,
compile_flags: u32,
shader_data: &mut Option<ID3DBlob>,
error: &mut Option<ID3DBlob>,
) -> Result<windows_core::Result<()>, crate::DeviceError> {
unsafe {
let raw_ep = alloc::ffi::CString::new(raw_ep).unwrap();
let full_stage = alloc::ffi::CString::new(full_stage).unwrap();
// If no name has been set, D3DCompile wants the null pointer.
let source_name = source_name
.map(|cstr| cstr.as_ptr().cast())
.unwrap_or(core::ptr::null());
let shader_data: *mut Option<ID3DBlob> = shader_data;
let error: *mut Option<ID3DBlob> = error;
{
profiling::scope!("Fxc::D3DCompile");
Ok((self.d3dcompile_fn)(
source.as_ptr().cast(),
source.len(),
PCSTR(source_name),
core::ptr::null(),
core::ptr::null_mut(),
PCSTR(raw_ep.as_ptr().cast()),
PCSTR(full_stage.as_ptr().cast()),
compile_flags,
0,
shader_data.cast(),
error.cast(),
)
.ok())
}
}
}
}
fn compile_fxc(
device: &super::Device,
source: &str,
source_name: Option<&CStr>,
raw_ep: &str,
stage_bit: wgt::ShaderStages,
full_stage: &str,
fxc: &FxcLib,
) -> Result<super::CompiledShader, crate::PipelineError> {
profiling::scope!("compile_fxc");
let mut compile_flags = Fxc::D3DCOMPILE_ENABLE_STRICTNESS;
if device
.shared
.private_caps
.instance_flags
.contains(wgt::InstanceFlags::DEBUG)
{
compile_flags |= Fxc::D3DCOMPILE_DEBUG | Fxc::D3DCOMPILE_SKIP_OPTIMIZATION;
}
let mut shader_data = None;
let mut error = None;
let hr = fxc.compile(
source,
source_name,
raw_ep,
full_stage,
compile_flags,
&mut shader_data,
&mut error,
)?;
match hr {
Ok(()) => {
let shader_data = shader_data.unwrap();
Ok(super::CompiledShader::Fxc(shader_data))
}
Err(e) => {
let mut full_msg = format!("FXC D3DCompile error ({e})");
if let Some(error) = error {
use core::fmt::Write as _;
let message = unsafe {
core::slice::from_raw_parts(
error.GetBufferPointer().cast(),
error.GetBufferSize(),
)
};
let _ = write!(full_msg, ": {}", String::from_utf8_lossy(message));
}
Err(crate::PipelineError::Linkage(stage_bit, full_msg))
}
}
}
trait DxcObj: Interface {
const CLSID: windows::core::GUID;
}
impl DxcObj for Dxc::IDxcCompiler3 {
const CLSID: windows::core::GUID = Dxc::CLSID_DxcCompiler;
}
impl DxcObj for Dxc::IDxcUtils {
const CLSID: windows::core::GUID = Dxc::CLSID_DxcUtils;
}
impl DxcObj for Dxc::IDxcValidator {
const CLSID: windows::core::GUID = Dxc::CLSID_DxcValidator;
}
#[derive(Debug)]
struct DxcLib {
lib: crate::dx12::DynLib,
}
impl DxcLib {
fn new_dynamic(lib_path: PathBuf) -> Result<Self, libloading::Error> {
unsafe { crate::dx12::DynLib::new(lib_path).map(|lib| Self { lib }) }
}
pub fn create_instance<T: DxcObj>(&self) -> Result<T, crate::DeviceError> {
unsafe {
type DxcCreateInstanceFn = unsafe extern "system" fn(
rclsid: *const windows_core::GUID,
riid: *const windows_core::GUID,
ppv: *mut *mut core::ffi::c_void,
)
-> windows_core::HRESULT;
let func: libloading::Symbol<DxcCreateInstanceFn> =
self.lib.get(c"DxcCreateInstance".to_bytes())?;
dxc_create_instance::<T>(|clsid, iid, ppv| func(clsid, iid, ppv))
}
}
}
/// Invokes the provided library function to create a DXC object.
unsafe fn dxc_create_instance<T: DxcObj>(
f: impl Fn(
*const windows_core::GUID,
*const windows_core::GUID,
*mut *mut core::ffi::c_void,
) -> windows_core::HRESULT,
) -> Result<T, crate::DeviceError> {
let mut result__ = None;
f(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__))
.ok()
.into_device_result("DxcCreateInstance")?;
result__.ok_or(crate::DeviceError::Unexpected)
}
/// Owned PCWSTR
#[allow(clippy::upper_case_acronyms)]
struct OPCWSTR {
inner: Vec<u16>,
}
impl OPCWSTR {
fn new(s: &str) -> Self {
let mut inner: Vec<_> = s.encode_utf16().collect();
inner.push(0);
Self { inner }
}
fn ptr(&self) -> PCWSTR {
PCWSTR(self.inner.as_ptr())
}
}
fn get_output<T: Interface>(
res: &Dxc::IDxcResult,
kind: Dxc::DXC_OUT_KIND,
) -> Result<T, crate::DeviceError> {
let mut result__: Option<T> = None;
unsafe { res.GetOutput::<T>(kind, &mut None, <*mut _>::cast(&mut result__)) }
.into_device_result("GetOutput")?;
result__.ok_or(crate::DeviceError::Unexpected)
}
fn as_err_str(blob: &Dxc::IDxcBlobUtf8) -> Result<&str, crate::DeviceError> {
let ptr = unsafe { blob.GetStringPointer() };
let len = unsafe { blob.GetStringLength() };
core::str::from_utf8(unsafe { core::slice::from_raw_parts(ptr.0, len) })
.map_err(|_| crate::DeviceError::Unexpected)
}
fn compile_dxc(
device: &crate::dx12::Device,
source: &str,
source_name: Option<&CStr>,
raw_ep: &str,
stage_bit: wgt::ShaderStages,
full_stage: &str,
compiler: &Dxc::IDxcCompiler3,
) -> Result<crate::dx12::CompiledShader, crate::PipelineError> {
profiling::scope!("compile_dxc");
let source_name = source_name.and_then(|cstr| cstr.to_str().ok());
let source_name = source_name.map(OPCWSTR::new);
let raw_ep = OPCWSTR::new(raw_ep);
let full_stage = OPCWSTR::new(full_stage);
let mut compile_args = arrayvec::ArrayVec::<PCWSTR, 13>::new_const();
if let Some(source_name) = source_name.as_ref() {
compile_args.push(source_name.ptr())
}
compile_args.extend([
windows::core::w!("-E"),
raw_ep.ptr(),
windows::core::w!("-T"),
full_stage.ptr(),
windows::core::w!("-HV"),
windows::core::w!("2018"), // Use HLSL 2018, Naga doesn't supported 2021 yet.
windows::core::w!("-no-warnings"),
Dxc::DXC_ARG_ENABLE_STRICTNESS,
]);
if device
.shared
.private_caps
.instance_flags
.contains(wgt::InstanceFlags::DEBUG)
&& !device
.shared
.private_caps
.workarounds
.avoid_shader_debug_info
{
compile_args.push(Dxc::DXC_ARG_DEBUG);
compile_args.push(Dxc::DXC_ARG_SKIP_OPTIMIZATIONS);
}
if device.features.contains(wgt::Features::SHADER_F16) {
compile_args.push(windows::core::w!("-enable-16bit-types"));
}
let buffer = Dxc::DxcBuffer {
Ptr: source.as_ptr().cast(),
Size: source.len(),
Encoding: Dxc::DXC_CP_UTF8.0,
};
let compile_res: Dxc::IDxcResult =
unsafe { compiler.Compile(&buffer, Some(&compile_args), None) }
.into_device_result("Compile")?;
drop(compile_args);
drop(source_name);
drop(raw_ep);
drop(full_stage);
let err_blob = get_output::<Dxc::IDxcBlobUtf8>(&compile_res, Dxc::DXC_OUT_ERRORS)?;
let len = unsafe { err_blob.GetStringLength() };
if len != 0 {
let err = as_err_str(&err_blob)?;
return Err(crate::PipelineError::Linkage(
stage_bit,
format!("DXC compile error: {err}"),
));
}
let blob = get_output::<Dxc::IDxcBlob>(&compile_res, Dxc::DXC_OUT_OBJECT)?;
Ok(crate::dx12::CompiledShader::Dxc(blob))
}
+603
View File
@@ -0,0 +1,603 @@
use alloc::sync::Arc;
use gpu_allocator::{d3d12::AllocationCreateDesc, MemoryLocation};
use parking_lot::Mutex;
use windows::Win32::Graphics::{Direct3D12, Dxgi};
use crate::{
auxil::dxgi::{name::ObjectExt as _, result::HResult as _},
dx12::conv,
AllocationSizes,
};
#[derive(Debug)]
pub(crate) enum AllocationType {
Buffer,
Texture,
AccelerationStructure,
}
#[derive(Debug)]
enum AllocationInner {
/// This resource is suballocated from a heap.
Placed {
inner: gpu_allocator::d3d12::Allocation,
},
/// This resource is a committed resource and does not belong to a
/// suballocated heap. We store an approximate size, so we can manage our counters
/// correctly.
///
/// This is only used for Intel Xe drivers, which have a bug that
/// prevents suballocation from working correctly.
Committed { size: u64 },
}
#[derive(Debug)]
pub(crate) struct Allocation {
inner: AllocationInner,
ty: AllocationType,
}
impl Allocation {
pub fn placed(inner: gpu_allocator::d3d12::Allocation, ty: AllocationType) -> Self {
Self {
inner: AllocationInner::Placed { inner },
ty,
}
}
pub fn none(ty: AllocationType, size: u64) -> Self {
Self {
inner: AllocationInner::Committed { size },
ty,
}
}
pub fn size(&self) -> u64 {
match self.inner {
AllocationInner::Placed { ref inner } => inner.size(),
AllocationInner::Committed { size } => size,
}
}
}
#[derive(Clone)]
pub(crate) struct Allocator {
inner: Arc<Mutex<gpu_allocator::d3d12::Allocator>>,
device_memblock_size: u64,
host_memblock_size: u64,
pub memory_budget_thresholds: wgt::MemoryBudgetThresholds,
}
impl Allocator {
pub(crate) fn new(
raw: &Direct3D12::ID3D12Device,
memory_hints: &wgt::MemoryHints,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
) -> Result<Self, crate::DeviceError> {
let allocation_sizes = AllocationSizes::from_memory_hints(memory_hints);
let device_memblock_size = allocation_sizes.min_device_memblock_size;
let host_memblock_size = allocation_sizes.min_host_memblock_size;
let allocator_desc = gpu_allocator::d3d12::AllocatorCreateDesc {
device: gpu_allocator::d3d12::ID3D12DeviceVersion::Device(raw.clone()),
debug_settings: Default::default(),
allocation_sizes: allocation_sizes.into(),
};
let allocator = gpu_allocator::d3d12::Allocator::new(&allocator_desc).inspect_err(|e| {
log::error!("Failed to create d3d12 allocator, error: {e}");
})?;
Ok(Self {
inner: Arc::new(Mutex::new(allocator)),
device_memblock_size,
host_memblock_size,
memory_budget_thresholds,
})
}
pub(crate) fn generate_report(&self) -> wgt::AllocatorReport {
let mut upstream = self.inner.lock().generate_report();
let allocations = upstream
.allocations
.iter_mut()
.map(|alloc| wgt::AllocationReport {
name: core::mem::take(&mut alloc.name),
offset: alloc.offset,
size: alloc.size,
})
.collect();
let blocks = upstream
.blocks
.iter()
.map(|block| wgt::MemoryBlockReport {
size: block.size,
allocations: block.allocations.clone(),
})
.collect();
wgt::AllocatorReport {
allocations,
blocks,
total_allocated_bytes: upstream.total_allocated_bytes,
total_reserved_bytes: upstream.total_capacity_bytes,
}
}
}
/// To allow us to construct buffers from both a `Device` and `CommandEncoder`
/// without needing each function to take a million arguments, we create a
/// borrowed context struct that contains the relevant members.
pub(crate) struct DeviceAllocationContext<'a> {
pub(crate) raw: &'a Direct3D12::ID3D12Device,
pub(crate) shared: &'a super::DeviceShared,
pub(crate) mem_allocator: &'a Allocator,
pub(crate) counters: &'a wgt::HalCounters,
}
impl<'a> From<&'a super::Device> for DeviceAllocationContext<'a> {
fn from(device: &'a super::Device) -> Self {
Self {
raw: &device.raw,
shared: &device.shared,
mem_allocator: &device.mem_allocator,
counters: &device.counters,
}
}
}
impl<'a> From<&'a super::CommandEncoder> for DeviceAllocationContext<'a> {
fn from(encoder: &'a super::CommandEncoder) -> Self {
Self {
raw: &encoder.device,
shared: &encoder.shared,
mem_allocator: &encoder.mem_allocator,
counters: &encoder.counters,
}
}
}
impl<'a> DeviceAllocationContext<'a> {
///////////////////////
// Resource Creation //
///////////////////////
pub(crate) fn create_buffer(
&self,
desc: &crate::BufferDescriptor,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let is_cpu_read = desc.usage.contains(wgt::BufferUses::MAP_READ);
let is_cpu_write = desc.usage.contains(wgt::BufferUses::MAP_WRITE);
let location = match (is_cpu_read, is_cpu_write) {
(true, true) => MemoryLocation::CpuToGpu,
(true, false) => MemoryLocation::GpuToCpu,
(false, true) => MemoryLocation::CpuToGpu,
(false, false) => MemoryLocation::GpuOnly,
};
let raw_desc = conv::map_buffer_descriptor(desc);
let allocation_info =
self.error_if_would_oom_on_resource_allocation(&raw_desc, location)?;
let (resource, allocation) = if self.shared.private_caps.suballocation_supported {
self.create_placed_buffer(desc, raw_desc, allocation_info, location)?
} else {
self.create_committed_buffer(raw_desc, location)?
};
if let Some(label) = desc.label {
resource.set_name(label)?;
}
self.counters.buffer_memory.add(allocation.size() as isize);
Ok((resource, allocation))
}
pub(crate) fn create_texture(
&self,
desc: &crate::TextureDescriptor,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let location = MemoryLocation::GpuOnly;
let allocation_info =
self.error_if_would_oom_on_resource_allocation(&raw_desc, location)?;
let (resource, allocation) = if self.shared.private_caps.suballocation_supported {
self.create_placed_texture(desc, raw_desc, allocation_info, location)?
} else {
self.create_committed_texture(desc, raw_desc)?
};
if let Some(label) = desc.label {
resource.set_name(label)?;
}
self.counters.texture_memory.add(allocation.size() as isize);
Ok((resource, allocation))
}
pub(crate) fn create_acceleration_structure(
&self,
desc: &crate::AccelerationStructureDescriptor,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let location = MemoryLocation::GpuOnly;
let allocation_info =
self.error_if_would_oom_on_resource_allocation(&raw_desc, location)?;
let (resource, allocation) = if self.shared.private_caps.suballocation_supported {
self.create_placed_acceleration_structure(desc, raw_desc, allocation_info, location)?
} else {
self.create_committed_acceleration_structure(desc, raw_desc)?
};
if let Some(label) = desc.label {
resource.set_name(label)?;
}
self.counters
.acceleration_structure_memory
.add(allocation.size() as isize);
Ok((resource, allocation))
}
//////////////////////////
// Resource Destruction //
//////////////////////////
pub(crate) fn free_resource(
&self,
resource: Direct3D12::ID3D12Resource,
allocation: Allocation,
) {
// Make sure the resource is released before we free the allocation.
drop(resource);
let counter = match allocation.ty {
AllocationType::Buffer => &self.counters.buffer_memory,
AllocationType::Texture => &self.counters.texture_memory,
AllocationType::AccelerationStructure => &self.counters.acceleration_structure_memory,
};
counter.sub(allocation.size() as isize);
if let AllocationInner::Placed { inner } = allocation.inner {
match self.mem_allocator.inner.lock().free(inner) {
Ok(_) => (),
// TODO: Don't panic here
Err(e) => panic!("Failed to destroy dx12 {:?}, {e}", allocation.ty),
};
}
}
///////////////////////////////
// Placed Resource Creation ///
///////////////////////////////
fn create_placed_buffer(
&self,
desc: &crate::BufferDescriptor<'_>,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
allocation_info: Direct3D12::D3D12_RESOURCE_ALLOCATION_INFO,
location: MemoryLocation,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let name = desc.label.unwrap_or("Unlabeled buffer");
let mut allocator = self.mem_allocator.inner.lock();
let allocation_desc = AllocationCreateDesc {
name,
location,
size: allocation_info.SizeInBytes,
alignment: allocation_info.Alignment,
resource_category: gpu_allocator::d3d12::ResourceCategory::from(&raw_desc),
};
let allocation = allocator.allocate(&allocation_desc)?;
let mut resource = None;
unsafe {
self.raw.CreatePlacedResource(
allocation.heap(),
allocation.offset(),
&raw_desc,
Direct3D12::D3D12_RESOURCE_STATE_COMMON,
None,
&mut resource,
)
}
.into_device_result("Placed buffer creation")?;
let resource = resource.ok_or(crate::DeviceError::Unexpected)?;
let wrapped_allocation = Allocation::placed(allocation, AllocationType::Buffer);
Ok((resource, wrapped_allocation))
}
fn create_placed_texture(
&self,
desc: &crate::TextureDescriptor<'_>,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
allocation_info: Direct3D12::D3D12_RESOURCE_ALLOCATION_INFO,
location: MemoryLocation,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let name = desc.label.unwrap_or("Unlabeled texture");
let mut allocator = self.mem_allocator.inner.lock();
let allocation_desc = AllocationCreateDesc {
name,
location,
size: allocation_info.SizeInBytes,
alignment: allocation_info.Alignment,
resource_category: gpu_allocator::d3d12::ResourceCategory::from(&raw_desc),
};
let allocation = allocator.allocate(&allocation_desc)?;
let mut resource = None;
unsafe {
self.raw.CreatePlacedResource(
allocation.heap(),
allocation.offset(),
&raw_desc,
Direct3D12::D3D12_RESOURCE_STATE_COMMON,
None, // clear value
&mut resource,
)
}
.into_device_result("Placed texture creation")?;
let resource = resource.ok_or(crate::DeviceError::Unexpected)?;
let wrapped_allocation = Allocation::placed(allocation, AllocationType::Texture);
Ok((resource, wrapped_allocation))
}
fn create_placed_acceleration_structure(
&self,
desc: &crate::AccelerationStructureDescriptor<'_>,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
allocation_info: Direct3D12::D3D12_RESOURCE_ALLOCATION_INFO,
location: MemoryLocation,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let name = desc.label.unwrap_or("Unlabeled acceleration structure");
let mut allocator = self.mem_allocator.inner.lock();
let allocation_desc = AllocationCreateDesc {
name,
location,
size: allocation_info.SizeInBytes,
alignment: allocation_info.Alignment,
resource_category: gpu_allocator::d3d12::ResourceCategory::from(&raw_desc),
};
let allocation = allocator.allocate(&allocation_desc)?;
let mut resource = None;
unsafe {
self.raw.CreatePlacedResource(
allocation.heap(),
allocation.offset(),
&raw_desc,
Direct3D12::D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE,
None,
&mut resource,
)
}
.into_device_result("Placed acceleration structure creation")?;
let resource = resource.ok_or(crate::DeviceError::Unexpected)?;
let wrapped_allocation =
Allocation::placed(allocation, AllocationType::AccelerationStructure);
Ok((resource, wrapped_allocation))
}
/////////////////////////////////
// Committed Resource Creation //
/////////////////////////////////
fn create_committed_buffer(
&self,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
location: MemoryLocation,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let is_uma = matches!(
self.shared.private_caps.memory_architecture,
crate::dx12::MemoryArchitecture::Unified { .. }
);
let heap_properties = Direct3D12::D3D12_HEAP_PROPERTIES {
Type: Direct3D12::D3D12_HEAP_TYPE_CUSTOM,
CPUPageProperty: match location {
MemoryLocation::GpuOnly => Direct3D12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
MemoryLocation::CpuToGpu => Direct3D12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
MemoryLocation::GpuToCpu => Direct3D12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
_ => unreachable!(),
},
MemoryPoolPreference: match (is_uma, location) {
// On dedicated GPUs, we only use L1 for GPU-only allocations.
(false, MemoryLocation::GpuOnly) => Direct3D12::D3D12_MEMORY_POOL_L1,
(_, _) => Direct3D12::D3D12_MEMORY_POOL_L0,
},
CreationNodeMask: 0,
VisibleNodeMask: 0,
};
let mut resource = None;
unsafe {
self.raw.CreateCommittedResource(
&heap_properties,
if self.shared.private_caps.heap_create_not_zeroed {
Direct3D12::D3D12_HEAP_FLAG_CREATE_NOT_ZEROED
} else {
Direct3D12::D3D12_HEAP_FLAG_NONE
},
&raw_desc,
Direct3D12::D3D12_RESOURCE_STATE_COMMON,
None,
&mut resource,
)
}
.into_device_result("Committed buffer creation")?;
let resource = resource.ok_or(crate::DeviceError::Unexpected)?;
let wrapped_allocation = Allocation::none(AllocationType::Buffer, raw_desc.Width);
Ok((resource, wrapped_allocation))
}
fn create_committed_texture(
&self,
desc: &crate::TextureDescriptor,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let heap_properties = Direct3D12::D3D12_HEAP_PROPERTIES {
Type: Direct3D12::D3D12_HEAP_TYPE_CUSTOM,
CPUPageProperty: Direct3D12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
MemoryPoolPreference: match self.shared.private_caps.memory_architecture {
crate::dx12::MemoryArchitecture::NonUnified => Direct3D12::D3D12_MEMORY_POOL_L1,
crate::dx12::MemoryArchitecture::Unified { .. } => Direct3D12::D3D12_MEMORY_POOL_L0,
},
CreationNodeMask: 0,
VisibleNodeMask: 0,
};
let mut resource = None;
unsafe {
self.raw.CreateCommittedResource(
&heap_properties,
if self.shared.private_caps.heap_create_not_zeroed {
Direct3D12::D3D12_HEAP_FLAG_CREATE_NOT_ZEROED
} else {
Direct3D12::D3D12_HEAP_FLAG_NONE
},
&raw_desc,
Direct3D12::D3D12_RESOURCE_STATE_COMMON,
None, // clear value
&mut resource,
)
}
.into_device_result("Committed texture creation")?;
let resource = resource.ok_or(crate::DeviceError::Unexpected)?;
let wrapped_allocation = Allocation::none(
AllocationType::Texture,
desc.format.theoretical_memory_footprint(desc.size),
);
Ok((resource, wrapped_allocation))
}
fn create_committed_acceleration_structure(
&self,
desc: &crate::AccelerationStructureDescriptor,
raw_desc: Direct3D12::D3D12_RESOURCE_DESC,
) -> Result<(Direct3D12::ID3D12Resource, Allocation), crate::DeviceError> {
let heap_properties = Direct3D12::D3D12_HEAP_PROPERTIES {
Type: Direct3D12::D3D12_HEAP_TYPE_CUSTOM,
CPUPageProperty: Direct3D12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
MemoryPoolPreference: match self.shared.private_caps.memory_architecture {
crate::dx12::MemoryArchitecture::NonUnified => Direct3D12::D3D12_MEMORY_POOL_L1,
crate::dx12::MemoryArchitecture::Unified { .. } => Direct3D12::D3D12_MEMORY_POOL_L0,
},
CreationNodeMask: 0,
VisibleNodeMask: 0,
};
let mut resource = None;
unsafe {
self.raw.CreateCommittedResource(
&heap_properties,
if self.shared.private_caps.heap_create_not_zeroed {
Direct3D12::D3D12_HEAP_FLAG_CREATE_NOT_ZEROED
} else {
Direct3D12::D3D12_HEAP_FLAG_NONE
},
&raw_desc,
Direct3D12::D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE,
None,
&mut resource,
)
}
.into_device_result("Committed acceleration structure creation")?;
let resource = resource.ok_or(crate::DeviceError::Unexpected)?;
let wrapped_allocation = Allocation::none(AllocationType::AccelerationStructure, desc.size);
Ok((resource, wrapped_allocation))
}
fn error_if_would_oom_on_resource_allocation(
&self,
desc: &Direct3D12::D3D12_RESOURCE_DESC,
location: MemoryLocation,
) -> Result<Direct3D12::D3D12_RESOURCE_ALLOCATION_INFO, crate::DeviceError> {
let allocation_info = unsafe {
self.raw
.GetResourceAllocationInfo(0, core::slice::from_ref(desc))
};
// Some versions of WARP return SizeInBytes == 0 for very large
// allocations. Proceeding to attempt to allocate a zero-sized resource
// will result in a device lost error, so it seems preferable to return
// an out of memory error now.
if allocation_info.SizeInBytes == 0 {
return Err(crate::DeviceError::OutOfMemory);
}
let Some(threshold) = self
.mem_allocator
.memory_budget_thresholds
.for_resource_creation
else {
return Ok(allocation_info);
};
let memory_segment_group = match location {
MemoryLocation::Unknown => unreachable!(),
MemoryLocation::GpuOnly => Dxgi::DXGI_MEMORY_SEGMENT_GROUP_LOCAL,
MemoryLocation::CpuToGpu | MemoryLocation::GpuToCpu => {
match self.shared.private_caps.memory_architecture {
super::MemoryArchitecture::Unified { .. } => {
Dxgi::DXGI_MEMORY_SEGMENT_GROUP_LOCAL
}
super::MemoryArchitecture::NonUnified => {
Dxgi::DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL
}
}
}
};
let info = self
.shared
.adapter
.query_video_memory_info(memory_segment_group)?;
let memblock_size = match location {
MemoryLocation::Unknown => unreachable!(),
MemoryLocation::GpuOnly => self.mem_allocator.device_memblock_size,
MemoryLocation::CpuToGpu | MemoryLocation::GpuToCpu => {
self.mem_allocator.host_memblock_size
}
};
if info
.CurrentUsage
.checked_add(allocation_info.SizeInBytes.max(memblock_size))
.is_none_or(|usage| usage >= info.Budget / 100 * threshold as u64)
{
return Err(crate::DeviceError::OutOfMemory);
}
Ok(allocation_info)
}
}
+39
View File
@@ -0,0 +1,39 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use windows::Win32::Graphics::Dxgi;
windows_core::imp::define_interface!(
ISwapChainPanelNative,
ISwapChainPanelNative_Vtbl,
0x63aad0b8_7c24_40ff_85a8_640d944cc325
);
impl core::ops::Deref for ISwapChainPanelNative {
type Target = windows_core::IUnknown;
fn deref(&self) -> &Self::Target {
unsafe { core::mem::transmute(self) }
}
}
windows_core::imp::interface_hierarchy!(ISwapChainPanelNative, windows_core::IUnknown);
impl ISwapChainPanelNative {
pub unsafe fn SetSwapChain<P0>(&self, swap_chain: P0) -> windows_core::Result<()>
where
P0: windows_core::Param<Dxgi::IDXGISwapChain1>,
{
unsafe {
(windows_core::Interface::vtable(self).SetSwapChain)(
windows_core::Interface::as_raw(self),
swap_chain.param().abi(),
)
}
.ok()
}
}
#[repr(C)]
pub struct ISwapChainPanelNative_Vtbl {
pub base__: windows_core::IUnknown_Vtbl,
pub SetSwapChain: unsafe extern "system" fn(
swap_chain_panel_native: *mut core::ffi::c_void,
swap_chain: *mut core::ffi::c_void,
) -> windows_core::HRESULT,
}
+340
View File
@@ -0,0 +1,340 @@
use windows::Win32::Graphics::{Direct3D12, Dxgi};
use crate::auxil;
pub(super) struct ViewDescriptor {
dimension: wgt::TextureViewDimension,
pub aspects: crate::FormatAspects,
pub rtv_dsv_format: Dxgi::Common::DXGI_FORMAT,
srv_uav_format: Option<Dxgi::Common::DXGI_FORMAT>,
multisampled: bool,
array_layer_base: u32,
array_layer_count: u32,
mip_level_base: u32,
mip_level_count: u32,
}
impl crate::TextureViewDescriptor<'_> {
pub(super) fn to_internal(&self, texture: &super::Texture) -> ViewDescriptor {
let aspects = crate::FormatAspects::new(texture.format, self.range.aspect);
ViewDescriptor {
dimension: self.dimension,
aspects,
rtv_dsv_format: auxil::dxgi::conv::map_texture_format(self.format),
srv_uav_format: auxil::dxgi::conv::map_texture_format_for_srv_uav(self.format, aspects),
multisampled: texture.sample_count > 1,
mip_level_base: self.range.base_mip_level,
mip_level_count: self.range.mip_level_count.unwrap_or(!0),
array_layer_base: self.range.base_array_layer,
array_layer_count: self.range.array_layer_count.unwrap_or(!0),
}
}
}
fn aspects_to_plane(aspects: crate::FormatAspects) -> u32 {
match aspects {
crate::FormatAspects::STENCIL => 1,
crate::FormatAspects::PLANE_1 => 1,
crate::FormatAspects::PLANE_2 => 2,
_ => 0,
}
}
impl ViewDescriptor {
pub(crate) unsafe fn to_srv(&self) -> Option<Direct3D12::D3D12_SHADER_RESOURCE_VIEW_DESC> {
let mut desc = Direct3D12::D3D12_SHADER_RESOURCE_VIEW_DESC {
Format: self.srv_uav_format?,
ViewDimension: Direct3D12::D3D12_SRV_DIMENSION_UNKNOWN,
Shader4ComponentMapping: Direct3D12::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
Anonymous: Default::default(),
};
match self.dimension {
wgt::TextureViewDimension::D1 => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE1D;
desc.Anonymous.Texture1D = Direct3D12::D3D12_TEX1D_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
ResourceMinLODClamp: 0.0,
}
}
/*
wgt::TextureViewDimension::D1Array => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
desc.Anonymous.Texture1DArray = Direct3D12::D3D12_TEX1D_ARRAY_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
ResourceMinLODClamp: 0.0,
}
}
*/
wgt::TextureViewDimension::D2 if self.multisampled && self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE2DMS;
desc.Anonymous.Texture2DMS = Direct3D12::D3D12_TEX2DMS_SRV {
UnusedField_NothingToDefine: 0,
}
}
wgt::TextureViewDimension::D2 if self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE2D;
desc.Anonymous.Texture2D = Direct3D12::D3D12_TEX2D_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
PlaneSlice: aspects_to_plane(self.aspects),
ResourceMinLODClamp: 0.0,
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array
if self.multisampled =>
{
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
desc.Anonymous.Texture2DMSArray = Direct3D12::D3D12_TEX2DMS_ARRAY_SRV {
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
desc.Anonymous.Texture2DArray = Direct3D12::D3D12_TEX2D_ARRAY_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: aspects_to_plane(self.aspects),
ResourceMinLODClamp: 0.0,
}
}
wgt::TextureViewDimension::D3 => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURE3D;
desc.Anonymous.Texture3D = Direct3D12::D3D12_TEX3D_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
ResourceMinLODClamp: 0.0,
}
}
wgt::TextureViewDimension::Cube if self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURECUBE;
desc.Anonymous.TextureCube = Direct3D12::D3D12_TEXCUBE_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
ResourceMinLODClamp: 0.0,
}
}
wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
desc.ViewDimension = Direct3D12::D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
desc.Anonymous.TextureCubeArray = Direct3D12::D3D12_TEXCUBE_ARRAY_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
First2DArrayFace: self.array_layer_base,
NumCubes: if self.array_layer_count == !0 {
!0
} else {
self.array_layer_count / 6
},
ResourceMinLODClamp: 0.0,
}
}
}
Some(desc)
}
pub(crate) unsafe fn to_uav(&self) -> Option<Direct3D12::D3D12_UNORDERED_ACCESS_VIEW_DESC> {
let mut desc = Direct3D12::D3D12_UNORDERED_ACCESS_VIEW_DESC {
Format: self.srv_uav_format?,
ViewDimension: Direct3D12::D3D12_UAV_DIMENSION_UNKNOWN,
Anonymous: Default::default(),
};
match self.dimension {
wgt::TextureViewDimension::D1 => {
desc.ViewDimension = Direct3D12::D3D12_UAV_DIMENSION_TEXTURE1D;
desc.Anonymous.Texture1D = Direct3D12::D3D12_TEX1D_UAV {
MipSlice: self.mip_level_base,
}
}
/*
wgt::TextureViewDimension::D1Array => {
desc.ViewDimension = Direct3D12::D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
desc.Anonymous.Texture1DArray = Direct3D12::D3D12_TEX1D_ARRAY_UAV {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize,
}
}*/
wgt::TextureViewDimension::D2 if self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_UAV_DIMENSION_TEXTURE2D;
desc.Anonymous.Texture2D = Direct3D12::D3D12_TEX2D_UAV {
MipSlice: self.mip_level_base,
PlaneSlice: aspects_to_plane(self.aspects),
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array => {
desc.ViewDimension = Direct3D12::D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
desc.Anonymous.Texture2DArray = Direct3D12::D3D12_TEX2D_ARRAY_UAV {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: aspects_to_plane(self.aspects),
}
}
wgt::TextureViewDimension::D3 => {
desc.ViewDimension = Direct3D12::D3D12_UAV_DIMENSION_TEXTURE3D;
desc.Anonymous.Texture3D = Direct3D12::D3D12_TEX3D_UAV {
MipSlice: self.mip_level_base,
FirstWSlice: 0,
WSize: u32::MAX,
}
}
wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
panic!("Unable to view texture as cube UAV")
}
}
Some(desc)
}
pub(crate) unsafe fn to_rtv(&self) -> Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC {
let mut desc = Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC {
Format: self.rtv_dsv_format,
ViewDimension: Direct3D12::D3D12_RTV_DIMENSION_UNKNOWN,
Anonymous: Default::default(),
};
match self.dimension {
wgt::TextureViewDimension::D1 => {
desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE1D;
desc.Anonymous.Texture1D = Direct3D12::D3D12_TEX1D_RTV {
MipSlice: self.mip_level_base,
}
}
/*
wgt::TextureViewDimension::D1Array => {
desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE1DARRAY;
desc.Anonymous.Texture1DArray = Direct3D12::D3D12_TEX1D_ARRAY_RTV {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize,
}
}*/
wgt::TextureViewDimension::D2 if self.multisampled && self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE2DMS;
desc.Anonymous.Texture2DMS = Direct3D12::D3D12_TEX2DMS_RTV {
UnusedField_NothingToDefine: 0,
}
}
wgt::TextureViewDimension::D2 if self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE2D;
desc.Anonymous.Texture2D = Direct3D12::D3D12_TEX2D_RTV {
MipSlice: self.mip_level_base,
PlaneSlice: aspects_to_plane(self.aspects),
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array
if self.multisampled =>
{
desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
desc.Anonymous.Texture2DMSArray = Direct3D12::D3D12_TEX2DMS_ARRAY_RTV {
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array => {
desc.ViewDimension = Direct3D12::D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
desc.Anonymous.Texture2DArray = Direct3D12::D3D12_TEX2D_ARRAY_RTV {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: aspects_to_plane(self.aspects),
}
}
wgt::TextureViewDimension::D3
| wgt::TextureViewDimension::Cube
| wgt::TextureViewDimension::CubeArray => {
panic!("Unable to view texture as cube or 3D RTV")
}
}
desc
}
pub(crate) unsafe fn to_dsv(
&self,
read_only: bool,
) -> Direct3D12::D3D12_DEPTH_STENCIL_VIEW_DESC {
let mut desc = Direct3D12::D3D12_DEPTH_STENCIL_VIEW_DESC {
Format: self.rtv_dsv_format,
ViewDimension: Direct3D12::D3D12_DSV_DIMENSION_UNKNOWN,
Flags: {
let mut flags = Direct3D12::D3D12_DSV_FLAG_NONE;
if read_only {
if self.aspects.contains(crate::FormatAspects::DEPTH) {
flags |= Direct3D12::D3D12_DSV_FLAG_READ_ONLY_DEPTH;
}
if self.aspects.contains(crate::FormatAspects::STENCIL) {
flags |= Direct3D12::D3D12_DSV_FLAG_READ_ONLY_STENCIL;
}
}
flags
},
Anonymous: Default::default(),
};
match self.dimension {
wgt::TextureViewDimension::D1 => {
desc.ViewDimension = Direct3D12::D3D12_DSV_DIMENSION_TEXTURE1D;
desc.Anonymous.Texture1D = Direct3D12::D3D12_TEX1D_DSV {
MipSlice: self.mip_level_base,
}
}
/*
wgt::TextureViewDimension::D1Array => {
desc.ViewDimension = Direct3D12::D3D12_DSV_DIMENSION_TEXTURE1DARRAY;
desc.Anonymous.Texture1DArray = Direct3D12::D3D12_TEX1D_ARRAY_DSV {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize,
}
}*/
wgt::TextureViewDimension::D2 if self.multisampled && self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_DSV_DIMENSION_TEXTURE2DMS;
desc.Anonymous.Texture2DMS = Direct3D12::D3D12_TEX2DMS_DSV {
UnusedField_NothingToDefine: 0,
}
}
wgt::TextureViewDimension::D2 if self.array_layer_base == 0 => {
desc.ViewDimension = Direct3D12::D3D12_DSV_DIMENSION_TEXTURE2D;
desc.Anonymous.Texture2D = Direct3D12::D3D12_TEX2D_DSV {
MipSlice: self.mip_level_base,
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array
if self.multisampled =>
{
desc.ViewDimension = Direct3D12::D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY;
desc.Anonymous.Texture2DMSArray = Direct3D12::D3D12_TEX2DMS_ARRAY_DSV {
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
}
}
wgt::TextureViewDimension::D2 | wgt::TextureViewDimension::D2Array => {
desc.ViewDimension = Direct3D12::D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
desc.Anonymous.Texture2DArray = Direct3D12::D3D12_TEX2D_ARRAY_DSV {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
}
}
wgt::TextureViewDimension::D3
| wgt::TextureViewDimension::Cube
| wgt::TextureViewDimension::CubeArray => {
panic!("Unable to view texture as cube or 3D DSV")
}
}
desc
}
}