use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; #[gpu_test] static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .downlevel_flags(DownlevelFlags::VIEW_FORMATS) .limits(Limits::downlevel_defaults()), ) .run_async(|ctx| async move { let unorm_data: [[u8; 4]; 4] = [ [180, 0, 0, 255], [0, 84, 0, 127], [0, 0, 62, 100], [62, 180, 84, 90], ]; let srgb_data: [[u8; 4]; 4] = [ [116, 0, 0, 255], [0, 23, 0, 127], [0, 0, 12, 100], [12, 116, 23, 90], ]; let size = wgpu::Extent3d { width: 2, height: 2, depth_or_array_layers: 1, }; let shader = ctx .device .create_shader_module(wgpu::include_wgsl!("view_format.wgsl")); // Reinterpret Rgba8Unorm as Rgba8UnormSrgb reinterpret( &ctx, &shader, size, TextureFormat::Rgba8Unorm, TextureFormat::Rgba8UnormSrgb, &unorm_data, &srgb_data, ) .await; // Reinterpret Rgba8UnormSrgb back to Rgba8Unorm reinterpret( &ctx, &shader, size, TextureFormat::Rgba8UnormSrgb, TextureFormat::Rgba8Unorm, &srgb_data, &unorm_data, ) .await; }); async fn reinterpret( ctx: &TestingContext, shader: &wgpu::ShaderModule, size: wgpu::Extent3d, src_format: wgpu::TextureFormat, reinterpret_to: wgpu::TextureFormat, src_data: &[[u8; 4]], expect_data: &[[u8; 4]], ) { let tex = ctx.device.create_texture_with_data( &ctx.queue, &wgpu::TextureDescriptor { label: None, dimension: wgpu::TextureDimension::D2, size, format: src_format, usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, mip_level_count: 1, sample_count: 1, view_formats: &[reinterpret_to], }, wgpu::util::TextureDataOrder::LayerMajor, bytemuck::cast_slice(src_data), ); let tv = tex.create_view(&wgpu::TextureViewDescriptor { format: Some(reinterpret_to), ..Default::default() }); let pipeline = ctx .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("reinterpret pipeline"), 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(src_format.into())], }), primitive: wgpu::PrimitiveState { front_face: wgpu::FrontFace::Cw, ..Default::default() }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, cache: None, }); let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &pipeline.get_bind_group_layout(0), entries: &[wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&tv), }], label: None, }); let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: src_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, view_formats: &[], }); let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default()); 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 { ops: wgpu::Operations::default(), resolve_target: None, view: &target_view, })], 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); drop(rpass); ctx.queue.submit(Some(encoder.finish())); let read_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { label: None, size: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as u64 * 2, usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); let mut encoder = ctx .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); encoder.copy_texture_to_buffer( wgpu::TexelCopyTextureInfo { texture: &target_tex, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, wgpu::TexelCopyBufferInfo { buffer: &read_buffer, layout: wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT), rows_per_image: None, }, }, size, ); ctx.queue.submit(Some(encoder.finish())); let slice = read_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); let data: Vec = slice.get_mapped_range().to_vec(); let tolerance_data: [[u8; 4]; 4] = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 1, 1, 0]]; for h in 0..size.height { let offset = h * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; for w in 0..size.width { let expect = expect_data[(h * size.width + w) as usize]; let tolerance = tolerance_data[(h * size.width + w) as usize]; let index = (w * 4 + offset) as usize; if expect[0].abs_diff(data[index]) > tolerance[0] || expect[1].abs_diff(data[index + 1]) > tolerance[1] || expect[2].abs_diff(data[index + 2]) > tolerance[2] || expect[3].abs_diff(data[index + 3]) > tolerance[3] { panic!( "Reinterpret {:?} as {:?} mismatch! expect {:?} get [{}, {}, {}, {}]", src_format, reinterpret_to, expect, data[index], data[index + 1], data[index + 2], data[index + 3] ) } } } }