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:
+1472
File diff suppressed because it is too large
Load Diff
+1859
File diff suppressed because it is too large
Load Diff
+449
@@ -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
@@ -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
@@ -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))
|
||||
}
|
||||
+2636
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
+1698
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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))
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user