Blade window transparency (#10973)
Release Notes: - N/A Following up to #10880 TODO: - [x] create window as transparent - [x] X11 - [x] Wayland - [ ] Windows - [x] MacOS (when used with Blade) - [x] enable GPU surface transparency - [x] adjust the pipeline blend modes - [x] adjust shader outputs  Blurred results from @jansol (on Wayland), who contributed to this work:  --------- Co-authored-by: Jan Solanti <jhs@psonet.com>
This commit is contained in:
co-authored by
Jan Solanti
parent
056c785f4e
commit
e4f13dd561
@@ -28,6 +28,7 @@ pub unsafe fn new_renderer(
|
||||
_native_window: *mut c_void,
|
||||
native_view: *mut c_void,
|
||||
bounds: crate::Size<f32>,
|
||||
transparent: bool,
|
||||
) -> Renderer {
|
||||
use raw_window_handle as rwh;
|
||||
struct RawWindow {
|
||||
@@ -64,10 +65,13 @@ pub unsafe fn new_renderer(
|
||||
|
||||
BladeRenderer::new(
|
||||
gpu,
|
||||
gpu::Extent {
|
||||
width: bounds.width as u32,
|
||||
height: bounds.height as u32,
|
||||
depth: 1,
|
||||
BladeSurfaceConfig {
|
||||
size: gpu::Extent {
|
||||
width: bounds.width as u32,
|
||||
height: bounds.height as u32,
|
||||
depth: 1,
|
||||
},
|
||||
transparent,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -76,7 +80,8 @@ pub unsafe fn new_renderer(
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
struct GlobalParams {
|
||||
viewport_size: [f32; 2],
|
||||
pad: [u32; 2],
|
||||
premultiplied_alpha: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
//Note: we can't use `Bounds` directly here because
|
||||
@@ -184,6 +189,10 @@ impl BladePipelines {
|
||||
fn new(gpu: &gpu::Context, surface_info: gpu::SurfaceInfo) -> Self {
|
||||
use gpu::ShaderData as _;
|
||||
|
||||
log::info!(
|
||||
"Initializing Blade pipelines for surface {:?}",
|
||||
surface_info
|
||||
);
|
||||
let shader = gpu.create_shader(gpu::ShaderDesc {
|
||||
source: include_str!("shaders.wgsl"),
|
||||
});
|
||||
@@ -200,6 +209,18 @@ impl BladePipelines {
|
||||
shader.check_struct_size::<MonochromeSprite>();
|
||||
shader.check_struct_size::<PolychromeSprite>();
|
||||
|
||||
// See https://apoorvaj.io/alpha-compositing-opengl-blending-and-premultiplied-alpha/
|
||||
let blend_mode = match surface_info.alpha {
|
||||
gpu::AlphaMode::Ignored => gpu::BlendState::ALPHA_BLENDING,
|
||||
gpu::AlphaMode::PreMultiplied => gpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
|
||||
gpu::AlphaMode::PostMultiplied => gpu::BlendState::ALPHA_BLENDING,
|
||||
};
|
||||
let color_targets = &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(blend_mode),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}];
|
||||
|
||||
Self {
|
||||
quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "quads",
|
||||
@@ -212,11 +233,7 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_quad"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "shadows",
|
||||
@@ -229,11 +246,7 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_shadow"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "path_rasterization",
|
||||
@@ -263,11 +276,7 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_path"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "underlines",
|
||||
@@ -280,11 +289,7 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_underline"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "mono-sprites",
|
||||
@@ -297,11 +302,7 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_mono_sprite"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "poly-sprites",
|
||||
@@ -314,11 +315,7 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_poly_sprite"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
|
||||
name: "surfaces",
|
||||
@@ -331,23 +328,25 @@ impl BladePipelines {
|
||||
},
|
||||
depth_stencil: None,
|
||||
fragment: shader.at("fs_surface"),
|
||||
color_targets: &[gpu::ColorTargetState {
|
||||
format: surface_info.format,
|
||||
blend: Some(gpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: gpu::ColorWrites::default(),
|
||||
}],
|
||||
color_targets,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BladeSurfaceConfig {
|
||||
pub size: gpu::Extent,
|
||||
pub transparent: bool,
|
||||
}
|
||||
|
||||
pub struct BladeRenderer {
|
||||
gpu: Arc<gpu::Context>,
|
||||
surface_config: gpu::SurfaceConfig,
|
||||
alpha_mode: gpu::AlphaMode,
|
||||
command_encoder: gpu::CommandEncoder,
|
||||
last_sync_point: Option<gpu::SyncPoint>,
|
||||
pipelines: BladePipelines,
|
||||
instance_belt: BladeBelt,
|
||||
viewport_size: gpu::Extent,
|
||||
path_tiles: HashMap<PathId, AtlasTile>,
|
||||
atlas: Arc<BladeAtlas>,
|
||||
atlas_sampler: gpu::Sampler,
|
||||
@@ -356,21 +355,19 @@ pub struct BladeRenderer {
|
||||
}
|
||||
|
||||
impl BladeRenderer {
|
||||
fn make_surface_config(size: gpu::Extent) -> gpu::SurfaceConfig {
|
||||
gpu::SurfaceConfig {
|
||||
size,
|
||||
pub fn new(gpu: Arc<gpu::Context>, config: BladeSurfaceConfig) -> Self {
|
||||
let surface_config = gpu::SurfaceConfig {
|
||||
size: config.size,
|
||||
usage: gpu::TextureUsage::TARGET,
|
||||
display_sync: gpu::DisplaySync::Recent,
|
||||
//Note: this matches the original logic of the Metal backend,
|
||||
// but ultimaterly we need to switch to `Linear`.
|
||||
color_space: gpu::ColorSpace::Srgb,
|
||||
allow_exclusive_full_screen: false,
|
||||
transparent: false,
|
||||
}
|
||||
}
|
||||
transparent: config.transparent,
|
||||
};
|
||||
let surface_info = gpu.resize(surface_config);
|
||||
|
||||
pub fn new(gpu: Arc<gpu::Context>, size: gpu::Extent) -> Self {
|
||||
let surface_info = gpu.resize(Self::make_surface_config(size));
|
||||
let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
|
||||
name: "main",
|
||||
buffer_count: 2,
|
||||
@@ -397,11 +394,12 @@ impl BladeRenderer {
|
||||
|
||||
Self {
|
||||
gpu,
|
||||
surface_config,
|
||||
alpha_mode: surface_info.alpha,
|
||||
command_encoder,
|
||||
last_sync_point: None,
|
||||
pipelines,
|
||||
instance_belt,
|
||||
viewport_size: size,
|
||||
path_tiles: HashMap::default(),
|
||||
atlas,
|
||||
atlas_sampler,
|
||||
@@ -425,15 +423,26 @@ impl BladeRenderer {
|
||||
depth: 1,
|
||||
};
|
||||
|
||||
if gpu_size != self.viewport_size() {
|
||||
if gpu_size != self.surface_config.size {
|
||||
self.wait_for_gpu();
|
||||
self.gpu.resize(Self::make_surface_config(gpu_size));
|
||||
self.viewport_size = gpu_size;
|
||||
self.surface_config.size = gpu_size;
|
||||
self.gpu.resize(self.surface_config);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_transparency(&mut self, transparent: bool) {
|
||||
if transparent != self.surface_config.transparent {
|
||||
self.wait_for_gpu();
|
||||
self.surface_config.transparent = transparent;
|
||||
let surface_info = self.gpu.resize(self.surface_config);
|
||||
self.pipelines = BladePipelines::new(&self.gpu, surface_info);
|
||||
self.alpha_mode = surface_info.alpha;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_os = "macos", allow(dead_code))]
|
||||
pub fn viewport_size(&self) -> gpu::Extent {
|
||||
self.viewport_size
|
||||
self.surface_config.size
|
||||
}
|
||||
|
||||
pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
|
||||
@@ -481,7 +490,8 @@ impl BladeRenderer {
|
||||
let tex_info = self.atlas.get_texture_info(texture_id);
|
||||
let globals = GlobalParams {
|
||||
viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
|
||||
pad: [0; 2],
|
||||
premultiplied_alpha: 0,
|
||||
pad: 0,
|
||||
};
|
||||
|
||||
let vertex_buf = unsafe { self.instance_belt.alloc_data(&vertices, &self.gpu) };
|
||||
@@ -526,10 +536,14 @@ impl BladeRenderer {
|
||||
|
||||
let globals = GlobalParams {
|
||||
viewport_size: [
|
||||
self.viewport_size.width as f32,
|
||||
self.viewport_size.height as f32,
|
||||
self.surface_config.size.width as f32,
|
||||
self.surface_config.size.height as f32,
|
||||
],
|
||||
pad: [0; 2],
|
||||
premultiplied_alpha: match self.alpha_mode {
|
||||
gpu::AlphaMode::Ignored | gpu::AlphaMode::PostMultiplied => 0,
|
||||
gpu::AlphaMode::PreMultiplied => 1,
|
||||
},
|
||||
pad: 0,
|
||||
};
|
||||
|
||||
if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
struct GlobalParams {
|
||||
viewport_size: vec2<f32>,
|
||||
pad: vec2<u32>,
|
||||
premultiplied_alpha: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
var<uniform> globals: GlobalParams;
|
||||
@@ -176,6 +177,13 @@ fn quad_sdf(point: vec2<f32>, bounds: Bounds, corner_radii: Corners) -> f32 {
|
||||
corner_radius;
|
||||
}
|
||||
|
||||
// Abstract away the final color transformation based on the
|
||||
// target alpha compositing mode.
|
||||
fn blend_color(color: vec4<f32>, alpha_factor: f32) -> vec4<f32> {
|
||||
let alpha = color.a * alpha_factor;
|
||||
return select(vec4<f32>(color.rgb, alpha), vec4<f32>(color.rgb, 1.0) * alpha, globals.premultiplied_alpha != 0u);
|
||||
}
|
||||
|
||||
// --- quads --- //
|
||||
|
||||
struct Quad {
|
||||
@@ -266,7 +274,7 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
|
||||
saturate(0.5 - inset_distance));
|
||||
}
|
||||
|
||||
return color * vec4<f32>(1.0, 1.0, 1.0, saturate(0.5 - distance));
|
||||
return blend_color(color, saturate(0.5 - distance));
|
||||
}
|
||||
|
||||
// --- shadows --- //
|
||||
@@ -339,7 +347,7 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4<f32> {
|
||||
y += step;
|
||||
}
|
||||
|
||||
return input.color * vec4<f32>(1.0, 1.0, 1.0, alpha);
|
||||
return blend_color(input.color, alpha);
|
||||
}
|
||||
|
||||
// --- path rasterization --- //
|
||||
@@ -415,7 +423,7 @@ fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) insta
|
||||
fn fs_path(input: PathVarying) -> @location(0) vec4<f32> {
|
||||
let sample = textureSample(t_sprite, s_sprite, input.tile_position).r;
|
||||
let mask = 1.0 - abs(1.0 - sample % 2.0);
|
||||
return input.color * mask;
|
||||
return blend_color(input.color, mask);
|
||||
}
|
||||
|
||||
// --- underlines --- //
|
||||
@@ -476,7 +484,7 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4<f32> {
|
||||
let distance_from_top_border = distance_in_pixels - half_thickness;
|
||||
let distance_from_bottom_border = distance_in_pixels + half_thickness;
|
||||
let alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border));
|
||||
return input.color * vec4<f32>(1.0, 1.0, 1.0, alpha);
|
||||
return blend_color(input.color, alpha);
|
||||
}
|
||||
|
||||
// --- monochrome sprites --- //
|
||||
@@ -520,7 +528,7 @@ fn fs_mono_sprite(input: MonoSpriteVarying) -> @location(0) vec4<f32> {
|
||||
if (any(input.clip_distances < vec4<f32>(0.0))) {
|
||||
return vec4<f32>(0.0);
|
||||
}
|
||||
return input.color * vec4<f32>(1.0, 1.0, 1.0, sample);
|
||||
return blend_color(input.color, sample);
|
||||
}
|
||||
|
||||
// --- polychrome sprites --- //
|
||||
@@ -571,8 +579,7 @@ fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4<f32> {
|
||||
let grayscale = dot(color.rgb, GRAYSCALE_FACTORS);
|
||||
color = vec4<f32>(vec3<f32>(grayscale), sample.a);
|
||||
}
|
||||
color.a *= saturate(0.5 - distance);
|
||||
return color;
|
||||
return blend_color(color, saturate(0.5 - distance));
|
||||
}
|
||||
|
||||
// --- surfaces --- //
|
||||
|
||||
Reference in New Issue
Block a user