From 375cf497e1e9dca2bcbbedba19a8ed7ed1e06cb4 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 12:56:37 -0500 Subject: [PATCH 01/59] Exposed new limits for multiview --- wgpu-hal/src/dx12/adapter.rs | 3 +++ wgpu-hal/src/gles/adapter.rs | 5 ++++- wgpu-hal/src/metal/adapter.rs | 6 ++++-- wgpu-hal/src/noop/mod.rs | 11 +++++++---- wgpu-hal/src/vulkan/adapter.rs | 31 +++++++++++++++++++++++++++++-- wgpu-info/src/human.rs | 10 ++++++++-- wgpu-types/src/lib.rs | 25 +++++++++++++++++++------ 7 files changed, 74 insertions(+), 17 deletions(-) diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 1645088afb4..a888d109416 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -671,6 +671,9 @@ impl super::Adapter { } else { 0 }, + + max_multiview_view_count: 0, + max_multiview_instance_index: 0, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new( diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 806f5567ba2..e3507b3ad09 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -803,13 +803,16 @@ impl super::Adapter { max_task_workgroup_total_count: 0, max_task_workgroups_per_dimension: 0, - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, max_mesh_output_layers: 0, max_blas_primitive_count: 0, max_blas_geometry_count: 0, max_tlas_instance_count: 0, max_acceleration_structures_per_shader_stage: 0, + + max_multiview_view_count: 0, + max_multiview_instance_index: 0, }; let mut workarounds = super::Workarounds::empty(); diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 47ffbd3c6c1..c09a157683a 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -1031,7 +1031,6 @@ impl super::PrivateCapabilities { downlevel .flags .set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, true); - let base = wgt::Limits::default(); crate::Capabilities { limits: wgt::Limits { @@ -1079,7 +1078,7 @@ impl super::PrivateCapabilities { max_task_workgroup_total_count: 0, max_task_workgroups_per_dimension: 0, - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, max_mesh_output_layers: 0, max_blas_primitive_count: 0, // When added: 2^28 from https://developer.apple.com/documentation/metal/mtlaccelerationstructureusage/extendedlimits @@ -1092,6 +1091,9 @@ impl super::PrivateCapabilities { // > [Acceleration structures] are opaque objects that can be bound directly using // buffer binding points or via argument buffers max_acceleration_structures_per_shader_stage: 0, + + max_multiview_view_count: 0, + max_multiview_instance_index: 0, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new(self.buffer_alignment).unwrap(), diff --git a/wgpu-hal/src/noop/mod.rs b/wgpu-hal/src/noop/mod.rs index abd7c628a98..3c5ac2588e8 100644 --- a/wgpu-hal/src/noop/mod.rs +++ b/wgpu-hal/src/noop/mod.rs @@ -192,15 +192,18 @@ pub const CAPABILITIES: crate::Capabilities = { max_push_constant_size: ALLOC_MAX_U32, max_non_sampler_bindings: ALLOC_MAX_U32, - max_task_workgroup_total_count: 0, - max_task_workgroups_per_dimension: 0, - max_mesh_multiview_count: 0, - max_mesh_output_layers: 0, + max_task_workgroup_total_count: ALLOC_MAX_U32, + max_task_workgroups_per_dimension: ALLOC_MAX_U32, + max_mesh_multiview_view_count: ALLOC_MAX_U32, + max_mesh_output_layers: ALLOC_MAX_U32, max_blas_primitive_count: ALLOC_MAX_U32, max_blas_geometry_count: ALLOC_MAX_U32, max_tlas_instance_count: ALLOC_MAX_U32, max_acceleration_structures_per_shader_stage: ALLOC_MAX_U32, + + max_multiview_view_count: ALLOC_MAX_U32, + max_multiview_instance_index: ALLOC_MAX_U32, }, alignments: crate::Alignments { // All maximally permissive diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 0ebf1fec9a4..4f57103db0a 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -958,6 +958,10 @@ pub struct PhysicalDeviceProperties { /// `VK_EXT_mesh_shader` extension. mesh_shader: Option>, + /// Additional `vk::PhysicalDevice` properties from the + /// `VK_KHR_multiview` extension. + multiview: Option>, + /// The device API version. /// /// Which is the version of Vulkan supported for device-level functionality. @@ -1187,7 +1191,7 @@ impl PhysicalDeviceProperties { let ( max_task_workgroup_total_count, max_task_workgroups_per_dimension, - max_mesh_multiview_count, + max_mesh_multiview_view_count, max_mesh_output_layers, ) = match self.mesh_shader { Some(m) => ( @@ -1249,6 +1253,16 @@ impl PhysicalDeviceProperties { properties.max_per_stage_descriptor_acceleration_structures; } + let (max_multiview_view_count, max_multiview_instance_index) = + if let Some(properties) = self.multiview { + ( + properties.max_multiview_view_count, + properties.max_multiview_instance_index, + ) + } else { + (0, 0) + }; + wgt::Limits { max_texture_dimension_1d: limits.max_image_dimension1_d, max_texture_dimension_2d: limits.max_image_dimension2_d, @@ -1309,13 +1323,16 @@ impl PhysicalDeviceProperties { max_task_workgroup_total_count, max_task_workgroups_per_dimension, - max_mesh_multiview_count, + max_mesh_multiview_view_count, max_mesh_output_layers, max_blas_primitive_count, max_blas_geometry_count, max_tlas_instance_count, max_acceleration_structures_per_shader_stage, + + max_multiview_view_count, + max_multiview_instance_index, } } @@ -1374,6 +1391,9 @@ impl super::InstanceShared { capabilities.properties = unsafe { self.raw.get_physical_device_properties(phd) }; capabilities.device_api_version = capabilities.properties.api_version; + let supports_multiview = capabilities.device_api_version >= vk::API_VERSION_1_1 + || capabilities.supports_extension(khr::multiview::NAME); + if let Some(ref get_device_properties) = self.get_physical_device_properties { // Get these now to avoid borrowing conflicts later let supports_maintenance3 = capabilities.device_api_version >= vk::API_VERSION_1_1 @@ -1451,6 +1471,13 @@ impl super::InstanceShared { properties2 = properties2.push_next(next); } + if supports_multiview { + let next = capabilities + .multiview + .insert(vk::PhysicalDeviceMultiviewProperties::default()); + properties2 = properties2.push_next(next); + } + unsafe { get_device_properties.get_physical_device_properties2(phd, &mut properties2) }; diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs index f0930bd0212..b1e4e7db1e4 100644 --- a/wgpu-info/src/human.rs +++ b/wgpu-info/src/human.rs @@ -163,13 +163,16 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize max_task_workgroup_total_count, max_task_workgroups_per_dimension, - max_mesh_multiview_count, + max_mesh_multiview_view_count: max_mesh_multiview_count, max_mesh_output_layers, max_blas_primitive_count, max_blas_geometry_count, max_tlas_instance_count, max_acceleration_structures_per_shader_stage, + + max_multiview_view_count, + max_multiview_instance_index } = limits; writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}")?; writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}")?; @@ -209,13 +212,16 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize writeln!(output, "\t\t Max Task Workgroup Total Count: {max_task_workgroup_total_count}")?; writeln!(output, "\t\t Max Task Workgroups Per Dimension: {max_task_workgroups_per_dimension}")?; - writeln!(output, "\t\t Max Mesh Multiview Count: {max_mesh_multiview_count}")?; + writeln!(output, "\t\t Max Mesh Multiview View Count: {max_mesh_multiview_count}")?; writeln!(output, "\t\t Max Mesh Output Layers: {max_mesh_output_layers}")?; writeln!(output, "\t\t Max BLAS Primitive count: {max_blas_primitive_count}")?; writeln!(output, "\t\t Max BLAS Geometry count: {max_blas_geometry_count}")?; writeln!(output, "\t\t Max TLAS Instance count: {max_tlas_instance_count}")?; writeln!(output, "\t\t Max Acceleration Structures Per Shader Stage: {max_acceleration_structures_per_shader_stage}")?; + + writeln!(output, "\t\t Max Multiview View Count: {max_multiview_view_count}")?; + writeln!(output, "\t\t Max Multiview Instance Index: {max_multiview_instance_index}")?; // This one reflects more of a wgpu implementation limitations than a hardware limit // so don't show it here. let _ = max_non_sampler_bindings; diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 7674c0a95d8..3d1aaaf3a1b 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -518,12 +518,15 @@ macro_rules! with_limits { $macro_name!(max_task_workgroup_total_count, Ordering::Less); $macro_name!(max_task_workgroups_per_dimension, Ordering::Less); - $macro_name!(max_mesh_multiview_count, Ordering::Less); + $macro_name!(max_mesh_multiview_view_count, Ordering::Less); $macro_name!(max_mesh_output_layers, Ordering::Less); $macro_name!(max_blas_primitive_count, Ordering::Less); $macro_name!(max_blas_geometry_count, Ordering::Less); $macro_name!(max_tlas_instance_count, Ordering::Less); + + $macro_name!(max_multiview_view_count, Ordering::Less); + $macro_name!(max_multiview_instance_index, Ordering::Less); }; } @@ -693,8 +696,8 @@ pub struct Limits { pub max_task_workgroups_per_dimension: u32, /// The maximum number of layers that can be output from a mesh shader pub max_mesh_output_layers: u32, - /// The maximum number of views that can be used by a mesh shader - pub max_mesh_multiview_count: u32, + /// The maximum number of views that can be used by a mesh shader in multiview rendering + pub max_mesh_multiview_view_count: u32, /// The maximum number of primitive (ex: triangles, aabbs) a BLAS is allowed to have. Requesting /// more than 0 during device creation only makes sense if [`Features::EXPERIMENTAL_RAY_QUERY`] @@ -712,6 +715,13 @@ pub struct Limits { /// Requesting more than 0 during device creation only makes sense if [`Features::EXPERIMENTAL_RAY_QUERY`] /// is enabled. pub max_acceleration_structures_per_shader_stage: u32, + + /// The maximum number of views that can be used in multiview rendering + pub max_multiview_view_count: u32, + /// For an instanced draw call using multiview, the maximum instance index. For example, + /// if draw is called with instances a..b, then b must be <= this limit + 1. + /// Note that this is NOT just the maximum total number of instances. + pub max_multiview_instance_index: u32, } impl Default for Limits { @@ -819,13 +829,16 @@ impl Limits { max_task_workgroup_total_count: 0, max_task_workgroups_per_dimension: 0, - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, max_mesh_output_layers: 0, max_blas_primitive_count: 0, max_blas_geometry_count: 0, max_tlas_instance_count: 0, max_acceleration_structures_per_shader_stage: 0, + + max_multiview_view_count: 0, + max_multiview_instance_index: 0, } } @@ -897,7 +910,7 @@ impl Limits { max_task_workgroups_per_dimension: 0, max_task_workgroup_total_count: 0, - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, max_mesh_output_layers: 0, ..Self::defaults() } @@ -1050,7 +1063,7 @@ impl Limits { max_task_workgroup_total_count: 65536, max_task_workgroups_per_dimension: 256, // llvmpipe reports 0 multiview count, which just means no multiview is allowed - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, // llvmpipe once again requires this to be 8. An RTX 3060 supports well over 1024. max_mesh_output_layers: 8, ..self From 88fcb2f0a6830ad648aa85c6fffbade0df211956 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 13:18:20 -0500 Subject: [PATCH 02/59] Added multiview view count checks --- wgpu-core/src/command/draw.rs | 2 ++ wgpu-core/src/command/render.rs | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/wgpu-core/src/command/draw.rs b/wgpu-core/src/command/draw.rs index 7a57077a4fa..527567f72bc 100644 --- a/wgpu-core/src/command/draw.rs +++ b/wgpu-core/src/command/draw.rs @@ -71,6 +71,8 @@ pub enum DrawError { limit: u32, max_total: u32, }, + #[error("Mesh shader calls in multiview render passes require `EXPERIMENTAL_MESH_SHADER_MULTIVIEW`, and the view count must be <= `Limits::max_mesh_multiview_view_count`")] + MeshPipelineMultiviewLimitsViolated, } impl WebGpuError for DrawError { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 344d55bd6d4..71aca699d8e 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -775,6 +775,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)] @@ -876,6 +878,7 @@ impl WebGpuError for RenderPassError { | RenderPassErrorInner::PushConstantOutOfMemory | RenderPassErrorInner::MultiViewMismatch | RenderPassErrorInner::MultiViewDimensionMismatch + | RenderPassErrorInner::TooManyMultiviewViews | RenderPassErrorInner::MissingOcclusionQuerySet | RenderPassErrorInner::PassEnded => return ErrorType::Validation, }; @@ -1010,8 +1013,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); @@ -2649,6 +2655,17 @@ fn draw_mesh_tasks( api_log!("RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}"); state.is_ready(DrawCommandFamily::DrawMeshTasks)?; + if let Some(mv) = state.info.multiview { + if !state + .general + .device + .features + .contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW) + || mv.get() > state.general.device.limits.max_mesh_multiview_view_count + { + return Err(DrawError::MeshPipelineMultiviewLimitsViolated); + } + } let groups_size_limit = state .general From 9e20b55081adfd7105f6f755c9076d4851c1045f Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 13:27:56 -0500 Subject: [PATCH 03/59] Added check for instance limit --- wgpu-core/src/command/draw.rs | 16 ++++++++++++++-- wgpu-core/src/command/render.rs | 14 +++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/wgpu-core/src/command/draw.rs b/wgpu-core/src/command/draw.rs index 527567f72bc..77c22f089c8 100644 --- a/wgpu-core/src/command/draw.rs +++ b/wgpu-core/src/command/draw.rs @@ -44,6 +44,13 @@ pub enum DrawError { instance_limit: u64, slot: u32, }, + #[error( + "Instance {last_instance} extends beyond limit {instance_limit} imposed by multiview restrictions on the render pass." + )] + InstanceBeyondMultiviewLimit { + last_instance: u64, + instance_limit: u64, + }, #[error("Index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")] IndexBeyondLimit { last_index: u64, index_limit: u64 }, #[error( @@ -71,8 +78,13 @@ pub enum DrawError { limit: u32, max_total: u32, }, - #[error("Mesh shader calls in multiview render passes require `EXPERIMENTAL_MESH_SHADER_MULTIVIEW`, and the view count must be <= `Limits::max_mesh_multiview_view_count`")] - MeshPipelineMultiviewLimitsViolated, + #[error( + "Mesh shader calls in multiview render passes require `EXPERIMENTAL_MESH_SHADER_MULTIVIEW`, and the view count ({views_given}) must be <= `Limits::max_multiview_view_count` ({max_multiviews})" + )] + MeshPipelineMultiviewLimitsViolated { + views_given: u32, + max_multiviews: u32, + }, } impl WebGpuError for DrawError { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 71aca699d8e..f364c0d1172 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -2631,6 +2631,15 @@ fn draw_indexed( .vertex .limits .validate_instance_limit(first_instance, instance_count)?; + if state.info.multiview.is_some() + && first_instance + instance_count + > state.general.device.limits.max_multiview_instance_index + 1 + { + return Err(DrawError::InstanceBeyondMultiviewLimit { + last_instance: (first_instance + instance_count) as u64, + instance_limit: (state.general.device.limits.max_multiview_instance_index + 1) as u64, + }); + } unsafe { if instance_count > 0 && index_count > 0 { @@ -2663,7 +2672,10 @@ fn draw_mesh_tasks( .contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW) || mv.get() > state.general.device.limits.max_mesh_multiview_view_count { - return Err(DrawError::MeshPipelineMultiviewLimitsViolated); + return Err(DrawError::MeshPipelineMultiviewLimitsViolated { + views_given: mv.get(), + max_multiviews: state.general.device.limits.max_mesh_multiview_view_count, + }); } } From f8ab779c8b9a05eaa11b545fa19c505b8581d595 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 14:26:30 -0500 Subject: [PATCH 04/59] Added multiview test --- tests/tests/wgpu-gpu/main.rs | 2 + tests/tests/wgpu-gpu/multiview.rs | 199 ++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 tests/tests/wgpu-gpu/multiview.rs diff --git a/tests/tests/wgpu-gpu/main.rs b/tests/tests/wgpu-gpu/main.rs index d461845e92b..07961318260 100644 --- a/tests/tests/wgpu-gpu/main.rs +++ b/tests/tests/wgpu-gpu/main.rs @@ -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; @@ -96,6 +97,7 @@ fn all_tests() -> Vec { 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); diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs new file mode 100644 index 00000000000..f368e9777e7 --- /dev/null +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -0,0 +1,199 @@ +use std::num::NonZero; + +use wgpu::{ + util::{BufferInitDescriptor, DeviceExt}, + vertex_attr_array, Features, Limits, +}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(DRAW_MULTIVIEW); +} + +#[gpu_test] +static DRAW_MULTIVIEW: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .features(Features::MULTIVIEW) + .limits(Limits { + max_multiview_view_count: 2, + max_multiview_instance_index: 1, + ..Limits::defaults() + }), + ) + .run_async(run_test); + +async fn run_test(ctx: TestingContext) { + let vertex_buffer_content: &[f32; 12] = &[ + // Triangle 1 + -1.0, -1.0, // Bottom left + 1.0, 1.0, // Top right + -1.0, 1.0, // Top left + // Triangle 2 + -1.0, -1.0, // Bottom left + 1.0, -1.0, // Bottom right + 1.0, 1.0, // Top right + ]; + let vertex_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(vertex_buffer_content), + usage: wgpu::BufferUsages::VERTEX, + }); + + let shader_src = " + @vertex + fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f { + return vec4f(position, 0.0, 1.0); + } + + @fragment + fn fs_main() -> @location(0) vec4f { + return vec4f(1.0); + } + "; + + 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: &[wgpu::VertexBufferLayout { + array_stride: 8, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &vertex_attr_array![0 => Float32x2], + }], + 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: NonZero::new(2), + multisample: Default::default(), + layout: None, + depth_stencil: None, + cache: None, + }; + const TEXTURE_SIZE: u32 = 512; + let pipeline = ctx.device.create_render_pipeline(&pipeline_desc); + let create_texture = || { + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: TEXTURE_SIZE, + height: TEXTURE_SIZE, + depth_or_array_layers: 1, + }, + 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 view = texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + format: Some(wgpu::TextureFormat::R8Unorm), + dimension: Some(wgpu::TextureViewDimension::D2), + 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: None, + }); + (texture, view) + }; + let (texture1, view1) = create_texture(); + let (texture2, view2) = create_texture(); + let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64 * 2, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + 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: &view1, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + }), + Some(wgpu::RenderPassColorAttachment { + view: &view2, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + }), + ], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&pipeline); + rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); + rpass.draw(0..6, 0..1); + drop(rpass); + for i in 0..2 { + let texture = [&texture1, &texture2][i]; + encoder.copy_texture_to_buffer( + wgpu::TexelCopyTextureInfo { + texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyBufferInfo { + buffer: &readback_buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: i as u64 * TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64, + bytes_per_row: Some(TEXTURE_SIZE), + rows_per_image: Some(TEXTURE_SIZE), + }, + }, + wgpu::Extent3d { + width: TEXTURE_SIZE, + height: TEXTURE_SIZE, + depth_or_array_layers: 1, + }, + ); + } + ctx.queue.submit([encoder.finish()]); + + let slice = readback_buffer.slice(..); + slice.map_async(wgpu::MapMode::Read, |_| ()); + + ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + + let data = slice.get_mapped_range(); + let succeeded = data.iter().all(|b| *b == u8::MAX); + assert!(succeeded); +} From 56f8fc15cb91d593372bb5f0c6864626bd01f855 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 14:33:36 -0500 Subject: [PATCH 05/59] Fixed various compile issues --- wgpu-hal/src/dx12/adapter.rs | 2 +- wgpu-types/src/lib.rs | 14 +++++++++++--- wgpu/src/backend/webgpu.rs | 5 ++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index a888d109416..b33d57d195b 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -648,7 +648,7 @@ impl super::Adapter { max_task_workgroup_total_count: 0, max_task_workgroups_per_dimension: 0, - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, max_mesh_output_layers: 0, max_blas_primitive_count: if supports_ray_tracing { diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 3d1aaaf3a1b..ee91b4feee7 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -776,12 +776,14 @@ impl Limits { /// max_non_sampler_bindings: 1_000_000, /// max_task_workgroup_total_count: 0, /// max_task_workgroups_per_dimension: 0, - /// max_mesh_multiview_count: 0, + /// max_mesh_multiview_view_count: 0, /// max_mesh_output_layers: 0, /// max_blas_primitive_count: 0, /// max_blas_geometry_count: 0, /// max_tlas_instance_count: 0, /// max_acceleration_structures_per_shader_stage: 0, + /// max_multiview_view_count: 0, + /// max_multiview_instance_index: 0, /// }); /// ``` /// @@ -887,13 +889,16 @@ impl Limits { /// /// max_task_workgroup_total_count: 0, /// max_task_workgroups_per_dimension: 0, - /// max_mesh_multiview_count: 0, + /// max_mesh_multiview_view_count: 0, /// max_mesh_output_layers: 0, /// /// max_blas_primitive_count: 0, /// max_blas_geometry_count: 0, /// max_tlas_instance_count: 0, /// max_acceleration_structures_per_shader_stage: 0, + /// + /// max_multiview_view_count: 0, + /// max_multiview_instance_index: 0, /// }); /// ``` #[must_use] @@ -962,13 +967,16 @@ impl Limits { /// /// max_task_workgroup_total_count: 0, /// max_task_workgroups_per_dimension: 0, - /// max_mesh_multiview_count: 0, + /// max_mesh_multiview_view_count: 0, /// max_mesh_output_layers: 0, /// /// max_blas_primitive_count: 0, /// max_blas_geometry_count: 0, /// max_tlas_instance_count: 0, /// max_acceleration_structures_per_shader_stage: 0, + /// + /// max_multiview_view_count: 0, + /// max_multiview_instance_index: 0, /// }); /// ``` #[must_use] diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 75049d38ceb..fadac604139 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -827,13 +827,16 @@ fn map_wgt_limits(limits: webgpu_sys::GpuSupportedLimits) -> wgt::Limits { max_task_workgroup_total_count: wgt::Limits::default().max_task_workgroup_total_count, max_task_workgroups_per_dimension: wgt::Limits::default().max_task_workgroups_per_dimension, max_mesh_output_layers: wgt::Limits::default().max_mesh_output_layers, - max_mesh_multiview_count: wgt::Limits::default().max_mesh_multiview_count, + max_mesh_multiview_view_count: wgt::Limits::default().max_mesh_multiview_view_count, max_blas_primitive_count: wgt::Limits::default().max_blas_primitive_count, max_blas_geometry_count: wgt::Limits::default().max_blas_geometry_count, max_tlas_instance_count: wgt::Limits::default().max_tlas_instance_count, max_acceleration_structures_per_shader_stage: wgt::Limits::default() .max_acceleration_structures_per_shader_stage, + + max_multiview_view_count: wgt::Limits::default().max_multiview_view_count, + max_multiview_instance_index: wgt::Limits::default().max_multiview_instance_index, } } From c103f4210f011790a571cecdd4493ef64ec8a4dd Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 14:45:05 -0500 Subject: [PATCH 06/59] Updated multiview test (idk what I'm doing) --- tests/tests/wgpu-gpu/multiview.rs | 48 ++++++++++++------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index f368e9777e7..127a347e83a 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -92,13 +92,13 @@ async fn run_test(ctx: TestingContext) { }; const TEXTURE_SIZE: u32 = 512; let pipeline = ctx.device.create_render_pipeline(&pipeline_desc); - let create_texture = || { + let (texture, view) = { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { width: TEXTURE_SIZE, height: TEXTURE_SIZE, - depth_or_array_layers: 1, + depth_or_array_layers: 2, }, mip_level_count: 1, sample_count: 1, @@ -110,18 +110,16 @@ async fn run_test(ctx: TestingContext) { let view = texture.create_view(&wgpu::TextureViewDescriptor { label: None, format: Some(wgpu::TextureFormat::R8Unorm), - dimension: Some(wgpu::TextureViewDimension::D2), + 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: None, + array_layer_count: Some(2), }); (texture, view) }; - let (texture1, view1) = create_texture(); - let (texture2, view2) = create_texture(); let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { label: None, size: TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64 * 2, @@ -134,26 +132,15 @@ async fn run_test(ctx: TestingContext) { .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, - color_attachments: &[ - Some(wgpu::RenderPassColorAttachment { - view: &view1, - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - }), - Some(wgpu::RenderPassColorAttachment { - view: &view2, - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - }), - ], + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, @@ -163,12 +150,15 @@ async fn run_test(ctx: TestingContext) { rpass.draw(0..6, 0..1); drop(rpass); for i in 0..2 { - let texture = [&texture1, &texture2][i]; encoder.copy_texture_to_buffer( wgpu::TexelCopyTextureInfo { - texture, + texture: &texture, mip_level: 0, - origin: wgpu::Origin3d::ZERO, + origin: wgpu::Origin3d { + x: 512, + y: 512, + z: i, + }, aspect: wgpu::TextureAspect::All, }, wgpu::TexelCopyBufferInfo { From 06ce95c11c5b41278e1f8e0248a5223b143862c2 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 14:53:42 -0500 Subject: [PATCH 07/59] Updated test again --- tests/tests/wgpu-gpu/multiview.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 127a347e83a..7c64bee2770 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -49,8 +49,8 @@ async fn run_test(ctx: TestingContext) { } @fragment - fn fs_main() -> @location(0) vec4f { - return vec4f(1.0); + fn fs_main(@view_index view_index: u32) -> @location(0) vec4f { + return vec4f(f32(view_index)); } "; @@ -154,11 +154,7 @@ async fn run_test(ctx: TestingContext) { wgpu::TexelCopyTextureInfo { texture: &texture, mip_level: 0, - origin: wgpu::Origin3d { - x: 512, - y: 512, - z: i, - }, + origin: wgpu::Origin3d { x: 0, y: 0, z: i }, aspect: wgpu::TextureAspect::All, }, wgpu::TexelCopyBufferInfo { @@ -184,6 +180,11 @@ async fn run_test(ctx: TestingContext) { ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); let data = slice.get_mapped_range(); - let succeeded = data.iter().all(|b| *b == u8::MAX); - assert!(succeeded); + for view_idx in 0..2 { + let texture_bytes = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; + let succeeded = &data[texture_bytes * view_idx..texture_bytes * (view_idx + 1)] + .iter() + .all(|b| *b == if view_idx == 1 { u8::MAX } else { 0 }); + assert!(succeeded); + } } From d4563107b24183397d0c78ab6d3624c29bd2bc77 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 10 Sep 2025 15:04:02 -0500 Subject: [PATCH 08/59] Fixed shader hopefully --- tests/tests/wgpu-gpu/multiview.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 7c64bee2770..03ae5de1c8b 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -49,7 +49,7 @@ async fn run_test(ctx: TestingContext) { } @fragment - fn fs_main(@view_index view_index: u32) -> @location(0) vec4f { + fn fs_main(@builtin(view_index) view_index: i32) -> @location(0) vec4f { return vec4f(f32(view_index)); } "; From de5b1be0e2f4d7d8fddd65a3d480f070e66d4407 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 10 Sep 2025 22:43:00 -0500 Subject: [PATCH 09/59] Added some of the functionality to dx12 --- wgpu-hal/src/dx12/adapter.rs | 21 ++++++++++++++++----- wgpu-hal/src/dx12/command.rs | 10 ++++++++++ wgpu-hal/src/vulkan/device.rs | 4 ---- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index b33d57d195b..2c8ecb53982 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -171,9 +171,9 @@ impl super::Adapter { && features2.DepthBoundsTestSupported.as_bool() }; - let casting_fully_typed_format_supported = { + let (casting_fully_typed_format_supported, view_instancing) = { let mut features3 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS3::default(); - unsafe { + if unsafe { device.CheckFeatureSupport( Direct3D12::D3D12_FEATURE_D3D12_OPTIONS3, <*mut _>::cast(&mut features3), @@ -181,7 +181,14 @@ impl super::Adapter { ) } .is_ok() - && features3.CastingFullyTypedFormatSupported.as_bool() + { + ( + features3.CastingFullyTypedFormatSupported.as_bool(), + features3.ViewInstancingTier.0 >= Direct3D12::D3D12_VIEW_INSTANCING_TIER_1.0, + ) + } else { + (false, false) + } }; let heap_create_not_zeroed = { @@ -528,6 +535,8 @@ impl super::Adapter { atomic_int64_on_typed_resource_supported, ); + features.set(wgt::Features::MULTIVIEW, view_instancing); + // TODO: Determine if IPresentationManager is supported let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi(); @@ -672,8 +681,10 @@ impl super::Adapter { 0 }, - max_multiview_view_count: 0, - max_multiview_instance_index: 0, + // See https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html#maximum-viewinstancecount + max_multiview_view_count: if view_instancing { 4 } else { 0 }, + // This limit is specific to vulkan + max_multiview_instance_index: if view_instancing { u32::MAX } else { 0 }, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new( diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index edfea952ed2..a521ccaeba8 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -887,6 +887,16 @@ impl crate::CommandEncoder for super::CommandEncoder { } } + if let Some(multiview) = desc.multiview { + // Currently with multiview we render to all views. + let multiview_mask = (1 << multiview.get()) - 1; + unsafe { + list.cast::() + .unwrap() + .SetViewInstanceMask(multiview_mask); + } + } + let raw_vp = Direct3D12::D3D12_VIEWPORT { TopLeftX: 0.0, TopLeftY: 0.0, diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 1297e57f09b..d9037118685 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -209,10 +209,6 @@ impl super::DeviceShared { let mut multiview_info; let mask; if let Some(multiview) = 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. From 263aa77b812c1aeef547c7688635fabcae8362ab Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 10 Sep 2025 23:30:14 -0500 Subject: [PATCH 10/59] Added multiview mask parameter (didn't update all code to use this parameter, compile is broken) --- .../standalone/02_hello_window/src/main.rs | 1 + player/src/lib.rs | 2 ++ tests/tests/wgpu-gpu/multiview.rs | 1 + wgpu-core/src/command/clear.rs | 2 +- wgpu-core/src/command/render.rs | 20 +++++++++++++++++-- wgpu-core/src/device/trace.rs | 3 ++- wgpu-hal/src/dx12/command.rs | 6 ++---- wgpu-hal/src/dynamic/command.rs | 2 +- wgpu-hal/src/lib.rs | 2 +- wgpu-hal/src/vulkan/command.rs | 17 +--------------- wgpu-hal/src/vulkan/device.rs | 9 ++++----- wgpu-hal/src/vulkan/mod.rs | 4 ++-- wgpu/src/api/render_pass.rs | 4 +++- wgpu/src/backend/wgpu_core.rs | 1 + wgpu/src/util/texture_blitter.rs | 1 + 15 files changed, 41 insertions(+), 34 deletions(-) diff --git a/examples/standalone/02_hello_window/src/main.rs b/examples/standalone/02_hello_window/src/main.rs index fc0d278a942..6da07c9f750 100644 --- a/examples/standalone/02_hello_window/src/main.rs +++ b/examples/standalone/02_hello_window/src/main.rs @@ -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. diff --git a/player/src/lib.rs b/player/src/lib.rs index 8ba7e13ce1b..d01a743be61 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -113,6 +113,7 @@ impl GlobalPlay for wgc::global::Global { target_depth_stencil, timestamp_writes, occlusion_query_set_id, + multiview_mask, } => { self.render_pass_end_with_unresolved_commands( encoder, @@ -121,6 +122,7 @@ impl GlobalPlay for wgc::global::Global { target_depth_stencil.as_ref(), timestamp_writes.as_ref(), occlusion_query_set_id, + multiview_mask, ); } trace::Command::BuildAccelerationStructures { blas, tlas } => { diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 03ae5de1c8b..7d77228e5b9 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -144,6 +144,7 @@ async fn run_test(ctx: TestingContext) { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: 3, }); rpass.set_pipeline(&pipeline); rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index 840e9068125..a6e603b7659 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -527,7 +527,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, }) diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index f364c0d1172..2044c0a32ae 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -229,6 +229,7 @@ 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, + pub multiview_mask: Option, } /// Describes the attachments of a render pass. @@ -243,6 +244,8 @@ struct ArcRenderPassDescriptor<'a> { pub timestamp_writes: Option, /// Defines where the occlusion query results will be stored for this pass. pub occlusion_query_set: Option>, + /// The multiview array layers that will be used + pub multiview_mask: Option, } pub type RenderBasePass = BasePass; @@ -270,6 +273,7 @@ pub struct RenderPass { depth_stencil_attachment: Option, timestamp_writes: Option, occlusion_query_set: Option>, + multiview_mask: Option, // Resource binding dedupe state. current_bind_groups: BindGroupStateChange, @@ -285,6 +289,7 @@ impl RenderPass { color_attachments, depth_stencil_attachment, occlusion_query_set, + multiview_mask, } = desc; Self { @@ -294,6 +299,7 @@ impl RenderPass { depth_stencil_attachment, timestamp_writes, occlusion_query_set, + multiview_mask, current_bind_groups: BindGroupStateChange::new(), current_pipeline: StateChange::new(), @@ -308,6 +314,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(), } @@ -331,6 +338,7 @@ impl fmt::Debug for RenderPass { "push constant u32 count", &self.base.push_constant_data.len(), ) + .field("multiview mask", &self.multiview_mask) .finish() } } @@ -915,6 +923,7 @@ struct RenderPassInfo { divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc)>, multiview: Option, + multiview_mask: Option, } impl RenderPassInfo { @@ -969,6 +978,7 @@ impl RenderPassInfo { pending_query_resets: &mut QueryResetMap, pending_discard_init_fixups: &mut SurfacesInDiscardState, snatch_guard: &SnatchGuard<'_>, + multiview_mask: Option, ) -> Result { profiling::scope!("RenderPassInfo::start"); @@ -1422,7 +1432,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, }; @@ -1459,6 +1469,7 @@ impl RenderPassInfo { extent, divergent_discarded_depth_stencil_aspect, multiview, + multiview_mask, }) } @@ -1525,7 +1536,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, }; @@ -1692,6 +1703,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), @@ -1751,6 +1763,7 @@ impl Global { depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>, timestamp_writes: Option<&PassTimestampWrites>, occlusion_query_set: Option, + multiview_mask: Option, ) { #[cfg(feature = "trace")] { @@ -1772,6 +1785,7 @@ impl Global { target_depth_stencil: depth_stencil_attachment.cloned(), timestamp_writes: timestamp_writes.cloned(), occlusion_query_set_id: occlusion_query_set, + multiview_mask, }); } } @@ -1793,6 +1807,7 @@ impl Global { depth_stencil_attachment, timestamp_writes, occlusion_query_set, + multiview_mask, }, ); if let Some(err) = encoder_error { @@ -1888,6 +1903,7 @@ impl Global { &mut pending_query_resets, &mut pending_discard_init_fixups, snatch_guard, + pass.multiview_mask, ) .map_pass_err(pass_scope)?; diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index 80432d5e938..33e27b8cdad 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -1,5 +1,5 @@ use alloc::{string::String, vec::Vec}; -use core::{convert::Infallible, ops::Range}; +use core::{convert::Infallible, num::NonZeroU32, ops::Range}; #[cfg(feature = "trace")] use {alloc::borrow::Cow, std::io::Write as _}; @@ -217,6 +217,7 @@ pub enum Command { target_depth_stencil: Option, timestamp_writes: Option, occlusion_query_set_id: Option, + multiview_mask: Option, }, BuildAccelerationStructures { blas: Vec, diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index a521ccaeba8..01ad8262fd3 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -887,13 +887,11 @@ impl crate::CommandEncoder for super::CommandEncoder { } } - if let Some(multiview) = desc.multiview { - // Currently with multiview we render to all views. - let multiview_mask = (1 << multiview.get()) - 1; + if let Some(multiview_mask) = desc.multiview_mask { unsafe { list.cast::() .unwrap() - .SetViewInstanceMask(multiview_mask); + .SetViewInstanceMask(multiview_mask.get()); } } diff --git a/wgpu-hal/src/dynamic/command.rs b/wgpu-hal/src/dynamic/command.rs index 43205c01d2f..a4fbfdbf64e 100644 --- a/wgpu-hal/src/dynamic/command.rs +++ b/wgpu-hal/src/dynamic/command.rs @@ -415,7 +415,7 @@ impl DynCommandEncoder for C { .depth_stencil_attachment .as_ref() .map(|ds| ds.expect_downcast()), - multiview: desc.multiview, + multiview_mask: desc.multiview_mask, timestamp_writes: desc .timestamp_writes .as_ref() diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index b4255a6c811..cdcc799400f 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -2488,7 +2488,7 @@ pub struct RenderPassDescriptor<'a, Q: DynQuerySet + ?Sized, T: DynTextureView + pub sample_count: u32, pub color_attachments: &'a [Option>], pub depth_stencil_attachment: Option>, - pub multiview: Option, + pub multiview_mask: Option, pub timestamp_writes: Option>, pub occlusion_query_set: Option<&'a Q>, } diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 651fdcf0cc6..fea5667f9a8 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -784,7 +784,7 @@ impl crate::CommandEncoder for super::CommandEncoder { colors: ArrayVec::default(), depth_stencil: None, sample_count: desc.sample_count, - multiview: desc.multiview, + multiview_mask: desc.multiview_mask, }; let mut fb_key = super::FramebufferKey { raw_pass: vk::RenderPass::null(), @@ -825,15 +825,6 @@ impl crate::CommandEncoder for super::CommandEncoder { vk_clear_values.push(unsafe { mem::zeroed() }); fb_key.push_view(at.view.identified_raw_view()); } - - // 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); - } - } } else { rp_key.colors.push(None); } @@ -850,12 +841,6 @@ impl crate::CommandEncoder for super::CommandEncoder { stencil_ops: ds.stencil_ops, }); fb_key.push_view(ds.target.view.identified_raw_view()); - - // 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); - } } let render_area = vk::Rect2D { diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index d9037118685..81790da2833 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -83,7 +83,7 @@ impl super::DeviceShared { ref colors, ref depth_stencil, sample_count, - multiview, + multiview_mask, } = *e.key(); let mut vk_attachments = Vec::new(); @@ -208,11 +208,11 @@ impl super::DeviceShared { let mut multiview_info; let mask; - if let Some(multiview) = multiview { + if let Some(multiview_mask) = multiview_mask { // 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]; + mask = [multiview_mask.get()]; // On Vulkan 1.1 or later, this is an alias for core functionality multiview_info = vk::RenderPassMultiviewCreateInfoKHR::default() @@ -1362,7 +1362,7 @@ impl crate::Device for super::Device { Ok(super::TextureView { raw_texture: texture.raw, raw, - layers, + _layers: layers, format: desc.format, raw_format, base_mip_level: desc.range.base_mip_level, @@ -1977,7 +1977,6 @@ impl crate::Device for super::Device { ]; let mut compatible_rp_key = super::RenderPassKey { sample_count: desc.multisample.count, - multiview: desc.multiview, ..Default::default() }; let mut stages = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new(); diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 8a0bb03fc3c..504fdeb3fdb 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -704,7 +704,7 @@ struct RenderPassKey { colors: ArrayVec, { crate::MAX_COLOR_ATTACHMENTS }>, depth_stencil: Option, sample_count: u32, - multiview: Option, + multiview_mask: Option, } struct DeviceShared { @@ -965,7 +965,7 @@ impl Texture { pub struct TextureView { raw_texture: vk::Image, raw: vk::ImageView, - layers: NonZeroU32, + _layers: NonZeroU32, format: wgt::TextureFormat, raw_format: vk::Format, base_mip_level: u32, diff --git a/wgpu/src/api/render_pass.rs b/wgpu/src/api/render_pass.rs index c8cc56018eb..5d4f6635278 100644 --- a/wgpu/src/api/render_pass.rs +++ b/wgpu/src/api/render_pass.rs @@ -1,4 +1,4 @@ -use core::ops::Range; +use core::{num::NonZeroU32, ops::Range}; use crate::{ api::{impl_deferred_command_buffer_actions, SharedDeferredCommandBufferActions}, @@ -654,6 +654,8 @@ pub struct RenderPassDescriptor<'a> { pub timestamp_writes: Option>, /// Defines where the occlusion query results will be stored for this pass. pub occlusion_query_set: Option<&'a QuerySet>, + /// The mask of multiview image layers to use for this render pass + pub multiview_mask: Option, } #[cfg(send_sync)] static_assertions::assert_impl_all!(RenderPassDescriptor<'_>: Send, Sync); diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 812cd5276a4..73012ca64c9 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -2534,6 +2534,7 @@ impl dispatch::CommandEncoderInterface for CoreCommandEncoder { color_attachments: Borrowed(&colors), depth_stencil_attachment: depth_stencil.as_ref(), occlusion_query_set: desc.occlusion_query_set.map(|qs| qs.inner.as_core().id), + multiview_mask: desc.multiview_mask, }, ); diff --git a/wgpu/src/util/texture_blitter.rs b/wgpu/src/util/texture_blitter.rs index e6b46e02859..edabd905be6 100644 --- a/wgpu/src/util/texture_blitter.rs +++ b/wgpu/src/util/texture_blitter.rs @@ -209,6 +209,7 @@ impl TextureBlitter { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &bind_group, &[]); From eca707f1b3bba8a62e6ae17b342061e3386e0c46 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 10 Sep 2025 23:50:44 -0500 Subject: [PATCH 11/59] Added multiview to HLSL writer --- naga/src/back/hlsl/conv.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/naga/src/back/hlsl/conv.rs b/naga/src/back/hlsl/conv.rs index ed40cbe5102..0aad453130d 100644 --- a/naga/src/back/hlsl/conv.rs +++ b/naga/src/back/hlsl/conv.rs @@ -172,6 +172,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 @@ -180,7 +181,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:?}"))) } }) From 1860e5fae8c2807762de920e06616f75fc5859b9 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 11:09:52 -0500 Subject: [PATCH 12/59] Other merge commit --- wgpu-core/src/command/encoder_command.rs | 3 ++- wgpu-core/src/device/trace.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/wgpu-core/src/command/encoder_command.rs b/wgpu-core/src/command/encoder_command.rs index 058caed025c..35c7a41813c 100644 --- a/wgpu-core/src/command/encoder_command.rs +++ b/wgpu-core/src/command/encoder_command.rs @@ -1,4 +1,4 @@ -use core::convert::Infallible; +use core::{convert::Infallible, num::NonZero}; use alloc::{string::String, vec::Vec}; @@ -62,6 +62,7 @@ pub enum Command { target_depth_stencil: Option, timestamp_writes: Option, occlusion_query_set_id: Option, + multiview_mask: Option>, }, BuildAccelerationStructures { blas: Vec, diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index fcb50ce49f4..fcb3f589154 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -1,5 +1,5 @@ use alloc::{string::String, vec::Vec}; -use core::{convert::Infallible, num::NonZeroU32, ops::Range}; +use core::{convert::Infallible, ops::Range}; #[cfg(feature = "trace")] use {alloc::borrow::Cow, std::io::Write as _}; From ac82457cc66f855461fd5863ebde575d1b891406 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 11:18:41 -0500 Subject: [PATCH 13/59] Added changelog entry which assumes the view bitmask is allowed --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bac70e359f..ce8cdcc5048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -189,6 +189,7 @@ By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162). - Require new `F16_IN_F32` downlevel flag for `quantizeToF16`, `pack2x16float`, and `unpack2x16float` in WGSL input. By @aleiserson in [#8130](https://github.com/gfx-rs/wgpu/pull/8130). - The error message for non-copyable depth/stencil formats no longer mentions the aspect when it is not relevant. By @reima in [#8156](https://github.com/gfx-rs/wgpu/pull/8156). - Track the initialization status of buffer memory correctly when `copy_texture_to_buffer` skips over padding space between rows or layers, or when the start/end of a texture-buffer transfer is not 4B aligned. By @andyleiserson in [#8099](https://github.com/gfx-rs/wgpu/pull/8099). +- Fix multiview, add tests, add support to DX12, add support for view bitmask. By @SupaMaggie70Incorporated in [#8206](https://github.com/gfx-rs/wgpu/pull/8206). #### naga From 424f31e7c3176ad4227fdd4a2168bd3569302e01 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 14:46:20 -0500 Subject: [PATCH 14/59] Updated changelog --- CHANGELOG.md | 24 +++++++++++++++++++++++- examples/features/src/boids/mod.rs | 3 +++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a545564fad7..d58ccf5a9b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -158,6 +158,29 @@ by if the `Feature::MULTI_DRAW_INDIRECT_COUNT` feature is available on the devic By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162). +#### Multiview on DX12 and support for view bitmasks + +Multiview has been reworked, adding support for DX12 and 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 multiple to non-adjacent layers. Note that this also influences apps that don't use multiview, as they have to set this field 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), ++ } +``` +By @SupaMaggie70Incorporated in [#8206](https://github.com/gfx-rs/wgpu/pull/8206). + ### New Features #### General @@ -189,7 +212,6 @@ By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162). - Require new `F16_IN_F32` downlevel flag for `quantizeToF16`, `pack2x16float`, and `unpack2x16float` in WGSL input. By @aleiserson in [#8130](https://github.com/gfx-rs/wgpu/pull/8130). - The error message for non-copyable depth/stencil formats no longer mentions the aspect when it is not relevant. By @reima in [#8156](https://github.com/gfx-rs/wgpu/pull/8156). - Track the initialization status of buffer memory correctly when `copy_texture_to_buffer` skips over padding space between rows or layers, or when the start/end of a texture-buffer transfer is not 4B aligned. By @andyleiserson in [#8099](https://github.com/gfx-rs/wgpu/pull/8099). -- Fix multiview, add tests, add support to DX12, add support for view bitmask. By @SupaMaggie70Incorporated in [#8206](https://github.com/gfx-rs/wgpu/pull/8206). #### naga diff --git a/examples/features/src/boids/mod.rs b/examples/features/src/boids/mod.rs index d272442275a..15f3a90bd05 100644 --- a/examples/features/src/boids/mod.rs +++ b/examples/features/src/boids/mod.rs @@ -1,6 +1,8 @@ // Flocking boids example with gpu compute update pass // adapted from https://github.com/austinEng/webgpu-samples/blob/master/src/examples/computeBoids.ts +use std::num::NonZero; + use nanorand::{Rng, WyRand}; use wgpu::util::DeviceExt; @@ -276,6 +278,7 @@ impl crate::framework::Example for Example { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: NonZero::new(3), }; // get command encoder From f933d2c7846fe27b5de609e14866e5357a188411 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 15:05:45 -0500 Subject: [PATCH 15/59] Exposed feature on metal, commented dx12, restricted vulkan to <=64 due to bitmask limitation --- wgpu-hal/src/dx12/adapter.rs | 2 ++ wgpu-hal/src/metal/adapter.rs | 26 ++++++++++++++++++++++++-- wgpu-hal/src/metal/mod.rs | 1 + wgpu-hal/src/vulkan/adapter.rs | 3 ++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 15696f238e0..e46fe981df5 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -684,6 +684,8 @@ impl super::Adapter { }, // See https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html#maximum-viewinstancecount + // This is really frickin annoying, 6 (probably 8) for cube mapping would be really nice. But they + // arbitrarily chose 4, eliminating tons of use cases. max_multiview_view_count: if view_instancing { 4 } else { 0 }, // This limit is specific to vulkan max_multiview_instance_index: if view_instancing { u32::MAX } else { 0 }, diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index c09a157683a..8bf8ee3e8fb 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -902,6 +902,16 @@ impl super::PrivateCapabilities { && (device.supports_family(MTLGPUFamily::Apple7) || device.supports_family(MTLGPUFamily::Mac2)), supports_shared_event: version.at_least((10, 14), (12, 0), os_is_mac), + supported_vertex_amplification_factor: { + let mut factor = 1; + // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf#page=8 + // The table specifies either none, 2, 8, or unsupported, implying it is a relatively small power of 2 + // The bitmask only uses 64 bits, so it can't be higher even if the device for some reason claims to support that. + while device.supports_vertex_amplification_count(factor) && factor <= 64 { + factor *= 2; + } + factor as u32 + }, } } @@ -1001,6 +1011,10 @@ impl super::PrivateCapabilities { features.insert(F::SUBGROUP | F::SUBGROUP_BARRIER); } + if self.supported_vertex_amplification_factor > 1 { + features.insert(F::MULTIVIEW); + } + features } @@ -1092,8 +1106,16 @@ impl super::PrivateCapabilities { // buffer binding points or via argument buffers max_acceleration_structures_per_shader_stage: 0, - max_multiview_view_count: 0, - max_multiview_instance_index: 0, + max_multiview_view_count: if self.supported_vertex_amplification_factor > 1 { + self.supported_vertex_amplification_factor + } else { + 0 + }, + max_multiview_instance_index: if self.supported_vertex_amplification_factor > 1 { + u32::MAX + } else { + 0 + }, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new(self.buffer_alignment).unwrap(), diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 00223b2f778..83c5994766c 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -300,6 +300,7 @@ struct PrivateCapabilities { int64_atomics: bool, float_atomics: bool, supports_shared_event: bool, + supported_vertex_amplification_factor: u32, } #[derive(Clone, Debug)] diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 4f57103db0a..c49f99e1563 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -1256,7 +1256,8 @@ impl PhysicalDeviceProperties { let (max_multiview_view_count, max_multiview_instance_index) = if let Some(properties) = self.multiview { ( - properties.max_multiview_view_count, + // The bitmask only uses 64 bits, so it can't be higher even if the device for some reason claims to support that. + properties.max_multiview_view_count.min(64), properties.max_multiview_instance_index, ) } else { From 13b012fa277ff41a3ab74aabff5be2000d90e60f Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 15:32:27 -0500 Subject: [PATCH 16/59] Updated multiview for metal --- wgpu-hal/src/metal/adapter.rs | 10 ++++++---- wgpu-hal/src/metal/command.rs | 15 +++++++++++++++ wgpu-hal/src/metal/device.rs | 4 ++++ wgpu-hal/src/vulkan/adapter.rs | 4 ++-- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 8bf8ee3e8fb..30d899672db 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -906,11 +906,13 @@ impl super::PrivateCapabilities { let mut factor = 1; // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf#page=8 // The table specifies either none, 2, 8, or unsupported, implying it is a relatively small power of 2 - // The bitmask only uses 64 bits, so it can't be higher even if the device for some reason claims to support that. - while device.supports_vertex_amplification_count(factor) && factor <= 64 { - factor *= 2; + // The bitmask only uses 32 bits, so it can't be higher even if the device for some reason claims to support that. + loop { + if factor >= 32 || !device.supports_vertex_amplification_count(factor * 2) { + break factor as u32; + } + factor *= 2 } - factor as u32 }, } } diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 5888eb4e909..758a1e332ba 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -9,6 +9,7 @@ use metal::{ MTLIndexType, MTLLoadAction, MTLPrimitiveType, MTLScissorRect, MTLSize, MTLStoreAction, MTLViewport, MTLVisibilityResultMode, NSRange, }; +use smallvec::SmallVec; // has to match `Temp::binding_sizes` const WORD_SIZE: usize = 4; @@ -656,6 +657,20 @@ impl crate::CommandEncoder for super::CommandEncoder { let raw = self.raw_cmd_buf.as_ref().unwrap(); let encoder = raw.new_render_command_encoder(descriptor); + if let Some(mv) = desc.multiview_mask { + let mv = mv.get(); + let mut maps: SmallVec<[metal::VertexAmplificationViewMapping; 32]> = + SmallVec::new(); + for i in 0..32 { + if (mv & (1 << i)) != 0 { + maps.push(metal::VertexAmplificationViewMapping { + renderTargetArrayIndexOffset: i, + viewportArrayIndexOffset: 0, + }); + } + } + encoder.set_vertex_amplification_count(mv.count_ones() as u64, Some(&maps)); + } if let Some(label) = desc.label { encoder.set_label(label); } diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index dd5d05b6d50..046d06b3b57 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -1337,6 +1337,10 @@ impl crate::Device for super::Device { descriptor.set_label(name); } + if let Some(mv) = desc.multiview { + descriptor.set_max_vertex_amplification_count(mv.get() as u64); + } + let raw = self .shared .device diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index c49f99e1563..d000254ea9b 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -1256,8 +1256,8 @@ impl PhysicalDeviceProperties { let (max_multiview_view_count, max_multiview_instance_index) = if let Some(properties) = self.multiview { ( - // The bitmask only uses 64 bits, so it can't be higher even if the device for some reason claims to support that. - properties.max_multiview_view_count.min(64), + // The bitmask only uses 32 bits, so it can't be higher even if the device for some reason claims to support that. + properties.max_multiview_view_count.min(32), properties.max_multiview_instance_index, ) } else { From 0cff8c5cef9adffc064171d14fa172bfe626cb6b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 15:35:30 -0500 Subject: [PATCH 17/59] Added view index thing to msl writer --- naga/src/back/msl/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/naga/src/back/msl/mod.rs b/naga/src/back/msl/mod.rs index 7e9180e0c25..49e318737a2 100644 --- a/naga/src/back/msl/mod.rs +++ b/naga/src/back/msl/mod.rs @@ -659,6 +659,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", @@ -685,7 +686,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)) } }; From bf9775aa1c0799587af262a7a10d02c8a675ea33 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 15:53:06 -0500 Subject: [PATCH 18/59] Finalized MSL writer changes --- naga/src/back/msl/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/naga/src/back/msl/mod.rs b/naga/src/back/msl/mod.rs index 49e318737a2..ad5276283b6 100644 --- a/naga/src/back/msl/mod.rs +++ b/naga/src/back/msl/mod.rs @@ -521,6 +521,12 @@ impl Options { crate::BuiltIn::PrimitiveIndex if self.lang_version < (2, 2) => { 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())); + } _ => {} } From 4ba47594ec6f18e05b3c0eb1f11a01378f509ac4 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 15:56:17 -0500 Subject: [PATCH 19/59] Removed nonsense multiview mask from boids example, fixed wgpu-hal examples multiview fields --- examples/features/src/boids/mod.rs | 1 - wgpu-hal/examples/halmark/main.rs | 2 +- wgpu-hal/examples/raw-gles.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/features/src/boids/mod.rs b/examples/features/src/boids/mod.rs index 15f3a90bd05..2fed2da61a8 100644 --- a/examples/features/src/boids/mod.rs +++ b/examples/features/src/boids/mod.rs @@ -278,7 +278,6 @@ impl crate::framework::Example for Example { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, - multiview_mask: NonZero::new(3), }; // get command encoder diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs index 22f211c909b..9bf5976ec74 100644 --- a/wgpu-hal/examples/halmark/main.rs +++ b/wgpu-hal/examples/halmark/main.rs @@ -727,7 +727,7 @@ impl Example { }, })], depth_stencil_attachment: None, - multiview: None, + multiview_mask: None, timestamp_writes: None, occlusion_query_set: None, }; diff --git a/wgpu-hal/examples/raw-gles.rs b/wgpu-hal/examples/raw-gles.rs index e90561efe19..bda0d345875 100644 --- a/wgpu-hal/examples/raw-gles.rs +++ b/wgpu-hal/examples/raw-gles.rs @@ -329,7 +329,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter, 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, }; From 863283f9070f1fc1a28b8c0521c41a4c6906ac8f Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:04:29 -0500 Subject: [PATCH 20/59] Mopped up miscellaneous crates, going to work on tests & examples now --- benches/benches/wgpu-benchmark/renderpass.rs | 2 ++ deno_webgpu/command_encoder.rs | 2 ++ deno_webgpu/render_pass.rs | 3 +++ wgpu-hal/Cargo.toml | 3 +-- wgpu-hal/src/metal/command.rs | 4 ++++ 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/benches/benches/wgpu-benchmark/renderpass.rs b/benches/benches/wgpu-benchmark/renderpass.rs index 946c32bfe40..37754116696 100644 --- a/benches/benches/wgpu-benchmark/renderpass.rs +++ b/benches/benches/wgpu-benchmark/renderpass.rs @@ -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()); diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs index 0106b0c0c8d..920e2386c8d 100644 --- a/deno_webgpu/command_encoder.rs +++ b/deno_webgpu/command_encoder.rs @@ -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; @@ -125,6 +126,7 @@ impl GPUCommandEncoder { depth_stencil_attachment: depth_stencil_attachment.as_ref(), timestamp_writes: timestamp_writes.as_ref(), 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 diff --git a/deno_webgpu/render_pass.rs b/deno_webgpu/render_pass.rs index 97f1bd8e860..8942291d36b 100644 --- a/deno_webgpu/render_pass.rs +++ b/deno_webgpu/render_pass.rs @@ -396,6 +396,9 @@ pub(crate) struct GPURenderPassDescriptor { /*#[webidl(default = 50000000)] #[options(enforce_range = true)] pub max_draw_count: u64,*/ + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub multiview_mask: u32, } #[derive(WebIDL)] diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 3b8c37c861e..ed272e9ef90 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -99,7 +99,6 @@ vulkan = [ "dep:ordered-float", "dep:parking_lot", "dep:profiling", - "dep:smallvec", "dep:windows", "windows/Win32", ] @@ -233,7 +232,7 @@ glow = { workspace = true, optional = true } ash = { workspace = true, optional = true } gpu-alloc = { workspace = true, optional = true } gpu-descriptor = { workspace = true, optional = true } -smallvec = { workspace = true, optional = true, features = ["union"] } +smallvec = { workspace = true, features = ["union"] } # Backend: GLES khronos-egl = { workspace = true, features = ["dynamic"], optional = true } libloading = { workspace = true, optional = true } diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 758a1e332ba..46b185c045b 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -658,6 +658,9 @@ impl crate::CommandEncoder for super::CommandEncoder { let raw = self.raw_cmd_buf.as_ref().unwrap(); let encoder = raw.new_render_command_encoder(descriptor); if let Some(mv) = desc.multiview_mask { + // Here we unpack the multiview bitmask. I'm not entirely sure why Apple makes us do this. + // Most likely the API just wasn't thought about enough. It's not like they ever allow you + // to use enough views to overflow a 32-bit bitmask. let mv = mv.get(); let mut maps: SmallVec<[metal::VertexAmplificationViewMapping; 32]> = SmallVec::new(); @@ -665,6 +668,7 @@ impl crate::CommandEncoder for super::CommandEncoder { if (mv & (1 << i)) != 0 { maps.push(metal::VertexAmplificationViewMapping { renderTargetArrayIndexOffset: i, + // WGPU doesn't allow rendering to multiple viewports in a single pass viewportArrayIndexOffset: 0, }); } From ccec13b5113f866678e01274e47e1bdde6192848 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:07:37 -0500 Subject: [PATCH 21/59] Fixed examples --- examples/features/src/boids/mod.rs | 3 +-- examples/features/src/bunnymark/mod.rs | 1 + examples/features/src/conservative_raster/mod.rs | 2 ++ examples/features/src/cube/mod.rs | 1 + examples/features/src/hello_triangle/mod.rs | 1 + examples/features/src/hello_windows/mod.rs | 1 + examples/features/src/mesh_shader/mod.rs | 1 + examples/features/src/mipmap/mod.rs | 2 ++ examples/features/src/msaa_line/mod.rs | 1 + examples/features/src/multiple_render_targets/mod.rs | 2 ++ examples/features/src/ray_cube_compute/mod.rs | 1 + examples/features/src/ray_cube_fragment/mod.rs | 1 + examples/features/src/ray_cube_normals/mod.rs | 1 + examples/features/src/ray_scene/mod.rs | 1 + examples/features/src/ray_shadows/mod.rs | 1 + examples/features/src/ray_traced_triangle/mod.rs | 1 + examples/features/src/render_to_texture/mod.rs | 1 + examples/features/src/shadow/mod.rs | 2 ++ examples/features/src/skybox/mod.rs | 1 + examples/features/src/srgb_blend/mod.rs | 1 + examples/features/src/stencil_triangles/mod.rs | 1 + examples/features/src/texture_arrays/mod.rs | 1 + examples/features/src/timestamp_queries/mod.rs | 1 + examples/features/src/uniform_values/mod.rs | 1 + examples/features/src/water/mod.rs | 3 +++ 25 files changed, 31 insertions(+), 2 deletions(-) diff --git a/examples/features/src/boids/mod.rs b/examples/features/src/boids/mod.rs index 2fed2da61a8..be79435b70d 100644 --- a/examples/features/src/boids/mod.rs +++ b/examples/features/src/boids/mod.rs @@ -1,8 +1,6 @@ // Flocking boids example with gpu compute update pass // adapted from https://github.com/austinEng/webgpu-samples/blob/master/src/examples/computeBoids.ts -use std::num::NonZero; - use nanorand::{Rng, WyRand}; use wgpu::util::DeviceExt; @@ -278,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 diff --git a/examples/features/src/bunnymark/mod.rs b/examples/features/src/bunnymark/mod.rs index 233fe8ba539..512137fd30f 100644 --- a/examples/features/src/bunnymark/mod.rs +++ b/examples/features/src/bunnymark/mod.rs @@ -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, &[]); diff --git a/examples/features/src/conservative_raster/mod.rs b/examples/features/src/conservative_raster/mod.rs index d2f5c74fdec..2f1f417b161 100644 --- a/examples/features/src/conservative_raster/mod.rs +++ b/examples/features/src/conservative_raster/mod.rs @@ -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); diff --git a/examples/features/src/cube/mod.rs b/examples/features/src/cube/mod.rs index 686dca3782e..ccdd31af08d 100644 --- a/examples/features/src/cube/mod.rs +++ b/examples/features/src/cube/mod.rs @@ -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); diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index eca99f6bb37..cf5d40e7659 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -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); diff --git a/examples/features/src/hello_windows/mod.rs b/examples/features/src/hello_windows/mod.rs index fbd69f0bff2..c1234e54998 100644 --- a/examples/features/src/hello_windows/mod.rs +++ b/examples/features/src/hello_windows/mod.rs @@ -133,6 +133,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc, wgpu::Color depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); } diff --git a/examples/features/src/mesh_shader/mod.rs b/examples/features/src/mesh_shader/mod.rs index 675150f5106..3b5afe4a37e 100644 --- a/examples/features/src/mesh_shader/mod.rs +++ b/examples/features/src/mesh_shader/mod.rs @@ -106,6 +106,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); diff --git a/examples/features/src/mipmap/mod.rs b/examples/features/src/mipmap/mod.rs index 5e5cc3d34e0..4e49132594e 100644 --- a/examples/features/src/mipmap/mod.rs +++ b/examples/features/src/mipmap/mod.rs @@ -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); @@ -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, &[]); diff --git a/examples/features/src/msaa_line/mod.rs b/examples/features/src/msaa_line/mod.rs index 71c6b277c4b..7dd4eb2ec06 100644 --- a/examples/features/src/msaa_line/mod.rs +++ b/examples/features/src/msaa_line/mod.rs @@ -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)); } diff --git a/examples/features/src/multiple_render_targets/mod.rs b/examples/features/src/multiple_render_targets/mod.rs index d708f701f0b..31c4481cd74 100644 --- a/examples/features/src/multiple_render_targets/mod.rs +++ b/examples/features/src/multiple_render_targets/mod.rs @@ -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, &[]); @@ -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, &[]); diff --git a/examples/features/src/ray_cube_compute/mod.rs b/examples/features/src/ray_cube_compute/mod.rs index 3d875b1c745..2e60e5fd956 100644 --- a/examples/features/src/ray_cube_compute/mod.rs +++ b/examples/features/src/ray_cube_compute/mod.rs @@ -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); diff --git a/examples/features/src/ray_cube_fragment/mod.rs b/examples/features/src/ray_cube_fragment/mod.rs index dde26e02832..6643152a937 100644 --- a/examples/features/src/ray_cube_fragment/mod.rs +++ b/examples/features/src/ray_cube_fragment/mod.rs @@ -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); diff --git a/examples/features/src/ray_cube_normals/mod.rs b/examples/features/src/ray_cube_normals/mod.rs index 4620b7551c0..a5553c7fa79 100644 --- a/examples/features/src/ray_cube_normals/mod.rs +++ b/examples/features/src/ray_cube_normals/mod.rs @@ -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); diff --git a/examples/features/src/ray_scene/mod.rs b/examples/features/src/ray_scene/mod.rs index 21c2aada802..eb367cc53a2 100644 --- a/examples/features/src/ray_scene/mod.rs +++ b/examples/features/src/ray_scene/mod.rs @@ -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); diff --git a/examples/features/src/ray_shadows/mod.rs b/examples/features/src/ray_shadows/mod.rs index 559ac32342a..93a86edf65d 100644 --- a/examples/features/src/ray_shadows/mod.rs +++ b/examples/features/src/ray_shadows/mod.rs @@ -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); diff --git a/examples/features/src/ray_traced_triangle/mod.rs b/examples/features/src/ray_traced_triangle/mod.rs index 0df2e829d2b..eefe0ce6d1f 100644 --- a/examples/features/src/ray_traced_triangle/mod.rs +++ b/examples/features/src/ray_traced_triangle/mod.rs @@ -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); diff --git a/examples/features/src/render_to_texture/mod.rs b/examples/features/src/render_to_texture/mod.rs index 7981f665506..e7aa5de14de 100644 --- a/examples/features/src/render_to_texture/mod.rs +++ b/examples/features/src/render_to_texture/mod.rs @@ -95,6 +95,7 @@ async fn run(_path: Option) { 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); diff --git a/examples/features/src/shadow/mod.rs b/examples/features/src/shadow/mod.rs index b89d2c902dc..477f6a7429e 100644 --- a/examples/features/src/shadow/mod.rs +++ b/examples/features/src/shadow/mod.rs @@ -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, &[]); diff --git a/examples/features/src/skybox/mod.rs b/examples/features/src/skybox/mod.rs index 699f6615ba9..3847a18824d 100644 --- a/examples/features/src/skybox/mod.rs +++ b/examples/features/src/skybox/mod.rs @@ -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, &[]); diff --git a/examples/features/src/srgb_blend/mod.rs b/examples/features/src/srgb_blend/mod.rs index fcd59265b76..dbc1455dfce 100644 --- a/examples/features/src/srgb_blend/mod.rs +++ b/examples/features/src/srgb_blend/mod.rs @@ -196,6 +196,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); diff --git a/examples/features/src/stencil_triangles/mod.rs b/examples/features/src/stencil_triangles/mod.rs index e1e44acf40f..c39deb1c2f6 100644 --- a/examples/features/src/stencil_triangles/mod.rs +++ b/examples/features/src/stencil_triangles/mod.rs @@ -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); diff --git a/examples/features/src/texture_arrays/mod.rs b/examples/features/src/texture_arrays/mod.rs index e7615444200..151bd0b2ca8 100644 --- a/examples/features/src/texture_arrays/mod.rs +++ b/examples/features/src/texture_arrays/mod.rs @@ -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); diff --git a/examples/features/src/timestamp_queries/mod.rs b/examples/features/src/timestamp_queries/mod.rs index 2531cc4e692..7deaa2b7f44 100644 --- a/examples/features/src/timestamp_queries/mod.rs +++ b/examples/features/src/timestamp_queries/mod.rs @@ -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; diff --git a/examples/features/src/uniform_values/mod.rs b/examples/features/src/uniform_values/mod.rs index 6a42f2635e1..344b3469471 100644 --- a/examples/features/src/uniform_values/mod.rs +++ b/examples/features/src/uniform_values/mod.rs @@ -319,6 +319,7 @@ async fn run(event_loop: EventLoop<()>, window: Arc) { depth_stencil_attachment: None, occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(&wgpu_context_ref.pipeline); // (9) diff --git a/examples/features/src/water/mod.rs b/examples/features/src/water/mod.rs index 5d1241fce39..2f29bd27bba 100644 --- a/examples/features/src/water/mod.rs +++ b/examples/features/src/water/mod.rs @@ -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); From c6da54a146962ff7dffe57945974c73583b78601 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:11:10 -0500 Subject: [PATCH 22/59] Fixed every test --- tests/tests/wgpu-gpu/binding_array/sampled_textures.rs | 1 + tests/tests/wgpu-gpu/clip_distances.rs | 1 + tests/tests/wgpu-gpu/device.rs | 1 + tests/tests/wgpu-gpu/draw_indirect.rs | 2 ++ tests/tests/wgpu-gpu/encoder.rs | 1 + tests/tests/wgpu-gpu/mem_leaks.rs | 1 + tests/tests/wgpu-gpu/mesh_shader/mod.rs | 2 ++ tests/tests/wgpu-gpu/multiview.rs | 2 +- tests/tests/wgpu-gpu/occlusion_query/mod.rs | 1 + tests/tests/wgpu-gpu/planar_texture/mod.rs | 1 + tests/tests/wgpu-gpu/regression/issue_3349.rs | 1 + tests/tests/wgpu-gpu/regression/issue_3457.rs | 2 ++ tests/tests/wgpu-gpu/regression/issue_4485.rs | 1 + tests/tests/wgpu-gpu/regression/issue_4514.rs | 1 + tests/tests/wgpu-gpu/render_pass_ownership.rs | 1 + tests/tests/wgpu-gpu/render_target.rs | 2 ++ tests/tests/wgpu-gpu/scissor_tests/mod.rs | 1 + tests/tests/wgpu-gpu/shader_primitive_index/mod.rs | 1 + tests/tests/wgpu-gpu/shader_view_format/mod.rs | 1 + tests/tests/wgpu-gpu/vertex_formats/mod.rs | 1 + tests/tests/wgpu-gpu/vertex_indices/mod.rs | 1 + tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs | 4 ++++ tests/tests/wgpu-validation/api/external_texture.rs | 1 + 23 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs b/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs index c445857a92f..5c6bcd79019 100644 --- a/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs +++ b/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs @@ -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, &[]); diff --git a/tests/tests/wgpu-gpu/clip_distances.rs b/tests/tests/wgpu-gpu/clip_distances.rs index 3152333e8dd..af1e3c3d0c4 100644 --- a/tests/tests/wgpu-gpu/clip_distances.rs +++ b/tests/tests/wgpu-gpu/clip_distances.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/device.rs b/tests/tests/wgpu-gpu/device.rs index 5accb3d33b8..7df1949bab4 100644 --- a/tests/tests/wgpu-gpu/device.rs +++ b/tests/tests/wgpu-gpu/device.rs @@ -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()]); diff --git a/tests/tests/wgpu-gpu/draw_indirect.rs b/tests/tests/wgpu-gpu/draw_indirect.rs index a0c8253be0b..a3be9a81d32 100644 --- a/tests/tests/wgpu-gpu/draw_indirect.rs +++ b/tests/tests/wgpu-gpu/draw_indirect.rs @@ -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); @@ -745,6 +746,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); diff --git a/tests/tests/wgpu-gpu/encoder.rs b/tests/tests/wgpu-gpu/encoder.rs index 426ef3c808e..ab1c10594ca 100644 --- a/tests/tests/wgpu-gpu/encoder.rs +++ b/tests/tests/wgpu-gpu/encoder.rs @@ -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. diff --git a/tests/tests/wgpu-gpu/mem_leaks.rs b/tests/tests/wgpu-gpu/mem_leaks.rs index c58981d5b1a..4078423f445 100644 --- a/tests/tests/wgpu-gpu/mem_leaks.rs +++ b/tests/tests/wgpu-gpu/mem_leaks.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/mesh_shader/mod.rs b/tests/tests/wgpu-gpu/mesh_shader/mod.rs index 23e2c6ccda5..ccda3732848 100644 --- a/tests/tests/wgpu-gpu/mesh_shader/mod.rs +++ b/tests/tests/wgpu-gpu/mesh_shader/mod.rs @@ -141,6 +141,7 @@ fn mesh_pipeline_build( }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_pipeline(&pipeline); pass.draw_mesh_tasks(1, 1, 1); @@ -234,6 +235,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 { diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 7d77228e5b9..3164511b044 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -144,7 +144,7 @@ async fn run_test(ctx: TestingContext) { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, - multiview_mask: 3, + multiview_mask: NonZero::new(3), }); rpass.set_pipeline(&pipeline); rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); diff --git a/tests/tests/wgpu-gpu/occlusion_query/mod.rs b/tests/tests/wgpu-gpu/occlusion_query/mod.rs index a30b0dc5893..2b1b2131794 100644 --- a/tests/tests/wgpu-gpu/occlusion_query/mod.rs +++ b/tests/tests/wgpu-gpu/occlusion_query/mod.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/planar_texture/mod.rs b/tests/tests/wgpu-gpu/planar_texture/mod.rs index 961d4b43f64..b491f9cc2da 100644 --- a/tests/tests/wgpu-gpu/planar_texture/mod.rs +++ b/tests/tests/wgpu-gpu/planar_texture/mod.rs @@ -100,6 +100,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, &[]); diff --git a/tests/tests/wgpu-gpu/regression/issue_3349.rs b/tests/tests/wgpu-gpu/regression/issue_3349.rs index adee4eb6527..8637f6c3217 100644 --- a/tests/tests/wgpu-gpu/regression/issue_3349.rs +++ b/tests/tests/wgpu-gpu/regression/issue_3349.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/regression/issue_3457.rs b/tests/tests/wgpu-gpu/regression/issue_3457.rs index 4b7503776ea..ccc6da4f39f 100644 --- a/tests/tests/wgpu-gpu/regression/issue_3457.rs +++ b/tests/tests/wgpu-gpu/regression/issue_3457.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/regression/issue_4485.rs b/tests/tests/wgpu-gpu/regression/issue_4485.rs index 5c814377822..006b2fcba21 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4485.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4485.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/regression/issue_4514.rs b/tests/tests/wgpu-gpu/regression/issue_4514.rs index f3ad2051a90..90d126dc3e8 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4514.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4514.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/render_pass_ownership.rs b/tests/tests/wgpu-gpu/render_pass_ownership.rs index 3e2b1a9831d..eea5b8f5bc4 100644 --- a/tests/tests/wgpu-gpu/render_pass_ownership.rs +++ b/tests/tests/wgpu-gpu/render_pass_ownership.rs @@ -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. diff --git a/tests/tests/wgpu-gpu/render_target.rs b/tests/tests/wgpu-gpu/render_target.rs index 9e69e805d8e..bf34bd658e9 100644 --- a/tests/tests/wgpu-gpu/render_target.rs +++ b/tests/tests/wgpu-gpu/render_target.rs @@ -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(..)); @@ -384,6 +385,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(..)); diff --git a/tests/tests/wgpu-gpu/scissor_tests/mod.rs b/tests/tests/wgpu-gpu/scissor_tests/mod.rs index 14b81eb4337..fdb8b4fcb5f 100644 --- a/tests/tests/wgpu-gpu/scissor_tests/mod.rs +++ b/tests/tests/wgpu-gpu/scissor_tests/mod.rs @@ -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( diff --git a/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs b/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs index eb6e6fa1d23..2fce464fdb5 100644 --- a/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs +++ b/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/shader_view_format/mod.rs b/tests/tests/wgpu-gpu/shader_view_format/mod.rs index d17f0e67572..c576445d042 100644 --- a/tests/tests/wgpu-gpu/shader_view_format/mod.rs +++ b/tests/tests/wgpu-gpu/shader_view_format/mod.rs @@ -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, &[]); diff --git a/tests/tests/wgpu-gpu/vertex_formats/mod.rs b/tests/tests/wgpu-gpu/vertex_formats/mod.rs index 88f7659307e..b27fa930114 100644 --- a/tests/tests/wgpu-gpu/vertex_formats/mod.rs +++ b/tests/tests/wgpu-gpu/vertex_formats/mod.rs @@ -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(..)); diff --git a/tests/tests/wgpu-gpu/vertex_indices/mod.rs b/tests/tests/wgpu-gpu/vertex_indices/mod.rs index 6d53e960829..849085338b0 100644 --- a/tests/tests/wgpu-gpu/vertex_indices/mod.rs +++ b/tests/tests/wgpu-gpu/vertex_indices/mod.rs @@ -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, }); { diff --git a/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs b/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs index 5d52b6e982a..193971f61c4 100644 --- a/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs +++ b/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs @@ -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, }); } diff --git a/tests/tests/wgpu-validation/api/external_texture.rs b/tests/tests/wgpu-validation/api/external_texture.rs index 989fe5ac8ae..0750cdf22e5 100644 --- a/tests/tests/wgpu-validation/api/external_texture.rs +++ b/tests/tests/wgpu-validation/api/external_texture.rs @@ -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); From e4653890fb2b32fae40cd3f49946cc843f18f9a8 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:12:56 -0500 Subject: [PATCH 23/59] Updated changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d58ccf5a9b9..91ec3da67dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -158,7 +158,7 @@ by if the `Feature::MULTI_DRAW_INDIRECT_COUNT` feature is available on the devic By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162). -#### Multiview on DX12 and support for view bitmasks +#### Multiview on all major platforms and support for multiview bitmasks Multiview has been reworked, adding support for DX12 and 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 multiple to non-adjacent layers. Note that this also influences apps that don't use multiview, as they have to set this field to `None`. From 30438b3b49b374adfc58adc218b7290c3630226b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:19:27 -0500 Subject: [PATCH 24/59] Made multiview use 32bit integer --- naga/src/valid/interface.rs | 2 +- tests/tests/wgpu-gpu/multiview.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/naga/src/valid/interface.rs b/naga/src/valid/interface.rs index 7c8cc903139..a8a0e0920c6 100644 --- a/naga/src/valid/interface.rs +++ b/naga/src/valid/interface.rs @@ -253,7 +253,7 @@ impl VaryingContext<'_> { St::Compute => false, St::Task | St::Mesh => unreachable!(), }, - *ty_inner == Ti::Scalar(crate::Scalar::I32), + *ty_inner == Ti::Scalar(crate::Scalar::U32), ), Bi::FragDepth => ( self.stage == St::Fragment && self.output, diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 3164511b044..7ecdce8c514 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -49,7 +49,7 @@ async fn run_test(ctx: TestingContext) { } @fragment - fn fs_main(@builtin(view_index) view_index: i32) -> @location(0) vec4f { + fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f { return vec4f(f32(view_index)); } "; From f20eaac395b5c9a77d4c3771641083fb1395a7ac Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:21:39 -0500 Subject: [PATCH 25/59] Made the default multiview mask 0 --- deno_webgpu/render_pass.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno_webgpu/render_pass.rs b/deno_webgpu/render_pass.rs index 8942291d36b..a6223d63149 100644 --- a/deno_webgpu/render_pass.rs +++ b/deno_webgpu/render_pass.rs @@ -396,7 +396,7 @@ pub(crate) struct GPURenderPassDescriptor { /*#[webidl(default = 50000000)] #[options(enforce_range = true)] pub max_draw_count: u64,*/ - #[webidl(default = 0xFFFFFFFF)] + #[webidl(default = 0)] #[options(enforce_range = true)] pub multiview_mask: u32, } From 1528421966012e081f2a7b3d80872068103a2bad Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:33:27 -0500 Subject: [PATCH 26/59] Fixed snapshots, final changelog update hopefully --- CHANGELOG.md | 2 ++ naga/tests/in/wgsl/multiview.toml | 4 +++- naga/tests/in/wgsl/multiview.wgsl | 2 +- naga/tests/in/wgsl/multiview_webgl.wgsl | 2 +- .../out/glsl/wgsl-multiview.main.Fragment.glsl | 2 +- .../glsl/wgsl-multiview_webgl.main.Fragment.glsl | 2 +- naga/tests/out/hlsl/wgsl-multiview.hlsl | 9 +++++++++ naga/tests/out/hlsl/wgsl-multiview.ron | 12 ++++++++++++ naga/tests/out/msl/wgsl-multiview.msl | 14 ++++++++++++++ naga/tests/out/spv/wgsl-multiview.spvasm | 2 +- naga/tests/out/wgsl/wgsl-multiview.wgsl | 2 +- 11 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 naga/tests/out/hlsl/wgsl-multiview.hlsl create mode 100644 naga/tests/out/hlsl/wgsl-multiview.ron create mode 100644 naga/tests/out/msl/wgsl-multiview.msl diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a80be45463..43667785663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -179,6 +179,8 @@ This change also introduces a view bitmask, a new field in `RenderPassDescriptor + multiview_mask: NonZero::new(3), + } ``` +One other breaking change worth noting is that `@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 diff --git a/naga/tests/in/wgsl/multiview.toml b/naga/tests/in/wgsl/multiview.toml index 690600e0957..2db377a7472 100644 --- a/naga/tests/in/wgsl/multiview.toml +++ b/naga/tests/in/wgsl/multiview.toml @@ -1,3 +1,5 @@ glsl_multiview = 2 god_mode = true -targets = "SPIRV | GLSL | WGSL" + +[msl] +lang_version = [2,3] \ No newline at end of file diff --git a/naga/tests/in/wgsl/multiview.wgsl b/naga/tests/in/wgsl/multiview.wgsl index 0eedd087866..ae01746ef24 100644 --- a/naga/tests/in/wgsl/multiview.wgsl +++ b/naga/tests/in/wgsl/multiview.wgsl @@ -1,2 +1,2 @@ @fragment -fn main(@builtin(view_index) view_index: i32) {} +fn main(@builtin(view_index) view_index: u32) {} diff --git a/naga/tests/in/wgsl/multiview_webgl.wgsl b/naga/tests/in/wgsl/multiview_webgl.wgsl index 0eedd087866..ae01746ef24 100644 --- a/naga/tests/in/wgsl/multiview_webgl.wgsl +++ b/naga/tests/in/wgsl/multiview_webgl.wgsl @@ -1,2 +1,2 @@ @fragment -fn main(@builtin(view_index) view_index: i32) {} +fn main(@builtin(view_index) view_index: u32) {} diff --git a/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl b/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl index 466aea062ff..93adf124266 100644 --- a/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl +++ b/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl @@ -6,7 +6,7 @@ precision highp int; void main() { - int view_index = gl_ViewIndex; + uint view_index = gl_ViewIndex; return; } diff --git a/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl b/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl index 30515289c95..f47c1a34cb4 100644 --- a/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl +++ b/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl @@ -6,7 +6,7 @@ precision highp int; void main() { - int view_index = int(gl_ViewID_OVR); + uint view_index = int(gl_ViewID_OVR); return; } diff --git a/naga/tests/out/hlsl/wgsl-multiview.hlsl b/naga/tests/out/hlsl/wgsl-multiview.hlsl new file mode 100644 index 00000000000..dc0997cd093 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-multiview.hlsl @@ -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; +} diff --git a/naga/tests/out/hlsl/wgsl-multiview.ron b/naga/tests/out/hlsl/wgsl-multiview.ron new file mode 100644 index 00000000000..341a4c528e3 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-multiview.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ( + entry_point:"main", + target_profile:"ps_5_1", + ), + ], + compute:[ + ], +) diff --git a/naga/tests/out/msl/wgsl-multiview.msl b/naga/tests/out/msl/wgsl-multiview.msl new file mode 100644 index 00000000000..e6c845ee232 --- /dev/null +++ b/naga/tests/out/msl/wgsl-multiview.msl @@ -0,0 +1,14 @@ +// language: metal2.3 +#include +#include + +using metal::uint; + + +struct main_Input { +}; +fragment void main_( + uint view_index [[amplification_id]] +) { + return; +} diff --git a/naga/tests/out/spv/wgsl-multiview.spvasm b/naga/tests/out/spv/wgsl-multiview.spvasm index 792dea5593c..93a218d62be 100644 --- a/naga/tests/out/spv/wgsl-multiview.spvasm +++ b/naga/tests/out/spv/wgsl-multiview.spvasm @@ -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 diff --git a/naga/tests/out/wgsl/wgsl-multiview.wgsl b/naga/tests/out/wgsl/wgsl-multiview.wgsl index 51192d2f7a6..64830243586 100644 --- a/naga/tests/out/wgsl/wgsl-multiview.wgsl +++ b/naga/tests/out/wgsl/wgsl-multiview.wgsl @@ -1,4 +1,4 @@ @fragment -fn main(@builtin(view_index) view_index: i32) { +fn main(@builtin(view_index) view_index: u32) { return; } From 81b681f37c7fbbf87ed94ef769b9b445a31a24b3 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:34:34 -0500 Subject: [PATCH 27/59] Reformatted multiview.toml --- naga/tests/in/wgsl/multiview.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/naga/tests/in/wgsl/multiview.toml b/naga/tests/in/wgsl/multiview.toml index 2db377a7472..adec5dab215 100644 --- a/naga/tests/in/wgsl/multiview.toml +++ b/naga/tests/in/wgsl/multiview.toml @@ -2,4 +2,4 @@ glsl_multiview = 2 god_mode = true [msl] -lang_version = [2,3] \ No newline at end of file +lang_version = [2, 3] From b04c75bc64fee2ab7a875ca572a74f2b1fbe16d6 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:46:13 -0500 Subject: [PATCH 28/59] Fixed GLSL writer --- naga/src/back/glsl/mod.rs | 1 - naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index 4c5a9d8cbcb..9ec36afd8bb 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -5204,7 +5204,6 @@ 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", // vertex Bi::BaseInstance => "uint(gl_BaseInstance)", diff --git a/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl b/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl index f47c1a34cb4..eeccb09c1e3 100644 --- a/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl +++ b/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl @@ -6,7 +6,7 @@ precision highp int; void main() { - uint view_index = int(gl_ViewID_OVR); + uint view_index = gl_ViewIndex; return; } From d0ab6d4f7f88cc3bc8e9f6b58e4a7cd1fc6d99dc Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 16:51:40 -0500 Subject: [PATCH 29/59] Attempted to fix temporarily the HLSL stuff --- naga/src/back/glsl/mod.rs | 2 -- naga/tests/in/wgsl/multiview.toml | 3 +++ naga/tests/out/hlsl/wgsl-multiview.ron | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index 9ec36afd8bb..802bd973bbb 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -460,7 +460,6 @@ impl IdGenerator { #[derive(Clone, Copy)] struct VaryingOptions { output: bool, - targeting_webgl: bool, draw_parameters: bool, } @@ -468,7 +467,6 @@ impl VaryingOptions { const fn from_writer_options(options: &Options, output: bool) -> Self { Self { output, - targeting_webgl: options.version.is_webgl(), draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS), } } diff --git a/naga/tests/in/wgsl/multiview.toml b/naga/tests/in/wgsl/multiview.toml index adec5dab215..04c49286f6c 100644 --- a/naga/tests/in/wgsl/multiview.toml +++ b/naga/tests/in/wgsl/multiview.toml @@ -3,3 +3,6 @@ god_mode = true [msl] lang_version = [2, 3] + +[hlsl] +shader_model = "V6_1" diff --git a/naga/tests/out/hlsl/wgsl-multiview.ron b/naga/tests/out/hlsl/wgsl-multiview.ron index 341a4c528e3..ff856788943 100644 --- a/naga/tests/out/hlsl/wgsl-multiview.ron +++ b/naga/tests/out/hlsl/wgsl-multiview.ron @@ -4,7 +4,7 @@ fragment:[ ( entry_point:"main", - target_profile:"ps_5_1", + target_profile:"ps_6_1", ), ], compute:[ From 63ce399b57c211c57f884d003f8e02dfa238b729 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 17:00:55 -0500 Subject: [PATCH 30/59] Made HLSL writer throw error if shader model is unsupported --- naga/src/back/hlsl/mod.rs | 2 ++ naga/src/back/hlsl/writer.rs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/naga/src/back/hlsl/mod.rs b/naga/src/back/hlsl/mod.rs index 8df06cf1323..bbd079dbd90 100644 --- a/naga/src/back/hlsl/mod.rs +++ b/naga/src/back/hlsl/mod.rs @@ -649,6 +649,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)] diff --git a/naga/src/back/hlsl/writer.rs b/naga/src/back/hlsl/writer.rs index ab95b9327f9..419a65fe5b6 100644 --- a/naga/src/back/hlsl/writer.rs +++ b/naga/src/back/hlsl/writer.rs @@ -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}")?; } From 11203bbfd749a6cea3eb2f352a03a430a33f5e85 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 17:07:40 -0500 Subject: [PATCH 31/59] Fixed webgl writing --- naga/src/back/glsl/mod.rs | 10 +++++++++- .../out/glsl/wgsl-multiview_webgl.main.Fragment.glsl | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index 802bd973bbb..cd7cf209048 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -460,6 +460,7 @@ impl IdGenerator { #[derive(Clone, Copy)] struct VaryingOptions { output: bool, + targetting_webgl: bool, draw_parameters: bool, } @@ -467,6 +468,7 @@ impl VaryingOptions { const fn from_writer_options(options: &Options, output: bool) -> Self { Self { output, + targetting_webgl: options.version.is_webgl(), draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS), } } @@ -5202,7 +5204,13 @@ const fn glsl_built_in(built_in: crate::BuiltIn, options: VaryingOptions) -> &'s "gl_FragCoord" } } - Bi::ViewIndex => "gl_ViewIndex", + Bi::ViewIndex => { + if options.targetting_webgl { + "gl_ViewID_OVR" + } else { + "gl_ViewIndex" + } + } // vertex Bi::BaseInstance => "uint(gl_BaseInstance)", Bi::BaseVertex => "uint(gl_BaseVertex)", diff --git a/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl b/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl index eeccb09c1e3..0e0e1246ccd 100644 --- a/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl +++ b/naga/tests/out/glsl/wgsl-multiview_webgl.main.Fragment.glsl @@ -6,7 +6,7 @@ precision highp int; void main() { - uint view_index = gl_ViewIndex; + uint view_index = gl_ViewID_OVR; return; } From dbe14005c62f9c3793ccc88c5cb9f1c23c4fc4f6 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 17:11:20 -0500 Subject: [PATCH 32/59] Fixed typos and hopefully also glsl (non-webgl) validation --- naga/src/back/glsl/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index cd7cf209048..c764430a568 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -460,7 +460,7 @@ impl IdGenerator { #[derive(Clone, Copy)] struct VaryingOptions { output: bool, - targetting_webgl: bool, + targeting_webgl: bool, draw_parameters: bool, } @@ -468,7 +468,7 @@ impl VaryingOptions { const fn from_writer_options(options: &Options, output: bool) -> Self { Self { output, - targetting_webgl: options.version.is_webgl(), + targeting_webgl: options.version.is_webgl(), draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS), } } @@ -5205,10 +5205,10 @@ const fn glsl_built_in(built_in: crate::BuiltIn, options: VaryingOptions) -> &'s } } Bi::ViewIndex => { - if options.targetting_webgl { + if options.targeting_webgl { "gl_ViewID_OVR" } else { - "gl_ViewIndex" + "uint(gl_ViewIndex)" } } // vertex From 7f8542d83bbaebc099359125457e3a9e42a8f0c3 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 17:15:12 -0500 Subject: [PATCH 33/59] Actually updated fixed snapshots --- naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl b/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl index 93adf124266..4ae52f27810 100644 --- a/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl +++ b/naga/tests/out/glsl/wgsl-multiview.main.Fragment.glsl @@ -6,7 +6,7 @@ precision highp int; void main() { - uint view_index = gl_ViewIndex; + uint view_index = uint(gl_ViewIndex); return; } From 7d66fec6574bb31de76ba23e003e5310faf706c2 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 22 Sep 2025 19:02:58 -0500 Subject: [PATCH 34/59] I think we're doing illegal shit in metal and somehow the second texture gets 255 no matter what. --- tests/tests/wgpu-gpu/multiview.rs | 146 +++++++++++++++--------------- 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 7ecdce8c514..d47690e18b4 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -50,7 +50,7 @@ async fn run_test(ctx: TestingContext) { @fragment fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f { - return vec4f(f32(view_index)); + return vec4f(view_index); } "; @@ -92,34 +92,31 @@ async fn run_test(ctx: TestingContext) { }; const TEXTURE_SIZE: u32 = 512; let pipeline = ctx.device.create_render_pipeline(&pipeline_desc); - let (texture, view) = { - let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: TEXTURE_SIZE, - height: TEXTURE_SIZE, - depth_or_array_layers: 2, - }, - 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 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(2), - }); - (texture, view) - }; + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: TEXTURE_SIZE, + height: TEXTURE_SIZE, + depth_or_array_layers: 2, + }, + 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 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(2), + }); let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { label: None, size: TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64 * 2, @@ -130,49 +127,48 @@ async fn run_test(ctx: TestingContext) { 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: &view, - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - multiview_mask: NonZero::new(3), - }); - rpass.set_pipeline(&pipeline); - rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); - rpass.draw(0..6, 0..1); - drop(rpass); - for i in 0..2 { - encoder.copy_texture_to_buffer( - wgpu::TexelCopyTextureInfo { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d { x: 0, y: 0, z: i }, - aspect: wgpu::TextureAspect::All, - }, - wgpu::TexelCopyBufferInfo { - buffer: &readback_buffer, - layout: wgpu::TexelCopyBufferLayout { - offset: i as u64 * TEXTURE_SIZE as u64 * TEXTURE_SIZE as u64, - bytes_per_row: Some(TEXTURE_SIZE), - rows_per_image: Some(TEXTURE_SIZE), + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: wgpu::StoreOp::Store, }, - }, - wgpu::Extent3d { - width: TEXTURE_SIZE, - height: TEXTURE_SIZE, - depth_or_array_layers: 1, - }, - ); + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + multiview_mask: NonZero::new(3), + }); + rpass.set_pipeline(&pipeline); + rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); + 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: 2, + }, + ); ctx.queue.submit([encoder.finish()]); let slice = readback_buffer.slice(..); @@ -182,10 +178,12 @@ async fn run_test(ctx: TestingContext) { let data = slice.get_mapped_range(); for view_idx in 0..2 { - let texture_bytes = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; - let succeeded = &data[texture_bytes * view_idx..texture_bytes * (view_idx + 1)] + let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; + let target_value = view_idx as u8 * u8::MAX; + let succeeded = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] .iter() - .all(|b| *b == if view_idx == 1 { u8::MAX } else { 0 }); - assert!(succeeded); + .all(|b| *b == target_value); + assert!(data.len() == each_texture_size * 2); + assert!(succeeded, "Failed with view {view_idx}"); } } From 5e3e662ecc13ba233a0672f5eebc6ab819b036b0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Mon, 22 Sep 2025 20:03:31 -0500 Subject: [PATCH 35/59] Fixed illegal behavior in shader that naga currently allows --- tests/tests/wgpu-gpu/multiview.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index d47690e18b4..fe72690f747 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -50,7 +50,7 @@ async fn run_test(ctx: TestingContext) { @fragment fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f { - return vec4f(view_index); + return vec4f(f32(view_index)); } "; @@ -179,6 +179,7 @@ async fn run_test(ctx: TestingContext) { let data = slice.get_mapped_range(); for view_idx in 0..2 { let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; + panic!("{}, {}", data[0], data[each_texture_size]); let target_value = view_idx as u8 * u8::MAX; let succeeded = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] .iter() From f612d0ff37e621aff4ce34ff4bc9f083a282f154 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 13:00:05 -0500 Subject: [PATCH 36/59] Fixed compiles --- deno_webgpu/command_encoder.rs | 2 +- tests/tests/wgpu-gpu/multiview.rs | 1 - tests/tests/wgpu-gpu/vertex_state.rs | 1 + wgpu-core/src/command/encoder_command.rs | 1 + wgpu-core/src/command/mod.rs | 2 ++ wgpu-core/src/command/render.rs | 1 + 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs index 66db31a1329..f0cdb99b523 100644 --- a/deno_webgpu/command_encoder.rs +++ b/deno_webgpu/command_encoder.rs @@ -143,7 +143,7 @@ impl GPUCommandEncoder { occlusion_query_set: descriptor .occlusion_query_set .map(|query_set| query_set.id), - multiview_mask: NonZer::new(descriptor.multiview_mask), + multiview_mask: NonZero::new(descriptor.multiview_mask), }; let (render_pass, err) = self diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index fe72690f747..ab65966e414 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -179,7 +179,6 @@ async fn run_test(ctx: TestingContext) { let data = slice.get_mapped_range(); for view_idx in 0..2 { let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; - panic!("{}, {}", data[0], data[each_texture_size]); let target_value = view_idx as u8 * u8::MAX; let succeeded = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] .iter() diff --git a/tests/tests/wgpu-gpu/vertex_state.rs b/tests/tests/wgpu-gpu/vertex_state.rs index fad6546d39f..8aa1b329a67 100644 --- a/tests/tests/wgpu-gpu/vertex_state.rs +++ b/tests/tests/wgpu-gpu/vertex_state.rs @@ -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. diff --git a/wgpu-core/src/command/encoder_command.rs b/wgpu-core/src/command/encoder_command.rs index 4a240d88ad8..0713cc55971 100644 --- a/wgpu-core/src/command/encoder_command.rs +++ b/wgpu-core/src/command/encoder_command.rs @@ -130,6 +130,7 @@ pub enum ArcCommand { depth_stencil_attachment: Option, timestamp_writes: Option, occlusion_query_set: Option>, + multiview_mask: Option>, }, BuildAccelerationStructures { blas: Vec, diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 887c19fb357..cd5adac4691 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -1329,6 +1329,7 @@ impl Global { depth_stencil_attachment, timestamp_writes, occlusion_query_set, + multiview_mask, } => { encode_render_pass( &mut state, @@ -1337,6 +1338,7 @@ impl Global { depth_stencil_attachment, timestamp_writes, occlusion_query_set, + multiview_mask, )?; } ArcCommand::RunComputePass { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 33e306ab82b..54572e4221b 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -1865,6 +1865,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, }) }) } From 988bc37f6d1f68f9339b0aa9f31e49fba9bbca7c Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 13:02:43 -0500 Subject: [PATCH 37/59] Tried to fix windows compiles --- wgpu-hal/src/dx12/adapter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index dacbf668967..bd4405009b2 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -679,7 +679,7 @@ impl super::Adapter { max_task_workgroups_per_dimension: Direct3D12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, // Multiview not supported by WGPU yet - max_mesh_multiview_count: 0, + max_mesh_multiview_view_count: 0, // This seems to be right, and I can't find anything to suggest it would be less than the 2048 provided here max_mesh_output_layers: Direct3D12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION, From bd696d43a179b2c28364df89760d0d9e0624dff0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 13:27:11 -0500 Subject: [PATCH 38/59] Updated multiview test to provide more information --- tests/tests/wgpu-gpu/multiview.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index ab65966e414..e67e1a78a92 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -50,7 +50,7 @@ async fn run_test(ctx: TestingContext) { @fragment fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f { - return vec4f(f32(view_index)); + return vec4f(f32(view_index) * 0.5); } "; @@ -177,13 +177,15 @@ async fn run_test(ctx: TestingContext) { ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); let data = slice.get_mapped_range(); + let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; + assert!(data.len() == each_texture_size * 2); for view_idx in 0..2 { - let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; - let target_value = view_idx as u8 * u8::MAX; - let succeeded = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] + // Some metal devices automatically initialize stuff to 255, so I decided to use 128 instead of that + let target_value = view_idx as u8 * 128; + let failed_value = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] .iter() - .all(|b| *b == target_value); - assert!(data.len() == each_texture_size * 2); - assert!(succeeded, "Failed with view {view_idx}"); + .copied() + .find(|b| *b != target_value); + assert_eq!(failed_value, None); } } From 15da6201884b39dd727e8fca2f35f0f6e7df726b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 13:37:29 -0500 Subject: [PATCH 39/59] ChatGPT thinks it fixed the vulkan backend (lets see!) --- wgpu-hal/src/vulkan/device.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index a91df0c77ba..7049261308a 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1987,8 +1987,12 @@ impl crate::Device for super::Device { vk::DynamicState::BLEND_CONSTANTS, vk::DynamicState::STENCIL_REFERENCE, ]; + let multiview_mask = desc + .multiview + .map(|v| NonZeroU32::new((1u32 << v.get()) - 1).unwrap()); let mut compatible_rp_key = super::RenderPassKey { sample_count: desc.multisample.count, + multiview_mask, ..Default::default() }; let mut stages = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new(); From 27035ddb6a4023308971a4ea6ff973b2fb75ca64 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 13:51:48 -0500 Subject: [PATCH 40/59] ChatGPT also failed to fix metal backend lmao --- wgpu-hal/src/metal/command.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 46b185c045b..3ed552e1353 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -674,6 +674,7 @@ impl crate::CommandEncoder for super::CommandEncoder { } } encoder.set_vertex_amplification_count(mv.count_ones() as u64, Some(&maps)); + descriptor.set_render_target_array_length(32 - mv.leading_zeros() as u64); } if let Some(label) = desc.label { encoder.set_label(label); From e8b74528a979990382a9855ce82315106df87c90 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 14:05:25 -0500 Subject: [PATCH 41/59] Undid fake fix for metal --- wgpu-hal/src/metal/command.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 3ed552e1353..79fb6e281ed 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -654,7 +654,6 @@ impl crate::CommandEncoder for super::CommandEncoder { descriptor .set_visibility_result_buffer(Some(occlusion_query_set.raw_buffer.as_ref())) } - let raw = self.raw_cmd_buf.as_ref().unwrap(); let encoder = raw.new_render_command_encoder(descriptor); if let Some(mv) = desc.multiview_mask { @@ -674,7 +673,6 @@ impl crate::CommandEncoder for super::CommandEncoder { } } encoder.set_vertex_amplification_count(mv.count_ones() as u64, Some(&maps)); - descriptor.set_render_target_array_length(32 - mv.leading_zeros() as u64); } if let Some(label) = desc.label { encoder.set_label(label); From b4ce29bd84eca63a2020a18e0ea364133cc83b30 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 14:13:46 -0500 Subject: [PATCH 42/59] Tried to fix again (still broken on metal but maybe this'll fix vulkan/dx12) --- tests/tests/wgpu-gpu/multiview.rs | 2 +- wgpu-core/src/command/render.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index e67e1a78a92..0bc371b347a 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -186,6 +186,6 @@ async fn run_test(ctx: TestingContext) { .iter() .copied() .find(|b| *b != target_value); - assert_eq!(failed_value, None); + assert_eq!(failed_value, None, "Expected {target_value}"); } } diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 54572e4221b..b1376c18fff 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -1685,6 +1685,8 @@ impl Global { None }; + arc_desc.multiview_mask = desc.multiview_mask; + Ok(()) } From a799a24c33a687dc1db15663981efb1ead7883e9 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 1 Oct 2025 15:06:14 -0500 Subject: [PATCH 43/59] I'm not having fun :( --- tests/tests/wgpu-gpu/multiview.rs | 5 +++-- wgpu-hal/src/metal/command.rs | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 0bc371b347a..c61fc3b3952 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -50,7 +50,7 @@ async fn run_test(ctx: TestingContext) { @fragment fn fs_main(@builtin(view_index) view_index: u32) -> @location(0) vec4f { - return vec4f(f32(view_index) * 0.5); + return vec4f(f32(view_index) * 0.25 + 0.125); } "; @@ -179,9 +179,10 @@ async fn run_test(ctx: TestingContext) { let data = slice.get_mapped_range(); let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; assert!(data.len() == each_texture_size * 2); + eprintln!("View values: {}, {}", data[0], data[each_texture_size]); for view_idx in 0..2 { // Some metal devices automatically initialize stuff to 255, so I decided to use 128 instead of that - let target_value = view_idx as u8 * 128; + let target_value = 32 + view_idx as u8 * 64; let failed_value = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] .iter() .copied() diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 79fb6e281ed..c79af76ae67 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -654,6 +654,9 @@ impl crate::CommandEncoder for super::CommandEncoder { descriptor .set_visibility_result_buffer(Some(occlusion_query_set.raw_buffer.as_ref())) } + if let Some(mv) = desc.multiview_mask { + descriptor.set_render_target_array_length(32 - mv.leading_zeros() as u64); + } let raw = self.raw_cmd_buf.as_ref().unwrap(); let encoder = raw.new_render_command_encoder(descriptor); if let Some(mv) = desc.multiview_mask { @@ -661,9 +664,10 @@ impl crate::CommandEncoder for super::CommandEncoder { // Most likely the API just wasn't thought about enough. It's not like they ever allow you // to use enough views to overflow a 32-bit bitmask. let mv = mv.get(); + let msb = 31 - mv.leading_zeros(); let mut maps: SmallVec<[metal::VertexAmplificationViewMapping; 32]> = SmallVec::new(); - for i in 0..32 { + for i in 0..=msb { if (mv & (1 << i)) != 0 { maps.push(metal::VertexAmplificationViewMapping { renderTargetArrayIndexOffset: i, From 9093910896c63644a3f164a7c3ab66f8241bc884 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Thu, 2 Oct 2025 01:45:12 -0500 Subject: [PATCH 44/59] Updated test to rely on new poll syntax --- tests/tests/wgpu-gpu/multiview.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index c61fc3b3952..59093067d15 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -174,7 +174,12 @@ async fn run_test(ctx: TestingContext) { let slice = readback_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::Wait { + submission_index: None, + timeout: None, + }) + .await + .unwrap(); let data = slice.get_mapped_range(); let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; From 5db19aa5ca4afdbef9a09e9eab4787dbc6b7ea26 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 10 Oct 2025 15:18:32 -0500 Subject: [PATCH 45/59] Added multiview test --- examples/features/src/lib.rs | 1 + examples/features/src/main.rs | 6 + examples/features/src/multiview/mod.rs | 213 +++++++++++++++++++++++++ tests/tests/wgpu-gpu/multiview.rs | 6 + 4 files changed, 226 insertions(+) create mode 100644 examples/features/src/multiview/mod.rs diff --git a/examples/features/src/lib.rs b/examples/features/src/lib.rs index baacf6a6b39..2299dded725 100644 --- a/examples/features/src/lib.rs +++ b/examples/features/src/lib.rs @@ -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; diff --git a/examples/features/src/main.rs b/examples/features/src/main.rs index c9fc31259c9..c28c7ad5f58 100644 --- a/examples/features/src/main.rs +++ b/examples/features/src/main.rs @@ -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 { diff --git a/examples/features/src/multiview/mod.rs b/examples/features/src/multiview/mod.rs new file mode 100644 index 00000000000..528daecd02e --- /dev/null +++ b/examples/features/src/multiview/mod.rs @@ -0,0 +1,213 @@ +use std::{num::NonZero, time::Instant}; + +use wgpu::{ + util::{BufferInitDescriptor, DeviceExt, TextureBlitter}, + vertex_attr_array, +}; + +const TEXTURE_SIZE: u32 = 512; + +pub struct Example { + pipeline: wgpu::RenderPipeline, + vertex_buffer: wgpu::Buffer, + view: wgpu::TextureView, + view1: wgpu::TextureView, + view2: 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 = " + @vertex + fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f { + return vec4f(position, 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); + } + "; + + 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: &[wgpu::VertexBufferLayout { + array_stride: 8, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &vertex_attr_array![0 => Float32x2], + }], + 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: NonZero::new(2), + multisample: Default::default(), + layout: None, + depth_stencil: None, + cache: None, + }); + let vertex_buffer_content: &[f32; 12] = &[ + // Triangle 1 + -1.0, -1.0, // Bottom left + 1.0, 1.0, // Top right + -1.0, 1.0, // Top left + // Triangle 2 + -1.0, -1.0, // Bottom left + 1.0, -1.0, // Bottom right + 1.0, 1.0, // Top right + ]; + let vertex_buffer = device.create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(vertex_buffer_content), + usage: wgpu::BufferUsages::VERTEX, + }); + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: TEXTURE_SIZE, + height: TEXTURE_SIZE, + depth_or_array_layers: 2, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_SRC + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + let 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(2), + }); + let view1 = texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + format: Some(wgpu::TextureFormat::R8Unorm), + 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: 0, + array_layer_count: Some(1), + }); + let view2 = texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + format: Some(wgpu::TextureFormat::R8Unorm), + 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: 1, + array_layer_count: Some(1), + }); + let blitter = wgpu::util::TextureBlitter::new(device, config.format); + Self { + pipeline, + vertex_buffer, + view, + view1, + view2, + blitter, + start_time: Instant::now(), + } + } + fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { + unsafe { + device.start_graphics_debugger_capture(); + } + 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.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(3), + }); + rpass.set_pipeline(&self.pipeline); + rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + rpass.draw(0..6, 0..1); + } + if (Instant::now() - self.start_time) + .as_secs() + .is_multiple_of(2) + { + self.blitter.copy(device, &mut encoder, &self.view1, view); + } else { + self.blitter.copy(device, &mut encoder, &self.view2, view); + } + queue.submit(Some(encoder.finish())); + unsafe { + device.stop_graphics_debugger_capture(); + } + } + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + Default::default() + } + fn required_features() -> wgpu::Features { + wgpu::Features::MULTIVIEW + } + fn required_limits() -> wgpu::Limits { + wgpu::Limits { + max_multiview_view_count: 2, + ..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::("multiview"); +} diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 59093067d15..28b70d6de65 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -26,6 +26,9 @@ static DRAW_MULTIVIEW: GpuTestConfiguration = GpuTestConfiguration::new() .run_async(run_test); async fn run_test(ctx: TestingContext) { + unsafe { + ctx.device.start_graphics_debugger_capture(); + } let vertex_buffer_content: &[f32; 12] = &[ // Triangle 1 -1.0, -1.0, // Bottom left @@ -180,6 +183,9 @@ async fn run_test(ctx: TestingContext) { }) .await .unwrap(); + unsafe { + ctx.device.stop_graphics_debugger_capture(); + } let data = slice.get_mapped_range(); let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; From 45b5a9da73bc05be99560257cd22883ff3072974 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 10 Oct 2025 15:35:09 -0500 Subject: [PATCH 46/59] Made it start on the second image for debugging reasons --- examples/features/src/multiview/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/features/src/multiview/mod.rs b/examples/features/src/multiview/mod.rs index 528daecd02e..da292009651 100644 --- a/examples/features/src/multiview/mod.rs +++ b/examples/features/src/multiview/mod.rs @@ -170,7 +170,7 @@ impl crate::framework::Example for Example { rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); rpass.draw(0..6, 0..1); } - if (Instant::now() - self.start_time) + if !(Instant::now() - self.start_time) .as_secs() .is_multiple_of(2) { From 3706ab5c7786921bed31675ae8bf0721cf9fd290 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 10 Oct 2025 19:00:05 -0500 Subject: [PATCH 47/59] Changed clear color, documented something in metal backend --- examples/features/src/multiview/mod.rs | 5 ++++- wgpu-hal/src/metal/command.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/features/src/multiview/mod.rs b/examples/features/src/multiview/mod.rs index da292009651..acc5c4bf198 100644 --- a/examples/features/src/multiview/mod.rs +++ b/examples/features/src/multiview/mod.rs @@ -157,7 +157,10 @@ impl crate::framework::Example for Example { depth_slice: None, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.02, + ..wgpu::Color::RED + }), store: wgpu::StoreOp::Store, }, })], diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index c79af76ae67..0b8528d7352 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -654,6 +654,10 @@ impl crate::CommandEncoder for super::CommandEncoder { descriptor .set_visibility_result_buffer(Some(occlusion_query_set.raw_buffer.as_ref())) } + // This strangely isn't mentioned in https://developer.apple.com/documentation/metal/improving-rendering-performance-with-vertex-amplification. + // The docs for [`renderTargetArrayLength`](https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor/rendertargetarraylength) + // also say "The number of active layers that all attachments must have for layered rendering," implying it is only for layered rendering. + // However, when I don't set this, I get undefined behavior in nonzero layers, so I think it is required. if let Some(mv) = desc.multiview_mask { descriptor.set_render_target_array_length(32 - mv.leading_zeros() as u64); } @@ -671,7 +675,6 @@ impl crate::CommandEncoder for super::CommandEncoder { if (mv & (1 << i)) != 0 { maps.push(metal::VertexAmplificationViewMapping { renderTargetArrayIndexOffset: i, - // WGPU doesn't allow rendering to multiple viewports in a single pass viewportArrayIndexOffset: 0, }); } From ad03c55975f719a01973a61f6153e576e15a7b24 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 10 Oct 2025 20:08:34 -0500 Subject: [PATCH 48/59] This is incredibly confusing --- wgpu-hal/src/metal/command.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 0b8528d7352..3f5504d9a14 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -668,10 +668,10 @@ impl crate::CommandEncoder for super::CommandEncoder { // Most likely the API just wasn't thought about enough. It's not like they ever allow you // to use enough views to overflow a 32-bit bitmask. let mv = mv.get(); - let msb = 31 - mv.leading_zeros(); + let msb = 32 - mv.leading_zeros(); let mut maps: SmallVec<[metal::VertexAmplificationViewMapping; 32]> = SmallVec::new(); - for i in 0..=msb { + for i in 0..msb { if (mv & (1 << i)) != 0 { maps.push(metal::VertexAmplificationViewMapping { renderTargetArrayIndexOffset: i, @@ -679,7 +679,7 @@ impl crate::CommandEncoder for super::CommandEncoder { }); } } - encoder.set_vertex_amplification_count(mv.count_ones() as u64, Some(&maps)); + encoder.set_vertex_amplification_count(msb as u64, Some(&maps)); } if let Some(label) = desc.label { encoder.set_label(label); From 78370afc20e833bab41992d7829fcf0f5b49b7d0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 10 Oct 2025 23:01:41 -0500 Subject: [PATCH 49/59] I'm giving up on metal for now --- wgpu-hal/src/metal/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index 3f5504d9a14..a4204501d68 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -679,7 +679,7 @@ impl crate::CommandEncoder for super::CommandEncoder { }); } } - encoder.set_vertex_amplification_count(msb as u64, Some(&maps)); + encoder.set_vertex_amplification_count(mv.count_ones() as u64, Some(&maps)); } if let Some(label) = desc.label { encoder.set_label(label); From 78fb4f7928a679f59079edb3f85609f28612e56a Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Sat, 11 Oct 2025 17:42:12 -0500 Subject: [PATCH 50/59] Removed the instance limit --- tests/tests/wgpu-gpu/multiview.rs | 1 - wgpu-core/src/command/draw.rs | 7 ------- wgpu-core/src/command/render.rs | 10 ---------- wgpu-hal/src/dx12/adapter.rs | 2 -- wgpu-hal/src/gles/adapter.rs | 1 - wgpu-hal/src/metal/adapter.rs | 5 ----- wgpu-hal/src/noop/mod.rs | 1 - wgpu-hal/src/vulkan/adapter.rs | 13 ++++++++++--- wgpu-info/src/human.rs | 2 -- wgpu-types/src/lib.rs | 9 --------- wgpu/src/backend/webgpu.rs | 1 - 11 files changed, 10 insertions(+), 42 deletions(-) diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 28b70d6de65..0933d9528a2 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -19,7 +19,6 @@ static DRAW_MULTIVIEW: GpuTestConfiguration = GpuTestConfiguration::new() .features(Features::MULTIVIEW) .limits(Limits { max_multiview_view_count: 2, - max_multiview_instance_index: 1, ..Limits::defaults() }), ) diff --git a/wgpu-core/src/command/draw.rs b/wgpu-core/src/command/draw.rs index 77c22f089c8..185da58cf46 100644 --- a/wgpu-core/src/command/draw.rs +++ b/wgpu-core/src/command/draw.rs @@ -44,13 +44,6 @@ pub enum DrawError { instance_limit: u64, slot: u32, }, - #[error( - "Instance {last_instance} extends beyond limit {instance_limit} imposed by multiview restrictions on the render pass." - )] - InstanceBeyondMultiviewLimit { - last_instance: u64, - instance_limit: u64, - }, #[error("Index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")] IndexBeyondLimit { last_index: u64, index_limit: u64 }, #[error( diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index b1376c18fff..09cd2ac687c 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -2671,16 +2671,6 @@ fn draw_indexed( .limits .validate_instance_limit(first_instance, instance_count)?; - if state.info.multiview.is_some() - && first_instance + instance_count - > state.pass.base.device.limits.max_multiview_instance_index + 1 - { - return Err(DrawError::InstanceBeyondMultiviewLimit { - last_instance: (first_instance + instance_count) as u64, - instance_limit: (state.pass.base.device.limits.max_multiview_instance_index + 1) as u64, - }); - } - unsafe { if instance_count > 0 && index_count > 0 { state.pass.base.raw_encoder.draw_indexed( diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index bd4405009b2..aef8b8e11ab 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -708,8 +708,6 @@ impl super::Adapter { // This is really frickin annoying, 6 (probably 8) for cube mapping would be really nice. But they // arbitrarily chose 4, eliminating tons of use cases. max_multiview_view_count: if view_instancing { 4 } else { 0 }, - // This limit is specific to vulkan - max_multiview_instance_index: if view_instancing { u32::MAX } else { 0 }, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new( diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index e3507b3ad09..2260658ad28 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -812,7 +812,6 @@ impl super::Adapter { max_acceleration_structures_per_shader_stage: 0, max_multiview_view_count: 0, - max_multiview_instance_index: 0, }; let mut workarounds = super::Workarounds::empty(); diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 30d899672db..a9c16cebfe4 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -1113,11 +1113,6 @@ impl super::PrivateCapabilities { } else { 0 }, - max_multiview_instance_index: if self.supported_vertex_amplification_factor > 1 { - u32::MAX - } else { - 0 - }, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new(self.buffer_alignment).unwrap(), diff --git a/wgpu-hal/src/noop/mod.rs b/wgpu-hal/src/noop/mod.rs index 92cc1cb91be..887b279af25 100644 --- a/wgpu-hal/src/noop/mod.rs +++ b/wgpu-hal/src/noop/mod.rs @@ -203,7 +203,6 @@ pub const CAPABILITIES: crate::Capabilities = { max_acceleration_structures_per_shader_stage: ALLOC_MAX_U32, max_multiview_view_count: ALLOC_MAX_U32, - max_multiview_instance_index: ALLOC_MAX_U32, }, alignments: crate::Alignments { // All maximally permissive diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index c7715c2b51b..b4d0708bd06 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -18,6 +18,9 @@ const INDEXING_FEATURES: wgt::Features = wgt::Features::TEXTURE_BINDING_ARRAY .union(wgt::Features::UNIFORM_BUFFER_BINDING_ARRAYS) .union(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY); +/// Pulled out of my ass +const MULTIVIEW_INSTANCE_MINIMUM: u32 = 256 * 1024 * 1024; + #[expect(rustdoc::private_intra_doc_links)] /// Features supported by a [`vk::PhysicalDevice`] and its extensions. /// @@ -721,7 +724,9 @@ impl PhysicalDeviceFeatures { features.set(F::CLIP_DISTANCES, self.core.shader_clip_distance != 0); if let Some(ref multiview) = self.multiview { - features.set(F::MULTIVIEW, multiview.multiview != 0); + if caps.multiview.unwrap().max_multiview_instance_index >= MULTIVIEW_INSTANCE_MINIMUM { + features.set(F::MULTIVIEW, multiview.multiview != 0); + } } features.set( @@ -1257,7 +1262,7 @@ impl PhysicalDeviceProperties { properties.max_per_stage_descriptor_acceleration_structures; } - let (max_multiview_view_count, max_multiview_instance_index) = + let (mut max_multiview_view_count, max_multiview_instance_index) = if let Some(properties) = self.multiview { ( // The bitmask only uses 32 bits, so it can't be higher even if the device for some reason claims to support that. @@ -1267,6 +1272,9 @@ impl PhysicalDeviceProperties { } else { (0, 0) }; + if max_multiview_instance_index < MULTIVIEW_INSTANCE_MINIMUM { + max_multiview_view_count = 0; + } wgt::Limits { max_texture_dimension_1d: limits.max_image_dimension1_d, @@ -1337,7 +1345,6 @@ impl PhysicalDeviceProperties { max_acceleration_structures_per_shader_stage, max_multiview_view_count, - max_multiview_instance_index, } } diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs index b1e4e7db1e4..6c1b34c28c4 100644 --- a/wgpu-info/src/human.rs +++ b/wgpu-info/src/human.rs @@ -172,7 +172,6 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize max_acceleration_structures_per_shader_stage, max_multiview_view_count, - max_multiview_instance_index } = limits; writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}")?; writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}")?; @@ -221,7 +220,6 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize writeln!(output, "\t\t Max Acceleration Structures Per Shader Stage: {max_acceleration_structures_per_shader_stage}")?; writeln!(output, "\t\t Max Multiview View Count: {max_multiview_view_count}")?; - writeln!(output, "\t\t Max Multiview Instance Index: {max_multiview_instance_index}")?; // This one reflects more of a wgpu implementation limitations than a hardware limit // so don't show it here. let _ = max_non_sampler_bindings; diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index a998ad0bf1e..32a999fff97 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -527,7 +527,6 @@ macro_rules! with_limits { $macro_name!(max_tlas_instance_count, Ordering::Less); $macro_name!(max_multiview_view_count, Ordering::Less); - $macro_name!(max_multiview_instance_index, Ordering::Less); }; } @@ -719,10 +718,6 @@ pub struct Limits { /// The maximum number of views that can be used in multiview rendering pub max_multiview_view_count: u32, - /// For an instanced draw call using multiview, the maximum instance index. For example, - /// if draw is called with instances a..b, then b must be <= this limit + 1. - /// Note that this is NOT just the maximum total number of instances. - pub max_multiview_instance_index: u32, } impl Default for Limits { @@ -784,7 +779,6 @@ impl Limits { /// max_tlas_instance_count: 0, /// max_acceleration_structures_per_shader_stage: 0, /// max_multiview_view_count: 0, - /// max_multiview_instance_index: 0, /// }); /// ``` /// @@ -841,7 +835,6 @@ impl Limits { max_acceleration_structures_per_shader_stage: 0, max_multiview_view_count: 0, - max_multiview_instance_index: 0, } } @@ -899,7 +892,6 @@ impl Limits { /// max_acceleration_structures_per_shader_stage: 0, /// /// max_multiview_view_count: 0, - /// max_multiview_instance_index: 0, /// }); /// ``` #[must_use] @@ -977,7 +969,6 @@ impl Limits { /// max_acceleration_structures_per_shader_stage: 0, /// /// max_multiview_view_count: 0, - /// max_multiview_instance_index: 0, /// }); /// ``` #[must_use] diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 38eca36509f..ba5a92c2229 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -837,7 +837,6 @@ fn map_wgt_limits(limits: webgpu_sys::GpuSupportedLimits) -> wgt::Limits { .max_acceleration_structures_per_shader_stage, max_multiview_view_count: wgt::Limits::default().max_multiview_view_count, - max_multiview_instance_index: wgt::Limits::default().max_multiview_instance_index, } } From b733999a0a511a8b673f5e389670f4b61b80f60d Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Sat, 11 Oct 2025 19:13:03 -0500 Subject: [PATCH 51/59] Added errors --- wgpu-hal/src/vulkan/adapter.rs | 4 ++++ wgpu-hal/src/vulkan/command.rs | 12 ++++++++++++ wgpu-hal/src/vulkan/mod.rs | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index b4d0708bd06..3b396ebfc26 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -1857,6 +1857,10 @@ impl super::Instance { shader_int8: phd_features .shader_float16_int8 .is_some_and(|features| features.shader_int8 != 0), + multiview_instance_index_limit: phd_capabilities + .multiview + .map(|a| a.max_multiview_instance_index) + .unwrap_or(0), }; let capabilities = crate::Capabilities { limits: phd_capabilities.to_wgpu_limits(), diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index fea5667f9a8..66dc79fbce6 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -1066,6 +1066,12 @@ impl crate::CommandEncoder for super::CommandEncoder { first_instance: u32, instance_count: u32, ) { + if self.current_pipeline_is_multiview + && (first_instance as u64 + instance_count as u64 - 1) + > self.device.private_caps.multiview_instance_index_limit as u64 + { + panic!("This vulkan device is affected by [#8333](https://github.com/gfx-rs/wgpu/issues/8333)"); + } unsafe { self.device.raw.cmd_draw( self.active, @@ -1084,6 +1090,12 @@ impl crate::CommandEncoder for super::CommandEncoder { first_instance: u32, instance_count: u32, ) { + if self.current_pipeline_is_multiview + && (first_instance as u64 + instance_count as u64 - 1) + > self.device.private_caps.multiview_instance_index_limit as u64 + { + panic!("This vulkan device is affected by [#8333](https://github.com/gfx-rs/wgpu/issues/8333)"); + } unsafe { self.device.raw.cmd_draw_indexed( self.active, diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index c82a06d7a7a..05b0fd4f4b0 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -636,6 +636,9 @@ struct PrivateCapabilities { /// [`VK_KHR_shader_float16_int8`]: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_shader_float16_int8.html /// [see spec]: https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceShaderFloat16Int8Features.html#extension-features-shaderInt8 shader_int8: bool, + + /// This is done to panic before undefined behavior, and is imperfect. + multiview_instance_index_limit: u32, } bitflags::bitflags!( @@ -1208,6 +1211,8 @@ pub struct CommandEncoder { temp_texture_views: FastHashMap, counters: Arc, + + current_pipeline_is_multiview: bool, } impl Drop for CommandEncoder { From 58d18c3988b9dac0f3c1d66b79f02f919ff91747 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Sat, 11 Oct 2025 19:18:36 -0500 Subject: [PATCH 52/59] Very sloppy --- wgpu-hal/src/vulkan/command.rs | 1 + wgpu-hal/src/vulkan/device.rs | 6 +++++- wgpu-hal/src/vulkan/mod.rs | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 66dc79fbce6..16ba1e320d1 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -979,6 +979,7 @@ impl crate::CommandEncoder for super::CommandEncoder { unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) { unsafe { + self.current_pipeline_is_multiview = pipeline.is_multiview; self.device.raw.cmd_bind_pipeline( self.active, vk::PipelineBindPoint::GRAPHICS, diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 7049261308a..7300ffd5d93 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1470,6 +1470,7 @@ impl crate::Device for super::Device { framebuffers: Default::default(), temp_texture_views: Default::default(), counters: Arc::clone(&self.counters), + current_pipeline_is_multiview: false, }) } @@ -2261,7 +2262,10 @@ impl crate::Device for super::Device { self.counters.render_pipelines.add(1); - Ok(super::RenderPipeline { raw }) + Ok(super::RenderPipeline { + raw, + is_multiview: desc.multiview.is_some(), + }) } unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 05b0fd4f4b0..1fa4d388b4b 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -1285,6 +1285,7 @@ impl crate::DynShaderModule for ShaderModule {} #[derive(Debug)] pub struct RenderPipeline { raw: vk::Pipeline, + is_multiview: bool, } impl crate::DynRenderPipeline for RenderPipeline {} From d1c7c410f4da88d4975f77499583deb5480b66a1 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Sat, 11 Oct 2025 23:35:58 -0500 Subject: [PATCH 53/59] Removed the arbitrary limit --- wgpu-hal/src/vulkan/adapter.rs | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 3b396ebfc26..86957573fee 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -17,10 +17,6 @@ const INDEXING_FEATURES: wgt::Features = wgt::Features::TEXTURE_BINDING_ARRAY .union(wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING) .union(wgt::Features::UNIFORM_BUFFER_BINDING_ARRAYS) .union(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY); - -/// Pulled out of my ass -const MULTIVIEW_INSTANCE_MINIMUM: u32 = 256 * 1024 * 1024; - #[expect(rustdoc::private_intra_doc_links)] /// Features supported by a [`vk::PhysicalDevice`] and its extensions. /// @@ -724,9 +720,7 @@ impl PhysicalDeviceFeatures { features.set(F::CLIP_DISTANCES, self.core.shader_clip_distance != 0); if let Some(ref multiview) = self.multiview { - if caps.multiview.unwrap().max_multiview_instance_index >= MULTIVIEW_INSTANCE_MINIMUM { - features.set(F::MULTIVIEW, multiview.multiview != 0); - } + features.set(F::MULTIVIEW, multiview.multiview != 0); } features.set( @@ -1262,19 +1256,10 @@ impl PhysicalDeviceProperties { properties.max_per_stage_descriptor_acceleration_structures; } - let (mut max_multiview_view_count, max_multiview_instance_index) = - if let Some(properties) = self.multiview { - ( - // The bitmask only uses 32 bits, so it can't be higher even if the device for some reason claims to support that. - properties.max_multiview_view_count.min(32), - properties.max_multiview_instance_index, - ) - } else { - (0, 0) - }; - if max_multiview_instance_index < MULTIVIEW_INSTANCE_MINIMUM { - max_multiview_view_count = 0; - } + let max_multiview_view_count = self + .multiview + .map(|a| a.max_multiview_view_count.min(32)) + .unwrap_or(0); wgt::Limits { max_texture_dimension_1d: limits.max_image_dimension1_d, From f9f764517ad799093b538c9b82838203c4870137 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 13 Oct 2025 14:03:00 -0500 Subject: [PATCH 54/59] Tried to expose multiview mesh shaders on DX12 --- wgpu-core/src/command/render.rs | 1 + wgpu-hal/src/dx12/adapter.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 09cd2ac687c..cfd96b31a82 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -235,6 +235,7 @@ 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, + /// The multiview array layers that will be used pub multiview_mask: Option, } diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index aef8b8e11ab..a63c5c5367c 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -554,6 +554,11 @@ impl super::Adapter { features.set(wgt::Features::MULTIVIEW, view_instancing); + features.set( + wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW, + mesh_shader_supported && view_instancing, + ); + // TODO: Determine if IPresentationManager is supported let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi(); @@ -580,6 +585,8 @@ impl super::Adapter { max_srv_count / 2 }; + let max_multiview_view_count = if view_instancing { 4 } else { 0 }; + Some(crate::ExposedAdapter { adapter: super::Adapter { raw: adapter, @@ -679,7 +686,11 @@ impl super::Adapter { max_task_workgroups_per_dimension: Direct3D12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, // Multiview not supported by WGPU yet - max_mesh_multiview_view_count: 0, + max_mesh_multiview_view_count: if mesh_shader_supported { + max_multiview_view_count + } else { + 0 + }, // This seems to be right, and I can't find anything to suggest it would be less than the 2048 provided here max_mesh_output_layers: Direct3D12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION, @@ -707,7 +718,7 @@ impl super::Adapter { // See https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html#maximum-viewinstancecount // This is really frickin annoying, 6 (probably 8) for cube mapping would be really nice. But they // arbitrarily chose 4, eliminating tons of use cases. - max_multiview_view_count: if view_instancing { 4 } else { 0 }, + max_multiview_view_count, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new( From 9bc293daf64c6a84ca91c7f041b3f887d9e454a0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 13 Oct 2025 14:12:11 -0500 Subject: [PATCH 55/59] Empty commit to see if linux vulkan failure was real From 6741ec9e3ce4fb41c398f59e113648abeaa08a82 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 13 Oct 2025 17:29:45 -0500 Subject: [PATCH 56/59] THATS IT???? --- wgpu-hal/src/metal/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs index a4204501d68..8c30d0ec862 100644 --- a/wgpu-hal/src/metal/command.rs +++ b/wgpu-hal/src/metal/command.rs @@ -675,7 +675,7 @@ impl crate::CommandEncoder for super::CommandEncoder { if (mv & (1 << i)) != 0 { maps.push(metal::VertexAmplificationViewMapping { renderTargetArrayIndexOffset: i, - viewportArrayIndexOffset: 0, + viewportArrayIndexOffset: i, }); } } From 9c8fe733606f534266c02e2cc742046ad2597b81 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 13 Oct 2025 17:58:04 -0500 Subject: [PATCH 57/59] Added non contiguous test --- examples/features/src/multiview/mod.rs | 17 +++++--- tests/tests/wgpu-gpu/multiview.rs | 58 ++++++++++++++++++++------ 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/examples/features/src/multiview/mod.rs b/examples/features/src/multiview/mod.rs index acc5c4bf198..875110e9d80 100644 --- a/examples/features/src/multiview/mod.rs +++ b/examples/features/src/multiview/mod.rs @@ -7,6 +7,9 @@ use wgpu::{ const TEXTURE_SIZE: u32 = 512; +// Change this to demonstrate non-contiguous multiview functionality +const LAYERS: u32 = 2; + pub struct Example { pipeline: wgpu::RenderPipeline, vertex_buffer: wgpu::Buffer, @@ -23,6 +26,7 @@ impl crate::framework::Example for Example { device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { + let layers = 2; let shader_src = " @vertex fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f { @@ -63,7 +67,7 @@ impl crate::framework::Example for Example { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: NonZero::new(2), + multiview: NonZero::new(layers), multisample: Default::default(), layout: None, depth_stencil: None, @@ -89,7 +93,7 @@ impl crate::framework::Example for Example { size: wgpu::Extent3d { width: TEXTURE_SIZE, height: TEXTURE_SIZE, - depth_or_array_layers: 2, + depth_or_array_layers: layers, }, mip_level_count: 1, sample_count: 1, @@ -109,7 +113,7 @@ impl crate::framework::Example for Example { base_mip_level: 0, mip_level_count: None, base_array_layer: 0, - array_layer_count: Some(2), + array_layer_count: Some(layers), }); let view1 = texture.create_view(&wgpu::TextureViewDescriptor { label: None, @@ -130,7 +134,7 @@ impl crate::framework::Example for Example { aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: None, - base_array_layer: 1, + base_array_layer: layers - 1, array_layer_count: Some(1), }); let blitter = wgpu::util::TextureBlitter::new(device, config.format); @@ -150,6 +154,7 @@ impl crate::framework::Example for Example { } let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); { + let multiview_mask = 1 | (1 << (LAYERS - 1)); let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { @@ -167,7 +172,7 @@ impl crate::framework::Example for Example { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, - multiview_mask: NonZero::new(3), + multiview_mask: NonZero::new(multiview_mask), }); rpass.set_pipeline(&self.pipeline); rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); @@ -194,7 +199,7 @@ impl crate::framework::Example for Example { } fn required_limits() -> wgpu::Limits { wgpu::Limits { - max_multiview_view_count: 2, + max_multiview_view_count: LAYERS, ..Default::default() } } diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 0933d9528a2..69a912a1f0a 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -10,6 +10,7 @@ use wgpu_test::{ pub fn all_tests(vec: &mut Vec) { vec.push(DRAW_MULTIVIEW); + vec.push(DRAW_MULTIVIEW_NONCONTIGUOUS); } #[gpu_test] @@ -22,9 +23,21 @@ static DRAW_MULTIVIEW: GpuTestConfiguration = GpuTestConfiguration::new() ..Limits::defaults() }), ) - .run_async(run_test); + .run_async(|ctx| run_test(ctx, 2)); -async fn run_test(ctx: TestingContext) { +#[gpu_test] +static DRAW_MULTIVIEW_NONCONTIGUOUS: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .features(Features::MULTIVIEW) + .limits(Limits { + max_multiview_view_count: 4, + ..Limits::defaults() + }), + ) + .run_async(|ctx| run_test(ctx, 4)); + +async fn run_test(ctx: TestingContext, num_layers: u32) { unsafe { ctx.device.start_graphics_debugger_capture(); } @@ -86,7 +99,7 @@ async fn run_test(ctx: TestingContext) { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: NonZero::new(2), + multiview: NonZero::new(num_layers), multisample: Default::default(), layout: None, depth_stencil: None, @@ -99,7 +112,7 @@ async fn run_test(ctx: TestingContext) { size: wgpu::Extent3d { width: TEXTURE_SIZE, height: TEXTURE_SIZE, - depth_or_array_layers: 2, + depth_or_array_layers: num_layers, }, mip_level_count: 1, sample_count: 1, @@ -117,19 +130,22 @@ async fn run_test(ctx: TestingContext) { base_mip_level: 0, mip_level_count: None, base_array_layer: 0, - array_layer_count: Some(2), + 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 * 2, + 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 multiview_mask = 1 | (1 << (num_layers - 1)); let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { @@ -137,14 +153,19 @@ async fn run_test(ctx: TestingContext) { depth_slice: None, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + load: wgpu::LoadOp::Clear(wgpu::Color { + r: clear_color, + g: clear_color, + b: clear_color, + a: clear_color, + }), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, - multiview_mask: NonZero::new(3), + multiview_mask: NonZero::new(multiview_mask), }); rpass.set_pipeline(&pipeline); rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); @@ -168,7 +189,7 @@ async fn run_test(ctx: TestingContext) { wgpu::Extent3d { width: TEXTURE_SIZE, height: TEXTURE_SIZE, - depth_or_array_layers: 2, + depth_or_array_layers: num_layers, }, ); ctx.queue.submit([encoder.finish()]); @@ -188,11 +209,22 @@ async fn run_test(ctx: TestingContext) { let data = slice.get_mapped_range(); let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; - assert!(data.len() == each_texture_size * 2); - eprintln!("View values: {}, {}", data[0], data[each_texture_size]); - for view_idx in 0..2 { + assert!(data.len() == each_texture_size * num_layers as usize); + eprintln!( + "View values: {}, ({}), {}", + data[0], + data[each_texture_size], + data[each_texture_size * (num_layers as usize - 1)] + ); + for view_idx in 0..num_layers as usize { + let target_value = if view_idx == 0 { + 32 + } else if view_idx == num_layers as usize - 1 { + 96 + } 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 target_value = 32 + view_idx as u8 * 64; let failed_value = data[each_texture_size * view_idx..each_texture_size * (view_idx + 1)] .iter() .copied() From bf2ae239c02cc33e2f3414a4be4de1166899a7be Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 13 Oct 2025 18:10:53 -0500 Subject: [PATCH 58/59] Removed the graphics debugger stuff --- examples/features/src/multiview/mod.rs | 6 ------ tests/tests/wgpu-gpu/multiview.rs | 6 ------ 2 files changed, 12 deletions(-) diff --git a/examples/features/src/multiview/mod.rs b/examples/features/src/multiview/mod.rs index 875110e9d80..bdc2b3dc7a4 100644 --- a/examples/features/src/multiview/mod.rs +++ b/examples/features/src/multiview/mod.rs @@ -149,9 +149,6 @@ impl crate::framework::Example for Example { } } fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { - unsafe { - device.start_graphics_debugger_capture(); - } let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); { let multiview_mask = 1 | (1 << (LAYERS - 1)); @@ -187,9 +184,6 @@ impl crate::framework::Example for Example { self.blitter.copy(device, &mut encoder, &self.view2, view); } queue.submit(Some(encoder.finish())); - unsafe { - device.stop_graphics_debugger_capture(); - } } fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { Default::default() diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index 69a912a1f0a..ff147031d97 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -38,9 +38,6 @@ static DRAW_MULTIVIEW_NONCONTIGUOUS: GpuTestConfiguration = GpuTestConfiguration .run_async(|ctx| run_test(ctx, 4)); async fn run_test(ctx: TestingContext, num_layers: u32) { - unsafe { - ctx.device.start_graphics_debugger_capture(); - } let vertex_buffer_content: &[f32; 12] = &[ // Triangle 1 -1.0, -1.0, // Bottom left @@ -203,9 +200,6 @@ async fn run_test(ctx: TestingContext, num_layers: u32) { }) .await .unwrap(); - unsafe { - ctx.device.stop_graphics_debugger_capture(); - } let data = slice.get_mapped_range(); let each_texture_size = (TEXTURE_SIZE * TEXTURE_SIZE) as usize; From de53fd29d463a5dc6897fd61bfa54c091d88c3d9 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 13 Oct 2025 18:56:06 -0500 Subject: [PATCH 59/59] Tried to change API --- benches/benches/wgpu-benchmark/renderpass.rs | 4 ++-- deno_webgpu/device.rs | 2 +- examples/features/src/boids/mod.rs | 2 +- examples/features/src/bunnymark/mod.rs | 2 +- .../features/src/conservative_raster/mod.rs | 8 ++++---- examples/features/src/cube/mod.rs | 4 ++-- examples/features/src/hello_triangle/mod.rs | 2 +- examples/features/src/mipmap/mod.rs | 4 ++-- examples/features/src/msaa_line/mod.rs | 2 +- .../src/multiple_render_targets/mod.rs | 4 ++-- examples/features/src/multiview/mod.rs | 9 ++++----- examples/features/src/ray_cube_compute/mod.rs | 2 +- .../features/src/ray_cube_fragment/mod.rs | 2 +- examples/features/src/ray_cube_normals/mod.rs | 2 +- examples/features/src/ray_scene/mod.rs | 2 +- examples/features/src/ray_shadows/mod.rs | 2 +- .../features/src/ray_traced_triangle/mod.rs | 2 +- .../features/src/render_to_texture/mod.rs | 2 +- examples/features/src/shadow/mod.rs | 4 ++-- examples/features/src/skybox/mod.rs | 4 ++-- examples/features/src/srgb_blend/mod.rs | 2 +- .../features/src/stencil_triangles/mod.rs | 4 ++-- examples/features/src/texture_arrays/mod.rs | 2 +- .../features/src/timestamp_queries/mod.rs | 2 +- examples/features/src/uniform_values/mod.rs | 2 +- examples/features/src/water/mod.rs | 4 ++-- .../binding_array/sampled_textures.rs | 2 +- tests/tests/wgpu-gpu/clip_distances.rs | 2 +- tests/tests/wgpu-gpu/device.rs | 4 ++-- tests/tests/wgpu-gpu/draw_indirect.rs | 4 ++-- tests/tests/wgpu-gpu/dual_source_blending.rs | 4 ++-- tests/tests/wgpu-gpu/mem_leaks.rs | 2 +- tests/tests/wgpu-gpu/multiview.rs | 2 +- tests/tests/wgpu-gpu/occlusion_query/mod.rs | 2 +- tests/tests/wgpu-gpu/pipeline.rs | 6 +++--- tests/tests/wgpu-gpu/planar_texture/mod.rs | 2 +- tests/tests/wgpu-gpu/push_constants.rs | 2 +- tests/tests/wgpu-gpu/regression/issue_3349.rs | 2 +- tests/tests/wgpu-gpu/regression/issue_3457.rs | 4 ++-- tests/tests/wgpu-gpu/regression/issue_4485.rs | 2 +- tests/tests/wgpu-gpu/regression/issue_4514.rs | 2 +- tests/tests/wgpu-gpu/regression/issue_5553.rs | 2 +- tests/tests/wgpu-gpu/render_pass_ownership.rs | 2 +- tests/tests/wgpu-gpu/render_target.rs | 4 ++-- tests/tests/wgpu-gpu/scissor_tests/mod.rs | 2 +- .../wgpu-gpu/shader_primitive_index/mod.rs | 2 +- .../tests/wgpu-gpu/shader_view_format/mod.rs | 2 +- tests/tests/wgpu-gpu/vertex_formats/mod.rs | 2 +- tests/tests/wgpu-gpu/vertex_indices/mod.rs | 2 +- tests/tests/wgpu-gpu/vertex_state.rs | 2 +- .../wgpu-validation/api/external_texture.rs | 2 +- .../wgpu-validation/api/render_pipeline.rs | 2 +- wgpu-core/src/command/bundle.rs | 4 ++-- wgpu-core/src/command/render.rs | 15 ++++++++++----- wgpu-core/src/device/global.rs | 2 +- wgpu-core/src/device/mod.rs | 8 ++++---- wgpu-core/src/device/resource.rs | 6 +++--- wgpu-core/src/device/trace.rs | 2 +- wgpu-core/src/pipeline.rs | 8 ++++---- wgpu-hal/examples/halmark/main.rs | 2 +- wgpu-hal/src/dynamic/device.rs | 2 +- wgpu-hal/src/gles/device.rs | 19 +++++++++++-------- wgpu-hal/src/lib.rs | 2 +- wgpu-hal/src/metal/device.rs | 4 ++-- wgpu-hal/src/vulkan/device.rs | 7 ++----- wgpu/src/api/render_pipeline.rs | 2 +- wgpu/src/backend/wgpu_core.rs | 2 +- wgpu/src/util/texture_blitter.rs | 2 +- 68 files changed, 119 insertions(+), 115 deletions(-) diff --git a/benches/benches/wgpu-benchmark/renderpass.rs b/benches/benches/wgpu-benchmark/renderpass.rs index a733804a44e..ba387b7016b 100644 --- a/benches/benches/wgpu-benchmark/renderpass.rs +++ b/benches/benches/wgpu-benchmark/renderpass.rs @@ -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, }, )); diff --git a/deno_webgpu/device.rs b/deno_webgpu/device.rs index b59fc633cd4..74fcbc8fa93 100644 --- a/deno_webgpu/device.rs +++ b/deno_webgpu/device.rs @@ -879,7 +879,7 @@ impl GPUDevice { multisample, fragment, cache: None, - multiview: None, + multiview_mask: None, }; let (id, err) = self.instance.device_create_render_pipeline( diff --git a/examples/features/src/boids/mod.rs b/examples/features/src/boids/mod.rs index be79435b70d..f4fabae008d 100644 --- a/examples/features/src/boids/mod.rs +++ b/examples/features/src/boids/mod.rs @@ -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, }); diff --git a/examples/features/src/bunnymark/mod.rs b/examples/features/src/bunnymark/mod.rs index 92d52d3d76c..86c39d87d0e 100644 --- a/examples/features/src/bunnymark/mod.rs +++ b/examples/features/src/bunnymark/mod.rs @@ -230,7 +230,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/conservative_raster/mod.rs b/examples/features/src/conservative_raster/mod.rs index 2f1f417b161..751f4b2f6bf 100644 --- a/examples/features/src/conservative_raster/mod.rs +++ b/examples/features/src/conservative_raster/mod.rs @@ -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, diff --git a/examples/features/src/cube/mod.rs b/examples/features/src/cube/mod.rs index ccdd31af08d..b0421978d23 100644 --- a/examples/features/src/cube/mod.rs +++ b/examples/features/src/cube/mod.rs @@ -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) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index cf5d40e7659..66696dbe815 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -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, }); diff --git a/examples/features/src/mipmap/mod.rs b/examples/features/src/mipmap/mod.rs index 72eedddbfdd..67f4a01ff89 100644 --- a/examples/features/src/mipmap/mod.rs +++ b/examples/features/src/mipmap/mod.rs @@ -104,7 +104,7 @@ impl Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); @@ -306,7 +306,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/msaa_line/mod.rs b/examples/features/src/msaa_line/mod.rs index 7dd4eb2ec06..a7a908fd56b 100644 --- a/examples/features/src/msaa_line/mod.rs +++ b/examples/features/src/msaa_line/mod.rs @@ -77,7 +77,7 @@ impl Example { count: sample_count, ..Default::default() }, - multiview: None, + multiview_mask: None, cache: None, }); let mut encoder = diff --git a/examples/features/src/multiple_render_targets/mod.rs b/examples/features/src/multiple_render_targets/mod.rs index 94d58781490..fbc47258c0f 100644 --- a/examples/features/src/multiple_render_targets/mod.rs +++ b/examples/features/src/multiple_render_targets/mod.rs @@ -161,7 +161,7 @@ impl MultiTargetRenderer { primitive: wgpu::PrimitiveState::default(), depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); @@ -267,7 +267,7 @@ impl TargetRenderer { primitive: wgpu::PrimitiveState::default(), depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/multiview/mod.rs b/examples/features/src/multiview/mod.rs index bdc2b3dc7a4..1c7f4861afb 100644 --- a/examples/features/src/multiview/mod.rs +++ b/examples/features/src/multiview/mod.rs @@ -26,7 +26,6 @@ impl crate::framework::Example for Example { device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { - let layers = 2; let shader_src = " @vertex fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f { @@ -67,7 +66,7 @@ impl crate::framework::Example for Example { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: NonZero::new(layers), + multiview_mask: NonZero::new(1 | (1 << (LAYERS - 1))), multisample: Default::default(), layout: None, depth_stencil: None, @@ -93,7 +92,7 @@ impl crate::framework::Example for Example { size: wgpu::Extent3d { width: TEXTURE_SIZE, height: TEXTURE_SIZE, - depth_or_array_layers: layers, + depth_or_array_layers: LAYERS, }, mip_level_count: 1, sample_count: 1, @@ -113,7 +112,7 @@ impl crate::framework::Example for Example { base_mip_level: 0, mip_level_count: None, base_array_layer: 0, - array_layer_count: Some(layers), + array_layer_count: Some(LAYERS), }); let view1 = texture.create_view(&wgpu::TextureViewDescriptor { label: None, @@ -134,7 +133,7 @@ impl crate::framework::Example for Example { aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: None, - base_array_layer: layers - 1, + base_array_layer: LAYERS - 1, array_layer_count: Some(1), }); let blitter = wgpu::util::TextureBlitter::new(device, config.format); diff --git a/examples/features/src/ray_cube_compute/mod.rs b/examples/features/src/ray_cube_compute/mod.rs index 36fca7bde51..0ba6277577d 100644 --- a/examples/features/src/ray_cube_compute/mod.rs +++ b/examples/features/src/ray_cube_compute/mod.rs @@ -326,7 +326,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/ray_cube_fragment/mod.rs b/examples/features/src/ray_cube_fragment/mod.rs index 6643152a937..6cd8fc48839 100644 --- a/examples/features/src/ray_cube_fragment/mod.rs +++ b/examples/features/src/ray_cube_fragment/mod.rs @@ -216,7 +216,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/ray_cube_normals/mod.rs b/examples/features/src/ray_cube_normals/mod.rs index 199be01bf4b..1c7acf7c377 100644 --- a/examples/features/src/ray_cube_normals/mod.rs +++ b/examples/features/src/ray_cube_normals/mod.rs @@ -317,7 +317,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/ray_scene/mod.rs b/examples/features/src/ray_scene/mod.rs index eb367cc53a2..ab399624099 100644 --- a/examples/features/src/ray_scene/mod.rs +++ b/examples/features/src/ray_scene/mod.rs @@ -395,7 +395,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/ray_shadows/mod.rs b/examples/features/src/ray_shadows/mod.rs index d273c63ed0d..5a3305ee38f 100644 --- a/examples/features/src/ray_shadows/mod.rs +++ b/examples/features/src/ray_shadows/mod.rs @@ -239,7 +239,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/ray_traced_triangle/mod.rs b/examples/features/src/ray_traced_triangle/mod.rs index 7b9c3e26e91..698ccfab1df 100644 --- a/examples/features/src/ray_traced_triangle/mod.rs +++ b/examples/features/src/ray_traced_triangle/mod.rs @@ -309,7 +309,7 @@ impl crate::framework::Example for Example { write_mask: Default::default(), })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/render_to_texture/mod.rs b/examples/features/src/render_to_texture/mod.rs index 8dfb1b5c459..e06c8d638d5 100644 --- a/examples/features/src/render_to_texture/mod.rs +++ b/examples/features/src/render_to_texture/mod.rs @@ -68,7 +68,7 @@ async fn run(_path: Option) { primitive: wgpu::PrimitiveState::default(), depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/shadow/mod.rs b/examples/features/src/shadow/mod.rs index 616ff21bcb4..77be87823ad 100644 --- a/examples/features/src/shadow/mod.rs +++ b/examples/features/src/shadow/mod.rs @@ -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, }); diff --git a/examples/features/src/skybox/mod.rs b/examples/features/src/skybox/mod.rs index 0ef8f1b30dd..0cca4c6f59f 100644 --- a/examples/features/src/skybox/mod.rs +++ b/examples/features/src/skybox/mod.rs @@ -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, }); diff --git a/examples/features/src/srgb_blend/mod.rs b/examples/features/src/srgb_blend/mod.rs index dbc1455dfce..cabf4cec52d 100644 --- a/examples/features/src/srgb_blend/mod.rs +++ b/examples/features/src/srgb_blend/mod.rs @@ -146,7 +146,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/examples/features/src/stencil_triangles/mod.rs b/examples/features/src/stencil_triangles/mod.rs index c39deb1c2f6..1a20669e91a 100644 --- a/examples/features/src/stencil_triangles/mod.rs +++ b/examples/features/src/stencil_triangles/mod.rs @@ -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, }); diff --git a/examples/features/src/texture_arrays/mod.rs b/examples/features/src/texture_arrays/mod.rs index 151bd0b2ca8..45942f9a191 100644 --- a/examples/features/src/texture_arrays/mod.rs +++ b/examples/features/src/texture_arrays/mod.rs @@ -358,7 +358,7 @@ impl crate::framework::Example for Example { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None }); diff --git a/examples/features/src/timestamp_queries/mod.rs b/examples/features/src/timestamp_queries/mod.rs index aac9332e19c..b15977156fe 100644 --- a/examples/features/src/timestamp_queries/mod.rs +++ b/examples/features/src/timestamp_queries/mod.rs @@ -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 { diff --git a/examples/features/src/uniform_values/mod.rs b/examples/features/src/uniform_values/mod.rs index 344b3469471..e4510ab4eb1 100644 --- a/examples/features/src/uniform_values/mod.rs +++ b/examples/features/src/uniform_values/mod.rs @@ -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 diff --git a/examples/features/src/water/mod.rs b/examples/features/src/water/mod.rs index 5ecbd43cd54..dc8bccfa441 100644 --- a/examples/features/src/water/mod.rs +++ b/examples/features/src/water/mod.rs @@ -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 }); diff --git a/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs b/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs index 5c6bcd79019..7d1c614bb66 100644 --- a/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs +++ b/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs @@ -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 diff --git a/tests/tests/wgpu-gpu/clip_distances.rs b/tests/tests/wgpu-gpu/clip_distances.rs index 9ca2f284312..50a6b793f31 100644 --- a/tests/tests/wgpu-gpu/clip_distances.rs +++ b/tests/tests/wgpu-gpu/clip_distances.rs @@ -43,7 +43,7 @@ async fn clip_distances(ctx: TestingContext) { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/device.rs b/tests/tests/wgpu-gpu/device.rs index fd57f774433..647d3edc330 100644 --- a/tests/tests/wgpu-gpu/device.rs +++ b/tests/tests/wgpu-gpu/device.rs @@ -459,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, }); @@ -617,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, }); diff --git a/tests/tests/wgpu-gpu/draw_indirect.rs b/tests/tests/wgpu-gpu/draw_indirect.rs index 33f312c0351..f9a84b726a7 100644 --- a/tests/tests/wgpu-gpu/draw_indirect.rs +++ b/tests/tests/wgpu-gpu/draw_indirect.rs @@ -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); @@ -698,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); diff --git a/tests/tests/wgpu-gpu/dual_source_blending.rs b/tests/tests/wgpu-gpu/dual_source_blending.rs index eeffa72e524..bbb6c1ff3d4 100644 --- a/tests/tests/wgpu-gpu/dual_source_blending.rs +++ b/tests/tests/wgpu-gpu/dual_source_blending.rs @@ -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, }; diff --git a/tests/tests/wgpu-gpu/mem_leaks.rs b/tests/tests/wgpu-gpu/mem_leaks.rs index 42b01238103..11db78a27dd 100644 --- a/tests/tests/wgpu-gpu/mem_leaks.rs +++ b/tests/tests/wgpu-gpu/mem_leaks.rs @@ -126,7 +126,7 @@ async fn draw_test_with_reports( write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/multiview.rs b/tests/tests/wgpu-gpu/multiview.rs index ff147031d97..945e1e2c345 100644 --- a/tests/tests/wgpu-gpu/multiview.rs +++ b/tests/tests/wgpu-gpu/multiview.rs @@ -96,7 +96,7 @@ async fn run_test(ctx: TestingContext, num_layers: u32) { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: NonZero::new(num_layers), + multiview_mask: NonZero::new(1 + (1 << (num_layers - 1))), multisample: Default::default(), layout: None, depth_stencil: None, diff --git a/tests/tests/wgpu-gpu/occlusion_query/mod.rs b/tests/tests/wgpu-gpu/occlusion_query/mod.rs index 46c33fde154..0f6de327c58 100644 --- a/tests/tests/wgpu-gpu/occlusion_query/mod.rs +++ b/tests/tests/wgpu-gpu/occlusion_query/mod.rs @@ -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, }); diff --git a/tests/tests/wgpu-gpu/pipeline.rs b/tests/tests/wgpu-gpu/pipeline.rs index aa62032a5f1..7efaae8dd5b 100644 --- a/tests/tests/wgpu-gpu/pipeline.rs +++ b/tests/tests/wgpu-gpu/pipeline.rs @@ -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, }); } diff --git a/tests/tests/wgpu-gpu/planar_texture/mod.rs b/tests/tests/wgpu-gpu/planar_texture/mod.rs index b491f9cc2da..7f41c32c3a9 100644 --- a/tests/tests/wgpu-gpu/planar_texture/mod.rs +++ b/tests/tests/wgpu-gpu/planar_texture/mod.rs @@ -46,7 +46,7 @@ fn test_planar_texture_creation_sampling( }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/push_constants.rs b/tests/tests/wgpu-gpu/push_constants.rs index 569fb426dfa..43c3d7c37ee 100644 --- a/tests/tests/wgpu-gpu/push_constants.rs +++ b/tests/tests/wgpu-gpu/push_constants.rs @@ -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, }); diff --git a/tests/tests/wgpu-gpu/regression/issue_3349.rs b/tests/tests/wgpu-gpu/regression/issue_3349.rs index 8637f6c3217..ef8e5dac905 100644 --- a/tests/tests/wgpu-gpu/regression/issue_3349.rs +++ b/tests/tests/wgpu-gpu/regression/issue_3349.rs @@ -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, }); diff --git a/tests/tests/wgpu-gpu/regression/issue_3457.rs b/tests/tests/wgpu-gpu/regression/issue_3457.rs index cb693a2fc1d..3aa9202ecd5 100644 --- a/tests/tests/wgpu-gpu/regression/issue_3457.rs +++ b/tests/tests/wgpu-gpu/regression/issue_3457.rs @@ -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, }); diff --git a/tests/tests/wgpu-gpu/regression/issue_4485.rs b/tests/tests/wgpu-gpu/regression/issue_4485.rs index 006b2fcba21..b7c8284ab52 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4485.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4485.rs @@ -68,7 +68,7 @@ async fn test_impl(ctx: &TestingContext) { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/regression/issue_4514.rs b/tests/tests/wgpu-gpu/regression/issue_4514.rs index 90d126dc3e8..dca272cf470 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4514.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4514.rs @@ -68,7 +68,7 @@ async fn test_impl(ctx: &TestingContext) { write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/regression/issue_5553.rs b/tests/tests/wgpu-gpu/regression/issue_5553.rs index 7c1884dc3a9..75562cdaa13 100644 --- a/tests/tests/wgpu-gpu/regression/issue_5553.rs +++ b/tests/tests/wgpu-gpu/regression/issue_5553.rs @@ -53,7 +53,7 @@ static ALLOW_INPUT_NOT_CONSUMED: GpuTestConfiguration = GpuTestConfiguration::ne write_mask: ColorWrites::all(), })], }), - multiview: None, + multiview_mask: None, cache: None, }); }); diff --git a/tests/tests/wgpu-gpu/render_pass_ownership.rs b/tests/tests/wgpu-gpu/render_pass_ownership.rs index 56aef55b93f..6be442ac0ac 100644 --- a/tests/tests/wgpu-gpu/render_pass_ownership.rs +++ b/tests/tests/wgpu-gpu/render_pass_ownership.rs @@ -549,7 +549,7 @@ fn resource_setup(ctx: &TestingContext) -> ResourceSetup { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/render_target.rs b/tests/tests/wgpu-gpu/render_target.rs index 3611986d856..394fb543420 100644 --- a/tests/tests/wgpu-gpu/render_target.rs +++ b/tests/tests/wgpu-gpu/render_target.rs @@ -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); @@ -319,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); diff --git a/tests/tests/wgpu-gpu/scissor_tests/mod.rs b/tests/tests/wgpu-gpu/scissor_tests/mod.rs index fdb8b4fcb5f..fa602170d03 100644 --- a/tests/tests/wgpu-gpu/scissor_tests/mod.rs +++ b/tests/tests/wgpu-gpu/scissor_tests/mod.rs @@ -69,7 +69,7 @@ async fn scissor_test_impl( write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs b/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs index 2fce464fdb5..9d19f16cdcc 100644 --- a/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs +++ b/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs @@ -150,7 +150,7 @@ async fn pulling_common( write_mask: wgpu::ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-gpu/shader_view_format/mod.rs b/tests/tests/wgpu-gpu/shader_view_format/mod.rs index 3891c48fcb5..8677d6493e3 100644 --- a/tests/tests/wgpu-gpu/shader_view_format/mod.rs +++ b/tests/tests/wgpu-gpu/shader_view_format/mod.rs @@ -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 { diff --git a/tests/tests/wgpu-gpu/vertex_formats/mod.rs b/tests/tests/wgpu-gpu/vertex_formats/mod.rs index 23d8c319a61..6621ebc8176 100644 --- a/tests/tests/wgpu-gpu/vertex_formats/mod.rs +++ b/tests/tests/wgpu-gpu/vertex_formats/mod.rs @@ -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, }; diff --git a/tests/tests/wgpu-gpu/vertex_indices/mod.rs b/tests/tests/wgpu-gpu/vertex_indices/mod.rs index 1ea8023fbd2..42c5b745af0 100644 --- a/tests/tests/wgpu-gpu/vertex_indices/mod.rs +++ b/tests/tests/wgpu-gpu/vertex_indices/mod.rs @@ -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); diff --git a/tests/tests/wgpu-gpu/vertex_state.rs b/tests/tests/wgpu-gpu/vertex_state.rs index cdb806883f2..be592404393 100644 --- a/tests/tests/wgpu-gpu/vertex_state.rs +++ b/tests/tests/wgpu-gpu/vertex_state.rs @@ -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(); diff --git a/tests/tests/wgpu-validation/api/external_texture.rs b/tests/tests/wgpu-validation/api/external_texture.rs index 0750cdf22e5..a2369d97927 100644 --- a/tests/tests/wgpu-validation/api/external_texture.rs +++ b/tests/tests/wgpu-validation/api/external_texture.rs @@ -652,7 +652,7 @@ var tex: texture_external; write_mask: ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/tests/tests/wgpu-validation/api/render_pipeline.rs b/tests/tests/wgpu-validation/api/render_pipeline.rs index 654197212e0..1b742b8c957 100644 --- a/tests/tests/wgpu-validation/api/render_pipeline.rs +++ b/tests/tests/wgpu-validation/api/render_pipeline.rs @@ -58,7 +58,7 @@ fn frag() -> @location({}) vec4f {{ primitive: Default::default(), depth_stencil: None, multisample: Default::default(), - multiview: None, + multiview_mask: None, cache: None, }) }, diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 8e8bf90b146..b5d141ddee2 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -221,7 +221,7 @@ impl RenderBundleEncoder { } sc }, - multiview: desc.multiview, + multiview_mask: desc.multiview, }, is_depth_read_only, @@ -242,7 +242,7 @@ impl RenderBundleEncoder { depth_stencil: None, }, sample_count: 0, - multiview: None, + multiview_mask: None, }, is_depth_read_only: false, is_stencil_read_only: false, diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index cfd96b31a82..e6b4e6210c5 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -927,7 +927,6 @@ struct RenderPassInfo { extent: wgt::Extent3d, divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc)>, - multiview: Option, multiview_mask: Option, } @@ -1373,7 +1372,14 @@ 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(); + assert!(mask_msb < detected_multiview.unwrap().get()); + } let attachment_formats = AttachmentData { colors: color_attachments @@ -1398,7 +1404,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() { @@ -1469,7 +1475,6 @@ impl RenderPassInfo { is_stencil_read_only, extent, divergent_discarded_depth_stencil_aspect, - multiview, multiview_mask, }) } @@ -2695,7 +2700,7 @@ fn draw_mesh_tasks( api_log!("RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}"); state.is_ready(DrawCommandFamily::DrawMeshTasks)?; - if let Some(mv) = state.info.multiview { + if let Some(mv) = state.info.multiview_mask { if !state .pass .base diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 0542c6ebd51..1d8c795181e 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -1524,7 +1524,7 @@ impl Global { depth_stencil: desc.depth_stencil.clone(), multisample: desc.multisample, fragment, - multiview: desc.multiview, + multiview_mask: desc.multiview_mask, cache, }; diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 38e1b1d08f8..baa031ce54a 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -61,7 +61,7 @@ impl Eq for AttachmentData {} pub(crate) struct RenderPassContext { pub attachments: AttachmentData, pub sample_count: u32, - pub multiview: Option, + pub multiview_mask: Option, } #[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(), }); } diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index d2233757fab..255ba9263ad 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -4269,7 +4269,7 @@ impl Device { }; // Multiview is only supported if the feature is enabled - if desc.multiview.is_some() { + if desc.multiview_mask.is_some() { self.require_features(wgt::Features::MULTIVIEW)?; } @@ -4320,7 +4320,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( @@ -4354,7 +4354,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(); diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index fcb3f589154..fe3f7a9193a 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -30,7 +30,7 @@ pub(crate) fn new_render_bundle_encoder_descriptor<'a>( } }), sample_count: context.sample_count, - multiview: context.multiview, + multiview: context.multiview_mask, } } diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index 753518f67ad..4cf5a2162dc 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -459,7 +459,7 @@ pub struct RenderPipelineDescriptor< pub fragment: Option>, /// If the pipeline will be used with a multiview render pass, this indicates how many array /// layers the attachments will have. - pub multiview: Option, + pub multiview_mask: Option, /// The pipeline cache to use when creating this pipeline. pub cache: Option, } @@ -524,7 +524,7 @@ pub(crate) struct GeneralRenderPipelineDescriptor< pub fragment: Option>, /// If the pipeline will be used with a multiview render pass, this indicates how many array /// layers the attachments will have. - pub multiview: Option, + pub multiview_mask: Option, /// The pipeline cache to use when creating this pipeline. pub cache: Option, } @@ -540,7 +540,7 @@ impl<'a, PLL, SM, PLC> From> depth_stencil: value.depth_stencil, multisample: value.multisample, fragment: value.fragment, - multiview: value.multiview, + multiview_mask: value.multiview_mask, cache: value.cache, } } @@ -557,7 +557,7 @@ impl<'a, PLL, SM, PLC> From> depth_stencil: value.depth_stencil, multisample: value.multisample, fragment: value.fragment, - multiview: value.multiview, + multiview_mask: value.multiview, cache: value.cache, } } diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs index ac8dedd8349..f5391012ca3 100644 --- a/wgpu-hal/examples/halmark/main.rs +++ b/wgpu-hal/examples/halmark/main.rs @@ -280,7 +280,7 @@ impl Example { 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() }; diff --git a/wgpu-hal/src/dynamic/device.rs b/wgpu-hal/src/dynamic/device.rs index 20263143808..eea26ac895f 100644 --- a/wgpu-hal/src/dynamic/device.rs +++ b/wgpu-hal/src/dynamic/device.rs @@ -407,7 +407,7 @@ impl DynDevice for D { multisample: desc.multisample, fragment_stage: desc.fragment_stage.clone().map(|f| f.expect_downcast()), color_targets: desc.color_targets, - multiview: desc.multiview, + multiview_mask: desc.multiview_mask, cache: desc.cache.map(|c| c.expect_downcast_ref()), }; diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index b45d53cc23e..0df7446ae92 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -22,7 +22,7 @@ struct CompilationContext<'a> { sampler_map: &'a mut super::SamplerBindMap, name_binding_map: &'a mut NameBindingMap, push_constant_items: &'a mut Vec, - multiview: Option, + multiview_mask: Option, clip_distance_count: &'a mut u32, } @@ -218,7 +218,9 @@ impl super::Device { let pipeline_options = glsl::PipelineOptions { shader_stage: naga_stage, entry_point: stage.entry_point.to_owned(), - multiview: context.multiview, + multiview: context + .multiview_mask + .map(|a| NonZeroU32::new(a.get().count_ones()).unwrap()), }; let (module, info) = naga::back::pipeline_constants::process_overrides( @@ -306,7 +308,7 @@ impl super::Device { shaders: ArrayVec, { crate::MAX_CONCURRENT_SHADER_STAGES }>, layout: &super::PipelineLayout, #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>, - multiview: Option, + multiview_mask: Option, ) -> Result, crate::PipelineError> { let mut program_stages = ArrayVec::new(); let mut group_to_binding_to_slot = Vec::with_capacity(layout.group_infos.len()); @@ -339,7 +341,7 @@ impl super::Device { shaders, layout, label, - multiview, + multiview_mask, self.shared.shading_language_version, self.shared.private_caps, ) @@ -355,7 +357,7 @@ impl super::Device { shaders: ArrayVec, { crate::MAX_CONCURRENT_SHADER_STAGES }>, layout: &super::PipelineLayout, #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>, - multiview: Option, + multiview_mask: Option, glsl_version: naga::back::glsl::Version, private_caps: PrivateCapabilities, ) -> Result, crate::PipelineError> { @@ -390,7 +392,7 @@ impl super::Device { sampler_map: &mut sampler_map, name_binding_map: &mut name_binding_map, push_constant_items: pc_item, - multiview, + multiview_mask, clip_distance_count: &mut clip_distance_count, }; @@ -1375,8 +1377,9 @@ impl crate::Device for super::Device { if let Some(ref fs) = desc.fragment_stage { shaders.push((naga::ShaderStage::Fragment, fs)); } - let inner = - unsafe { self.create_pipeline(gl, shaders, desc.layout, desc.label, desc.multiview) }?; + let inner = unsafe { + self.create_pipeline(gl, shaders, desc.layout, desc.label, desc.multiview_mask) + }?; let (vertex_buffers, vertex_attributes) = { let mut buffers = Vec::new(); diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 116633b82c0..0350edef345 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -2361,7 +2361,7 @@ pub struct RenderPipelineDescriptor< pub color_targets: &'a [Option], /// If the pipeline will be used with a multiview render pass, this indicates how many array /// layers the attachments will have. - pub multiview: Option, + pub multiview_mask: Option, /// The cache which will be used and filled when compiling this pipeline pub cache: Option<&'a Pc>, } diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index be4a32ad9eb..5925cc513cf 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -1345,8 +1345,8 @@ impl crate::Device for super::Device { descriptor.set_label(name); } - if let Some(mv) = desc.multiview { - descriptor.set_max_vertex_amplification_count(mv.get() as u64); + if let Some(mv) = desc.multiview_mask { + descriptor.set_max_vertex_amplification_count(mv.get().count_ones() as u64); } let raw = self diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 7300ffd5d93..53cd55f8853 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1988,12 +1988,9 @@ impl crate::Device for super::Device { vk::DynamicState::BLEND_CONSTANTS, vk::DynamicState::STENCIL_REFERENCE, ]; - let multiview_mask = desc - .multiview - .map(|v| NonZeroU32::new((1u32 << v.get()) - 1).unwrap()); let mut compatible_rp_key = super::RenderPassKey { sample_count: desc.multisample.count, - multiview_mask, + multiview_mask: desc.multiview_mask, ..Default::default() }; let mut stages = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new(); @@ -2264,7 +2261,7 @@ impl crate::Device for super::Device { Ok(super::RenderPipeline { raw, - is_multiview: desc.multiview.is_some(), + is_multiview: desc.multiview_mask.is_some(), }) } diff --git a/wgpu/src/api/render_pipeline.rs b/wgpu/src/api/render_pipeline.rs index e887bb4b97e..c15f9c6ab0d 100644 --- a/wgpu/src/api/render_pipeline.rs +++ b/wgpu/src/api/render_pipeline.rs @@ -229,7 +229,7 @@ pub struct RenderPipelineDescriptor<'a> { pub fragment: Option>, /// If the pipeline will be used with a multiview render pass, this indicates how many array /// layers the attachments will have. - pub multiview: Option, + pub multiview_mask: Option, /// The pipeline cache to use when creating this pipeline. pub cache: Option<&'a PipelineCache>, } diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index f7244ebc0f6..d32adc51c37 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -1367,7 +1367,7 @@ impl dispatch::DeviceInterface for CoreDevice { targets: Borrowed(frag.targets), } }), - multiview: desc.multiview, + multiview_mask: desc.multiview_mask, cache: desc.cache.map(|cache| cache.inner.as_core().id), }; diff --git a/wgpu/src/util/texture_blitter.rs b/wgpu/src/util/texture_blitter.rs index edabd905be6..f05ea151252 100644 --- a/wgpu/src/util/texture_blitter.rs +++ b/wgpu/src/util/texture_blitter.rs @@ -130,7 +130,7 @@ impl<'a> TextureBlitterBuilder<'a> { write_mask: ColorWrites::ALL, })], }), - multiview: None, + multiview_mask: None, cache: None, });