Skip to content

Commit 387f96b

Browse files
committed
Fix alpha blending, entry points, and doc comments (PR review)
Address all 5 Copilot review comments from PR #223: - Switch entire UI pipeline and compositor to premultiplied-alpha blending. The fragment shader now premultiplies RGB by final alpha before output, and both the UI pipeline and compositor use PREMULTIPLIED_ALPHA_BLENDING. This fixes edge darkening on borders and correct compositing of MSAA-resolved overlay textures. - Fix border coverage in the fragment shader: compute straight-alpha weighted blend of border and fill colors, then premultiply at the end, avoiding the previous double-application of alpha at AA edges. - Use explicit entry points (Some(linkage.entry_point)) in the UI render pipeline for consistency with the rest of the codebase. - Update clip_rect doc to note it is reserved for future use (not currently enforced by the shader or renderer). - Fix ui_vertex doc comment to only mention Path elements reading from the slab (TextGlyph uses the standard quad generation path).
1 parent 851284c commit 387f96b

6 files changed

Lines changed: 27 additions & 14 deletions

File tree

crates/renderling-ui/src/renderer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,7 +2299,7 @@ impl UiRenderer {
22992299
layout: Some(&pipeline_layout),
23002300
vertex: wgpu::VertexState {
23012301
module: &vertex_linkage.module,
2302-
entry_point: None,
2302+
entry_point: Some(vertex_linkage.entry_point),
23032303
compilation_options: wgpu::PipelineCompilationOptions::default(),
23042304
buffers: &[],
23052305
},
@@ -2320,11 +2320,11 @@ impl UiRenderer {
23202320
},
23212321
fragment: Some(wgpu::FragmentState {
23222322
module: &fragment_linkage.module,
2323-
entry_point: None,
2323+
entry_point: Some(fragment_linkage.entry_point),
23242324
compilation_options: wgpu::PipelineCompilationOptions::default(),
23252325
targets: &[Some(wgpu::ColorTargetState {
23262326
format,
2327-
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
2327+
blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
23282328
write_mask: wgpu::ColorWrites::ALL,
23292329
})],
23302330
}),
328 Bytes
Binary file not shown.

crates/renderling/src/compositor/cpu.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl Compositor {
8888
compilation_options: wgpu::PipelineCompilationOptions::default(),
8989
targets: &[Some(wgpu::ColorTargetState {
9090
format,
91-
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
91+
blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
9292
write_mask: wgpu::ColorWrites::ALL,
9393
})],
9494
}),

crates/renderling/src/ui_slab/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ pub struct UiDrawCallDescriptor {
164164
/// Set to `Id::NONE` when unused.
165165
pub atlas_descriptor_id: Id<AtlasDescriptor>,
166166
/// Scissor/clip rectangle (x, y, width, height).
167-
/// Elements outside this rect are clipped. Set to (0,0, viewport_w,
168-
/// viewport_h) for no clipping.
167+
/// Reserved for future use — not currently enforced by the shader
168+
/// or renderer. Set to (0, 0, viewport_w, viewport_h) by default.
169169
pub clip_rect: Vec4,
170170
/// Element opacity (0.0 = fully transparent, 1.0 = fully opaque).
171171
/// Multiplied with the final alpha.

crates/renderling/src/ui_slab/shader.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! crate's `UiRenderer`.
55
66
use crabslab::{Id, Slab, SlabItem};
7-
use glam::{Vec2, Vec4};
7+
use glam::{Vec2, Vec4, Vec4Swizzles};
88
use spirv_std::{image::Image2dArray, spirv, Sampler};
99

1010
use super::{GradientType, UiDrawCallDescriptor, UiElementType, UiVertex, UiViewport};
@@ -95,8 +95,8 @@ fn eval_gradient(
9595
/// size, reading from the slab. The vertex index (0..5) selects which
9696
/// corner of the quad.
9797
///
98-
/// For Path and TextGlyph elements, the vertex data is read directly
99-
/// from the slab (pre-tessellated vertices).
98+
/// For Path elements, the vertex data is read directly from the slab
99+
/// (pre-tessellated vertices).
100100
#[spirv(vertex)]
101101
pub fn ui_vertex(
102102
#[spirv(vertex_index)] vertex_index: u32,
@@ -254,11 +254,21 @@ pub fn ui_fragment(
254254
let inner_distance = distance + draw_call.border_width;
255255
let border_alpha =
256256
1.0 - crate::math::smoothstep(-aa_width, aa_width, inner_distance);
257-
// Inside the border but outside the fill = border color.
258-
let in_border = fill_alpha;
259-
let in_fill = border_alpha;
260-
color = draw_call.border_color * (in_border - in_fill) + fill * in_fill;
261-
color.w = draw_call.border_color.w * (in_border - in_fill) + fill.w * in_fill;
257+
// Coverage weights.
258+
let border_weight = fill_alpha - border_alpha;
259+
let fill_weight = border_alpha;
260+
let total = fill_alpha;
261+
// Straight-alpha RGB: weighted blend of border and fill.
262+
if total > 0.0 {
263+
let rgb = (draw_call.border_color.xyz() * border_weight
264+
+ fill.xyz() * fill_weight)
265+
/ total;
266+
let a =
267+
(draw_call.border_color.w * border_weight + fill.w * fill_weight) / total;
268+
color = rgb.extend(a * total);
269+
} else {
270+
color = Vec4::ZERO;
271+
}
262272
} else {
263273
color = fill;
264274
color.w *= fill_alpha;
@@ -269,5 +279,8 @@ pub fn ui_fragment(
269279
// Apply element opacity.
270280
color.w *= draw_call.opacity;
271281

282+
// Premultiply RGB by final alpha for premultiplied-alpha blending.
283+
color = (color.xyz() * color.w).extend(color.w);
284+
272285
*frag_color = color;
273286
}

test_img/ui2d/bordered_rect.png

7 Bytes
Loading

0 commit comments

Comments
 (0)