Multiview on DX12 (#8495)

Co-authored-by: Andreas Reich <r_andreas2@web.de>
This commit is contained in:
Inner Daemons 2025-11-16 11:46:52 -05:00 committed by GitHub
parent c78f944f2f
commit 05369a6b83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 9 deletions

View File

@ -82,7 +82,7 @@ can use this feature.
Multiview is also called view instancing in DX12 land or vertex amplification in Metal land.
Multiview has been reworked, adding support for Metal, and adding testing and validation to wgpu itself.
Multiview has been reworked, adding support for Metal and DX12, and adding testing and validation to wgpu itself.
This change also introduces a view bitmask, a new field in `RenderPassDescriptor` that allows a render pass to render to multiple non-adjacent layers
when using the `SELECTIVE_MULTIVIEW` feature. Note that this also influences apps that don't use multiview, as they have to set this mask to `None`.
```diff

View File

@ -601,13 +601,20 @@ impl super::Adapter {
shader_barycentrics_supported,
);
// Re-enable this when multiview is supported on DX12
// features.set(wgt::Features::MULTIVIEW, view_instancing);
// features.set(wgt::Features::SELECTIVE_MULTIVIEW, view_instancing);
features.set(
wgt::Features::MULTIVIEW,
view_instancing && shader_model >= naga::back::hlsl::ShaderModel::V6_1,
);
features.set(
wgt::Features::SELECTIVE_MULTIVIEW,
view_instancing && shader_model >= naga::back::hlsl::ShaderModel::V6_1,
);
features.set(
wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW,
mesh_shader_supported && view_instancing,
mesh_shader_supported
&& view_instancing
&& shader_model >= naga::back::hlsl::ShaderModel::V6_1,
);
// TODO: Determine if IPresentationManager is supported

View File

@ -5,6 +5,7 @@ use alloc::{
sync::Arc,
vec::Vec,
};
use arrayvec::ArrayVec;
use core::{ffi, num::NonZeroU32, ptr, time::Duration};
use std::time::Instant;
@ -983,7 +984,7 @@ impl crate::Device for super::Device {
let mut ranges = Vec::with_capacity(total_non_dynamic_entries);
let mut bind_group_infos =
arrayvec::ArrayVec::<super::BindGroupInfo, { crate::MAX_BIND_GROUPS }>::default();
ArrayVec::<super::BindGroupInfo, { crate::MAX_BIND_GROUPS }>::default();
for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
let mut info = super::BindGroupInfo {
tables: super::TableTypes::empty(),
@ -1952,6 +1953,22 @@ impl crate::Device for super::Device {
};
let flags = Direct3D12::D3D12_PIPELINE_STATE_FLAG_NONE;
let mut view_instancing =
core::pin::pin!(ArrayVec::<Direct3D12::D3D12_VIEW_INSTANCE_LOCATION, 32>::new());
if let Some(mask) = desc.multiview_mask {
let mask = mask.get();
// This array is just what _could_ be rendered to. We actually apply the mask at
// renderpass creation time. The `view_index` passed to the shader depends on the
// view's index in this array, so if we include every view in this array, `view_index`
// actually the texture array layer, like in vulkan.
for i in 0..32 - mask.leading_zeros() {
view_instancing.push(Direct3D12::D3D12_VIEW_INSTANCE_LOCATION {
ViewportArrayIndex: 0,
RenderTargetArrayIndex: i,
});
}
}
let mut stream_desc = RenderPipelineStateStreamDesc {
// Shared by vertex and mesh pipelines
root_signature: desc.layout.shared.signature.as_ref(),
@ -1970,6 +1987,16 @@ impl crate::Device for super::Device {
node_mask: 0,
cached_pso,
flags,
view_instancing: if !view_instancing.is_empty() {
Some(Direct3D12::D3D12_VIEW_INSTANCING_DESC {
ViewInstanceCount: view_instancing.len() as u32,
pViewInstanceLocations: view_instancing.as_ptr(),
// This lets us hide/mask certain values later, at renderpass creation time.
Flags: Direct3D12::D3D12_VIEW_INSTANCING_FLAG_ENABLE_VIEW_INSTANCE_MASKING,
})
} else {
None
},
// Optional data that depends on the pipeline type (vertex vs mesh).
vertex_shader: Default::default(),

View File

@ -91,6 +91,7 @@ implement_stream_object! { unsafe D3D12_PIPELINE_STATE_FLAGS => D3D12_PIPELINE_S
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.
@ -183,6 +184,7 @@ pub struct RenderPipelineStateStreamDesc<'a> {
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,
@ -230,6 +232,9 @@ impl RenderPipelineStateStreamDesc<'_> {
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));
}

View File

@ -920,10 +920,9 @@ bitflags_array! {
/// Supported platforms:
/// - Vulkan
/// - Metal
/// - DX12
/// - OpenGL (web only)
///
/// DX12 support is a WIP.
///
/// This is a native only feature.
const MULTIVIEW = 1 << 26;
/// Enables using 64-bit types for vertex attributes.
@ -1249,8 +1248,8 @@ bitflags_array! {
///
/// Supported platforms
/// - Vulkan
/// - DX12
///
/// DX12 will support this when it supports multiview in general.
///
/// While metal supports this in theory, the behavior of `view_index` differs from vulkan and dx12 so the feature isn't exposed.
const SELECTIVE_MULTIVIEW = 1 << 54;

View File

@ -685,6 +685,9 @@ pub struct RenderPassDescriptor<'a> {
/// 2nd layer, you would use 2=0b10. If you aren't using multiview this should be `None`.
///
/// Note that setting bits higher than the number of texture layers is a validation error.
///
/// This doesn't influence load/store/clear/etc operations, as those are defined for attachments,
/// therefore affecting all attachments. Meaning, this affects only any shaders executed on the `RenderPass`.
pub multiview_mask: Option<NonZeroU32>,
}
#[cfg(send_sync)]