]> git.nega.tv - josh/narcissus/commitdiff
shark: Add support for ui rects
authorJosh Simmons <josh@nega.tv>
Thu, 22 Aug 2024 17:40:47 +0000 (19:40 +0200)
committerJosh Simmons <josh@nega.tv>
Thu, 22 Aug 2024 17:40:47 +0000 (19:40 +0200)
title/shark-shaders/shaders/compute_bindings.h
title/shark-shaders/shaders/display_transform.comp.glsl
title/shark-shaders/shaders/primitive_2d.h
title/shark-shaders/shaders/primitive_2d_bin.comp.glsl
title/shark-shaders/shaders/primitive_2d_bin_clear.comp.glsl
title/shark-shaders/shaders/primitive_2d_rasterize.comp.glsl
title/shark/src/main.rs
title/shark/src/pipelines/mod.rs

index 88498ed74446c80b76087a7b2bb290900fe9e9a8..bbf7cf62c5b545a2ff64d1710688f2158f4df222 100644 (file)
@@ -1,16 +1,16 @@
+#ifndef COMPUTE_BINDINGS_INCLUDE
+#define COMPUTE_BINDINGS_INCLUDE
 
-struct Glyph {
-    ivec2 atlas_min;
-    ivec2 atlas_max;
+#include "primitive_2d.h"
 
-    vec2 offset_min;
-    vec2 offset_max;
+layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer PrimitiveInstances
+{
+    PrimitiveInstance values[];
 };
 
-struct GlyphInstance {
-    vec2 position;
-    uint index;
-    uint color;
+layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Rects
+{
+    Rect values[];
 };
 
 layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Glyphs
@@ -18,11 +18,6 @@ layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Gl
     Glyph values[];
 };
 
-layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer GlyphInstances
-{
-    GlyphInstance values[];
-};
-
 layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer TilesRead
 {
     uint values[];
@@ -43,8 +38,9 @@ struct ComputeUniforms {
     uint num_primitives_1024;
     uint tile_stride;
 
+    PrimitiveInstances primitive_instances;
+    Rects rects;
     Glyphs glyphs;
-    GlyphInstances glyph_instances;
     TilesWrite tiles;
 };
 
@@ -59,3 +55,5 @@ layout (set = 0, binding = 3, rgba16f) uniform writeonly image2D ui_layer_write;
 layout (set = 0, binding = 3, rgba16f) uniform readonly image2D ui_layer_read;
 layout (set = 0, binding = 4, rgba16f) uniform readonly image2D color_layer;
 layout (set = 0, binding = 5, rgba16f) uniform writeonly image2D composited_output;
+
+#endif
index 113d314b4726e7dbdbdc57400b5a99c436e365c9..465f44a21fbda38001fc0111386e45ea92630bd2 100644 (file)
@@ -7,7 +7,6 @@
 #extension GL_EXT_scalar_block_layout : require
 
 #include "compute_bindings.h"
-#include "primitive_2d.h"
 
 float srgb_oetf(float a) {
     return (.0031308f >= a) ? 12.92f * a : 1.055f * pow(a, .4166666666666667f) - .055f;
index 010735d934a0e9d5a8f2c0a899ca8421073acaaf..ae6a2ec80a5a454ad449476eb410f87339c23672 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef PRIMITIVE_2D_INCLUDE
+#define PRIMITIVE_2D_INCLUDE
+
 const uint TILE_SIZE = 32;
 
 const uint MAX_PRIMS = 1 << 18;
@@ -9,10 +12,27 @@ const uint TILE_BITMAP_RANGE_HI_OFFSET = (TILE_BITMAP_RANGE_LO_OFFSET + 1);
 const uint TILE_BITMAP_L1_OFFSET = (TILE_BITMAP_RANGE_HI_OFFSET + 1);
 const uint TILE_BITMAP_L0_OFFSET = (TILE_BITMAP_L1_OFFSET + TILE_BITMAP_L1_WORDS);
 
-bool test_glyph(uint index, vec2 tile_min, vec2 tile_max) {
-    const GlyphInstance gi = uniforms.glyph_instances.values[index];
-    const Glyph gl = uniforms.glyphs.values[gi.index];
-    const vec2 glyph_min = gi.position + gl.offset_min;
-    const vec2 glyph_max = gi.position + gl.offset_max;
-    return !(any(lessThan(tile_max, glyph_min)) || any(greaterThan(tile_min, glyph_max)));
-}
+const uint PRIMITIVE_TYPE_RECT = 0;
+const uint PRIMITIVE_TYPE_GLYPH = 1;
+
+struct PrimitiveInstance {
+    uint packed;
+    uint color;
+    vec2 position;
+};
+
+struct Rect {
+    vec2 half_extent;
+    float border_width;
+    float border_radius;
+};
+
+struct Glyph {
+    ivec2 atlas_min;
+    ivec2 atlas_max;
+
+    vec2 offset_min;
+    vec2 offset_max;
+};
+
+#endif
\ No newline at end of file
index a826daef1601488393f9172a5c12a4aa94078f0f..639861510798b54b87b9f7ce34f15f4b7ced7ea0 100644 (file)
@@ -12,7 +12,6 @@
 #extension GL_KHR_shader_subgroup_vote : require
 
 #include "compute_bindings.h"
-#include "primitive_2d.h"
 
 const uint SUBGROUP_SIZE = 64;
 const uint NUM_SUBGROUPS = 16;
@@ -27,14 +26,34 @@ void main() {
     for (uint i = 0; i < NUM_PRIMITIVES_WG; i += gl_SubgroupSize.x) {
         const uint primitive_index = gl_WorkGroupID.x * NUM_PRIMITIVES_WG + i + gl_SubgroupInvocationID;
 
+        // Bounds for this primitive, any tiles which intersect this AABB will be written.
         vec2 primitive_min = vec2(99999.9);
         vec2 primitive_max = vec2(-99999.9);
 
         if (primitive_index < uniforms.num_primitives) {
-            const GlyphInstance gi = uniforms.glyph_instances.values[primitive_index];
-            const Glyph gl = uniforms.glyphs.values[gi.index];
-            primitive_min = gi.position + gl.offset_min;
-            primitive_max = gi.position + gl.offset_max;
+            const PrimitiveInstance primitive_instance = uniforms.primitive_instances.values[primitive_index];
+            const uint type = bitfieldExtract(primitive_instance.packed, 30, 2);
+            const uint offset = bitfieldExtract(primitive_instance.packed, 0, 20);
+
+            for (;;) {
+                const uint scalar_type = subgroupBroadcastFirst(type);
+                [[branch]]
+                if (scalar_type == type) {
+                    switch (type) {
+                        case PRIMITIVE_TYPE_RECT:
+                            const Rect rect = uniforms.rects.values[offset];
+                            primitive_min = primitive_instance.position - rect.half_extent;
+                            primitive_max = primitive_instance.position + rect.half_extent;
+                            break;
+                        case PRIMITIVE_TYPE_GLYPH:
+                            const Glyph glyph = uniforms.glyphs.values[offset];
+                            primitive_min = primitive_instance.position + glyph.offset_min;
+                            primitive_max = primitive_instance.position + glyph.offset_max;
+                            break;
+                    }
+                    break;
+                }
+            }
         }
 
         const vec2 primitives_min = subgroupMin(primitive_min);
@@ -51,7 +70,6 @@ void main() {
         for (int y = tile_start.y; y < tile_end.y; y++) {
             for (int x = tile_start.x; x < tile_end.x; x++) {
                 const uvec2 tile_coord = uvec2(x, y);
-                const uint tile_index = tile_coord.y * uniforms.tile_stride + tile_coord.x;
                 const vec2 tile_min = tile_coord * TILE_SIZE;
                 const vec2 tile_max = min(tile_min + TILE_SIZE, uniforms.screen_resolution);
 
@@ -62,6 +80,8 @@ void main() {
                     continue;
                 }
 
+                const uint tile_index = tile_coord.y * uniforms.tile_stride + tile_coord.x;
+
                 if (ballot.x != 0) {
                     uniforms.tiles.values[tile_index * TILE_STRIDE + TILE_BITMAP_L0_OFFSET + gl_WorkGroupID.x * 32 + word_index + 0] = ballot.x;
                 }
index 8cfa4fba499e8b9a0ca792b1c0aa615d1d56e28c..8d443a2df0628ddb27737afde6e2c1f3775d00f8 100644 (file)
@@ -11,7 +11,6 @@
 #extension GL_KHR_shader_subgroup_ballot : require
 
 #include "compute_bindings.h"
-#include "primitive_2d.h"
 
 // TODO: Spec constant support for different subgroup sizes.
 layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
index 67f878c30fba06604251f95db80b8dc34d36182a..c61ce614e7529b6e1155e61a03b1c7ed94b4f55e 100644 (file)
@@ -102,20 +102,36 @@ void main() {
 
                 // Set bits in the L0 bitmap indicate binned primitives for this tile.
                 const uint primitive_index = index_l0 * 32 + j;
-
-                const GlyphInstance gi = uniforms.glyph_instances.values[primitive_index];
-                const Glyph gl = uniforms.glyphs.values[gi.index];
-                const vec2 glyph_min = gi.position + gl.offset_min;
-                const vec2 glyph_max = gi.position + gl.offset_max;
-
-                [[branch]]
-                if (all(greaterThanEqual(sample_center, glyph_min)) && all(lessThanEqual(sample_center, glyph_max))) {
-                    const vec2 glyph_size = gl.offset_max - gl.offset_min;
-                    const vec2 uv = mix(gl.atlas_min, gl.atlas_max, (sample_center - glyph_min) / glyph_size) / uniforms.atlas_resolution;
-                    const vec4 color = unpackUnorm4x8(gi.color).bgra;
-                    const float coverage = textureLod(sampler2D(glyph_atlas, bilinear_sampler), uv, 0.0).r * color.a;
-                    accum.rgb = (coverage * color.rgb) + accum.rgb * (1.0 - coverage);
-                    accum.a = coverage + accum.a * (1.0 - coverage);
+                const PrimitiveInstance primitive_instance = uniforms.primitive_instances.values[primitive_index];
+                const uint type = bitfieldExtract(primitive_instance.packed, 30, 2);
+                const uint offset = bitfieldExtract(primitive_instance.packed, 0, 20);
+
+                switch (type) {
+                    case PRIMITIVE_TYPE_RECT: {
+                        const Rect rect = uniforms.rects.values[offset];
+                        const vec2 rect_min = primitive_instance.position - rect.half_extent;
+                        const vec2 rect_max = primitive_instance.position + rect.half_extent;
+                        if (all(greaterThanEqual(sample_center, rect_min)) && all(lessThanEqual(sample_center, rect_max))) {
+                            const vec4 color = unpackUnorm4x8(primitive_instance.color).bgra;
+                            accum.rgb = color.rgb * color.a + accum.rgb * (1.0 - color.a);
+                            accum.a = color.a + accum.a * (1.0 - color.a);
+                        }
+                        break;
+                    }
+                    case PRIMITIVE_TYPE_GLYPH: {
+                        const Glyph glyph = uniforms.glyphs.values[offset];
+                        const vec2 glyph_min = primitive_instance.position + glyph.offset_min;
+                        const vec2 glyph_max = primitive_instance.position + glyph.offset_max;
+                        if (all(greaterThanEqual(sample_center, glyph_min)) && all(lessThanEqual(sample_center, glyph_max))) {
+                            const vec2 glyph_size = glyph.offset_max - glyph.offset_min;
+                            const vec2 uv = mix(glyph.atlas_min, glyph.atlas_max, (sample_center - glyph_min) / glyph_size) / uniforms.atlas_resolution;
+                            const vec4 color = unpackUnorm4x8(primitive_instance.color).bgra;
+                            const float coverage = textureLod(sampler2D(glyph_atlas, bilinear_sampler), uv, 0.0).r * color.a;
+                            accum.rgb = color.rgb * coverage + accum.rgb * (1.0 - coverage);
+                            accum.a = coverage + accum.a * (1.0 - coverage);
+                        }
+                        break;
+                    }
                 }
             }
         }
index 1671a9e29800017e5ffc7cb1f985af2ed3177ff0..1adfc5fc008c0834e42f007f83a778638e4686ca 100644 (file)
@@ -5,7 +5,7 @@ use std::time::{Duration, Instant};
 
 use narcissus_core::{dds, Widen as _};
 use pipelines::basic::BasicPipeline;
-use pipelines::{GlyphInstance, PrimitiveUniforms, TILE_SIZE, TILE_STRIDE};
+use pipelines::{PrimitiveInstance, PrimitiveUniforms, Rect, TILE_SIZE, TILE_STRIDE};
 use renderdoc_sys as rdoc;
 
 use fonts::{FontFamily, Fonts};
@@ -452,7 +452,8 @@ struct UiState<'a> {
 
     tmp_string: String,
 
-    primitive_instances: Vec<GlyphInstance>,
+    primitive_instances: Vec<PrimitiveInstance>,
+    rects: Vec<Rect>,
 }
 
 impl<'a> UiState<'a> {
@@ -465,9 +466,30 @@ impl<'a> UiState<'a> {
             glyph_cache,
             tmp_string: default(),
             primitive_instances: vec![],
+            rects: vec![],
         }
     }
 
+    fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
+        let half_extent_x = width / 2.0;
+        let half_extent_y = height / 2.0;
+        let center_x = x + half_extent_x;
+        let center_y = y + half_extent_y;
+
+        let rect_index = self.rects.len() as u32;
+
+        self.rects.push(Rect {
+            half_extent_x,
+            half_extent_y,
+            border_width: 0.0,
+            border_radius: 0.0,
+        });
+
+        self.primitive_instances.push(PrimitiveInstance::rect(
+            rect_index, 0x4400ff00, center_x, center_y,
+        ))
+    }
+
     fn text_fmt(
         &mut self,
         mut x: f32,
@@ -508,12 +530,12 @@ impl<'a> UiState<'a> {
 
             x += advance * scale;
 
-            self.primitive_instances.push(GlyphInstance {
+            self.primitive_instances.push(PrimitiveInstance::glyph(
+                touched_glyph_index,
+                0x880000ff,
                 x,
                 y,
-                touched_glyph_index,
-                color: 0x880000ff,
-            });
+            ));
 
             x += advance_width * scale;
         }
@@ -1389,17 +1411,23 @@ impl<'gpu> DrawState<'gpu> {
                     microshades::PURPLE_RGBA_F32[3],
                 );
 
+                let primitive_instance_buffer = gpu.request_transient_buffer_with_data(
+                    frame,
+                    thread_token,
+                    BufferUsageFlags::STORAGE,
+                    ui_state.primitive_instances.as_slice(),
+                );
                 let glyph_buffer = gpu.request_transient_buffer_with_data(
                     frame,
                     thread_token,
                     BufferUsageFlags::STORAGE,
                     touched_glyphs,
                 );
-                let glyph_instance_buffer = gpu.request_transient_buffer_with_data(
+                let rect_buffer = gpu.request_transient_buffer_with_data(
                     frame,
                     thread_token,
                     BufferUsageFlags::STORAGE,
-                    ui_state.primitive_instances.as_slice(),
+                    ui_state.rects.as_slice(),
                 );
 
                 let num_primitives = ui_state.primitive_instances.len() as u32;
@@ -1474,9 +1502,10 @@ impl<'gpu> DrawState<'gpu> {
                         tile_resolution_x: self.tile_resolution_x,
                         tile_resolution_y: self.tile_resolution_y,
                         tile_stride: self.tile_resolution_x,
+                        primitives_instances_buffer: gpu
+                            .get_buffer_address(primitive_instance_buffer.to_arg()),
                         glyphs_buffer: gpu.get_buffer_address(glyph_buffer.to_arg()),
-                        glyph_instances_buffer: gpu
-                            .get_buffer_address(glyph_instance_buffer.to_arg()),
+                        rects_buffer: gpu.get_buffer_address(rect_buffer.to_arg()),
                         tiles_buffer: gpu.get_buffer_address(self.tiles_buffer.to_arg()),
                     },
                 );
@@ -1730,6 +1759,10 @@ pub fn main() {
             let base_x = (base_x + 1.0) * 0.5;
             let base_y = (base_y + 1.0) * 0.5;
 
+            for _ in 0..100 {
+                ui_state.rect(0.0, 0.0, width as f32, height as f32);
+            }
+
             for i in 0..80 {
                 let i = i as f32;
                 ui_state.text_fmt(
@@ -1754,6 +1787,17 @@ pub fn main() {
                     );
             }
 
+            ui_state.rect(base_x * 60.0, base_y * 60.0, 120.0, 120.0);
+            ui_state.rect(base_x * 500.0, base_y * 100.0, 120.0, 120.0);
+            ui_state.rect(base_x * 90.0, base_y * 290.0, 140.0, 120.0);
+            ui_state.rect(base_x * 800.0, base_y * 320.0, 120.0, 120.0);
+            ui_state.rect(base_x * 200.0, base_y * 200.0, 120.0, 120.0);
+            ui_state.rect(base_x * 300.0, base_y * 120.0, 120.0, 170.0);
+            ui_state.rect(base_x * 1000.0, base_y * 30.0, 50.0, 120.0);
+            ui_state.rect(base_x * 340.0, base_y * 400.0, 120.0, 110.0);
+            ui_state.rect(base_x * 290.0, base_y * 80.0, 120.0, 10.0);
+            ui_state.rect(base_x * 310.0, base_y * 190.0, 10.0, 120.0);
+
             draw_state.draw(
                 thread_token,
                 frame,
index 4464b1aaa0cf2ee9a30c502d45bb8cea9814c1f3..d544f07023a041135cf96195798ff38c73e8eb2b 100644 (file)
@@ -25,16 +25,55 @@ pub struct PrimitiveUniforms {
     pub num_primitives_1024: u32,
     pub tile_stride: u32,
 
+    pub primitives_instances_buffer: u64,
+    pub rects_buffer: u64,
     pub glyphs_buffer: u64,
-    pub glyph_instances_buffer: u64,
     pub tiles_buffer: u64,
 }
 
+#[repr(u32)]
+pub enum PrimitiveType {
+    Rect,
+    Glyph,
+}
+
 #[allow(unused)]
 #[repr(C)]
-pub struct GlyphInstance {
+pub struct PrimitiveInstance {
+    pub packed: u32,
+    pub color: u32,
     pub x: f32,
     pub y: f32,
-    pub touched_glyph_index: TouchedGlyphIndex,
-    pub color: u32,
+}
+
+#[repr(C)]
+pub struct Rect {
+    pub half_extent_x: f32,
+    pub half_extent_y: f32,
+    pub border_width: f32,
+    pub border_radius: f32,
+}
+
+impl PrimitiveInstance {
+    #[inline(always)]
+    pub fn glyph(glyph_index: TouchedGlyphIndex, color: u32, x: f32, y: f32) -> Self {
+        let packed = glyph_index.as_u32() | ((PrimitiveType::Glyph as u32) << 30);
+        Self {
+            packed,
+            color,
+            x,
+            y,
+        }
+    }
+
+    #[inline(always)]
+    pub fn rect(rect_index: u32, color: u32, x: f32, y: f32) -> Self {
+        let packed = rect_index | ((PrimitiveType::Rect as u32) << 30);
+        Self {
+            packed,
+            color,
+            x,
+            y,
+        }
+    }
 }