Add multiview limits and tests (#8206)

Co-authored-by: Andreas Reich <r_andreas2@web.de>
Co-authored-by: Magnus <85136135+SupaMaggie70Incorporated@users.noreply.github.com>
This commit is contained in:
Inner Daemons 2025-11-01 07:08:15 -05:00 committed by GitHub
parent 5633ae8649
commit ad0f3111b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
124 changed files with 1015 additions and 191 deletions

View File

@ -40,7 +40,7 @@ Bottom level categories:
## Unreleased
### Major changes
### Major Changes
#### `wgpu::Instance::enumerate_adapters` is now `async` & available on WebGPU
@ -73,6 +73,38 @@ SamplerDescriptor {
}
```
#### Multiview on all major platforms and support for multiview bitmasks
Multiview is a feature that allows rendering the same content to multiple layers of a texture. This is useful primarily in VR where you wish to
display almost identical content to 2 views, just with a different perspective. Instead of using 2 draw calls or 2 instances for each object, you
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.
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
- wgpu::RenderPassDescriptor {
- label: None,
- color_attachments: &color_attachments,
- depth_stencil_attachment: None,
- timestamp_writes: None,
- occlusion_query_set: None,
- }
+ wgpu::RenderPassDescriptor {
+ label: None,
+ color_attachments: &color_attachments,
+ depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
+ multiview_mask: NonZero::new(3),
+ }
```
One other breaking change worth noting is that in WGSL `@builtin(view_index)` now requires a type of `u32`, where previously it required `i32`.
By @SupaMaggie70Incorporated in [#8206](https://github.com/gfx-rs/wgpu/pull/8206).
### New Features
- Added support for transient textures on Vulkan and Metal. By @opstic in [#8247](https://github.com/gfx-rs/wgpu/pull/8247)

View File

@ -224,7 +224,7 @@ impl RenderpassState {
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -322,7 +322,7 @@ impl RenderpassState {
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
multiview: None,
multiview_mask: None,
cache: None,
},
));
@ -370,6 +370,7 @@ impl RenderpassState {
occlusion_query_set: None,
timestamp_writes: None,
depth_stencil_attachment: None,
multiview_mask: None,
});
let start_idx = pass_number * draws_per_pass;
@ -417,6 +418,7 @@ impl RenderpassState {
occlusion_query_set: None,
timestamp_writes: None,
depth_stencil_attachment: None,
multiview_mask: None,
});
render_pass.set_pipeline(self.bindless_pipeline.as_ref().unwrap());

View File

@ -2,6 +2,7 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::num::NonZero;
use deno_core::cppgc::Ptr;
use deno_core::op2;
@ -142,6 +143,7 @@ impl GPUCommandEncoder {
occlusion_query_set: descriptor
.occlusion_query_set
.map(|query_set| query_set.id),
multiview_mask: NonZero::new(descriptor.multiview_mask),
};
let (render_pass, err) = self

View File

@ -876,7 +876,7 @@ impl GPUDevice {
multisample,
fragment,
cache: None,
multiview: None,
multiview_mask: None,
};
let (id, err) = self.instance.device_create_render_pipeline(

View File

@ -460,6 +460,9 @@ pub(crate) struct GPURenderPassDescriptor {
/*#[webidl(default = 50000000)]
#[options(enforce_range = true)]
pub max_draw_count: u64,*/
#[webidl(default = 0)]
#[options(enforce_range = true)]
pub multiview_mask: u32,
}
#[derive(WebIDL)]

View File

@ -148,7 +148,7 @@ impl crate::framework::Example for Example {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -276,6 +276,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
};
// get command encoder

View File

@ -122,6 +122,7 @@ impl Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);
rpass.set_bind_group(0, &self.global_group, &[]);
@ -229,7 +230,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});

View File

@ -106,7 +106,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -129,7 +129,7 @@ impl crate::framework::Example for Example {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -160,7 +160,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
}),
)
@ -217,7 +217,7 @@ impl crate::framework::Example for Example {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
}),
bind_group_layout,
@ -273,6 +273,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline_triangle_conservative);
@ -295,6 +296,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline_upscale);

View File

@ -256,7 +256,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -298,7 +298,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
Some(pipeline_wire)
@ -356,6 +356,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.push_debug_group("Prepare data for draw.");
rpass.set_pipeline(&self.pipeline);

View File

@ -71,7 +71,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -129,6 +129,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&render_pipeline);
rpass.draw(0..3, 0..1);

View File

@ -133,6 +133,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc<Window>, wgpu::Color
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
}

View File

@ -17,6 +17,7 @@ pub mod mesh_shader;
pub mod mipmap;
pub mod msaa_line;
pub mod multiple_render_targets;
pub mod multiview;
pub mod ray_cube_compute;
pub mod ray_cube_fragment;
pub mod ray_cube_normals;

View File

@ -188,6 +188,12 @@ const EXAMPLES: &[ExampleDesc] = &[
webgl: false,
webgpu: false,
},
ExampleDesc {
name: "multiview",
function: wgpu_examples::multiview::main,
webgl: false,
webgpu: false,
},
];
fn get_example_name() -> Option<String> {

View File

@ -144,6 +144,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.push_debug_group("Prepare data for draw.");
rpass.set_pipeline(&self.pipeline);

View File

@ -104,7 +104,7 @@ impl Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -170,6 +170,7 @@ impl Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
if let Some(ref query_sets) = query_sets {
rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base);
@ -305,7 +306,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -492,6 +493,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.draw_pipeline);
rpass.set_bind_group(0, &self.bind_group, &[]);

View File

@ -77,7 +77,7 @@ impl Example {
count: sample_count,
..Default::default()
},
multiview: None,
multiview_mask: None,
cache: None,
});
let mut encoder =
@ -307,6 +307,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
})
.execute_bundles(iter::once(&self.bundle));
}

View File

@ -161,7 +161,7 @@ impl MultiTargetRenderer {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -182,6 +182,7 @@ impl MultiTargetRenderer {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);
rpass.set_bind_group(0, &self.bindgroup, &[]);
@ -266,7 +267,7 @@ impl TargetRenderer {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -339,6 +340,7 @@ impl TargetRenderer {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);
rpass.set_bind_group(0, &self.bindgroup_left, &[]);

View File

@ -0,0 +1,182 @@
//! Renders different content to different layers of an array texture using multiview,
//! a feature commonly used for VR rendering.
use std::{num::NonZero, time::Instant};
use wgpu::util::TextureBlitter;
const TEXTURE_SIZE: u32 = 512;
// Change this to demonstrate non-contiguous multiview functionality
const LAYER_MASK: u32 = 0b11;
const NUM_LAYERS: u32 = 32 - LAYER_MASK.leading_zeros();
pub struct Example {
pipeline: wgpu::RenderPipeline,
entire_texture_view: wgpu::TextureView,
views: Vec<wgpu::TextureView>,
start_time: Instant,
blitter: TextureBlitter,
}
impl crate::framework::Example for Example {
fn init(
config: &wgpu::SurfaceConfiguration,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> Self {
let shader_src = include_str!("shader.wgsl");
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(shader_src.into()),
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
vertex: wgpu::VertexState {
buffers: &[],
module: &shader,
entry_point: Some("vs_main"),
compilation_options: Default::default(),
},
primitive: wgpu::PrimitiveState::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: NonZero::new(LAYER_MASK),
multisample: Default::default(),
layout: None,
depth_stencil: None,
cache: None,
});
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: TEXTURE_SIZE,
height: TEXTURE_SIZE,
depth_or_array_layers: NUM_LAYERS,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let entire_texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8Unorm),
dimension: Some(wgpu::TextureViewDimension::D2Array),
usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: Some(NUM_LAYERS),
});
let mut views = Vec::new();
for i in 0..NUM_LAYERS {
views.push(texture.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8Unorm),
dimension: Some(wgpu::TextureViewDimension::D2),
usage: Some(wgpu::TextureUsages::TEXTURE_BINDING),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: i,
array_layer_count: Some(1),
}));
}
let blitter = wgpu::util::TextureBlitter::new(device, config.format);
Self {
pipeline,
entire_texture_view,
views,
blitter,
start_time: Instant::now(),
}
}
fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &self.entire_texture_view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.02,
g: 0.02,
b: 0.02,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: NonZero::new(LAYER_MASK),
});
rpass.set_pipeline(&self.pipeline);
rpass.draw(0..6, 0..1);
}
let layer = (Instant::now() - self.start_time).as_secs() % NUM_LAYERS as u64;
self.blitter
.copy(device, &mut encoder, &self.views[layer as usize], view);
queue.submit(Some(encoder.finish()));
}
fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
Default::default()
}
fn required_features() -> wgpu::Features {
wgpu::Features::MULTIVIEW
| if !(LAYER_MASK + 1).is_power_of_two() {
wgpu::Features::SELECTIVE_MULTIVIEW
} else {
wgpu::Features::empty()
}
}
fn required_limits() -> wgpu::Limits {
wgpu::Limits {
max_multiview_view_count: NUM_LAYERS,
..Default::default()
}
}
fn resize(
&mut self,
_config: &wgpu::SurfaceConfiguration,
_device: &wgpu::Device,
_queue: &wgpu::Queue,
) {
// empty
}
fn update(&mut self, _event: winit::event::WindowEvent) {
// empty
}
}
pub fn main() {
crate::framework::run::<Example>("multiview");
}

View File

@ -0,0 +1,11 @@
const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {
return vec4f(triangles[vertex_index], 0.0, 1.0);
}
@fragment
fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f {
return vec4f(f32(view_index) * 0.25 + 0.125, 1.0 - f32(view_index) * 0.25, 1.0 - 0.5 * f32(view_index), 1.0);
}

View File

@ -326,7 +326,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -469,6 +469,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.blit_pipeline);

View File

@ -216,7 +216,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -355,6 +355,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);

View File

@ -317,7 +317,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -455,6 +455,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.blit_pipeline);

View File

@ -395,7 +395,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -535,6 +535,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);

View File

@ -239,7 +239,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -348,6 +348,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);

View File

@ -309,7 +309,7 @@ impl crate::framework::Example for Example {
write_mask: Default::default(),
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -414,6 +414,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.blit_pipeline);

View File

@ -68,7 +68,7 @@ async fn run(_path: Option<String>) {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -95,6 +95,7 @@ async fn run(_path: Option<String>) {
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.draw(0..3, 0..1);

View File

@ -518,7 +518,7 @@ impl crate::framework::Example for Example {
},
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -653,7 +653,7 @@ impl crate::framework::Example for Example {
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -771,6 +771,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&self.shadow_pass.pipeline);
pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]);
@ -816,6 +817,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&self.forward_pass.pipeline);
pass.set_bind_group(0, &self.forward_pass.bind_group, &[]);

View File

@ -218,7 +218,7 @@ impl crate::framework::Example for Example {
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
let entity_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
@ -252,7 +252,7 @@ impl crate::framework::Example for Example {
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -444,6 +444,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_bind_group(0, &self.bind_group, &[]);

View File

@ -146,7 +146,7 @@ impl<const SRGB: bool> crate::framework::Example for Example<SRGB> {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -196,6 +196,7 @@ impl<const SRGB: bool> crate::framework::Example for Example<SRGB> {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.push_debug_group("Prepare data for draw.");
rpass.set_pipeline(&self.pipeline);

View File

@ -100,7 +100,7 @@ impl crate::framework::Example for Example {
bias: Default::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -136,7 +136,7 @@ impl crate::framework::Example for Example {
bias: Default::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -222,6 +222,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_stencil_reference(1);

View File

@ -358,7 +358,7 @@ impl crate::framework::Example for Example {
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None
});
@ -402,6 +402,7 @@ impl crate::framework::Example for Example {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.pipeline);

View File

@ -358,7 +358,7 @@ fn render_pass(
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
let render_target = device.create_texture(&wgpu::TextureDescriptor {
@ -395,6 +395,7 @@ fn render_pass(
end_of_pass_write_index: Some(*next_unused_query + 1),
}),
occlusion_query_set: None,
multiview_mask: None,
});
*next_unused_query += 2;

View File

@ -186,7 +186,7 @@ impl WgpuContext {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
let surface_config = surface
@ -319,6 +319,7 @@ async fn run(event_loop: EventLoop<()>, window: Arc<Window>) {
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
multiview_mask: None,
});
render_pass.set_pipeline(&wgpu_context_ref.pipeline);
// (9)

View File

@ -567,7 +567,7 @@ impl crate::framework::Example for Example {
}),
// No multisampling is used.
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
// No pipeline caching is used
cache: None,
});
@ -605,7 +605,7 @@ impl crate::framework::Example for Example {
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None
});
@ -750,6 +750,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.execute_bundles([&self.terrain_bundle]);
@ -778,6 +779,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.terrain_pipeline);
rpass.set_bind_group(0, &self.terrain_normal_bind_group, &[]);
@ -805,6 +807,7 @@ impl crate::framework::Example for Example {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&self.water_pipeline);

View File

@ -107,6 +107,7 @@ impl State {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
// If you wanted to call any drawing commands, they would go here.

View File

@ -5215,8 +5215,13 @@ const fn glsl_built_in(built_in: crate::BuiltIn, options: VaryingOptions) -> &'s
"gl_FragCoord"
}
}
Bi::ViewIndex if options.targeting_webgl => "int(gl_ViewID_OVR)",
Bi::ViewIndex => "gl_ViewIndex",
Bi::ViewIndex => {
if options.targeting_webgl {
"gl_ViewID_OVR"
} else {
"uint(gl_ViewIndex)"
}
}
// vertex
Bi::BaseInstance => "uint(gl_BaseInstance)",
Bi::BaseVertex => "uint(gl_BaseVertex)",

View File

@ -173,6 +173,7 @@ impl crate::BuiltIn {
// to this field will get replaced with references to `SPECIAL_CBUF_VAR`
// in `Writer::write_expr`.
Self::NumWorkGroups => "SV_GroupID",
Self::ViewIndex => "SV_ViewID",
// These builtins map to functions
Self::SubgroupSize
| Self::SubgroupInvocationId
@ -181,7 +182,7 @@ impl crate::BuiltIn {
Self::BaseInstance | Self::BaseVertex | Self::WorkGroupSize => {
return Err(Error::Unimplemented(format!("builtin {self:?}")))
}
Self::PointSize | Self::ViewIndex | Self::PointCoord | Self::DrawID => {
Self::PointSize | Self::PointCoord | Self::DrawID => {
return Err(Error::Custom(format!("Unsupported builtin {self:?}")))
}
Self::CullPrimitive => "SV_CullPrimitive",

View File

@ -650,6 +650,8 @@ pub enum Error {
ResolveArraySizeError(#[from] proc::ResolveArraySizeError),
#[error("entry point with stage {0:?} and name '{1}' not found")]
EntryPointNotFound(ir::ShaderStage, String),
#[error("requires shader model {1:?} for reason: {0}")]
ShaderModelTooLow(String, ShaderModel),
}
#[derive(PartialEq, Eq, Hash)]

View File

@ -569,6 +569,14 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
) -> BackendResult {
match *binding {
Some(crate::Binding::BuiltIn(builtin)) if !is_subgroup_builtin_binding(binding) => {
if builtin == crate::BuiltIn::ViewIndex
&& self.options.shader_model < ShaderModel::V6_1
{
return Err(Error::ShaderModelTooLow(
"used @builtin(view_index) or SV_ViewID".to_string(),
ShaderModel::V6_1,
));
}
let builtin_str = builtin.to_hlsl_str()?;
write!(self.out, " : {builtin_str}")?;
}

View File

@ -530,6 +530,12 @@ impl Options {
crate::BuiltIn::PrimitiveIndex if self.lang_version < (2, 3) => {
return Err(Error::UnsupportedAttribute("primitive_id".to_string()));
}
// macOS: since Metal 2.3
// iOS: Since Metal 2.2
// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf#page=114
crate::BuiltIn::ViewIndex if self.lang_version < (2, 2) => {
return Err(Error::UnsupportedAttribute("amplification_id".to_string()));
}
// macOS: Since Metal 2.2
// iOS: Since Metal 2.3 (check depends on https://github.com/gfx-rs/wgpu/issues/4414)
crate::BuiltIn::Barycentric if self.lang_version < (2, 3) => {
@ -674,6 +680,7 @@ impl ResolvedBinding {
let name = match built_in {
Bi::Position { invariant: false } => "position",
Bi::Position { invariant: true } => "position, invariant",
Bi::ViewIndex => "amplification_id",
// vertex
Bi::BaseInstance => "base_instance",
Bi::BaseVertex => "base_vertex",
@ -701,7 +708,7 @@ impl ResolvedBinding {
Bi::SubgroupId => "simdgroup_index_in_threadgroup",
Bi::SubgroupSize => "threads_per_simdgroup",
Bi::SubgroupInvocationId => "thread_index_in_simdgroup",
Bi::CullDistance | Bi::ViewIndex | Bi::DrawID => {
Bi::CullDistance | Bi::DrawID => {
return Err(Error::UnsupportedBuiltIn(built_in))
}
Bi::CullPrimitive => "primitive_culled",

View File

@ -301,7 +301,7 @@ impl VaryingContext<'_> {
St::Vertex | St::Fragment | St::Task | St::Mesh => !self.output,
St::Compute => false,
},
*ty_inner == Ti::Scalar(crate::Scalar::I32),
*ty_inner == Ti::Scalar(crate::Scalar::U32),
),
Bi::FragDepth => (
self.stage == St::Fragment && self.output,

View File

@ -1,3 +1,8 @@
glsl_multiview = 2
god_mode = true
targets = "SPIRV | GLSL | WGSL"
[msl]
lang_version = [2, 3]
[hlsl]
shader_model = "V6_1"

View File

@ -1,2 +1,2 @@
@fragment
fn main(@builtin(view_index) view_index: i32) {}
fn main(@builtin(view_index) view_index: u32) {}

View File

@ -1,2 +1,2 @@
@fragment
fn main(@builtin(view_index) view_index: i32) {}
fn main(@builtin(view_index) view_index: u32) {}

View File

@ -6,7 +6,7 @@ precision highp int;
void main() {
int view_index = gl_ViewIndex;
uint view_index = uint(gl_ViewIndex);
return;
}

View File

@ -6,7 +6,7 @@ precision highp int;
void main() {
int view_index = int(gl_ViewID_OVR);
uint view_index = gl_ViewID_OVR;
return;
}

View File

@ -0,0 +1,9 @@
struct FragmentInput_main {
uint view_index_1 : SV_ViewID;
};
void main(FragmentInput_main fragmentinput_main)
{
uint view_index = fragmentinput_main.view_index_1;
return;
}

View File

@ -0,0 +1,12 @@
(
vertex:[
],
fragment:[
(
entry_point:"main",
target_profile:"ps_6_1",
),
],
compute:[
],
)

View File

@ -0,0 +1,14 @@
// language: metal2.3
#include <metal_stdlib>
#include <simd/simd.h>
using metal::uint;
struct main_Input {
};
fragment void main_(
uint view_index [[amplification_id]]
) {
return;
}

View File

@ -12,7 +12,7 @@ OpExecutionMode %8 OriginUpperLeft
OpDecorate %5 BuiltIn ViewIndex
OpDecorate %5 Flat
%2 = OpTypeVoid
%3 = OpTypeInt 32 1
%3 = OpTypeInt 32 0
%6 = OpTypePointer Input %3
%5 = OpVariable %6 Input
%9 = OpTypeFunction %2

View File

@ -1,4 +1,4 @@
@fragment
fn main(@builtin(view_index) view_index: i32) {
fn main(@builtin(view_index) view_index: u32) {
return;
}

View File

@ -674,7 +674,7 @@ impl Player {
depth_stencil: desc.depth_stencil,
multisample: desc.multisample,
fragment,
multiview: desc.multiview,
multiview_mask: desc.multiview_mask,
cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)),
}
}
@ -851,6 +851,7 @@ impl Player {
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
multiview_mask,
} => Command::RunRenderPass {
pass: self.resolve_render_pass(pass),
color_attachments: self.resolve_color_attachments(color_attachments),
@ -858,6 +859,7 @@ impl Player {
.map(|att| self.resolve_depth_stencil_attachment(att)),
timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)),
occlusion_query_set: occlusion_query_set.map(|qs| self.resolve_query_set_id(qs)),
multiview_mask,
},
Command::BuildAccelerationStructures { blas, tlas } => {
Command::BuildAccelerationStructures {

View File

@ -207,7 +207,7 @@ async fn binding_array_sampled_textures(ctx: TestingContext, partially_bound: bo
depth_stencil: None,
multisample: MultisampleState::default(),
cache: None,
multiview: None,
multiview_mask: None,
});
let mut encoder = ctx
@ -228,6 +228,7 @@ async fn binding_array_sampled_textures(ctx: TestingContext, partially_bound: bo
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.set_bind_group(0, &bind_group, &[]);

View File

@ -43,7 +43,7 @@ async fn clip_distances(ctx: TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -87,6 +87,7 @@ async fn clip_distances(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
rpass.draw(0..3, 0..1);

View File

@ -346,6 +346,7 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
drop(pass);
ctx.queue.submit([encoder_for_render_pass.finish()]);
@ -458,7 +459,7 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
fragment: None,
multiview: None,
multiview_mask: None,
cache: None,
});
@ -616,7 +617,7 @@ static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConf
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});

View File

@ -207,7 +207,7 @@ async fn run_test(ctx: TestingContext, test_data: TestData, expect_noop: bool) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
@ -272,6 +272,7 @@ async fn run_test(ctx: TestingContext, test_data: TestData, expect_noop: bool) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
@ -697,7 +698,7 @@ async fn indirect_buffer_offsets(ctx: TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
@ -747,6 +748,7 @@ async fn indirect_buffer_offsets(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -96,7 +96,7 @@ async fn dual_source_blending_disabled(ctx: TestingContext) {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
},
@ -159,7 +159,7 @@ async fn dual_source_blending_enabled(ctx: TestingContext) {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
};

View File

@ -77,6 +77,7 @@ static DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::ne
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
// This viewport is invalid because it has negative size.

View File

@ -36,6 +36,7 @@ mod instance;
mod life_cycle;
mod mem_leaks;
mod mesh_shader;
mod multiview;
mod occlusion_query;
mod oob_indexing;
mod oom;
@ -99,6 +100,7 @@ fn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {
life_cycle::all_tests(&mut tests);
mem_leaks::all_tests(&mut tests);
mesh_shader::all_tests(&mut tests);
multiview::all_tests(&mut tests);
occlusion_query::all_tests(&mut tests);
oob_indexing::all_tests(&mut tests);
oom::all_tests(&mut tests);

View File

@ -126,7 +126,7 @@ async fn draw_test_with_reports(
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -207,6 +207,7 @@ async fn draw_test_with_reports(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -242,6 +242,7 @@ fn mesh_pipeline_build(ctx: &TestingContext, info: MeshPipelineTestInfo) {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&pipeline);
pass.draw_mesh_tasks(1, 1, 1);
@ -348,6 +349,7 @@ fn mesh_draw(ctx: &TestingContext, draw_type: DrawType) {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&pipeline);
match draw_type {

View File

@ -0,0 +1,193 @@
use std::num::NonZero;
use wgpu::{Features, Limits};
use wgpu_test::{
gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,
};
pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
vec.push(DRAW_MULTIVIEW_SINGLE);
vec.push(DRAW_MULTIVIEW);
vec.push(DRAW_MULTIVIEW_NONCONTIGUOUS);
}
#[gpu_test]
static DRAW_MULTIVIEW_SINGLE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.features(Features::MULTIVIEW)
.limits(Limits {
max_multiview_view_count: 1,
..Limits::defaults()
}),
)
.run_async(|ctx| run_test(ctx, 0b1));
#[gpu_test]
static DRAW_MULTIVIEW: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.features(Features::MULTIVIEW)
.limits(Limits {
max_multiview_view_count: 2,
..Limits::defaults()
}),
)
.run_async(|ctx| run_test(ctx, 0b11));
#[gpu_test]
static DRAW_MULTIVIEW_NONCONTIGUOUS: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.features(Features::MULTIVIEW | Features::SELECTIVE_MULTIVIEW)
.limits(Limits {
max_multiview_view_count: 4,
..Limits::defaults()
}),
)
.run_async(|ctx| run_test(ctx, 0b1001));
async fn run_test(ctx: TestingContext, layer_mask: u32) {
let num_layers = 32 - layer_mask.leading_zeros();
let shader_src = include_str!("shader.wgsl");
let shader = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(shader_src.into()),
});
let pipeline_desc = wgpu::RenderPipelineDescriptor {
label: None,
vertex: wgpu::VertexState {
buffers: &[],
module: &shader,
entry_point: Some("vs_main"),
compilation_options: Default::default(),
},
primitive: wgpu::PrimitiveState::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::R8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: NonZero::new(layer_mask),
multisample: Default::default(),
layout: None,
depth_stencil: None,
cache: None,
};
const TEXTURE_SIZE: u32 = 256;
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: TEXTURE_SIZE,
height: TEXTURE_SIZE,
depth_or_array_layers: 32 - layer_mask.leading_zeros(),
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::R8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let entire_texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::R8Unorm),
dimension: Some(wgpu::TextureViewDimension::D2Array),
usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: Some(num_layers),
});
let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64 * num_layers as u64,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let clear_color = 0.0;
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &entire_texture_view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: NonZero::new(layer_mask),
});
rpass.set_pipeline(&pipeline);
rpass.draw(0..6, 0..1);
}
encoder.copy_texture_to_buffer(
wgpu::TexelCopyTextureInfo {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyBufferInfo {
buffer: &readback_buffer,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(TEXTURE_SIZE),
rows_per_image: Some(TEXTURE_SIZE),
},
},
wgpu::Extent3d {
width: TEXTURE_SIZE,
height: TEXTURE_SIZE,
depth_or_array_layers: num_layers,
},
);
ctx.queue.submit([encoder.finish()]);
let slice = readback_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| ());
ctx.async_poll(wgpu::PollType::wait_indefinitely())
.await
.unwrap();
let data = slice.get_mapped_range();
let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize;
assert_eq!(data.len(), each_texture_size * num_layers as usize);
for view_idx in 0..num_layers as usize {
let target_value = if (layer_mask & (1 << view_idx)) != 0 {
(32 + 64 * view_idx) as u8
} else {
(clear_color * 255.0) as u8
};
// Some metal devices automatically initialize stuff to 255, so I decided to use 128 instead of that
let failed_value = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)]
.iter()
.copied()
.find(|b| b.abs_diff(target_value) > 1);
assert_eq!(failed_value, None, "Expected {target_value}");
}
}

View File

@ -0,0 +1,11 @@
const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {
return vec4f(triangles[vertex_index], 0.0, 1.0);
}
@fragment
fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f {
return vec4f(f32(view_index) * 0.25 + 0.125);
}

View File

@ -56,7 +56,7 @@ static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -84,6 +84,7 @@ static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()
}),
timestamp_writes: None,
occlusion_query_set: Some(&query_set),
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);

View File

@ -129,7 +129,7 @@ static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration =
depth_stencil: None,
multisample: Default::default(),
fragment: None,
multiview: None,
multiview_mask: None,
cache: None,
});
@ -182,7 +182,7 @@ static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration =
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -223,7 +223,7 @@ static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new()
..Default::default()
},
fragment: None,
multiview: None,
multiview_mask: None,
cache: None,
});
}

View File

@ -47,7 +47,7 @@ fn test_planar_texture_creation_sampling(
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -101,6 +101,7 @@ fn test_planar_texture_creation_sampling(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
@ -142,7 +143,7 @@ fn test_planar_texture_rendering(
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -170,7 +171,7 @@ fn test_planar_texture_rendering(
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -190,6 +191,7 @@ fn test_planar_texture_rendering(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&y_pipeline);
rpass.draw(0..3, 0..1);
@ -206,6 +208,7 @@ fn test_planar_texture_rendering(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&uv_pipeline);
rpass.draw(0..3, 0..1);

View File

@ -301,7 +301,7 @@ async fn render_pass_test(ctx: &TestingContext, use_render_bundle: bool) {
},
depth_stencil: None,
multisample: MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});

View File

@ -123,7 +123,7 @@ async fn multi_stage_data_binding_test(ctx: TestingContext) {
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -166,6 +166,7 @@ async fn multi_stage_data_binding_test(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -84,7 +84,7 @@ static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = GpuTestConfiguration::ne
write_mask: ColorWrites::all(),
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -116,7 +116,7 @@ static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = GpuTestConfiguration::ne
write_mask: ColorWrites::all(),
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -156,6 +156,7 @@ static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = GpuTestConfiguration::ne
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
double_rpass.set_pipeline(&double_pipeline);
@ -192,6 +193,7 @@ static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = GpuTestConfiguration::ne
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
single_rpass.set_pipeline(&single_pipeline);

View File

@ -68,7 +68,7 @@ async fn test_impl(ctx: &TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -98,6 +98,7 @@ async fn test_impl(ctx: &TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.draw(0..3, 0..1);

View File

@ -68,7 +68,7 @@ async fn test_impl(ctx: &TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -98,6 +98,7 @@ async fn test_impl(ctx: &TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.draw(0..3, 0..1);

View File

@ -53,7 +53,7 @@ static ALLOW_INPUT_NOT_CONSUMED: GpuTestConfiguration = GpuTestConfiguration::ne
write_mask: ColorWrites::all(),
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
});

View File

@ -91,6 +91,7 @@ async fn render_pass_resource_ownership(ctx: TestingContext) {
}),
timestamp_writes: None,
occlusion_query_set: Some(&occlusion_query_set),
multiview_mask: None,
});
// Drop render pass attachments right away.
@ -548,7 +549,7 @@ fn resource_setup(ctx: &TestingContext) -> ResourceSetup {
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
multiview_mask: None,
cache: None,
});

View File

@ -106,7 +106,7 @@ async fn run_test(
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
@ -203,6 +203,7 @@ async fn run_test(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
@ -318,7 +319,7 @@ async fn run_test_3d(ctx: TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
@ -386,6 +387,7 @@ async fn run_test_3d(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));

View File

@ -69,7 +69,7 @@ async fn scissor_test_impl(
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -98,6 +98,7 @@ async fn scissor_test_impl(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.set_scissor_rect(

View File

@ -95,7 +95,7 @@ async fn barycentric(ctx: TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -138,6 +138,7 @@ async fn barycentric(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -150,7 +150,7 @@ async fn pulling_common(
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -193,6 +193,7 @@ async fn pulling_common(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -113,7 +113,7 @@ async fn reinterpret(
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
multiview_mask: None,
cache: None,
});
let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
@ -151,6 +151,7 @@ async fn reinterpret(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);

View File

@ -64,7 +64,7 @@ static RESOLVE_WITH_TRANSIENT: GpuTestConfiguration = GpuTestConfiguration::new(
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
@ -119,6 +119,7 @@ static RESOLVE_WITH_TRANSIENT: GpuTestConfiguration = GpuTestConfiguration::new(
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -314,7 +314,7 @@ async fn vertex_formats_common(ctx: TestingContext, tests: &[Test<'_>]) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
@ -360,6 +360,7 @@ async fn vertex_formats_common(ctx: TestingContext, tests: &[Test<'_>]) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
rpass.set_vertex_buffer(0, buffer_input.slice(..));

View File

@ -259,7 +259,7 @@ async fn vertex_index_common(ctx: TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let builtin_pipeline = ctx.device.create_render_pipeline(&pipeline_desc);
@ -366,6 +366,7 @@ async fn vertex_index_common(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
{

View File

@ -99,7 +99,7 @@ async fn set_array_stride_to_0(ctx: TestingContext) {
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
};
let mut first_pipeline_desc = pipeline_desc.clone();
@ -148,6 +148,7 @@ async fn set_array_stride_to_0(ctx: TestingContext) {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
// The D3D12 backend used to not set the stride of vertex buffers if it was 0.

View File

@ -173,6 +173,7 @@ impl<'ctx> TestCase<'ctx> {
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
ctx.queue.submit([encoder.finish()]);
} else {
@ -259,6 +260,7 @@ impl<'ctx> TestCase<'ctx> {
),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
}
@ -284,6 +286,7 @@ impl<'ctx> TestCase<'ctx> {
),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
}
@ -309,6 +312,7 @@ impl<'ctx> TestCase<'ctx> {
),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
}

View File

@ -652,7 +652,7 @@ var tex: texture_external;
write_mask: ColorWrites::ALL,
})],
}),
multiview: None,
multiview_mask: None,
cache: None,
});
@ -685,6 +685,7 @@ var tex: texture_external;
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&pipeline);

View File

@ -58,7 +58,7 @@ fn frag() -> @location({}) vec4f {{
primitive: Default::default(),
depth_stencil: None,
multisample: Default::default(),
multiview: None,
multiview_mask: None,
cache: None,
})
},

View File

@ -674,6 +674,7 @@ fn transient_invalid_storeop() {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
drop(invalid_render_pass);

View File

@ -218,7 +218,7 @@ impl RenderBundleEncoder {
}
sc
},
multiview: desc.multiview,
multiview_mask: desc.multiview,
},
is_depth_read_only,
@ -239,7 +239,7 @@ impl RenderBundleEncoder {
depth_stencil: None,
},
sample_count: 0,
multiview: None,
multiview_mask: None,
},
is_depth_read_only: false,
is_stencil_read_only: false,

View File

@ -532,7 +532,7 @@ fn clear_texture_via_render_passes(
sample_count: dst_texture.desc.sample_count,
color_attachments,
depth_stencil_attachment,
multiview: None,
multiview_mask: None,
timestamp_writes: None,
occlusion_query_set: None,
})

View File

@ -71,6 +71,13 @@ pub enum DrawError {
limit: u32,
max_total: u32,
},
#[error(
"Mesh shader calls in multiview render passes require enabling the `EXPERIMENTAL_MESH_SHADER_MULTIVIEW` feature, and the highest bit ({highest_view_index}) in the multiview mask must be <= `Limits::max_multiview_view_count` ({max_multiviews})"
)]
MeshPipelineMultiviewLimitsViolated {
highest_view_index: u32,
max_multiviews: u32,
},
}
impl WebGpuError for DrawError {

View File

@ -1,4 +1,4 @@
use core::convert::Infallible;
use core::{convert::Infallible, num::NonZero};
use alloc::{string::String, sync::Arc, vec::Vec};
#[cfg(feature = "serde")]
@ -171,6 +171,7 @@ pub enum Command<R: ReferenceType> {
Option<crate::command::ResolvedRenderPassDepthStencilAttachment<R::TextureView>>,
timestamp_writes: Option<crate::command::PassTimestampWrites<R::QuerySet>>,
occlusion_query_set: Option<R::QuerySet>,
multiview_mask: Option<NonZero<u32>>,
},
BuildAccelerationStructures {
blas: Vec<crate::ray_tracing::OwnedBlasBuildEntry<R>>,

View File

@ -1019,6 +1019,7 @@ impl CommandEncoder {
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
multiview_mask,
} => {
api_log!(
"Begin encoding render pass with '{}' label",
@ -1031,6 +1032,7 @@ impl CommandEncoder {
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
multiview_mask,
);
match res.as_ref() {
Err(err) => api_log!("Finished encoding render pass ({err:?})"),

View File

@ -250,6 +250,8 @@ pub struct RenderPassDescriptor<'a> {
pub timestamp_writes: Option<&'a PassTimestampWrites>,
/// Defines where the occlusion query results will be stored for this pass.
pub occlusion_query_set: Option<id::QuerySetId>,
/// The multiview array layers that will be used
pub multiview_mask: Option<NonZeroU32>,
}
/// Describes the attachments of a render pass.
@ -265,6 +267,8 @@ struct ArcRenderPassDescriptor<'a> {
pub timestamp_writes: Option<ArcPassTimestampWrites>,
/// Defines where the occlusion query results will be stored for this pass.
pub occlusion_query_set: Option<Arc<QuerySet>>,
/// The multiview array layers that will be used
pub multiview_mask: Option<NonZeroU32>,
}
pub type RenderBasePass = BasePass<ArcRenderCommand, RenderPassError>;
@ -292,6 +296,7 @@ pub struct RenderPass {
depth_stencil_attachment: Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,
timestamp_writes: Option<ArcPassTimestampWrites>,
occlusion_query_set: Option<Arc<QuerySet>>,
multiview_mask: Option<NonZeroU32>,
// Resource binding dedupe state.
current_bind_groups: BindGroupStateChange,
@ -307,6 +312,7 @@ impl RenderPass {
color_attachments,
depth_stencil_attachment,
occlusion_query_set,
multiview_mask,
} = desc;
Self {
@ -316,6 +322,7 @@ impl RenderPass {
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
multiview_mask,
current_bind_groups: BindGroupStateChange::new(),
current_pipeline: StateChange::new(),
@ -330,6 +337,7 @@ impl RenderPass {
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
current_bind_groups: BindGroupStateChange::new(),
current_pipeline: StateChange::new(),
}
@ -353,6 +361,7 @@ impl fmt::Debug for RenderPass {
"push constant u32 count",
&self.base.push_constant_data.len(),
)
.field("multiview mask", &self.multiview_mask)
.finish()
}
}
@ -806,6 +815,8 @@ pub enum RenderPassErrorInner {
"Multiview pass texture views with more than one array layer must have D2Array dimension"
)]
MultiViewDimensionMismatch,
#[error("Multiview view count limit violated")]
TooManyMultiviewViews,
#[error("missing occlusion query set")]
MissingOcclusionQuerySet,
#[error(transparent)]
@ -907,6 +918,7 @@ impl WebGpuError for RenderPassError {
| RenderPassErrorInner::PushConstantOutOfMemory
| RenderPassErrorInner::MultiViewMismatch
| RenderPassErrorInner::MultiViewDimensionMismatch
| RenderPassErrorInner::TooManyMultiviewViews
| RenderPassErrorInner::MissingOcclusionQuerySet
| RenderPassErrorInner::PassEnded => return ErrorType::Validation,
};
@ -942,7 +954,7 @@ struct RenderPassInfo {
extent: wgt::Extent3d,
divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc<TextureView>)>,
multiview: Option<NonZeroU32>,
multiview_mask: Option<NonZeroU32>,
}
impl RenderPassInfo {
@ -996,6 +1008,7 @@ impl RenderPassInfo {
pending_query_resets: &mut QueryResetMap,
pending_discard_init_fixups: &mut SurfacesInDiscardState,
snatch_guard: &SnatchGuard<'_>,
multiview_mask: Option<NonZeroU32>,
) -> Result<Self, RenderPassErrorInner> {
profiling::scope!("RenderPassInfo::start");
@ -1040,8 +1053,11 @@ impl RenderPassInfo {
}
} else {
// Multiview is only supported if the feature is enabled
if this_multiview.is_some() {
if let Some(this_multiview) = this_multiview {
device.require_features(wgt::Features::MULTIVIEW)?;
if this_multiview.get() > device.limits.max_multiview_view_count {
return Err(RenderPassErrorInner::TooManyMultiviewViews);
}
}
detected_multiview = Some(this_multiview);
@ -1389,7 +1405,20 @@ impl RenderPassInfo {
}
let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
let multiview = detected_multiview.expect("Multiview was not detected, no attachments");
let detected_multiview =
detected_multiview.expect("Multiview was not detected, no attachments");
if let Some(mask) = multiview_mask {
// 0x01 will have msb 0
let mask_msb = 31 - mask.leading_zeros();
let detected_mv = detected_multiview.map(NonZeroU32::get).unwrap_or(1);
if mask_msb >= detected_mv {
return Err(RenderPassErrorInner::MultiViewMismatch);
}
if mask.get() != (1 << detected_mv) - 1 {
device.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
}
}
let attachment_formats = AttachmentData {
colors: color_attachments
@ -1414,7 +1443,7 @@ impl RenderPassInfo {
let context = RenderPassContext {
attachments: attachment_formats,
sample_count,
multiview,
multiview_mask,
};
let timestamp_writes_hal = if let Some(tw) = timestamp_writes.as_ref() {
@ -1450,7 +1479,7 @@ impl RenderPassInfo {
sample_count,
color_attachments: &color_attachments_hal,
depth_stencil_attachment: depth_stencil,
multiview,
multiview_mask,
timestamp_writes: timestamp_writes_hal,
occlusion_query_set: occlusion_query_set_hal,
};
@ -1485,7 +1514,7 @@ impl RenderPassInfo {
is_stencil_read_only,
extent,
divergent_discarded_depth_stencil_aspect,
multiview,
multiview_mask,
})
}
@ -1552,7 +1581,7 @@ impl RenderPassInfo {
stencil_ops,
clear_value: (0.0, 0),
}),
multiview: self.multiview,
multiview_mask: self.multiview_mask,
timestamp_writes: None,
occlusion_query_set: None,
};
@ -1713,6 +1742,8 @@ impl Global {
None
};
arc_desc.multiview_mask = desc.multiview_mask;
Ok(())
}
@ -1731,6 +1762,7 @@ impl Global {
color_attachments: ArrayVec::new(),
depth_stencil_attachment: None,
occlusion_query_set: None,
multiview_mask: None,
};
match fill_arc_desc(hub, desc, &mut arc_desc, &cmd_enc.device) {
Ok(()) => (RenderPass::new(cmd_enc, arc_desc), None),
@ -1816,6 +1848,7 @@ impl Global {
depth_stencil_attachment: pass.depth_stencil_attachment.take(),
timestamp_writes: pass.timestamp_writes.take(),
occlusion_query_set: pass.occlusion_query_set.take(),
multiview_mask: pass.multiview_mask,
})
})
}
@ -1830,6 +1863,7 @@ pub(super) fn encode_render_pass(
>,
mut timestamp_writes: Option<ArcPassTimestampWrites>,
occlusion_query_set: Option<Arc<QuerySet>>,
multiview_mask: Option<NonZeroU32>,
) -> Result<(), RenderPassError> {
let pass_scope = PassErrorScope::Pass;
@ -1868,6 +1902,7 @@ pub(super) fn encode_render_pass(
&mut pending_query_resets,
&mut pending_discard_init_fixups,
parent_state.snatch_guard,
multiview_mask,
)
.map_pass_err(pass_scope)?;
@ -2561,6 +2596,26 @@ fn set_scissor(state: &mut State, rect: Rect<u32>) -> Result<(), RenderPassError
Ok(())
}
fn validate_mesh_draw_multiview(state: &State) -> Result<(), RenderPassErrorInner> {
if let Some(mv) = state.info.multiview_mask {
let highest_bit = 31 - mv.leading_zeros();
let features = state.pass.base.device.features;
if !features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW)
|| highest_bit > state.pass.base.device.limits.max_mesh_multiview_view_count
{
return Err(RenderPassErrorInner::Draw(
DrawError::MeshPipelineMultiviewLimitsViolated {
highest_view_index: highest_bit,
max_multiviews: state.pass.base.device.limits.max_mesh_multiview_view_count,
},
));
}
}
Ok(())
}
fn draw(
state: &mut State,
vertex_count: u32,
@ -2645,7 +2700,9 @@ fn draw_mesh_tasks(
api_log!("RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}");
state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
state.flush_bindings()?;
validate_mesh_draw_multiview(state)?;
let groups_size_limit = state
.pass
@ -2696,6 +2753,10 @@ fn multi_draw_indirect(
state.is_ready(family)?;
state.flush_bindings()?;
if family == DrawCommandFamily::DrawMeshTasks {
validate_mesh_draw_multiview(state)?;
}
state
.pass
.base
@ -2878,6 +2939,10 @@ fn multi_draw_indirect_count(
state.is_ready(family)?;
state.flush_bindings()?;
if family == DrawCommandFamily::DrawMeshTasks {
validate_mesh_draw_multiview(state)?;
}
let stride = get_stride_of_indirect_args(family);
state

View File

@ -1478,7 +1478,7 @@ impl Global {
depth_stencil: desc.depth_stencil.clone(),
multisample: desc.multisample,
fragment,
multiview: desc.multiview,
multiview_mask: desc.multiview_mask,
cache,
};

View File

@ -61,7 +61,7 @@ impl<T: PartialEq> Eq for AttachmentData<T> {}
pub(crate) struct RenderPassContext {
pub attachments: AttachmentData<TextureFormat>,
pub sample_count: u32,
pub multiview: Option<NonZeroU32>,
pub multiview_mask: Option<NonZeroU32>,
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
@ -144,10 +144,10 @@ impl RenderPassContext {
res: res.error_ident(),
});
}
if self.multiview != other.multiview {
if self.multiview_mask != other.multiview_mask {
return Err(RenderPassCompatibilityError::IncompatibleMultiview {
expected: self.multiview,
actual: other.multiview,
expected: self.multiview_mask,
actual: other.multiview_mask,
res: res.error_ident(),
});
}

View File

@ -4442,8 +4442,11 @@ impl Device {
};
// Multiview is only supported if the feature is enabled
if desc.multiview.is_some() {
if let Some(mv_mask) = desc.multiview_mask {
self.require_features(wgt::Features::MULTIVIEW)?;
if !(mv_mask.get() + 1).is_power_of_two() {
self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
}
}
if !self
@ -4493,7 +4496,7 @@ impl Device {
multisample: desc.multisample,
fragment_stage,
color_targets,
multiview: desc.multiview,
multiview_mask: desc.multiview_mask,
cache: cache.as_ref().map(|it| it.raw()),
};
unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
@ -4527,7 +4530,7 @@ impl Device {
depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
},
sample_count: samples,
multiview: desc.multiview,
multiview_mask: desc.multiview_mask,
};
let mut flags = pipeline::PipelineFlags::empty();

View File

@ -39,7 +39,7 @@ pub(crate) fn new_render_bundle_encoder_descriptor(
}
}),
sample_count: context.sample_count,
multiview: context.multiview,
multiview: context.multiview_mask,
}
}
@ -195,12 +195,14 @@ impl IntoTrace for ArcCommand {
depth_stencil_attachment,
timestamp_writes,
occlusion_query_set,
multiview_mask,
} => Command::RunRenderPass {
pass: pass.into_trace(),
color_attachments: color_attachments.into_trace(),
depth_stencil_attachment: depth_stencil_attachment.map(|d| d.into_trace()),
timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()),
occlusion_query_set: occlusion_query_set.map(|q| q.to_trace()),
multiview_mask,
},
ArcCommand::BuildAccelerationStructures { blas, tlas } => {
Command::BuildAccelerationStructures {
@ -660,7 +662,7 @@ impl<'a> IntoTrace for crate::pipeline::ResolvedGeneralRenderPipelineDescriptor<
depth_stencil: self.depth_stencil,
multisample: self.multisample,
fragment: self.fragment.map(|f| f.into_trace()),
multiview: self.multiview,
multiview_mask: self.multiview_mask,
cache: self.cache.map(|c| c.into_trace()),
}
}

View File

@ -467,7 +467,7 @@ pub struct RenderPipelineDescriptor<
pub fragment: Option<FragmentState<'a, SM>>,
/// 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>,
pub multiview_mask: Option<NonZeroU32>,
/// The pipeline cache to use when creating this pipeline.
pub cache: Option<PLC>,
}
@ -539,7 +539,7 @@ pub struct GeneralRenderPipelineDescriptor<
pub fragment: Option<FragmentState<'a, SM>>,
/// 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>,
pub multiview_mask: Option<NonZeroU32>,
/// The pipeline cache to use when creating this pipeline.
pub cache: Option<PLC>,
}
@ -555,7 +555,7 @@ impl<'a, PLL, SM, PLC> From<RenderPipelineDescriptor<'a, PLL, SM, PLC>>
depth_stencil: value.depth_stencil,
multisample: value.multisample,
fragment: value.fragment,
multiview: value.multiview,
multiview_mask: value.multiview_mask,
cache: value.cache,
}
}
@ -572,7 +572,7 @@ impl<'a, PLL, SM, PLC> From<MeshPipelineDescriptor<'a, PLL, SM, PLC>>
depth_stencil: value.depth_stencil,
multisample: value.multisample,
fragment: value.fragment,
multiview: value.multiview,
multiview_mask: value.multiview,
cache: value.cache,
}
}

View File

@ -84,6 +84,7 @@ metal = [
"dep:objc",
"dep:parking_lot",
"dep:profiling",
"dep:smallvec",
]
vulkan = [
"naga/spv-out",

View File

@ -280,7 +280,7 @@ impl<A: hal::Api> Example<A> {
blend: Some(wgpu_types::BlendState::ALPHA_BLENDING),
write_mask: wgpu_types::ColorWrites::default(),
})],
multiview: None,
multiview_mask: None,
cache: None,
};
let pipeline = unsafe { device.create_render_pipeline(&pipeline_desc).unwrap() };
@ -727,7 +727,7 @@ impl<A: hal::Api> Example<A> {
},
})],
depth_stencil_attachment: None,
multiview: None,
multiview_mask: None,
timestamp_writes: None,
occlusion_query_set: None,
};

View File

@ -329,7 +329,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
clear_value: wgpu_types::Color::BLUE,
})],
depth_stencil_attachment: None,
multiview: None,
multiview_mask: None,
timestamp_writes: None,
occlusion_query_set: None,
};

Some files were not shown because too many files have changed in this diff Show More