2022-12-23 22:06:19 +00:00
|
|
|
use crate::blend::ComplexBlend;
|
|
|
|
use enum_map::{enum_map, EnumMap};
|
2023-01-06 22:17:42 +00:00
|
|
|
use naga_oil::compose::{
|
|
|
|
ComposableModuleDescriptor, Composer, ComposerError, NagaModuleDescriptor, ShaderDefValue,
|
|
|
|
};
|
|
|
|
use std::borrow::Cow;
|
|
|
|
use std::collections::HashMap;
|
2022-12-23 22:06:19 +00:00
|
|
|
|
2022-09-07 01:41:23 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Shaders {
|
|
|
|
pub color_shader: wgpu::ShaderModule,
|
|
|
|
pub bitmap_shader: wgpu::ShaderModule,
|
render: Compile a different version of 'bitmap.wgsl' for Stage3D
When using the bitmap.wgsl shader for normal rendering, we need
to saturate immediately after applying the color transformation
to reproduce Flash Player's behavior. This makes the (possibly
transformed) alpha value get multiplied by a in-range color,
instead of a potentially out-of-range color.
However, Stage3D just applies a no-op color transformation,
and should only saturate at the very end
(not after the intermediate division by the original alpha value).
To support both of these requirements, I've added in a new
`early_saturate` ifdef that controls when we apply 'saturate'.
We then compile the shader twice (once with early_saturate=true
and once with early_saturate=false), and use the two versions
in the right pipelines.
We could use a simpler shader for Stage3D - however, it can't just
be a plain copy, as we need to apply the viewport transformation.
For now, I'm re-using the shader code to keep things simple. If
this becomes a performance issue in stage3d, we could revisit this.
2023-03-17 14:44:54 +00:00
|
|
|
/// Like `bitmap_shader` but performs saturation after we've
|
|
|
|
/// re-multiplied the alpha. This is used for the Stage3D
|
|
|
|
/// `bitmap_opaque` pipeline, which needs to able to
|
|
|
|
/// avoid changing initially-in-range rgb values (regadless
|
|
|
|
/// of whether dividing by the alpha value would produce
|
|
|
|
/// an out-of-range value).
|
|
|
|
pub bitmap_late_saturate_shader: wgpu::ShaderModule,
|
2023-03-02 23:25:47 +00:00
|
|
|
pub gradient_shader: wgpu::ShaderModule,
|
2022-09-07 01:41:23 +00:00
|
|
|
pub copy_srgb_shader: wgpu::ShaderModule,
|
2022-09-21 00:44:56 +00:00
|
|
|
pub copy_shader: wgpu::ShaderModule,
|
2022-12-23 22:06:19 +00:00
|
|
|
pub blend_shaders: EnumMap<ComplexBlend, wgpu::ShaderModule>,
|
2023-01-20 23:48:03 +00:00
|
|
|
pub color_matrix_filter: wgpu::ShaderModule,
|
2023-01-21 20:33:12 +00:00
|
|
|
pub blur_filter: wgpu::ShaderModule,
|
2022-09-07 01:41:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Shaders {
|
2023-01-06 22:17:42 +00:00
|
|
|
pub fn new(device: &wgpu::Device) -> Self {
|
|
|
|
let mut composer = composer().expect("Couldn't create shader composer");
|
|
|
|
let mut shader_defs = HashMap::new();
|
|
|
|
shader_defs.insert(
|
|
|
|
"use_push_constants".to_owned(),
|
|
|
|
ShaderDefValue::Bool(device.limits().max_push_constant_size > 0),
|
|
|
|
);
|
render: Compile a different version of 'bitmap.wgsl' for Stage3D
When using the bitmap.wgsl shader for normal rendering, we need
to saturate immediately after applying the color transformation
to reproduce Flash Player's behavior. This makes the (possibly
transformed) alpha value get multiplied by a in-range color,
instead of a potentially out-of-range color.
However, Stage3D just applies a no-op color transformation,
and should only saturate at the very end
(not after the intermediate division by the original alpha value).
To support both of these requirements, I've added in a new
`early_saturate` ifdef that controls when we apply 'saturate'.
We then compile the shader twice (once with early_saturate=true
and once with early_saturate=false), and use the two versions
in the right pipelines.
We could use a simpler shader for Stage3D - however, it can't just
be a plain copy, as we need to apply the viewport transformation.
For now, I'm re-using the shader code to keep things simple. If
this becomes a performance issue in stage3d, we could revisit this.
2023-03-17 14:44:54 +00:00
|
|
|
shader_defs.insert("early_saturate".to_owned(), ShaderDefValue::Bool(true));
|
|
|
|
|
|
|
|
let mut late_saturate_shader_defs = shader_defs.clone();
|
|
|
|
late_saturate_shader_defs.insert("early_saturate".to_owned(), ShaderDefValue::Bool(false));
|
|
|
|
|
2023-01-06 22:17:42 +00:00
|
|
|
let color_shader = make_shader(
|
2023-02-11 18:28:53 +00:00
|
|
|
device,
|
2023-01-06 22:17:42 +00:00
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"color.wgsl",
|
2023-01-06 17:41:24 +00:00
|
|
|
include_str!("../shaders/color.wgsl"),
|
|
|
|
);
|
2023-01-06 22:17:42 +00:00
|
|
|
let bitmap_shader = make_shader(
|
2023-02-11 18:28:53 +00:00
|
|
|
device,
|
2023-01-06 22:17:42 +00:00
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"bitmap.wgsl",
|
2023-01-06 17:41:24 +00:00
|
|
|
include_str!("../shaders/bitmap.wgsl"),
|
|
|
|
);
|
render: Compile a different version of 'bitmap.wgsl' for Stage3D
When using the bitmap.wgsl shader for normal rendering, we need
to saturate immediately after applying the color transformation
to reproduce Flash Player's behavior. This makes the (possibly
transformed) alpha value get multiplied by a in-range color,
instead of a potentially out-of-range color.
However, Stage3D just applies a no-op color transformation,
and should only saturate at the very end
(not after the intermediate division by the original alpha value).
To support both of these requirements, I've added in a new
`early_saturate` ifdef that controls when we apply 'saturate'.
We then compile the shader twice (once with early_saturate=true
and once with early_saturate=false), and use the two versions
in the right pipelines.
We could use a simpler shader for Stage3D - however, it can't just
be a plain copy, as we need to apply the viewport transformation.
For now, I'm re-using the shader code to keep things simple. If
this becomes a performance issue in stage3d, we could revisit this.
2023-03-17 14:44:54 +00:00
|
|
|
let bitmap_late_saturate_shader = make_shader(
|
|
|
|
device,
|
|
|
|
&mut composer,
|
|
|
|
&late_saturate_shader_defs,
|
|
|
|
"bitmap.wgsl",
|
|
|
|
include_str!("../shaders/bitmap.wgsl"),
|
|
|
|
);
|
2023-01-06 22:17:42 +00:00
|
|
|
let copy_srgb_shader = make_shader(
|
2023-02-11 18:28:53 +00:00
|
|
|
device,
|
2023-01-06 22:17:42 +00:00
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"copy_srgb.wgsl",
|
2022-09-07 01:41:23 +00:00
|
|
|
include_str!("../shaders/copy_srgb.wgsl"),
|
|
|
|
);
|
2023-01-06 22:17:42 +00:00
|
|
|
let copy_shader = make_shader(
|
2023-02-11 18:28:53 +00:00
|
|
|
device,
|
2023-01-06 22:17:42 +00:00
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"copy.wgsl",
|
2023-01-06 17:41:24 +00:00
|
|
|
include_str!("../shaders/copy.wgsl"),
|
|
|
|
);
|
2023-01-20 23:48:03 +00:00
|
|
|
let color_matrix_filter = make_shader(
|
2023-02-11 18:28:53 +00:00
|
|
|
device,
|
2023-01-20 23:48:03 +00:00
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"filter/color_matrix.wgsl",
|
|
|
|
include_str!("../shaders/filter/color_matrix.wgsl"),
|
|
|
|
);
|
2023-01-21 20:33:12 +00:00
|
|
|
let blur_filter = make_shader(
|
2023-02-11 18:28:53 +00:00
|
|
|
device,
|
2023-01-21 20:33:12 +00:00
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"filter/blur.wgsl",
|
|
|
|
include_str!("../shaders/filter/blur.wgsl"),
|
|
|
|
);
|
2023-03-02 23:25:47 +00:00
|
|
|
let gradient_shader = make_shader(
|
|
|
|
device,
|
|
|
|
&mut composer,
|
|
|
|
&shader_defs,
|
|
|
|
"gradient.wgsl",
|
|
|
|
include_str!("../shaders/gradient.wgsl"),
|
|
|
|
);
|
2022-12-23 22:06:19 +00:00
|
|
|
|
|
|
|
let blend_shaders = enum_map! {
|
2023-01-06 22:17:42 +00:00
|
|
|
ComplexBlend::Lighten => make_shader(device, &mut composer, &shader_defs, "blend/lighten.wgsl", include_str!("../shaders/blend/lighten.wgsl")),
|
|
|
|
ComplexBlend::Darken => make_shader(device, &mut composer, &shader_defs, "blend/darken.wgsl", include_str!("../shaders/blend/darken.wgsl")),
|
|
|
|
ComplexBlend::Difference => make_shader(device, &mut composer, &shader_defs, "blend/difference.wgsl", include_str!("../shaders/blend/difference.wgsl")),
|
|
|
|
ComplexBlend::Invert => make_shader(device, &mut composer, &shader_defs, "blend/invert.wgsl", include_str!("../shaders/blend/invert.wgsl")),
|
|
|
|
ComplexBlend::Alpha => make_shader(device, &mut composer, &shader_defs, "blend/alpha.wgsl", include_str!("../shaders/blend/alpha.wgsl")),
|
|
|
|
ComplexBlend::Erase => make_shader(device, &mut composer, &shader_defs, "blend/erase.wgsl", include_str!("../shaders/blend/erase.wgsl")),
|
|
|
|
ComplexBlend::Overlay => make_shader(device, &mut composer, &shader_defs, "blend/overlay.wgsl", include_str!("../shaders/blend/overlay.wgsl")),
|
|
|
|
ComplexBlend::HardLight => make_shader(device, &mut composer, &shader_defs, "blend/hardlight.wgsl", include_str!("../shaders/blend/hardlight.wgsl")),
|
2022-12-25 00:20:57 +00:00
|
|
|
};
|
|
|
|
|
2022-09-07 01:41:23 +00:00
|
|
|
Self {
|
|
|
|
color_shader,
|
|
|
|
bitmap_shader,
|
render: Compile a different version of 'bitmap.wgsl' for Stage3D
When using the bitmap.wgsl shader for normal rendering, we need
to saturate immediately after applying the color transformation
to reproduce Flash Player's behavior. This makes the (possibly
transformed) alpha value get multiplied by a in-range color,
instead of a potentially out-of-range color.
However, Stage3D just applies a no-op color transformation,
and should only saturate at the very end
(not after the intermediate division by the original alpha value).
To support both of these requirements, I've added in a new
`early_saturate` ifdef that controls when we apply 'saturate'.
We then compile the shader twice (once with early_saturate=true
and once with early_saturate=false), and use the two versions
in the right pipelines.
We could use a simpler shader for Stage3D - however, it can't just
be a plain copy, as we need to apply the viewport transformation.
For now, I'm re-using the shader code to keep things simple. If
this becomes a performance issue in stage3d, we could revisit this.
2023-03-17 14:44:54 +00:00
|
|
|
bitmap_late_saturate_shader,
|
2023-03-02 23:25:47 +00:00
|
|
|
gradient_shader,
|
2022-09-07 01:41:23 +00:00
|
|
|
copy_srgb_shader,
|
2022-09-21 00:44:56 +00:00
|
|
|
copy_shader,
|
2022-12-23 22:06:19 +00:00
|
|
|
blend_shaders,
|
2023-01-20 23:48:03 +00:00
|
|
|
color_matrix_filter,
|
2023-01-21 20:33:12 +00:00
|
|
|
blur_filter,
|
2022-09-07 01:41:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-06 22:17:42 +00:00
|
|
|
fn composer() -> Result<Composer, ComposerError> {
|
|
|
|
let mut composer = Composer::default();
|
|
|
|
// [NA] Hack to get all capabilities since nobody exposes this type easily
|
|
|
|
let capabilities = composer.capabilities;
|
|
|
|
composer = composer.with_capabilities(!capabilities);
|
|
|
|
composer.add_composable_module(ComposableModuleDescriptor {
|
|
|
|
source: include_str!("../shaders/common.wgsl"),
|
|
|
|
file_path: "common.wgsl",
|
|
|
|
..Default::default()
|
|
|
|
})?;
|
2023-01-20 23:48:03 +00:00
|
|
|
composer.add_composable_module(ComposableModuleDescriptor {
|
|
|
|
source: include_str!("../shaders/filter/common.wgsl"),
|
|
|
|
file_path: "filter/common.wgsl",
|
|
|
|
..Default::default()
|
|
|
|
})?;
|
2023-01-06 22:17:42 +00:00
|
|
|
Ok(composer)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_shader(
|
2023-01-06 17:41:24 +00:00
|
|
|
device: &wgpu::Device,
|
2023-01-06 22:17:42 +00:00
|
|
|
composer: &mut Composer,
|
|
|
|
shader_defs: &HashMap<String, ShaderDefValue>,
|
2023-01-06 17:41:24 +00:00
|
|
|
name: &str,
|
2023-01-06 22:17:42 +00:00
|
|
|
source: &'static str,
|
2023-01-06 17:41:24 +00:00
|
|
|
) -> wgpu::ShaderModule {
|
2023-01-06 22:17:42 +00:00
|
|
|
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
|
|
label: create_debug_label!("Shader {}", name).as_deref(),
|
|
|
|
source: wgpu::ShaderSource::Naga(Cow::Owned(
|
|
|
|
composer
|
|
|
|
.make_naga_module(NagaModuleDescriptor {
|
|
|
|
source,
|
|
|
|
file_path: name,
|
|
|
|
shader_defs: shader_defs.clone(),
|
|
|
|
..Default::default()
|
|
|
|
})
|
2023-01-20 21:12:48 +00:00
|
|
|
.unwrap_or_else(|e| {
|
|
|
|
panic!(
|
|
|
|
"{name} failed to compile:\n{}\n{:#?}",
|
2023-02-11 18:28:53 +00:00
|
|
|
e.emit_to_string(composer),
|
2023-01-20 21:12:48 +00:00
|
|
|
e
|
|
|
|
)
|
|
|
|
}),
|
2023-01-06 22:17:42 +00:00
|
|
|
)),
|
|
|
|
})
|
2022-09-07 01:41:23 +00:00
|
|
|
}
|