Skip to content

Mismatching behavior on Vulkan/GL with 2D texture array create_view function #7428

@kaphula

Description

@kaphula

Attempting to create a texture view for 2D texture array (a texture with depth_or_array_layers > 1 produces this error message on GL backend:

ERROR [wgpu_hal::gles] wgpu-hal heuristics assumed that the view dimension will be equal to `D2Array` rather than `D2`.
`D2` textures with `depth_or_array_layers == 1` are assumed to have view dimension `D2`
`D2` textures with `depth_or_array_layers > 1` are assumed to have view dimension `D2Array`
`D2` textures with `depth_or_array_layers == 6` are assumed to have view dimension `Cube`
`D2` textures with `depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` are assumed to have view dimension `CubeArray`

I am using the code below to generate mipmaps for 2D texture array. On Vulkan it works and renders properly without errors. On GL I get the above error message and the mipmaps render wrong textures. (quick search for "HERE IS THE PROBLEMATIC VIEW ON GL BACKEND:")

 pub fn generate_mipmaps(
      device: &wgpu::Device,
      queue: &Queue,
      texture: &wgpu::Texture,
      format: TextureFormat,
      mip_count: u32,
      texture_type: MipMapGenerationType,
   ) {
      assert!(mip_count >= 1, "mip count cannot be 0!");
      if mip_count == 1 {
         return;
      }

      let mut init_encoder =
         device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });

      let shader = device.create_shader_module(wgpu::include_wgsl!("blit_mipmap.wgsl"));

      let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
         label: Some("blit_mipmaps"),
         layout: None,
         vertex: wgpu::VertexState {
            module: &shader,
            entry_point: Some("vs_main"),
            compilation_options: Default::default(),
            buffers: &[],
         },
         fragment: Some(wgpu::FragmentState {
            module: &shader,
            entry_point: Some("fs_main"),
            compilation_options: Default::default(),
            targets: &[Some(format.into())],
         }),
         primitive: wgpu::PrimitiveState {
            topology: wgpu::PrimitiveTopology::TriangleList,
            ..Default::default()
         },
         depth_stencil: None,
         multisample: wgpu::MultisampleState::default(),
         multiview: None,
         cache: None,
      });

      let bind_group_layout = pipeline.get_bind_group_layout(0);

      let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
         label: Some("mip"),
         address_mode_u: wgpu::AddressMode::ClampToEdge,
         address_mode_v: wgpu::AddressMode::ClampToEdge,
         address_mode_w: wgpu::AddressMode::ClampToEdge,
         mag_filter: wgpu::FilterMode::Linear,
         min_filter: wgpu::FilterMode::Linear,
         mipmap_filter: wgpu::FilterMode::Nearest,
         ..Default::default()
      });

      match texture_type {
         MipMapGenerationType::Texture2D => {
            let views = (0..mip_count)
               .map(|mip| {
                  texture.create_view(&wgpu::TextureViewDescriptor {
                     label: Some("mip"),
                     format: None,
                     dimension: None,
                     aspect: wgpu::TextureAspect::All,
                     base_mip_level: mip,
                     mip_level_count: Some(1),
                     base_array_layer: 0,
                     array_layer_count: None,
                  })
               })
               .collect::<Vec<_>>();

            for target_mip in 1..mip_count as usize {
               let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
                  layout: &bind_group_layout,
                  entries: &[
                     wgpu::BindGroupEntry {
                        binding: 0,
                        resource: wgpu::BindingResource::TextureView(&views[target_mip - 1]),
                     },
                     wgpu::BindGroupEntry {
                        binding: 1,
                        resource: wgpu::BindingResource::Sampler(&sampler),
                     },
                  ],
                  label: None,
               });

               let mut rpass = init_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                  label: None,
                  color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                     view: &views[target_mip],
                     resolve_target: None,
                     ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
                        store: wgpu::StoreOp::Store,
                     },
                  })],
                  depth_stencil_attachment: None,
                  timestamp_writes: None,
                  occlusion_query_set: None,
               });
               rpass.set_pipeline(&pipeline);
               rpass.set_bind_group(0, &bind_group, &[]);
               rpass.draw(0..3, 0..1);
            }

         }
         MipMapGenerationType::Texture2DArray { num_pages } => {
            for page_num in 0..num_pages {
               let views = (0..mip_count)
                  .map(|mip| {
                    // HERE IS THE PROBLEMATIC VIEW ON GL BACKEND:
                     texture.create_view(&wgpu::TextureViewDescriptor {
                        label: Some("mip"),
                        format: None,
                        dimension: Some(wgpu::TextureViewDimension::D2), 
                        aspect: wgpu::TextureAspect::All,
                        base_mip_level: mip,
                        mip_level_count: Some(1),
                        base_array_layer: page_num as u32,
                        array_layer_count: None,
                     })
                  })
                  .collect::<Vec<_>>();

               for target_mip in 1..mip_count as usize {
                  let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
                     layout: &bind_group_layout,
                     entries: &[
                        wgpu::BindGroupEntry {
                           binding: 0,
                           resource: wgpu::BindingResource::TextureView(&views[target_mip - 1]),
                        },
                        wgpu::BindGroupEntry {
                           binding: 1,
                           resource: wgpu::BindingResource::Sampler(&sampler),
                        },
                     ],
                     label: None,
                  });

                  let mut rpass = init_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                     label: None,
                     color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                        view: &views[target_mip],
                        resolve_target: None,
                        ops: wgpu::Operations {
                           load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
                           store: wgpu::StoreOp::Store,
                        },
                     })],
                     depth_stencil_attachment: None,
                     timestamp_writes: None,
                     occlusion_query_set: None,
                  });
                  rpass.set_pipeline(&pipeline);
                  rpass.set_bind_group(0, &bind_group, &[]);

                  let num_draw_calls = 3;

                  rpass.draw(0..num_draw_calls, 0..1);
               }
            }
         }
      }
      queue.submit(Some(init_encoder.finish()));
   }

pub enum MipMapGenerationType {
   Texture2D,
   Texture2DArray {
      num_pages: usize
   },
}

WGPU version:

wgpu = { version = "23.0.0", default-features = false, features = [
    "fragile-send-sync-non-atomic-wasm",
    "wgsl",
] }

Instance flags:

      let instance_flags = wgpu::InstanceFlags::default() | wgpu::InstanceFlags::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER;
      let id = InstanceDescriptor {
         backends: Backends::GL,
         flags: instance_flags,
         dx12_shader_compiler: Default::default(),
         gles_minor_version: Default::default(),
      };

Features:

      let (device, queue) = pollster::block_on(adapter.request_device(
         &wgpu::DeviceDescriptor {
            label: None,
            required_features: Features::default() | Features::TEXTURE_COMPRESSION_BC,
            required_limits: Default::default(),
            memory_hints: Default::default(),
         },
         None, 
      ))
         .map_err(|e| e.to_string())?;

Platform: Linux
GPU: AMD Radeon RX 6600 (RADV NAVI23)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions