Skip to content

Commit 05369a6

Browse files
inner-daemonsWumpf
andauthored
Multiview on DX12 (#8495)
Co-authored-by: Andreas Reich <r_andreas2@web.de>
1 parent c78f944 commit 05369a6

File tree

6 files changed

+50
-9
lines changed

6 files changed

+50
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ can use this feature.
8282

8383
Multiview is also called view instancing in DX12 land or vertex amplification in Metal land.
8484

85-
Multiview has been reworked, adding support for Metal, and adding testing and validation to wgpu itself.
85+
Multiview has been reworked, adding support for Metal and DX12, and adding testing and validation to wgpu itself.
8686
This change also introduces a view bitmask, a new field in `RenderPassDescriptor` that allows a render pass to render to multiple non-adjacent layers
8787
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`.
8888
```diff

wgpu-hal/src/dx12/adapter.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -601,13 +601,20 @@ impl super::Adapter {
601601
shader_barycentrics_supported,
602602
);
603603

604-
// Re-enable this when multiview is supported on DX12
605-
// features.set(wgt::Features::MULTIVIEW, view_instancing);
606-
// features.set(wgt::Features::SELECTIVE_MULTIVIEW, view_instancing);
604+
features.set(
605+
wgt::Features::MULTIVIEW,
606+
view_instancing && shader_model >= naga::back::hlsl::ShaderModel::V6_1,
607+
);
608+
features.set(
609+
wgt::Features::SELECTIVE_MULTIVIEW,
610+
view_instancing && shader_model >= naga::back::hlsl::ShaderModel::V6_1,
611+
);
607612

608613
features.set(
609614
wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW,
610-
mesh_shader_supported && view_instancing,
615+
mesh_shader_supported
616+
&& view_instancing
617+
&& shader_model >= naga::back::hlsl::ShaderModel::V6_1,
611618
);
612619

613620
// TODO: Determine if IPresentationManager is supported

wgpu-hal/src/dx12/device.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use alloc::{
55
sync::Arc,
66
vec::Vec,
77
};
8+
use arrayvec::ArrayVec;
89
use core::{ffi, num::NonZeroU32, ptr, time::Duration};
910
use std::time::Instant;
1011

@@ -983,7 +984,7 @@ impl crate::Device for super::Device {
983984
let mut ranges = Vec::with_capacity(total_non_dynamic_entries);
984985

985986
let mut bind_group_infos =
986-
arrayvec::ArrayVec::<super::BindGroupInfo, { crate::MAX_BIND_GROUPS }>::default();
987+
ArrayVec::<super::BindGroupInfo, { crate::MAX_BIND_GROUPS }>::default();
987988
for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
988989
let mut info = super::BindGroupInfo {
989990
tables: super::TableTypes::empty(),
@@ -1952,6 +1953,22 @@ impl crate::Device for super::Device {
19521953
};
19531954
let flags = Direct3D12::D3D12_PIPELINE_STATE_FLAG_NONE;
19541955

1956+
let mut view_instancing =
1957+
core::pin::pin!(ArrayVec::<Direct3D12::D3D12_VIEW_INSTANCE_LOCATION, 32>::new());
1958+
if let Some(mask) = desc.multiview_mask {
1959+
let mask = mask.get();
1960+
// This array is just what _could_ be rendered to. We actually apply the mask at
1961+
// renderpass creation time. The `view_index` passed to the shader depends on the
1962+
// view's index in this array, so if we include every view in this array, `view_index`
1963+
// actually the texture array layer, like in vulkan.
1964+
for i in 0..32 - mask.leading_zeros() {
1965+
view_instancing.push(Direct3D12::D3D12_VIEW_INSTANCE_LOCATION {
1966+
ViewportArrayIndex: 0,
1967+
RenderTargetArrayIndex: i,
1968+
});
1969+
}
1970+
}
1971+
19551972
let mut stream_desc = RenderPipelineStateStreamDesc {
19561973
// Shared by vertex and mesh pipelines
19571974
root_signature: desc.layout.shared.signature.as_ref(),
@@ -1970,6 +1987,16 @@ impl crate::Device for super::Device {
19701987
node_mask: 0,
19711988
cached_pso,
19721989
flags,
1990+
view_instancing: if !view_instancing.is_empty() {
1991+
Some(Direct3D12::D3D12_VIEW_INSTANCING_DESC {
1992+
ViewInstanceCount: view_instancing.len() as u32,
1993+
pViewInstanceLocations: view_instancing.as_ptr(),
1994+
// This lets us hide/mask certain values later, at renderpass creation time.
1995+
Flags: Direct3D12::D3D12_VIEW_INSTANCING_FLAG_ENABLE_VIEW_INSTANCE_MASKING,
1996+
})
1997+
} else {
1998+
None
1999+
},
19732000

19742001
// Optional data that depends on the pipeline type (vertex vs mesh).
19752002
vertex_shader: Default::default(),

wgpu-hal/src/dx12/pipeline_desc.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ implement_stream_object! { unsafe D3D12_PIPELINE_STATE_FLAGS => D3D12_PIPELINE_S
9191
implement_stream_object! { unsafe D3D12_INPUT_LAYOUT_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT }
9292
implement_stream_object! { unsafe D3D12_INDEX_BUFFER_STRIP_CUT_VALUE => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE }
9393
implement_stream_object! { unsafe D3D12_STREAM_OUTPUT_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT }
94+
implement_stream_object! { unsafe D3D12_VIEW_INSTANCING_DESC => D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING }
9495

9596
/// Implementaation of a pipeline state stream, which is a sequence of subobjects put into
9697
/// a byte array according to some basic alignment rules.
@@ -183,6 +184,7 @@ pub struct RenderPipelineStateStreamDesc<'a> {
183184
pub node_mask: u32,
184185
pub cached_pso: D3D12_CACHED_PIPELINE_STATE,
185186
pub flags: D3D12_PIPELINE_STATE_FLAGS,
187+
pub view_instancing: Option<D3D12_VIEW_INSTANCING_DESC>,
186188

187189
// Vertex pipeline specific
188190
pub vertex_shader: D3D12_SHADER_BYTECODE,
@@ -230,6 +232,9 @@ impl RenderPipelineStateStreamDesc<'_> {
230232
stream.add_object(self.cached_pso);
231233
}
232234
stream.add_object(self.flags);
235+
if let Some(view_instancing) = self.view_instancing {
236+
stream.add_object(view_instancing);
237+
}
233238
if !self.pixel_shader.pShaderBytecode.is_null() {
234239
stream.add_object(PixelShader(self.pixel_shader));
235240
}

wgpu-types/src/features.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -920,10 +920,9 @@ bitflags_array! {
920920
/// Supported platforms:
921921
/// - Vulkan
922922
/// - Metal
923+
/// - DX12
923924
/// - OpenGL (web only)
924925
///
925-
/// DX12 support is a WIP.
926-
///
927926
/// This is a native only feature.
928927
const MULTIVIEW = 1 << 26;
929928
/// Enables using 64-bit types for vertex attributes.
@@ -1249,8 +1248,8 @@ bitflags_array! {
12491248
///
12501249
/// Supported platforms
12511250
/// - Vulkan
1251+
/// - DX12
12521252
///
1253-
/// DX12 will support this when it supports multiview in general.
12541253
///
12551254
/// While metal supports this in theory, the behavior of `view_index` differs from vulkan and dx12 so the feature isn't exposed.
12561255
const SELECTIVE_MULTIVIEW = 1 << 54;

wgpu/src/api/render_pass.rs

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

0 commit comments

Comments
 (0)