git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit3df508093b18c538c56411a439acd64e84eba8c0
parent3a5d9af50d
authorLucas Galante <[email protected]>
date2026-08-13 21:28
test: pin the shared glyph-shader vertex layout at compile time

ImageVertex and GlyphVertex feed the same shader (locations 0..=4) and each
pipeline hardcodes one offset per location, but nothing tied the structs to
those numbers — which is how ImageVertex lost clip_extents and left location 4
undescribed. Assert size and every field offset in both files, so adding,
reordering, or resizing a field fails the build instead of silently feeding
mis-aligned attributes.

Verified by injecting a uv/color reorder: size stays 52 so a size-only check
would pass, but the build fails with
"assertion failed: offset_of!(ImageVertex, uv) == 8".

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/vk/image.rs | 13 +++++++++++++
 src/vk/text.rs  | 11 +++++++++++
 2 files changed, 24 insertions(+)

diff --git a/src/vk/image.rs b/src/vk/image.rs
index 08ab1b5..c970497 100644
--- a/src/vk/image.rs
+++ b/src/vk/image.rs
@@ -71,6 +71,19 @@ struct ImageVertex {
     clip_extents: [f32; 2],
 }
 
+// The pipeline below hardcodes one offset per shader location. Pin the struct to
+// them so adding, reordering, or resizing a field fails the build instead of
+// silently feeding the shader mis-aligned attributes — the drift that left
+// location 4 undescribed. `text.rs` pins `GlyphVertex` to the same layout.
+const _: () = {
+    assert!(std::mem::size_of::<ImageVertex>() == 52);
+    assert!(std::mem::offset_of!(ImageVertex, position) == 0);
+    assert!(std::mem::offset_of!(ImageVertex, uv) == 8);
+    assert!(std::mem::offset_of!(ImageVertex, color) == 16);
+    assert!(std::mem::offset_of!(ImageVertex, clip_circle) == 32);
+    assert!(std::mem::offset_of!(ImageVertex, clip_extents) == 44);
+};
+
 struct GpuImage {
     image: vk::Image,
     view: vk::ImageView,
diff --git a/src/vk/text.rs b/src/vk/text.rs
index 9dbec40..24af1ca 100644
--- a/src/vk/text.rs
+++ b/src/vk/text.rs
@@ -65,6 +65,17 @@ struct GlyphVertex {
     clip_extents: [f32; 2],
 }
 
+// See the matching block in `image.rs`: both pipelines feed the same glyph
+// shader (locations 0..=4), so both vertex structs must hold this exact layout.
+const _: () = {
+    assert!(std::mem::size_of::<GlyphVertex>() == 52);
+    assert!(std::mem::offset_of!(GlyphVertex, position) == 0);
+    assert!(std::mem::offset_of!(GlyphVertex, uv) == 8);
+    assert!(std::mem::offset_of!(GlyphVertex, color) == 16);
+    assert!(std::mem::offset_of!(GlyphVertex, clip_circle) == 32);
+    assert!(std::mem::offset_of!(GlyphVertex, clip_extents) == 44);
+};
+
 #[derive(Clone, Copy)]
 struct GlyphEntry {
     /// Atlas texel rect.