Skip to content

Confusing error when attempting to pass surface texture as storage WRITE_ONLY #8909

@Jomy10

Description

@Jomy10

Description
When attempting to create a bind group with the surface texture as a resource, a validation error is thrown:

wgpu error: Validation Error

Caused by:
  In Device::create_bind_group, label = 'Copy texture bind group layout'
    The adapter does not support write access for storage textures of format Bgra8Unorm

Example code:

let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());

let copy_texture_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
    label: Some("Copy texture bind group layout"),
    layout: &self.write_texture_bind_group_layout,
    entries: &[
        wgpu::BindGroupEntry {
            binding: 0,
            resource: wgpu::BindingResource::TextureView(&view)
        }
    ]
});

However, this error is confusing "The adapter does not support write access for storage textures of format Bgra8Unorm". This is simple not the case.

The BGRA8UNORM_STORAGE feature was passed when creating the device:

let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
    label: None,
    required_features: wgpu::Features::BGRA8UNORM_STORAGE,
    experimental_features: wgpu::ExperimentalFeatures::disabled(),
    required_limits: wgpu::Limits::defaults(),
    memory_hints: wgpu::MemoryHints::Performance,
    trace: wgpu::Trace::Off,
}).await?;

And I've also checked STORAGE_WRITE_ONLY is available for the texture format Bgra8Unorm:

assert!(
    adapter.get_texture_format_features(surface_format)
        .flags.contains(wgpu::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY)
);

Quite confusing.

When I create a texture with format Bgra8Unorm, then it works fine without errors.

Example code
use wgpu::include_wgsl;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
  let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
      backends: wgpu::Backends::PRIMARY,
      ..Default::default()
  });

  let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
      power_preference: wgpu::PowerPreference::default(),
      compatible_surface: None,
      force_fallback_adapter: false,
  }).await?;

  let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
      label: None,
      required_features: wgpu::Features::BGRA8UNORM_STORAGE,
      experimental_features: wgpu::ExperimentalFeatures::disabled(),
      required_limits: wgpu::Limits::defaults(),
      ..Default::default()
  }).await?;

  assert!(
      adapter.get_texture_format_features(wgpu::TextureFormat::Bgra8Unorm)
          .flags.contains(wgpu::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY)
  );

  let size = wgpu::Extent3d {
      width: 100, height: 100,
      depth_or_array_layers: 1
  };

  let texture = device.create_texture(&wgpu::TextureDescriptor {
      label: None,
      size: size,
      mip_level_count: 1,
      sample_count: 1,
      dimension: wgpu::TextureDimension::D2,
      format: wgpu::TextureFormat::Bgra8Unorm,
      usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::STORAGE_BINDING,
      view_formats: &[]
  });
  let view = texture.create_view(&wgpu::TextureViewDescriptor::default());

  let write_texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
      label: Some("Copy texture bind group layout"),
      entries: &[
          wgpu::BindGroupLayoutEntry {
              binding: 0,
              visibility: wgpu::ShaderStages::COMPUTE,
              ty: wgpu::BindingType::StorageTexture {
                  access: wgpu::StorageTextureAccess::WriteOnly,
                  format: wgpu::TextureFormat::Bgra8Unorm,
                  view_dimension: wgpu::TextureViewDimension::D2,
              },
              count: None
          },
      ]
  });

  let write_texture_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
      label: Some("Copy texture pipeline layout"),
      bind_group_layouts: &[&write_texture_bind_group_layout],
      ..Default::default()
  });
  let write_texture_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
      label: Some("Copy texture pipeline layout"),
      layout: Some(&write_texture_pipeline_layout),
      module: &device.create_shader_module(include_wgsl!("shaders/copy_texture.wgsl")),
      entry_point: None,
      compilation_options: Default::default(),
      cache: Default::default(),
  });

  let copy_texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
      label: Some("Copy texture bind group layout"),
      layout: &write_texture_bind_group_layout,
      entries: &[
          wgpu::BindGroupEntry {
              binding: 0,
              resource: wgpu::BindingResource::TextureView(&view)
          }
      ]
  });

  let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
      label: Some("Render encoder")
  });

  {
      let mut pass = encoder.begin_compute_pass(&Default::default());
      pass.set_pipeline(&write_texture_pipeline);
      pass.set_bind_group(0, &copy_texture_bind_group, &[]);
      pass.dispatch_workgroups(texture.width().div_ceil(16), texture.height().div_ceil(16), 0);
  }

  queue.submit(std::iter::once(encoder.finish()));

  Ok(())
}

This means the error is wrong. I am not sure if wgpu can't write to the surface texture directly, or if something else is causing the error to be thrown.

Repro steps
I have a minimal reproducable example here: repo

Expected vs observed behavior
Expected either the example to work, or an error message which describes what the user is doing wrong.

Observed an error message which is wrong.

Platform
OS: macOS
wgpu: 28
tech stech:

  • rust
  • anyhow for errors
  • env_logger for printing the wgpu errors
  • winit for window handling
  • pollster for awaiting async functions
  • in the compute example I used tokio instead of pollster

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions