mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 08:13:27 +00:00
Add multiview support (#2187)
Co-authored-by: Layl Bongers <2385329-layl@users.noreply.gitlab.com>
This commit is contained in:
parent
fca82ddd7e
commit
2ef72b9313
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -984,18 +984,18 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=eda078d#eda078d736a906b4267e4803db6b32e3162aac30"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=29571cc#29571cc4cfbb28558948b1b31ad764f55b69f37b"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
"codespan-reporting",
|
||||
"fxhash",
|
||||
"hexf-parse",
|
||||
"indexmap",
|
||||
"log",
|
||||
"num-traits 0.2.14",
|
||||
"petgraph",
|
||||
"pp-rs",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"spirv",
|
||||
"thiserror",
|
||||
@ -1437,6 +1437,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rusttype"
|
||||
version = "0.9.2"
|
||||
|
@ -68,6 +68,7 @@ pub fn op_webgpu_create_render_bundle_encoder(
|
||||
color_formats: Cow::from(color_formats),
|
||||
sample_count: args.sample_count,
|
||||
depth_stencil,
|
||||
multiview: None,
|
||||
};
|
||||
|
||||
let res = wgpu_core::command::RenderBundleEncoder::new(&descriptor, device, None);
|
||||
|
@ -358,6 +358,7 @@ pub fn op_webgpu_create_render_pipeline(
|
||||
depth_stencil: args.depth_stencil.map(TryInto::try_into).transpose()?,
|
||||
multisample: args.multisample.into(),
|
||||
fragment,
|
||||
multiview: None,
|
||||
};
|
||||
|
||||
let implicit_pipelines = match args.layout {
|
||||
|
@ -37,7 +37,7 @@ thiserror = "1"
|
||||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "eda078d"
|
||||
rev = "29571cc"
|
||||
#version = "0.7"
|
||||
features = ["span", "validate", "wgsl-in"]
|
||||
|
||||
|
@ -54,7 +54,7 @@ use crate::{
|
||||
Label, LabelHelpers, LifeGuard, Stored,
|
||||
};
|
||||
use arrayvec::ArrayVec;
|
||||
use std::{borrow::Cow, mem, ops::Range};
|
||||
use std::{borrow::Cow, mem, num::NonZeroU32, ops::Range};
|
||||
use thiserror::Error;
|
||||
|
||||
use hal::CommandEncoder as _;
|
||||
@ -75,6 +75,8 @@ pub struct RenderBundleEncoderDescriptor<'a> {
|
||||
/// Sample count this render bundle is capable of rendering to. This must match the pipelines and
|
||||
/// the renderpasses it is used in.
|
||||
pub sample_count: u32,
|
||||
/// If this render bundle will rendering to multiple array layers in the attachments at the same time.
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -112,6 +114,7 @@ impl RenderBundleEncoder {
|
||||
}
|
||||
sc
|
||||
},
|
||||
multiview: desc.multiview,
|
||||
},
|
||||
is_ds_read_only: match desc.depth_stencil {
|
||||
Some(ds) => {
|
||||
@ -135,6 +138,7 @@ impl RenderBundleEncoder {
|
||||
depth_stencil: None,
|
||||
},
|
||||
sample_count: 0,
|
||||
multiview: None,
|
||||
},
|
||||
is_ds_read_only: false,
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ use crate::{
|
||||
RenderCommandError, StateChange,
|
||||
},
|
||||
device::{
|
||||
AttachmentData, MissingDownlevelFlags, MissingFeatures, RenderPassCompatibilityError,
|
||||
RenderPassContext,
|
||||
AttachmentData, Device, MissingDownlevelFlags, MissingFeatures,
|
||||
RenderPassCompatibilityError, RenderPassContext,
|
||||
},
|
||||
error::{ErrorFormatter, PrettyError},
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
|
||||
@ -29,7 +29,8 @@ use arrayvec::ArrayVec;
|
||||
use hal::CommandEncoder as _;
|
||||
use thiserror::Error;
|
||||
use wgt::{
|
||||
BufferAddress, BufferSize, BufferUsages, Color, IndexFormat, TextureUsages, VertexStepMode,
|
||||
BufferAddress, BufferSize, BufferUsages, Color, IndexFormat, TextureUsages,
|
||||
TextureViewDimension, VertexStepMode,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "serial-pass", feature = "replay"))]
|
||||
@ -461,6 +462,12 @@ pub enum RenderPassErrorInner {
|
||||
Bind(#[from] BindError),
|
||||
#[error(transparent)]
|
||||
QueryUse(#[from] QueryUseError),
|
||||
#[error("multiview layer count must match")]
|
||||
MultiViewMismatch,
|
||||
#[error(
|
||||
"multiview pass texture views with more than one array layer must have D2Array dimension"
|
||||
)]
|
||||
MultiViewDimensionMismatch,
|
||||
}
|
||||
|
||||
impl PrettyError for RenderPassErrorInner {
|
||||
@ -542,6 +549,7 @@ struct RenderPassInfo<'a, A: hal::Api> {
|
||||
|
||||
pending_discard_init_fixups: SurfacesInDiscardState,
|
||||
divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, &'a TextureView<A>)>,
|
||||
multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
@ -582,6 +590,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
}
|
||||
|
||||
fn start(
|
||||
device: &Device<A>,
|
||||
label: Option<&str>,
|
||||
color_attachments: &[RenderPassColorAttachment],
|
||||
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
|
||||
@ -605,6 +614,39 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
let mut extent = None;
|
||||
let mut sample_count = 0;
|
||||
|
||||
let mut detected_multiview: Option<Option<NonZeroU32>> = None;
|
||||
|
||||
let mut check_multiview = |view: &TextureView<A>| {
|
||||
// Get the multiview configuration for this texture view
|
||||
let layers = view.selector.layers.end - view.selector.layers.start;
|
||||
let this_multiview = if layers >= 2 {
|
||||
// Trivially proven by the if above
|
||||
Some(unsafe { NonZeroU32::new_unchecked(layers) })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Make sure that if this view is a multiview, it is set to be an array
|
||||
if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {
|
||||
return Err(RenderPassErrorInner::MultiViewDimensionMismatch);
|
||||
}
|
||||
|
||||
// Validate matching first, or store the first one
|
||||
if let Some(multiview) = detected_multiview {
|
||||
if multiview != this_multiview {
|
||||
return Err(RenderPassErrorInner::MultiViewMismatch);
|
||||
}
|
||||
} else {
|
||||
// Multiview is only supported if the feature is enabled
|
||||
if this_multiview.is_some() {
|
||||
device.require_features(wgt::Features::MULTIVIEW)?;
|
||||
}
|
||||
|
||||
detected_multiview = Some(this_multiview);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
let mut add_view = |view: &TextureView<A>, type_name| {
|
||||
if let Some(ex) = extent {
|
||||
if ex != view.extent {
|
||||
@ -637,6 +679,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
.views
|
||||
.use_extend(&*view_guard, at.view, (), ())
|
||||
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?;
|
||||
check_multiview(view)?;
|
||||
add_view(view, "depth")?;
|
||||
|
||||
let ds_aspects = view.desc.aspects();
|
||||
@ -745,6 +788,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
.views
|
||||
.use_extend(&*view_guard, at.view, (), ())
|
||||
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?;
|
||||
check_multiview(color_view)?;
|
||||
add_view(color_view, "color")?;
|
||||
|
||||
if !color_view
|
||||
@ -774,6 +818,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
.views
|
||||
.use_extend(&*view_guard, resolve_target, (), ())
|
||||
.map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?;
|
||||
check_multiview(resolve_view)?;
|
||||
if color_view.extent != resolve_view.extent {
|
||||
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
|
||||
previous: (attachment_type_name, extent.unwrap_or_default()),
|
||||
@ -829,9 +874,12 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
depth_stencil: depth_stencil_attachment.map(|at| view_guard.get(at.view).unwrap()),
|
||||
};
|
||||
let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
|
||||
|
||||
let multiview = detected_multiview.expect("Multiview was not detected, no attachments");
|
||||
let context = RenderPassContext {
|
||||
attachments: view_data.map(|view| view.desc.format),
|
||||
sample_count,
|
||||
multiview,
|
||||
};
|
||||
|
||||
let hal_desc = hal::RenderPassDescriptor {
|
||||
@ -840,6 +888,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
sample_count,
|
||||
color_attachments: &colors,
|
||||
depth_stencil_attachment: depth_stencil,
|
||||
multiview,
|
||||
};
|
||||
unsafe {
|
||||
cmd_buf.encoder.raw.begin_render_pass(&hal_desc);
|
||||
@ -854,6 +903,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
_phantom: PhantomData,
|
||||
pending_discard_init_fixups,
|
||||
divergent_discarded_depth_stencil_aspect,
|
||||
multiview,
|
||||
})
|
||||
}
|
||||
|
||||
@ -916,6 +966,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
stencil_ops,
|
||||
clear_value: (0.0, 0),
|
||||
}),
|
||||
multiview: self.multiview,
|
||||
};
|
||||
unsafe {
|
||||
raw.begin_render_pass(&desc);
|
||||
@ -997,6 +1048,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
);
|
||||
|
||||
let mut info = RenderPassInfo::start(
|
||||
device,
|
||||
base.label,
|
||||
color_attachments,
|
||||
depth_stencil_attachment,
|
||||
|
@ -72,6 +72,7 @@ impl<T> AttachmentData<T> {
|
||||
pub(crate) struct RenderPassContext {
|
||||
pub attachments: AttachmentData<TextureFormat>,
|
||||
pub sample_count: u32,
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum RenderPassCompatibilityError {
|
||||
@ -84,6 +85,8 @@ pub enum RenderPassCompatibilityError {
|
||||
IncompatibleDepthStencilAttachment(Option<TextureFormat>, Option<TextureFormat>),
|
||||
#[error("Incompatible sample count: {0:?} != {1:?}")]
|
||||
IncompatibleSampleCount(u32, u32),
|
||||
#[error("Incompatible multiview: {0:?} != {1:?}")]
|
||||
IncompatibleMultiview(Option<NonZeroU32>, Option<NonZeroU32>),
|
||||
}
|
||||
|
||||
impl RenderPassContext {
|
||||
@ -112,6 +115,12 @@ impl RenderPassContext {
|
||||
other.sample_count,
|
||||
));
|
||||
}
|
||||
if self.multiview != other.multiview {
|
||||
return Err(RenderPassCompatibilityError::IncompatibleMultiview(
|
||||
self.multiview,
|
||||
other.multiview,
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -2454,6 +2463,11 @@ impl<A: HalApi> Device<A> {
|
||||
.get(pipeline_layout_id)
|
||||
.map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?;
|
||||
|
||||
// Multiview is only supported if the feature is enabled
|
||||
if desc.multiview.is_some() {
|
||||
self.require_features(wgt::Features::MULTIVIEW)?;
|
||||
}
|
||||
|
||||
let pipeline_desc = hal::RenderPipelineDescriptor {
|
||||
label: desc.label.borrow_option(),
|
||||
layout: &layout.raw,
|
||||
@ -2464,6 +2478,7 @@ impl<A: HalApi> Device<A> {
|
||||
multisample: desc.multisample,
|
||||
fragment_stage,
|
||||
color_targets,
|
||||
multiview: desc.multiview,
|
||||
};
|
||||
let raw =
|
||||
unsafe { self.raw.create_render_pipeline(&pipeline_desc) }.map_err(
|
||||
@ -2490,6 +2505,7 @@ impl<A: HalApi> Device<A> {
|
||||
depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
|
||||
},
|
||||
sample_count: samples,
|
||||
multiview: desc.multiview,
|
||||
};
|
||||
|
||||
let mut flags = pipeline::PipelineFlags::empty();
|
||||
|
@ -27,6 +27,7 @@ pub(crate) fn new_render_bundle_encoder_descriptor<'a>(
|
||||
}
|
||||
}),
|
||||
sample_count: context.sample_count,
|
||||
multiview: context.multiview,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
id::{DeviceId, PipelineLayoutId, ShaderModuleId},
|
||||
validation, Label, LifeGuard, Stored,
|
||||
};
|
||||
use std::{borrow::Cow, error::Error, fmt};
|
||||
use std::{borrow::Cow, error::Error, fmt, num::NonZeroU32};
|
||||
use thiserror::Error;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@ -243,6 +243,9 @@ pub struct RenderPipelineDescriptor<'a> {
|
||||
pub multisample: wgt::MultisampleState,
|
||||
/// The fragment processing state for this pipeline.
|
||||
pub fragment: Option<FragmentState<'a>>,
|
||||
/// If the pipeline will be used with a multiview render pass, this indicates how many array
|
||||
/// layers the attachments will have.
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -75,12 +75,12 @@ js-sys = { version = "0.3" }
|
||||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "eda078d"
|
||||
rev = "29571cc"
|
||||
#version = "0.7"
|
||||
|
||||
[dev-dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "eda078d"
|
||||
rev = "29571cc"
|
||||
#version = "0.7"
|
||||
features = ["wgsl-in"]
|
||||
|
||||
|
@ -238,6 +238,7 @@ impl<A: hal::Api> Example<A> {
|
||||
blend: Some(wgt::BlendState::ALPHA_BLENDING),
|
||||
write_mask: wgt::ColorWrites::default(),
|
||||
}],
|
||||
multiview: None,
|
||||
};
|
||||
let pipeline = unsafe { device.create_render_pipeline(&pipeline_desc).unwrap() };
|
||||
|
||||
@ -664,6 +665,7 @@ impl<A: hal::Api> Example<A> {
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
multiview: None,
|
||||
};
|
||||
unsafe {
|
||||
ctx.encoder.begin_render_pass(&pass_desc);
|
||||
|
@ -93,7 +93,7 @@ pub use vulkan::UpdateAfterBindTypes;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
fmt,
|
||||
num::NonZeroU8,
|
||||
num::{NonZeroU32, NonZeroU8},
|
||||
ops::{Range, RangeInclusive},
|
||||
ptr::NonNull,
|
||||
};
|
||||
@ -991,6 +991,9 @@ pub struct RenderPipelineDescriptor<'a, A: Api> {
|
||||
pub fragment_stage: Option<ProgrammableStage<'a, A>>,
|
||||
/// The effect of draw calls on the color aspect of the output target.
|
||||
pub color_targets: &'a [wgt::ColorTargetState],
|
||||
/// If the pipeline will be used with a multiview render pass, this indicates how many array
|
||||
/// layers the attachments will have.
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
/// Specifies how the alpha channel of the textures should be handled during (martin mouv i step)
|
||||
@ -1144,6 +1147,7 @@ pub struct RenderPassDescriptor<'a, A: Api> {
|
||||
pub sample_count: u32,
|
||||
pub color_attachments: &'a [ColorAttachment<'a, A>],
|
||||
pub depth_stencil_attachment: Option<DepthStencilAttachment<'a, A>>,
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -16,6 +16,7 @@ fn indexing_features() -> wgt::Features {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PhysicalDeviceFeatures {
|
||||
core: vk::PhysicalDeviceFeatures,
|
||||
vulkan_1_1: Option<vk::PhysicalDeviceVulkan11Features>,
|
||||
pub(super) vulkan_1_2: Option<vk::PhysicalDeviceVulkan12Features>,
|
||||
pub(super) descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT>,
|
||||
imageless_framebuffer: Option<vk::PhysicalDeviceImagelessFramebufferFeaturesKHR>,
|
||||
@ -23,6 +24,7 @@ pub struct PhysicalDeviceFeatures {
|
||||
image_robustness: Option<vk::PhysicalDeviceImageRobustnessFeaturesEXT>,
|
||||
robustness2: Option<vk::PhysicalDeviceRobustness2FeaturesEXT>,
|
||||
depth_clip_enable: Option<vk::PhysicalDeviceDepthClipEnableFeaturesEXT>,
|
||||
multiview: Option<vk::PhysicalDeviceMultiviewFeaturesKHR>,
|
||||
}
|
||||
|
||||
// This is safe because the structs have `p_next: *mut c_void`, which we null out/never read.
|
||||
@ -159,6 +161,15 @@ impl PhysicalDeviceFeatures {
|
||||
//.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY))
|
||||
.geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX))
|
||||
.build(),
|
||||
vulkan_1_1: if api_version >= vk::API_VERSION_1_1 {
|
||||
Some(
|
||||
vk::PhysicalDeviceVulkan11Features::builder()
|
||||
.multiview(requested_features.contains(wgt::Features::MULTIVIEW))
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
vulkan_1_2: if api_version >= vk::API_VERSION_1_2 {
|
||||
Some(
|
||||
vk::PhysicalDeviceVulkan12Features::builder()
|
||||
@ -295,6 +306,15 @@ impl PhysicalDeviceFeatures {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
multiview: if enabled_extensions.contains(&vk::KhrMultiviewFn::name()) {
|
||||
Some(
|
||||
vk::PhysicalDeviceMultiviewFeatures::builder()
|
||||
.multiview(requested_features.contains(wgt::Features::MULTIVIEW))
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,6 +410,10 @@ impl PhysicalDeviceFeatures {
|
||||
|
||||
let intel_windows = caps.properties.vendor_id == db::intel::VENDOR && cfg!(windows);
|
||||
|
||||
if let Some(ref vulkan_1_1) = self.vulkan_1_1 {
|
||||
features.set(F::MULTIVIEW, vulkan_1_1.multiview != 0);
|
||||
}
|
||||
|
||||
if let Some(ref vulkan_1_2) = self.vulkan_1_2 {
|
||||
const STORAGE: F = F::STORAGE_RESOURCE_BINDING_ARRAY;
|
||||
if Self::all_features_supported(
|
||||
@ -479,6 +503,10 @@ impl PhysicalDeviceFeatures {
|
||||
features.set(F::DEPTH_CLIP_CONTROL, feature.depth_clip_enable != 0);
|
||||
}
|
||||
|
||||
if let Some(ref multiview) = self.multiview {
|
||||
features.set(F::MULTIVIEW, multiview.multiview != 0);
|
||||
}
|
||||
|
||||
(features, dl_flags)
|
||||
}
|
||||
|
||||
@ -522,6 +550,11 @@ impl PhysicalDeviceCapabilities {
|
||||
extensions.push(vk::KhrMaintenance1Fn::name());
|
||||
extensions.push(vk::KhrMaintenance2Fn::name());
|
||||
|
||||
// Below Vulkan 1.1 we can get multiview from an extension
|
||||
if requested_features.contains(wgt::Features::MULTIVIEW) {
|
||||
extensions.push(vk::KhrMultiviewFn::name());
|
||||
}
|
||||
|
||||
// `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside `VK_KHR_maintenance1` or a 1.1+ device.
|
||||
if !self.supports_extension(vk::KhrMaintenance1Fn::name()) {
|
||||
extensions.push(vk::AmdNegativeViewportHeightFn::name());
|
||||
@ -727,6 +760,13 @@ impl super::InstanceShared {
|
||||
.features(core)
|
||||
.build();
|
||||
|
||||
if capabilities.properties.api_version >= vk::API_VERSION_1_1 {
|
||||
features.vulkan_1_1 = Some(vk::PhysicalDeviceVulkan11Features::builder().build());
|
||||
|
||||
let mut_ref = features.vulkan_1_1.as_mut().unwrap();
|
||||
mut_ref.p_next = mem::replace(&mut features2.p_next, mut_ref as *mut _ as *mut _);
|
||||
}
|
||||
|
||||
if capabilities.properties.api_version >= vk::API_VERSION_1_2 {
|
||||
features.vulkan_1_2 = Some(vk::PhysicalDeviceVulkan12Features::builder().build());
|
||||
|
||||
@ -793,6 +833,7 @@ impl super::InstanceShared {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
null_p_next(&mut features.vulkan_1_1);
|
||||
null_p_next(&mut features.vulkan_1_2);
|
||||
null_p_next(&mut features.descriptor_indexing);
|
||||
null_p_next(&mut features.imageless_framebuffer);
|
||||
@ -998,6 +1039,7 @@ impl super::Adapter {
|
||||
raw_device: ash::Device,
|
||||
handle_is_owned: bool,
|
||||
enabled_extensions: &[&'static CStr],
|
||||
features: wgt::Features,
|
||||
uab_types: super::UpdateAfterBindTypes,
|
||||
family_index: u32,
|
||||
queue_index: u32,
|
||||
@ -1044,7 +1086,8 @@ impl super::Adapter {
|
||||
|
||||
let naga_options = {
|
||||
use naga::back::spv;
|
||||
let capabilities = [
|
||||
|
||||
let mut capabilities = vec![
|
||||
spv::Capability::Shader,
|
||||
spv::Capability::Matrix,
|
||||
spv::Capability::Sampled1D,
|
||||
@ -1058,6 +1101,11 @@ impl super::Adapter {
|
||||
spv::Capability::StorageImageExtendedFormats,
|
||||
//TODO: fill out the rest
|
||||
];
|
||||
|
||||
if features.contains(wgt::Features::MULTIVIEW) {
|
||||
capabilities.push(spv::Capability::MultiView);
|
||||
}
|
||||
|
||||
let mut flags = spv::WriterFlags::empty();
|
||||
flags.set(
|
||||
spv::WriterFlags::DEBUG,
|
||||
@ -1078,17 +1126,17 @@ impl super::Adapter {
|
||||
lang_version: (1, 0),
|
||||
flags,
|
||||
capabilities: Some(capabilities.iter().cloned().collect()),
|
||||
bounds_check_policies: naga::back::BoundsCheckPolicies {
|
||||
index: naga::back::BoundsCheckPolicy::Restrict,
|
||||
bounds_check_policies: naga::proc::BoundsCheckPolicies {
|
||||
index: naga::proc::BoundsCheckPolicy::Restrict,
|
||||
buffer: if self.private_caps.robust_buffer_access {
|
||||
naga::back::BoundsCheckPolicy::Unchecked
|
||||
naga::proc::BoundsCheckPolicy::Unchecked
|
||||
} else {
|
||||
naga::back::BoundsCheckPolicy::Restrict
|
||||
naga::proc::BoundsCheckPolicy::Restrict
|
||||
},
|
||||
image: if self.private_caps.robust_image_access {
|
||||
naga::back::BoundsCheckPolicy::Unchecked
|
||||
naga::proc::BoundsCheckPolicy::Unchecked
|
||||
} else {
|
||||
naga::back::BoundsCheckPolicy::Restrict
|
||||
naga::proc::BoundsCheckPolicy::Restrict
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1222,6 +1270,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
|
||||
raw_device,
|
||||
true,
|
||||
&enabled_extensions,
|
||||
features,
|
||||
uab_types,
|
||||
family_info.queue_family_index,
|
||||
0,
|
||||
|
@ -379,6 +379,15 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
vk_image_views.push(at.view.raw);
|
||||
fb_key.attachments.push(at.view.attachment.clone());
|
||||
}
|
||||
|
||||
// Assert this attachment is valid for the detected multiview, as a sanity check
|
||||
// The driver crash for this is really bad on AMD, so the check is worth it
|
||||
if let Some(multiview) = desc.multiview {
|
||||
assert_eq!(cat.target.view.layers, multiview);
|
||||
if let Some(ref resolve_target) = cat.resolve_target {
|
||||
assert_eq!(resolve_target.view.layers, multiview);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref ds) = desc.depth_stencil_attachment {
|
||||
vk_clear_values.push(vk::ClearValue {
|
||||
@ -393,8 +402,15 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
stencil_ops: ds.stencil_ops,
|
||||
});
|
||||
fb_key.attachments.push(ds.target.view.attachment.clone());
|
||||
|
||||
// Assert this attachment is valid for the detected multiview, as a sanity check
|
||||
// The driver crash for this is really bad on AMD, so the check is worth it
|
||||
if let Some(multiview) = desc.multiview {
|
||||
assert_eq!(ds.target.view.layers, multiview);
|
||||
}
|
||||
}
|
||||
rp_key.sample_count = fb_key.sample_count;
|
||||
rp_key.multiview = desc.multiview;
|
||||
|
||||
let render_area = vk::Rect2D {
|
||||
offset: vk::Offset2D { x: 0, y: 0 },
|
||||
|
@ -5,7 +5,9 @@ use ash::{extensions::khr, vk};
|
||||
use inplace_it::inplace_or_alloc_from_iter;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use std::{borrow::Cow, collections::hash_map::Entry, ffi::CString, ptr, sync::Arc};
|
||||
use std::{
|
||||
borrow::Cow, collections::hash_map::Entry, ffi::CString, num::NonZeroU32, ptr, sync::Arc,
|
||||
};
|
||||
|
||||
impl super::DeviceShared {
|
||||
pub(super) unsafe fn set_object_name(
|
||||
@ -140,10 +142,30 @@ impl super::DeviceShared {
|
||||
vk_subpass.build()
|
||||
}];
|
||||
|
||||
let vk_info = vk::RenderPassCreateInfo::builder()
|
||||
let mut vk_info = vk::RenderPassCreateInfo::builder()
|
||||
.attachments(&vk_attachments)
|
||||
.subpasses(&vk_subpasses);
|
||||
|
||||
let mut multiview_info;
|
||||
let mask;
|
||||
if let Some(multiview) = e.key().multiview {
|
||||
// Sanity checks, better to panic here than cause a driver crash
|
||||
assert!(multiview.get() <= 8);
|
||||
assert!(multiview.get() > 1);
|
||||
|
||||
// Right now we enable all bits on the view masks and correlation masks.
|
||||
// This means we're rendering to all views in the subpass, and that all views
|
||||
// can be rendered concurrently.
|
||||
mask = [(1 << multiview.get()) - 1];
|
||||
|
||||
// On Vulkan 1.1 or later, this is an alias for core functionality
|
||||
multiview_info = vk::RenderPassMultiviewCreateInfoKHR::builder()
|
||||
.view_masks(&mask)
|
||||
.correlation_masks(&mask)
|
||||
.build();
|
||||
vk_info = vk_info.push_next(&mut multiview_info);
|
||||
}
|
||||
|
||||
let raw = unsafe { self.raw.create_render_pass(&vk_info, None)? };
|
||||
|
||||
*e.insert(raw)
|
||||
@ -605,10 +627,10 @@ impl super::Device {
|
||||
let temp_options;
|
||||
let options = if !runtime_checks {
|
||||
temp_options = naga::back::spv::Options {
|
||||
bounds_check_policies: naga::back::BoundsCheckPolicies {
|
||||
index: naga::back::BoundsCheckPolicy::Unchecked,
|
||||
buffer: naga::back::BoundsCheckPolicy::Unchecked,
|
||||
image: naga::back::BoundsCheckPolicy::Unchecked,
|
||||
bounds_check_policies: naga::proc::BoundsCheckPolicies {
|
||||
index: naga::proc::BoundsCheckPolicy::Unchecked,
|
||||
buffer: naga::proc::BoundsCheckPolicy::Unchecked,
|
||||
image: naga::proc::BoundsCheckPolicy::Unchecked,
|
||||
},
|
||||
..self.naga_options.clone()
|
||||
};
|
||||
@ -843,12 +865,15 @@ impl crate::Device<super::Api> for super::Device {
|
||||
texture: &super::Texture,
|
||||
desc: &crate::TextureViewDescriptor,
|
||||
) -> Result<super::TextureView, crate::DeviceError> {
|
||||
let subresource_range = conv::map_subresource_range(&desc.range, texture.aspects);
|
||||
let mut vk_info = vk::ImageViewCreateInfo::builder()
|
||||
.flags(vk::ImageViewCreateFlags::empty())
|
||||
.image(texture.raw)
|
||||
.view_type(conv::map_view_dimension(desc.dimension))
|
||||
.format(self.shared.private_caps.map_texture_format(desc.format))
|
||||
.subresource_range(conv::map_subresource_range(&desc.range, texture.aspects));
|
||||
.subresource_range(subresource_range);
|
||||
let layers =
|
||||
NonZeroU32::new(subresource_range.layer_count).expect("Unexpected zero layer count");
|
||||
|
||||
let mut image_view_info;
|
||||
let view_usage = if self.shared.private_caps.image_view_usage && !desc.usage.is_empty() {
|
||||
@ -879,7 +904,11 @@ impl crate::Device<super::Api> for super::Device {
|
||||
view_format: desc.format,
|
||||
};
|
||||
|
||||
Ok(super::TextureView { raw, attachment })
|
||||
Ok(super::TextureView {
|
||||
raw,
|
||||
layers,
|
||||
attachment,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_texture_view(&self, view: super::TextureView) {
|
||||
if !self.shared.private_caps.imageless_framebuffers {
|
||||
@ -1286,10 +1315,10 @@ impl crate::Device<super::Api> for super::Device {
|
||||
}
|
||||
let mut naga_options = self.naga_options.clone();
|
||||
if !desc.runtime_checks {
|
||||
naga_options.bounds_check_policies = naga::back::BoundsCheckPolicies {
|
||||
index: naga::back::BoundsCheckPolicy::Unchecked,
|
||||
buffer: naga::back::BoundsCheckPolicy::Unchecked,
|
||||
image: naga::back::BoundsCheckPolicy::Unchecked,
|
||||
naga_options.bounds_check_policies = naga::proc::BoundsCheckPolicies {
|
||||
index: naga::proc::BoundsCheckPolicy::Unchecked,
|
||||
buffer: naga::proc::BoundsCheckPolicy::Unchecked,
|
||||
image: naga::proc::BoundsCheckPolicy::Unchecked,
|
||||
};
|
||||
}
|
||||
Cow::Owned(
|
||||
@ -1335,6 +1364,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
];
|
||||
let mut compatible_rp_key = super::RenderPassKey {
|
||||
sample_count: desc.multisample.count,
|
||||
multiview: desc.multiview,
|
||||
..Default::default()
|
||||
};
|
||||
let mut stages = ArrayVec::<_, 2>::new();
|
||||
|
@ -31,7 +31,7 @@ mod conv;
|
||||
mod device;
|
||||
mod instance;
|
||||
|
||||
use std::{borrow::Borrow, ffi::CStr, sync::Arc};
|
||||
use std::{borrow::Borrow, ffi::CStr, num::NonZeroU32, sync::Arc};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use ash::{
|
||||
@ -210,6 +210,7 @@ struct RenderPassKey {
|
||||
colors: ArrayVec<ColorAttachmentKey, { crate::MAX_COLOR_TARGETS }>,
|
||||
depth_stencil: Option<DepthStencilAttachmentKey>,
|
||||
sample_count: u32,
|
||||
multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
@ -373,6 +374,7 @@ impl Texture {
|
||||
#[derive(Debug)]
|
||||
pub struct TextureView {
|
||||
raw: vk::ImageView,
|
||||
layers: NonZeroU32,
|
||||
attachment: FramebufferAttachment,
|
||||
}
|
||||
|
||||
|
@ -526,6 +526,13 @@ bitflags::bitflags! {
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const SHADER_PRIMITIVE_INDEX = 1 << 39;
|
||||
/// Enables multiview render passes and `builtin(view_index)` in vertex shaders.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - Vulkan
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const MULTIVIEW = 1 << 40;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,20 +135,20 @@ env_logger = "0.8"
|
||||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "eda078d"
|
||||
rev = "29571cc"
|
||||
#version = "0.7"
|
||||
optional = true
|
||||
|
||||
# used to test all the example shaders
|
||||
[dev-dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "eda078d"
|
||||
rev = "29571cc"
|
||||
#version = "0.7"
|
||||
features = ["wgsl-in"]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "eda078d"
|
||||
rev = "29571cc"
|
||||
#version = "0.7"
|
||||
features = ["wgsl-out"]
|
||||
|
||||
|
@ -159,6 +159,7 @@ impl framework::Example for Example {
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
// create compute pipeline
|
||||
|
@ -127,6 +127,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let texture = {
|
||||
|
@ -113,6 +113,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let pipeline_triangle_regular =
|
||||
@ -132,6 +133,7 @@ impl framework::Example for Example {
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let pipeline_lines = if device
|
||||
@ -159,6 +161,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
@ -215,6 +218,7 @@ impl framework::Example for Example {
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
}),
|
||||
bind_group_layout,
|
||||
)
|
||||
|
@ -260,6 +260,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let pipeline_wire = if device.features().contains(wgt::Features::POLYGON_MODE_LINE) {
|
||||
@ -295,6 +296,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
Some(pipeline_wire)
|
||||
} else {
|
||||
|
@ -64,6 +64,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let mut config = wgpu::SurfaceConfiguration {
|
||||
|
@ -104,6 +104,7 @@ impl Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let bind_group_layout = pipeline.get_bind_group_layout(0);
|
||||
@ -300,6 +301,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
// Create bind group
|
||||
|
@ -72,6 +72,7 @@ impl Example {
|
||||
count: sample_count,
|
||||
..Default::default()
|
||||
},
|
||||
multiview: None,
|
||||
});
|
||||
let mut encoder =
|
||||
device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
|
||||
@ -79,6 +80,7 @@ impl Example {
|
||||
color_formats: &[config.format],
|
||||
depth_stencil: None,
|
||||
sample_count,
|
||||
multiview: None,
|
||||
});
|
||||
encoder.set_pipeline(&pipeline);
|
||||
encoder.set_vertex_buffer(0, vertex_buffer.slice(..));
|
||||
|
@ -527,6 +527,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Pass {
|
||||
@ -658,6 +659,7 @@ impl framework::Example for Example {
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Pass {
|
||||
|
@ -223,6 +223,7 @@ impl framework::Example for Skybox {
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
let entity_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Entity"),
|
||||
@ -253,6 +254,7 @@ impl framework::Example for Skybox {
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
|
@ -250,6 +250,7 @@ impl framework::Example for Example {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
|
@ -562,6 +562,7 @@ impl framework::Example for Example {
|
||||
}),
|
||||
// No multisampling is used.
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
// Same idea as the water pipeline.
|
||||
@ -595,6 +596,7 @@ impl framework::Example for Example {
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
// Done
|
||||
|
@ -1285,6 +1285,7 @@ impl crate::Context for Context {
|
||||
},
|
||||
targets: Borrowed(frag.targets),
|
||||
}),
|
||||
multiview: desc.multiview,
|
||||
};
|
||||
|
||||
let global = &self.0;
|
||||
@ -1506,6 +1507,7 @@ impl crate::Context for Context {
|
||||
color_formats: Borrowed(desc.color_formats),
|
||||
depth_stencil: desc.depth_stencil,
|
||||
sample_count: desc.sample_count,
|
||||
multiview: desc.multiview,
|
||||
};
|
||||
match wgc::command::RenderBundleEncoder::new(&descriptor, device.id, None) {
|
||||
Ok(id) => id,
|
||||
|
@ -1294,6 +1294,9 @@ pub struct RenderPipelineDescriptor<'a> {
|
||||
pub multisample: MultisampleState,
|
||||
/// The compiled fragment stage, its entry point, and the color targets.
|
||||
pub fragment: Option<FragmentState<'a>>,
|
||||
/// If the pipeline will be used with a multiview render pass, this indicates how many array
|
||||
/// layers the attachments will have.
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
/// Describes the attachments of a compute pass.
|
||||
@ -1349,6 +1352,8 @@ pub struct RenderBundleEncoderDescriptor<'a> {
|
||||
/// Sample count this render bundle is capable of rendering to. This must match the pipelines and
|
||||
/// the renderpasses it is used in.
|
||||
pub sample_count: u32,
|
||||
/// If this render bundle will rendering to multiple array layers in the attachments at the same time.
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
/// Surface texture that can be rendered to.
|
||||
|
@ -77,6 +77,7 @@ fn pulling_common(
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
}],
|
||||
}),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let dummy = ctx
|
||||
|
Loading…
Reference in New Issue
Block a user