wgpu: Use linear color render target

Always use a non-sRGB texture as the render target so that color
and alpha blending are in sRGB space, matching Flash behavior.

If the surface format requires sRGB, render to an intermediate
linear buffer and copy to the surface as a final render step.
This commit is contained in:
Mike Welsh 2022-04-14 11:03:45 -07:00
parent 00419c6959
commit 9c11870a08
8 changed files with 302 additions and 72 deletions

View File

@ -30,6 +30,5 @@ fn main_fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
color = color * transforms.mult_color + transforms.add_color; color = color * transforms.mult_color + transforms.add_color;
color = vec4<f32>(color.rgb * color.a, color.a); color = vec4<f32>(color.rgb * color.a, color.a);
} }
let out = color; return color;
return output(out);
} }

View File

@ -13,6 +13,5 @@ fn main_vertex(in: VertexInput) -> VertexOutput {
[[stage(fragment)]] [[stage(fragment)]]
fn main_fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> { fn main_fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let out = in.color * transforms.mult_color + transforms.add_color; return in.color * transforms.mult_color + transforms.add_color;
return output(out);
} }

View File

@ -0,0 +1,26 @@
/// Shader used for drawing bitmap fills.
struct VertexOutput {
[[builtin(position)]] position: vec4<f32>;
[[location(0)]] uv: vec2<f32>;
};
[[group(2), binding(0)]]
var<uniform> textureTransforms: TextureTransforms;
[[group(2), binding(1)]]
var texture: texture_2d<f32>;
[[group(3), binding(0)]]
var texture_sampler: sampler;
[[stage(vertex)]]
fn main_vertex(in: VertexInput) -> VertexOutput {
let matrix = textureTransforms.matrix;
let uv = (mat3x3<f32>(matrix[0].xyz, matrix[1].xyz, matrix[2].xyz) * vec3<f32>(in.position, 1.0)).xy;
let pos = globals.view_matrix * transforms.world_matrix * vec4<f32>(in.position.x, in.position.y, 0.0, 1.0);
return VertexOutput(pos, uv);
}
[[stage(fragment)]]
fn main_fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
return srgb_to_linear(textureSample(texture, texture_sampler, in.uv));
}

View File

@ -101,6 +101,5 @@ fn main_fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
if( gradient.interpolation != 0 ) { if( gradient.interpolation != 0 ) {
color = linear_to_srgb(color); color = linear_to_srgb(color);
} }
let out = color * transforms.mult_color + transforms.add_color; return color * transforms.mult_color + transforms.add_color;
return output(out);
} }

View File

@ -1,3 +0,0 @@
fn output(srgb: vec4<f32>) -> vec4<f32> {
return srgb_to_linear(srgb);
}

View File

@ -1,3 +0,0 @@
fn output(srgb: vec4<f32>) -> vec4<f32> {
return srgb;
}

View File

@ -44,6 +44,7 @@ pub struct Descriptors {
pub info: wgpu::AdapterInfo, pub info: wgpu::AdapterInfo,
pub limits: wgpu::Limits, pub limits: wgpu::Limits,
pub surface_format: wgpu::TextureFormat, pub surface_format: wgpu::TextureFormat,
frame_buffer_format: wgpu::TextureFormat,
queue: wgpu::Queue, queue: wgpu::Queue,
globals: Globals, globals: Globals,
uniform_buffers: UniformBuffer<Transforms>, uniform_buffers: UniformBuffer<Transforms>,
@ -83,9 +84,41 @@ impl Descriptors {
uniform_buffer_layout, uniform_buffer_layout,
limits.min_uniform_buffer_offset_alignment, limits.min_uniform_buffer_offset_alignment,
); );
// We want to render directly onto a linear render target to avoid any gamma correction.
// If our surface is sRGB, render to a linear texture and than copy over to the surface.
// Remove Srgb from texture format.
let frame_buffer_format = match surface_format {
wgpu::TextureFormat::Rgba8UnormSrgb => wgpu::TextureFormat::Rgba8Unorm,
wgpu::TextureFormat::Bgra8UnormSrgb => wgpu::TextureFormat::Bgra8Unorm,
wgpu::TextureFormat::Bc1RgbaUnormSrgb => wgpu::TextureFormat::Bc1RgbaUnorm,
wgpu::TextureFormat::Bc2RgbaUnormSrgb => wgpu::TextureFormat::Bc2RgbaUnorm,
wgpu::TextureFormat::Bc3RgbaUnormSrgb => wgpu::TextureFormat::Bc3RgbaUnorm,
wgpu::TextureFormat::Bc7RgbaUnormSrgb => wgpu::TextureFormat::Bc7RgbaUnorm,
wgpu::TextureFormat::Etc2Rgb8UnormSrgb => wgpu::TextureFormat::Etc2Rgb8Unorm,
wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb => wgpu::TextureFormat::Etc2Rgb8A1Unorm,
wgpu::TextureFormat::Etc2Rgba8UnormSrgb => wgpu::TextureFormat::Etc2Rgba8Unorm,
wgpu::TextureFormat::Astc4x4RgbaUnormSrgb => wgpu::TextureFormat::Astc4x4RgbaUnorm,
wgpu::TextureFormat::Astc5x4RgbaUnormSrgb => wgpu::TextureFormat::Astc5x4RgbaUnorm,
wgpu::TextureFormat::Astc5x5RgbaUnormSrgb => wgpu::TextureFormat::Astc5x5RgbaUnorm,
wgpu::TextureFormat::Astc6x5RgbaUnormSrgb => wgpu::TextureFormat::Astc6x5RgbaUnorm,
wgpu::TextureFormat::Astc6x6RgbaUnormSrgb => wgpu::TextureFormat::Astc6x6RgbaUnorm,
wgpu::TextureFormat::Astc8x5RgbaUnormSrgb => wgpu::TextureFormat::Astc8x5RgbaUnorm,
wgpu::TextureFormat::Astc8x6RgbaUnormSrgb => wgpu::TextureFormat::Astc8x6RgbaUnorm,
wgpu::TextureFormat::Astc10x5RgbaUnormSrgb => wgpu::TextureFormat::Astc10x5RgbaUnorm,
wgpu::TextureFormat::Astc10x6RgbaUnormSrgb => wgpu::TextureFormat::Astc10x6RgbaUnorm,
wgpu::TextureFormat::Astc8x8RgbaUnormSrgb => wgpu::TextureFormat::Astc8x8RgbaUnorm,
wgpu::TextureFormat::Astc10x8RgbaUnormSrgb => wgpu::TextureFormat::Astc10x8RgbaUnorm,
wgpu::TextureFormat::Astc10x10RgbaUnormSrgb => wgpu::TextureFormat::Astc10x10RgbaUnorm,
wgpu::TextureFormat::Astc12x10RgbaUnormSrgb => wgpu::TextureFormat::Astc12x10RgbaUnorm,
wgpu::TextureFormat::Astc12x12RgbaUnormSrgb => wgpu::TextureFormat::Astc12x12RgbaUnorm,
_ => surface_format,
};
let pipelines = Pipelines::new( let pipelines = Pipelines::new(
&device, &device,
surface_format, surface_format,
frame_buffer_format,
msaa_sample_count, msaa_sample_count,
bitmap_samplers.layout(), bitmap_samplers.layout(),
globals.layout(), globals.layout(),
@ -97,6 +130,7 @@ impl Descriptors {
info, info,
limits, limits,
surface_format, surface_format,
frame_buffer_format,
queue, queue,
globals, globals,
uniform_buffers, uniform_buffers,
@ -112,6 +146,8 @@ pub struct WgpuRenderBackend<T: RenderTarget> {
target: T, target: T,
frame_buffer_view: wgpu::TextureView, frame_buffer_view: wgpu::TextureView,
depth_texture_view: wgpu::TextureView, depth_texture_view: wgpu::TextureView,
copy_srgb_view: wgpu::TextureView,
copy_srgb_bind_group: wgpu::BindGroup,
current_frame: Option<Frame<'static, T>>, current_frame: Option<Frame<'static, T>>,
meshes: Vec<Mesh>, meshes: Vec<Mesh>,
mask_state: MaskState, mask_state: MaskState,
@ -371,7 +407,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
mip_level_count: 1, mip_level_count: 1,
sample_count: descriptors.msaa_sample_count, sample_count: descriptors.msaa_sample_count,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: target.format(), format: descriptors.frame_buffer_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
}); });
let frame_buffer_view = frame_buffer.create_view(&Default::default()); let frame_buffer_view = frame_buffer.create_view(&Default::default());
@ -391,6 +427,40 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
let (quad_vbo, quad_ibo, quad_tex_transforms) = create_quad_buffers(&descriptors.device); let (quad_vbo, quad_ibo, quad_tex_transforms) = create_quad_buffers(&descriptors.device);
let copy_srgb_buffer = descriptors.device.create_texture(&wgpu::TextureDescriptor {
label: create_debug_label!("Copy sRGB framebuffer texture").as_deref(),
size: extent,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: descriptors.frame_buffer_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
});
let copy_srgb_view = copy_srgb_buffer.create_view(&Default::default());
let copy_srgb_bind_group =
descriptors
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &descriptors.pipelines.bitmap_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &quad_tex_transforms,
offset: 0,
size: wgpu::BufferSize::new(
std::mem::size_of::<TextureTransforms>() as u64,
),
}),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&copy_srgb_view),
},
],
label: create_debug_label!("Copy sRGB bind group").as_deref(),
});
descriptors descriptors
.globals .globals
.set_resolution(target.width(), target.height()); .set_resolution(target.width(), target.height());
@ -400,6 +470,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
target, target,
frame_buffer_view, frame_buffer_view,
depth_texture_view, depth_texture_view,
copy_srgb_view,
copy_srgb_bind_group,
current_frame: None, current_frame: None,
meshes: Vec::new(), meshes: Vec::new(),
shape_tessellator: ShapeTessellator::new(), shape_tessellator: ShapeTessellator::new(),
@ -767,21 +839,23 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
self.target.resize(&self.descriptors.device, width, height); self.target.resize(&self.descriptors.device, width, height);
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let label = create_debug_label!("Framebuffer texture"); let label = create_debug_label!("Framebuffer texture");
let frame_buffer = self let frame_buffer = self
.descriptors .descriptors
.device .device
.create_texture(&wgpu::TextureDescriptor { .create_texture(&wgpu::TextureDescriptor {
label: label.as_deref(), label: label.as_deref(),
size: wgpu::Extent3d { size,
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1, mip_level_count: 1,
sample_count: self.descriptors.msaa_sample_count, sample_count: self.descriptors.msaa_sample_count,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: self.target.format(), format: self.descriptors.frame_buffer_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
}); });
self.frame_buffer_view = frame_buffer.create_view(&Default::default()); self.frame_buffer_view = frame_buffer.create_view(&Default::default());
@ -792,11 +866,7 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
.device .device
.create_texture(&wgpu::TextureDescriptor { .create_texture(&wgpu::TextureDescriptor {
label: label.as_deref(), label: label.as_deref(),
size: wgpu::Extent3d { size,
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1, mip_level_count: 1,
sample_count: self.descriptors.msaa_sample_count, sample_count: self.descriptors.msaa_sample_count,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
@ -804,6 +874,45 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
}); });
self.depth_texture_view = depth_texture.create_view(&Default::default()); self.depth_texture_view = depth_texture.create_view(&Default::default());
let copy_srgb_buffer = self
.descriptors
.device
.create_texture(&wgpu::TextureDescriptor {
label: create_debug_label!("Copy sRGB framebuffer texture").as_deref(),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: self.descriptors.frame_buffer_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
});
self.copy_srgb_view = copy_srgb_buffer.create_view(&Default::default());
self.copy_srgb_bind_group =
self.descriptors
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.descriptors.pipelines.bitmap_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &self.quad_tex_transforms,
offset: 0,
size: wgpu::BufferSize::new(
std::mem::size_of::<TextureTransforms>() as u64,
),
}),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&self.copy_srgb_view),
},
],
label: create_debug_label!("Copy sRBG bind group").as_deref(),
});
self.descriptors.globals.set_resolution(width, height); self.descriptors.globals.set_resolution(width, height);
} }
@ -910,10 +1019,15 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
.globals .globals
.update_uniform(&self.descriptors.device, &mut frame_data.0); .update_uniform(&self.descriptors.device, &mut frame_data.0);
let (color_view, resolve_target) = if self.descriptors.msaa_sample_count >= 2 { // Use intermediate render targets when resolving MSAA or copying from linear-to-sRGB texture.
(&self.frame_buffer_view, Some(frame_data.1.view())) let (color_view, resolve_target) = match (
} else { self.descriptors.frame_buffer_format != self.descriptors.surface_format,
(frame_data.1.view(), None) self.descriptors.msaa_sample_count >= 2,
) {
(false, false) => (frame_data.1.view(), None),
(false, true) => (&self.frame_buffer_view, Some(frame_data.1.view())),
(true, false) => (&self.copy_srgb_view, None),
(true, true) => (&self.frame_buffer_view, Some(&self.copy_srgb_view)),
}; };
let render_pass = frame_data.0.begin_render_pass(&wgpu::RenderPassDescriptor { let render_pass = frame_data.0.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -1216,15 +1330,81 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
fn end_frame(&mut self) { fn end_frame(&mut self) {
if let Some(frame) = self.current_frame.take() { if let Some(frame) = self.current_frame.take() {
// Finalize render pass.
drop(frame.render_pass);
self.descriptors.uniform_buffers.finish();
let draw_encoder = frame.frame_data.0; let draw_encoder = frame.frame_data.0;
let uniform_encoder = frame.frame_data.2; let mut uniform_encoder = frame.frame_data.2;
let render_pass = frame.render_pass;
// Finalize render pass.
drop(render_pass);
// If we have an sRGB surface, copy from our linear intermediate buffer to the sRGB surface.
let command_buffers = if self.descriptors.frame_buffer_format
!= self.descriptors.surface_format
{
let mut copy_encoder = self.descriptors.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor {
label: create_debug_label!("Frame copy command encoder").as_deref(),
},
);
let mut render_pass = copy_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachment {
view: &frame.frame_data.1.view(),
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
resolve_target: None,
}],
depth_stencil_attachment: None,
label: None,
});
render_pass.set_pipeline(&self.descriptors.pipelines.copy_srgb_pipeline);
render_pass.set_bind_group(0, self.descriptors.globals.bind_group(), &[]);
self.descriptors.uniform_buffers.write_uniforms(
&self.descriptors.device,
&mut uniform_encoder,
&mut render_pass,
1,
&Transforms {
world_matrix: [
[self.target.width() as f32, 0.0, 0.0, 0.0],
[0.0, self.target.height() as f32, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
color_adjustments: ColorAdjustments {
mult_color: [1.0, 1.0, 1.0, 1.0],
add_color: [0.0, 0.0, 0.0, 0.0],
},
},
);
render_pass.set_bind_group(2, &self.copy_srgb_bind_group, &[]);
render_pass.set_bind_group(
3,
self.descriptors
.bitmap_samplers
.get_bind_group(false, false),
&[],
);
render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..));
render_pass.set_index_buffer(self.quad_ibo.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..6, 0, 0..1);
drop(render_pass);
vec![
uniform_encoder.finish(),
draw_encoder.finish(),
copy_encoder.finish(),
]
} else {
vec![uniform_encoder.finish(), draw_encoder.finish()]
};
self.descriptors.uniform_buffers.finish();
self.target.submit( self.target.submit(
&self.descriptors.device, &self.descriptors.device,
&self.descriptors.queue, &self.descriptors.queue,
vec![uniform_encoder.finish(), draw_encoder.finish()], command_buffers,
frame.frame_data.1, frame.frame_data.1,
); );
} }

View File

@ -16,6 +16,9 @@ pub struct Pipelines {
pub gradient_pipelines: ShapePipeline, pub gradient_pipelines: ShapePipeline,
pub gradient_layout: wgpu::BindGroupLayout, pub gradient_layout: wgpu::BindGroupLayout,
pub copy_srgb_pipeline: wgpu::RenderPipeline,
pub copy_srgb_layout: wgpu::BindGroupLayout,
} }
impl ShapePipeline { impl ShapePipeline {
@ -28,31 +31,20 @@ impl Pipelines {
pub fn new( pub fn new(
device: &wgpu::Device, device: &wgpu::Device,
surface_format: wgpu::TextureFormat, surface_format: wgpu::TextureFormat,
frame_buffer_format: wgpu::TextureFormat,
msaa_sample_count: u32, msaa_sample_count: u32,
sampler_layout: &wgpu::BindGroupLayout, sampler_layout: &wgpu::BindGroupLayout,
globals_layout: &wgpu::BindGroupLayout, globals_layout: &wgpu::BindGroupLayout,
dynamic_uniforms_layout: &wgpu::BindGroupLayout, dynamic_uniforms_layout: &wgpu::BindGroupLayout,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
// If the surface is sRGB, the GPU will automatically convert colors from linear to sRGB, let color_shader = create_shader(device, "color", include_str!("../shaders/color.wgsl"));
// so our shader should output linear colors. let bitmap_shader = create_shader(device, "bitmap", include_str!("../shaders/bitmap.wgsl"));
let output_srgb = !surface_format.describe().srgb; let gradient_shader =
let color_shader = create_shader( create_shader(device, "gradient", include_str!("../shaders/gradient.wgsl"));
let copy_srgb_shader = create_shader(
device, device,
"color", "copy sRGB",
include_str!("../shaders/color.wgsl"), include_str!("../shaders/copy_srgb.wgsl"),
output_srgb,
);
let bitmap_shader = create_shader(
device,
"bitmap",
include_str!("../shaders/bitmap.wgsl"),
output_srgb,
);
let gradient_shader = create_shader(
device,
"gradient",
include_str!("../shaders/gradient.wgsl"),
output_srgb,
); );
let vertex_buffers_description = [wgpu::VertexBufferLayout { let vertex_buffers_description = [wgpu::VertexBufferLayout {
@ -66,7 +58,7 @@ impl Pipelines {
let color_pipelines = create_color_pipelines( let color_pipelines = create_color_pipelines(
device, device,
surface_format, frame_buffer_format,
&color_shader, &color_shader,
msaa_sample_count, msaa_sample_count,
&vertex_buffers_description, &vertex_buffers_description,
@ -104,7 +96,7 @@ impl Pipelines {
let bitmap_pipelines = create_bitmap_pipeline( let bitmap_pipelines = create_bitmap_pipeline(
device, device,
surface_format, frame_buffer_format,
&bitmap_shader, &bitmap_shader,
msaa_sample_count, msaa_sample_count,
&vertex_buffers_description, &vertex_buffers_description,
@ -144,7 +136,7 @@ impl Pipelines {
let gradient_pipelines = create_gradient_pipeline( let gradient_pipelines = create_gradient_pipeline(
device, device,
surface_format, frame_buffer_format,
&gradient_shader, &gradient_shader,
msaa_sample_count, msaa_sample_count,
&vertex_buffers_description, &vertex_buffers_description,
@ -153,41 +145,82 @@ impl Pipelines {
&gradient_bind_layout, &gradient_bind_layout,
); );
let copy_srgb_bind_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
},
count: None,
},
],
label: create_debug_label!("Copy sRGB bind group layout").as_deref(),
});
let copy_texture_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: create_debug_label!("Copy sRGB pipeline layout").as_deref(),
bind_group_layouts: &[
globals_layout,
dynamic_uniforms_layout,
&bitmap_bind_layout,
sampler_layout,
],
push_constant_ranges: &[],
});
let copy_srgb_pipeline = device.create_render_pipeline(&create_pipeline_descriptor(
create_debug_label!("Copy sRGB pipeline").as_deref(),
&copy_srgb_shader,
&copy_srgb_shader,
&copy_texture_pipeline_layout,
None,
&[wgpu::ColorTargetState {
format: surface_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: Default::default(),
}],
&vertex_buffers_description,
1,
));
Ok(Self { Ok(Self {
color_pipelines, color_pipelines,
bitmap_pipelines, bitmap_pipelines,
bitmap_layout: bitmap_bind_layout, bitmap_layout: bitmap_bind_layout,
gradient_pipelines, gradient_pipelines,
gradient_layout: gradient_bind_layout, gradient_layout: gradient_bind_layout,
copy_srgb_pipeline,
copy_srgb_layout: copy_srgb_bind_layout,
}) })
} }
} }
/// Builds a `wgpu::ShaderModule` the given WGSL source in `src`. /// Builds a `wgpu::ShaderModule` the given WGSL source in `src`.
/// ///
/// The source is prepended with common code in `common.wgsl` and sRGB/linear conversions in /// The source is prepended with common code in `common.wgsl`, simulating a `#include` preprocessor.
/// `output_srgb.wgsl`/`output_linear.wgsl`, simulating a `#include` preprocessor. We could /// We could possibly does this as an offline build step instead.
/// possibly does this as an offline build step instead.
fn create_shader( fn create_shader(
device: &wgpu::Device, device: &wgpu::Device,
name: &'static str, name: &'static str,
src: &'static str, src: &'static str,
output_srgb: bool,
) -> wgpu::ShaderModule { ) -> wgpu::ShaderModule {
const COMMON_SRC: &str = include_str!("../shaders/common.wgsl"); const COMMON_SRC: &str = include_str!("../shaders/common.wgsl");
const OUTPUT_LINEAR_SRC: &str = include_str!("../shaders/output_linear.wgsl"); let src = [COMMON_SRC, src].concat();
const OUTPUT_SRGB_SRC: &str = include_str!("../shaders/output_srgb.wgsl"); let label = create_debug_label!("Shader {}", name,);
let src = if output_srgb {
[COMMON_SRC, OUTPUT_SRGB_SRC, src].concat()
} else {
[COMMON_SRC, OUTPUT_LINEAR_SRC, src].concat()
};
let label = create_debug_label!(
"Shader {} ({})",
name,
if output_srgb { "sRGB" } else { "linear" }
);
let desc = wgpu::ShaderModuleDescriptor { let desc = wgpu::ShaderModuleDescriptor {
label: label.as_deref(), label: label.as_deref(),
source: wgpu::ShaderSource::Wgsl(src.into()), source: wgpu::ShaderSource::Wgsl(src.into()),