Open
Conversation
Introduces a dedicated lightweight 2D rendering pipeline separate from the 3D PBR pipeline. Uses SlabAllocator for GPU memory management with Hybrid<UiDrawCallDescriptor> wrapper types (UiRect, UiCircle, UiEllipse) following the Camera pattern for live GPU updates via set_*/with_* methods. - SDF-based shape rendering (rect, rounded rect, circle, ellipse) - Gradient fills (linear, radial), borders, anti-aliased edges - 3 GPU bindings and 32-byte vertices (vs 13 bindings / ~160 bytes for 3D) - Instanced quads with per-pixel SDF evaluation in fragment shader - 6 visual regression tests with baseline images
Add UiImage element type with atlas texture support, enabling texture/image rendering in the 2D UI pipeline. Fix critical bug where Atlas::new() was allocated before UiViewport on the slab, causing the shader (which reads viewport at offset 0) to read garbage data and render blank white output. - Add UiImage wrapper with set/with builders for position, size, tint, opacity, z-depth - Replace dummy atlas with real Atlas integration (512x512x2) - Add atlas_size field to UiViewport for correct UV mapping - Fix shader to use viewport.atlas_size instead of viewport.size for AtlasTextureDescriptor::uv() calls - Add AtlasImage, AtlasTexture, UiImage to public re-exports - Recompile shaders with updated UiViewport layout - Add 2 image rendering tests (checkerboard + tint) - Regenerate baseline images with correct rendering - 8 renderling-ui tests + 101 renderling tests pass
Port text rendering from the old UI module to the new lightweight 2D pipeline. Glyph rasterization uses glyph_brush to produce a Luma8 cache image, which is converted to RGBA (white + alpha) and uploaded to the atlas. Each visible glyph gets its own AtlasTextureDescriptor pointing to its sub-region in the cache, and a UiDrawCallDescriptor with element_type TextGlyph. The existing fragment shader samples glyph coverage from the atlas alpha channel and multiplies by fill_color. - Add GlyphCache wrapping GlyphBrush with Luma8 cache image - Add UiText handle type with set_z/set_opacity methods - Add add_font(), add_text(), remove_text() to UiRenderer - Add image as optional dep behind 'text' feature - Re-export FontArc, FontId, Section, Text, UiText - Add 2 text tests (plain text + text overlaid on a rounded rect) - 10 renderling-ui tests + 101 renderling tests pass, 0 clippy warnings
Port path/vector rendering from the old UI module to the new lightweight 2D pipeline. Uses lyon for tessellation (fill and stroke), de-indexes the triangle mesh, and writes UiVertex arrays to the slab. The existing vertex shader's Path branch reads pre-tessellated vertices from the slab, and the fragment shader passes through the interpolated vertex color. - Add UiPathBuilder with lyon path commands (begin, line_to, quadratic/cubic bezier, add_rectangle, add_circle, etc.) - Add fill() and stroke() methods for tessellation - Add StrokeConfig (line width, cap, join) configuration - Add UiPath handle type with z-depth and opacity setters - Add path_builder() and remove_path() to UiRenderer - Re-export StrokeConfig, UiPath, UiPathBuilder behind 'path' feature - Add 3 path tests (filled triangle, stroked circle, mixed shapes) - 13 renderling-ui tests + 101 renderling tests pass, 0 clippy warnings
…ling-ui Delete the old renderling::ui module (cpu.rs, sdf.rs, text.rs, path.rs) and its test baselines, remove the 'ui' feature flag and its optional deps (glyph_brush, lyon, loading-bytes). Move loading-bytes to dev-dependencies since it's still needed by wasm tests. Update the example crate to use renderling-ui's UiRenderer, UiRect, UiText, and Section APIs instead of the removed Ui type.
Add UiPathBuilder::with_fill_image() for filling tessellated paths with atlas textures. The shader samples the atlas when atlas_texture_id is set on Path elements, using bounding-box-derived UVs. Add UiRenderer::upload_image() to load images without creating a draw call. Enable 4x MSAA by default (matching the old Ui behavior) and expose with_msaa_sample_count() for configuration. Replace raw u32 fields in UiDrawCallDescriptor with typed Ids: atlas_texture_id becomes Id<AtlasTextureDescriptor> and atlas_descriptor_id becomes Id<AtlasDescriptor>, using Id::NONE as the unset sentinel. Update shader and CPU code accordingly.
Add a Compositor to the renderling crate (compositor_vertex + compositor_fragment shaders) that alpha-blends a source texture onto a target framebuffer via a fullscreen quad. This fixes the black screen when overlaying the UI renderer on a 3D stage with MSAA enabled: the UI now renders to an intermediate texture, resolves MSAA there, and the compositor blends it onto the final view preserving the scene beneath. Add property getters to all UI element types (UiRect, UiCircle, UiEllipse, UiImage, UiPath, UiText) so current values can be read back — a prerequisite for the upcoming tweening/animation system. Fix the example app's UI renderer clearing the 3D scene by removing the erroneous .with_background_color(Vec4::ZERO) call, which was causing a LoadOp::Clear that wiped the stage output.
There was a problem hiding this comment.
Pull request overview
This PR replaces the legacy renderling::ui (3D-stage-based) UI path with a dedicated 2D UI pipeline via a new renderling-ui crate, backed by shared CPU/GPU slab types and new rust-gpu shaders, plus a compositor path for overlaying UI over 3D.
Changes:
- Added
renderling-uicrate (SDF shapes, gradients, images, optional text/path) and visual regression tests/baselines. - Added
renderling::ui_slabshared slab types + UI vertex/fragment shaders and generated linkage artifacts. - Added
renderling::compositor(fullscreen-quad alpha blend) and migrated the example toUiRenderer; removed the oldrenderling::uimodule/feature.
Reviewed changes
Copilot reviewed 26 out of 53 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| Cargo.toml | Adds crates/renderling-ui workspace member. |
| Cargo.lock | Adds renderling-ui package to lockfile graph. |
| crates/renderling/Cargo.toml | Removes legacy ui feature/deps; adjusts dev-deps. |
| crates/renderling/src/lib.rs | Exports new compositor + ui_slab; removes legacy ui module export. |
| crates/renderling/src/context.rs | Removes Context::new_ui() legacy constructor. |
| crates/renderling/src/ui_slab/mod.rs | Introduces shared UI slab types (CPU/GPU). |
| crates/renderling/src/ui_slab/shader.rs | Adds rust-gpu UI vertex/fragment shaders. |
| crates/renderling/src/compositor.rs | Adds compositor rust-gpu shaders + CPU re-export. |
| crates/renderling/src/compositor/cpu.rs | Adds CPU-side compositor pipeline for overlay blending. |
| crates/renderling/src/linkage.rs | Registers new compositor + UI shader linkage modules. |
| crates/renderling/src/linkage/ui_vertex.rs | Adds generated linkage for UI vertex shader. |
| crates/renderling/src/linkage/ui_fragment.rs | Adds generated linkage for UI fragment shader. |
| crates/renderling/src/linkage/compositor_vertex.rs | Adds generated linkage for compositor vertex shader. |
| crates/renderling/src/linkage/compositor_fragment.rs | Adds generated linkage for compositor fragment shader. |
| crates/renderling/src/linkage/light_tiling_compute_tile_min_and_max_depth_multisampled.rs | Formatting-only update to generated include paths. |
| crates/renderling/shaders/manifest.json | Registers new compositor + UI SPIR-V artifacts. |
| crates/renderling/shaders/compositor-compositor_vertex.spv | Adds compiled SPIR-V for compositor vertex shader. |
| crates/renderling/shaders/compositor-compositor_fragment.spv | Adds compiled SPIR-V for compositor fragment shader. |
| crates/renderling/shaders/ui_slab-shader-ui_vertex.spv | Adds compiled SPIR-V for UI vertex shader. |
| crates/renderling/shaders/ui_slab-shader-ui_fragment.spv | Adds compiled SPIR-V for UI fragment shader. |
| crates/renderling/src/ui.rs | Removes legacy UI module root. |
| crates/renderling/src/ui/cpu.rs | Removes legacy CPU UI implementation. |
| crates/renderling/src/ui/cpu/path.rs | Removes legacy path UI implementation. |
| crates/renderling/src/ui/cpu/text.rs | Removes legacy text UI implementation. |
| crates/renderling/src/ui/sdf.rs | Removes legacy UI SDF stub. |
| crates/renderling-ui/Cargo.toml | Adds new renderling-ui crate configuration/features. |
| crates/renderling-ui/src/lib.rs | Adds renderling-ui public API + re-exports. |
| crates/renderling-ui/src/renderer.rs | Implements UiRenderer and element builders. |
| crates/renderling-ui/src/test.rs | Adds visual regression tests for the new UI renderer. |
| crates/example/Cargo.toml | Adds dependency on renderling-ui. |
| crates/example/src/lib.rs | Migrates example UI usage to renderling_ui::UiRenderer. |
| test_img/ui2d/rect.png | New baseline image for 2D UI rect test. |
| test_img/ui2d/rounded_rect.png | New baseline image for rounded rect test. |
| test_img/ui2d/circle.png | New baseline image for circle test. |
| test_img/ui2d/bordered_rect.png | New baseline image for bordered rect test. |
| test_img/ui2d/multiple_shapes.png | New baseline image for multiple shapes test. |
| test_img/ui2d/image.png | New baseline image for image test. |
| test_img/ui2d/image_tint.png | New baseline image for tinted image test. |
| test_img/ui2d/gradient_rect.png | New baseline image for gradient rect test. |
| test_img/ui2d/text.png | New baseline image for text test. |
| test_img/ui2d/text_with_shapes.png | New baseline image for text-over-shape test. |
| test_img/ui2d/filled_path.png | New baseline image for filled path test. |
| test_img/ui2d/stroked_path.png | New baseline image for stroked path test. |
| test_img/ui2d/path_shapes.png | New baseline image for path shapes test. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+251
to
+265
| if draw_call.border_width > 0.0 { | ||
| // Border: the border region is between the outer edge and | ||
| // (outer edge - border_width). | ||
| let inner_distance = distance + draw_call.border_width; | ||
| let border_alpha = | ||
| 1.0 - crate::math::smoothstep(-aa_width, aa_width, inner_distance); | ||
| // Inside the border but outside the fill = border color. | ||
| let in_border = fill_alpha; | ||
| let in_fill = border_alpha; | ||
| color = draw_call.border_color * (in_border - in_fill) + fill * in_fill; | ||
| color.w = draw_call.border_color.w * (in_border - in_fill) + fill.w * in_fill; | ||
| } else { | ||
| color = fill; | ||
| color.w *= fill_alpha; | ||
| } |
Comment on lines
+166
to
+175
| /// Scissor/clip rectangle (x, y, width, height). | ||
| /// Elements outside this rect are clipped. Set to (0,0, viewport_w, | ||
| /// viewport_h) for no clipping. | ||
| pub clip_rect: Vec4, | ||
| /// Element opacity (0.0 = fully transparent, 1.0 = fully opaque). | ||
| /// Multiplied with the final alpha. | ||
| pub opacity: f32, | ||
| /// Z-depth for sorting (painter's algorithm). Lower values are drawn | ||
| /// first (further back). | ||
| pub z: f32, |
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).
…scene Split the scene node query API into two explicit methods: - root_nodes_in_scene: returns only the top-level nodes directly referenced by the scene (the original behavior) - recursive_nodes_in_scene: returns all nodes including descendants in depth-first order This fixes a bug where animations targeting child (parented) nodes were silently skipped because only root nodes were collected. Update call sites: - Animator test now uses recursive_nodes_in_scene for correctness - Example crate simplified by removing manual get_children helper Co-authored-by: Andreas Streichardt <andreas@mop.koeln>
9c4baa9 to
c0e1ded
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace the old
renderling::uimodule (a thin wrapper around the full 3D Stage that ran every UI pixel through the PBR fragment shader) with a new (really an old crate but everything old is new again) standalonerenderling-uicrate built on a lightweight, purpose-built GPU pipeline.Motivation
The old UI system had significant overhead: 13 GPU bindings, ~160-byte vertices, and the full PBR fragment shader for flat 2D elements. The new system uses 3 GPU bindings, 32-byte vertices, SDF-based shape rendering, and a dedicated vertex/fragment shader pair — making it dramatically simpler and more appropriate for 2D/UI workloads.
What's New
renderling-ui crate
renderling crate changes
Example crate