Add multiview support (#2187)

Co-authored-by: Layl Bongers <2385329-layl@users.noreply.gitlab.com>
This commit is contained in:
Layl 2021-11-19 16:56:48 +01:00 committed by GitHub
parent fca82ddd7e
commit 2ef72b9313
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 256 additions and 34 deletions

10
Cargo.lock generated
View File

@ -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"

View File

@ -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);

View File

@ -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 {

View File

@ -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"]

View File

@ -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,
}

View File

@ -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,

View File

@ -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();

View File

@ -27,6 +27,7 @@ pub(crate) fn new_render_bundle_encoder_descriptor<'a>(
}
}),
sample_count: context.sample_count,
multiview: context.multiview,
}
}

View File

@ -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)]

View File

@ -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"]

View File

@ -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);

View File

@ -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)]

View File

@ -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,

View File

@ -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 },

View File

@ -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();

View File

@ -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,
}

View File

@ -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;
}
}

View File

@ -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"]

View File

@ -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

View File

@ -127,6 +127,7 @@ impl framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
let texture = {

View File

@ -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,
)

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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(..));

View File

@ -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 {

View File

@ -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 {

View File

@ -250,6 +250,7 @@ impl framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
Self {

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -77,6 +77,7 @@ fn pulling_common(
write_mask: wgpu::ColorWrites::ALL,
}],
}),
multiview: None,
});
let dummy = ctx