From 50627ba5a2559afca3da53761f2214aa21685a50 Mon Sep 17 00:00:00 2001 From: Josh Simmons Date: Thu, 14 Nov 2024 21:13:20 +0100 Subject: [PATCH] shark-shaders: Draw the rest of the rectangles --- title/shark-shaders/shaders/draw_2d.h | 34 ++++---- .../shaders/draw_2d_bin_1_scatter.comp | 18 +++-- .../shaders/draw_2d_bin_3_resolve.comp | 24 +++--- .../shaders/draw_2d_rasterize.comp | 37 +++++++-- title/shark-shaders/shaders/sdf.h | 20 +++++ title/shark-shaders/src/pipelines.rs | 49 ++++++------ title/shark/src/main.rs | 77 ++++++++++++------- 7 files changed, 165 insertions(+), 94 deletions(-) create mode 100644 title/shark-shaders/shaders/sdf.h diff --git a/title/shark-shaders/shaders/draw_2d.h b/title/shark-shaders/shaders/draw_2d.h index f79e3dd..37af77f 100644 --- a/title/shark-shaders/shaders/draw_2d.h +++ b/title/shark-shaders/shaders/draw_2d.h @@ -25,35 +25,33 @@ struct Draw2dCmd { }; struct Draw2dCmdRect { - float border_width; - vec2 position; - vec2 half_extent; - uint background_color; + vec2 bounds_min; + vec2 bounds_max; + + uint border_radii; uint border_color; + + uint background_color; }; struct Draw2dCmdGlyph { - uint index; vec2 position; uint color; }; Draw2dCmdRect decode_rect(Draw2dCmd cmd) { - return Draw2dCmdRect( - uintBitsToFloat(cmd.words[0]), - vec2(uintBitsToFloat(cmd.words[1]), uintBitsToFloat(cmd.words[2])), - vec2(uintBitsToFloat(cmd.words[3]), uintBitsToFloat(cmd.words[4])), - cmd.words[5], - cmd.words[6] - ); + Draw2dCmdRect rect = { + { uintBitsToFloat(cmd.words[0]), uintBitsToFloat(cmd.words[1]) }, // bounds_min + { uintBitsToFloat(cmd.words[2]), uintBitsToFloat(cmd.words[3]) }, // bounds_max + cmd.words[4], // border_radii + cmd.words[5], // border_color + cmd.words[6], // background_color + }; + return rect; } -Draw2dCmdGlyph decode_glyph(Draw2dCmd cmd) { - return Draw2dCmdGlyph( - cmd.packed_type & 0xffffff, - vec2(uintBitsToFloat(cmd.words[0]), uintBitsToFloat(cmd.words[1])), - cmd.words[2] - ); +Draw2dCmdGlyph decode_glyph(in Draw2dCmd cmd) { + return Draw2dCmdGlyph(vec2(uintBitsToFloat(cmd.words[0]), uintBitsToFloat(cmd.words[1])), cmd.words[2]); } layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Draw2dCommandRef diff --git a/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp b/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp index c9cfbb6..ad4ad57 100644 --- a/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp +++ b/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp @@ -46,21 +46,23 @@ void main() { vec2 cmd_min = vec2(99999.9); vec2 cmd_max = vec2(-99999.9); if (in_bounds) { - const Draw2dCmd cmd = constants.draw_buffer.values[draw_index]; - const uint cmd_type = cmd.packed_type >> 24; + const uint packed_type = constants.draw_buffer.values[draw_index].packed_type; + const uint cmd_type = packed_type >> 24; + const uint cmd_packed = packed_type & 0xffffff; + for (;;) { const uint scalar_type = subgroupBroadcastFirst(cmd_type); [[branch]] if (scalar_type == cmd_type) { switch (scalar_type) { case DRAW_2D_CMD_RECT: - const Draw2dCmdRect cmd_rect = decode_rect(cmd); - cmd_min = cmd_rect.position - cmd_rect.half_extent - cmd_rect.border_width; - cmd_max = cmd_rect.position + cmd_rect.half_extent + cmd_rect.border_width; + const Draw2dCmdRect cmd_rect = decode_rect(constants.draw_buffer.values[draw_index]); + cmd_min = cmd_rect.bounds_min; + cmd_max = cmd_rect.bounds_max; break; case DRAW_2D_CMD_GLYPH: - const Draw2dCmdGlyph cmd_glyph = decode_glyph(cmd); - const Glyph glyph = constants.glyph_buffer.values[cmd_glyph.index]; + const Draw2dCmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[draw_index]); + const Glyph glyph = constants.glyph_buffer.values[cmd_packed]; cmd_min = cmd_glyph.position + glyph.offset_min; cmd_max = cmd_glyph.position + glyph.offset_max; break; @@ -73,7 +75,7 @@ void main() { // For any out-of-bounds draws, we'll get the defaults of 99999.9 and -99999.9, which will fail // here. Out-of-bounds draws are therefore off-screen. Well, so long as you don't have 27 4k // monitors arranged horizontally. - const bool offscreen = any(greaterThan(cmd_min, constants.screen_resolution)) || any(lessThan(cmd_max, vec2(0.0))); + const bool offscreen = any(greaterThanEqual(cmd_min, cmd_max)) || any(greaterThan(cmd_min, constants.screen_resolution)) || any(lessThan(cmd_max, vec2(0.0))); // Are all draws off-screen? if (subgroupAll(offscreen)) { diff --git a/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp b/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp index bde745d..43fc0d9 100644 --- a/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp +++ b/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp @@ -84,8 +84,9 @@ void main() { vec2 cmd_min = vec2(99999.9); vec2 cmd_max = vec2(-99999.9); - const Draw2dCmd cmd = constants.draw_buffer.values[draw_index]; - const uint cmd_type = cmd.packed_type >> 24; + const uint packed_type = constants.draw_buffer.values[draw_index].packed_type; + const uint cmd_type = packed_type >> 24; + const uint cmd_packed = packed_type & 0xffffff; for (;;) { const uint scalar_type = subgroupBroadcastFirst(cmd_type); @@ -93,15 +94,20 @@ void main() { if (scalar_type == cmd_type) { switch (scalar_type) { case DRAW_2D_CMD_RECT: - const Draw2dCmdRect cmd_rect = decode_rect(cmd); - cmd_min = cmd_rect.position - cmd_rect.half_extent - cmd_rect.border_width; - cmd_max = cmd_rect.position + cmd_rect.half_extent + cmd_rect.border_width; - opaque_tile = all(greaterThanEqual(tile_min, cmd_min)) && all(lessThanEqual(tile_max, cmd_max)); - opaque_tile = opaque_tile && ((cmd_rect.background_color & 0xff000000) == 0xff000000); + const Draw2dCmdRect cmd_rect = decode_rect(constants.draw_buffer.values[draw_index]); + cmd_min = cmd_rect.bounds_min; + cmd_max = cmd_rect.bounds_max; + if ((cmd_rect.background_color & 0xff000000) == 0xff000000) { + const float border_width = float(cmd_packed & 0xff); + const vec4 border_radii = unpackUnorm4x8(cmd_rect.border_radii); + const float max_border_radius = max(border_radii.x, max(border_radii.y, max(border_radii.z, border_radii.w))) * 255.0; + const float shrink = (2.0 - sqrt(2.0)) * max_border_radius + (cmd_rect.border_color & 0xff000000) == 0xff000000 ? 0.0 : border_width; + opaque_tile = all(greaterThanEqual(tile_min, cmd_min + shrink)) && all(lessThanEqual(tile_max, cmd_max - shrink)); + } break; case DRAW_2D_CMD_GLYPH: - const Draw2dCmdGlyph cmd_glyph = decode_glyph(cmd); - const Glyph glyph = constants.glyph_buffer.values[cmd_glyph.index]; + const Draw2dCmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[draw_index]); + const Glyph glyph = constants.glyph_buffer.values[cmd_packed]; cmd_min = cmd_glyph.position + glyph.offset_min; cmd_max = cmd_glyph.position + glyph.offset_max; break; diff --git a/title/shark-shaders/shaders/draw_2d_rasterize.comp b/title/shark-shaders/shaders/draw_2d_rasterize.comp index d36bf76..4ba591c 100644 --- a/title/shark-shaders/shaders/draw_2d_rasterize.comp +++ b/title/shark-shaders/shaders/draw_2d_rasterize.comp @@ -12,6 +12,7 @@ #include "compute_bindings.h" #include "draw_2d.h" +#include "sdf.h" struct Draw2dRasterizeConstants { uvec2 screen_resolution; @@ -87,22 +88,42 @@ void main() { bitmap ^= bitmap & -bitmap; const uint base_index = (constants.coarse_buffer.values[i] & 0xffff) * 32; - const Draw2dCmd cmd = constants.draw_buffer.values[base_index + index]; - const uint cmd_type = cmd.packed_type >> 24; + const uint packed_type = constants.draw_buffer.values[base_index + index].packed_type; + const uint cmd_type = packed_type >> 24; + const uint cmd_packed = packed_type & 0xffffff; vec4 primitive_color = vec4(0.0); switch (cmd_type) { case DRAW_2D_CMD_RECT: - const Draw2dCmdRect cmd_rect = decode_rect(cmd); - const vec2 rect_min = cmd_rect.position - cmd_rect.half_extent - cmd_rect.border_width; - const vec2 rect_max = cmd_rect.position + cmd_rect.half_extent + cmd_rect.border_width; - if (all(greaterThanEqual(sample_center, rect_min)) && all(lessThanEqual(sample_center, rect_max))) { + const Draw2dCmdRect cmd_rect = decode_rect(constants.draw_buffer.values[base_index + index]); + + const float border_width = float(cmd_packed & 0xff); + const vec4 border_radii = unpackUnorm4x8(cmd_rect.border_radii) * 255.0; + const float max_border_radius = max(border_radii.x, max(border_radii.y, max(border_radii.z, border_radii.w))); + const float shrink = (2.0 - sqrt(2.0)) * max_border_radius; + + if (all(greaterThan(sample_center, cmd_rect.bounds_min + border_width + shrink)) && all(lessThan(sample_center, cmd_rect.bounds_max - border_width - shrink))) { primitive_color = unpackUnorm4x8(cmd_rect.background_color).bgra; + } else { + const vec2 b = (cmd_rect.bounds_max - cmd_rect.bounds_min) / 2.0; + const vec2 p = cmd_rect.bounds_min + b - sample_center; + + float d; + if (all(equal(border_radii, vec4(0.0)))) { + d = sdf_box(p, b); + } else { + d = sdf_rounded_box(p, b, border_radii); + } + + const vec4 background_color = unpackUnorm4x8(cmd_rect.background_color).bgra; + const vec4 border_color = unpackUnorm4x8(cmd_rect.border_color).bgra; + primitive_color = mix(background_color, border_color, smoothstep(1.0, 0.0, 1.0 - d - border_width)); + primitive_color = mix(primitive_color, vec4(0), smoothstep(1.0, 0.0, 1.0 - d)); } break; case DRAW_2D_CMD_GLYPH: - const Draw2dCmdGlyph cmd_glyph = decode_glyph(cmd); - const Glyph glyph = constants.glyph_buffer.values[cmd_glyph.index]; + const Draw2dCmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[base_index + index]); + const Glyph glyph = constants.glyph_buffer.values[cmd_packed]; const vec2 glyph_min = cmd_glyph.position + glyph.offset_min; const vec2 glyph_max = cmd_glyph.position + glyph.offset_max; if (all(greaterThanEqual(sample_center, glyph_min)) && all(lessThanEqual(sample_center, glyph_max))) { diff --git a/title/shark-shaders/shaders/sdf.h b/title/shark-shaders/shaders/sdf.h new file mode 100644 index 0000000..a7b71c6 --- /dev/null +++ b/title/shark-shaders/shaders/sdf.h @@ -0,0 +1,20 @@ +#ifndef SDF_H +#define SDF_H + +// https://iquilezles.org/articles/distfunctions2d/ + +float sdf_box(in vec2 p, in vec2 b) +{ + const vec2 d = abs(p) - b; + return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); +} + +float sdf_rounded_box( in vec2 p, in vec2 b, in vec4 r ) +{ + r.xy = (p.x > 0.0) ? r.xy : r.zw; + r.x = (p.y > 0.0) ? r.x : r.y; + const vec2 q = abs(p) - b + r.x; + return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x; +} + +#endif \ No newline at end of file diff --git a/title/shark-shaders/src/pipelines.rs b/title/shark-shaders/src/pipelines.rs index 9809d0f..b3c314b 100644 --- a/title/shark-shaders/src/pipelines.rs +++ b/title/shark-shaders/src/pipelines.rs @@ -7,7 +7,7 @@ use narcissus_gpu::{ Sampler, SamplerAddressMode, SamplerDesc, SamplerFilter, ShaderDesc, ShaderStageFlags, SpecConstant, Topology, }; -use narcissus_maths::Mat4; +use narcissus_maths::{Mat4, Vec2}; pub const DRAW_2D_TILE_SIZE: u32 = 32; @@ -38,8 +38,7 @@ const _: () = assert!(std::mem::size_of::() == 32); #[derive(Clone, Copy)] struct CmdGlyph { packed: u32, - x: f32, - y: f32, + position: Vec2, color: u32, } @@ -48,27 +47,32 @@ const _: () = assert!(std::mem::size_of::() == 16); #[repr(C)] #[derive(Clone, Copy)] struct CmdRect { + /// 31 . . . 0 + /// tttt tttt 0000 0000 0000 0000 bbbb bbbb + /// + /// t: Type + /// b: Border width packed: u32, - border_width: f32, - x: f32, - y: f32, - half_extent_x: f32, - half_extent_y: f32, - background_color: u32, + + bounds_min: Vec2, + bounds_max: Vec2, + + border_radii: u32, border_color: u32, + + background_color: u32, } const _: () = assert!(std::mem::size_of::() == 32); impl Draw2dCmd { #[inline(always)] - pub fn glyph(touched_glyph_index: TouchedGlyphIndex, color: u32, x: f32, y: f32) -> Self { + pub fn glyph(touched_glyph_index: TouchedGlyphIndex, color: u32, position: Vec2) -> Self { Self { glyph: CmdGlyph { packed: (Draw2dCmdType::Glyph as u32) << 24 | (touched_glyph_index.as_u32() & 0xffffff), - x, - y, + position, color, }, } @@ -76,22 +80,19 @@ impl Draw2dCmd { #[inline(always)] pub fn rect( - x: f32, - y: f32, - half_extent_x: f32, - half_extent_y: f32, - border_width: f32, - background_color: u32, + bounds_min: Vec2, + bounds_max: Vec2, + border_width: u8, + border_radii: [u8; 4], border_color: u32, + background_color: u32, ) -> Self { Self { rect: CmdRect { - packed: (Draw2dCmdType::Rect as u32) << 24, - border_width, - x, - y, - half_extent_x, - half_extent_y, + packed: (Draw2dCmdType::Rect as u32) << 24 | border_width as u32, + bounds_min, + bounds_max, + border_radii: u32::from_ne_bytes(border_radii), background_color, border_color, }, diff --git a/title/shark/src/main.rs b/title/shark/src/main.rs index d0eed25..bfa9806 100644 --- a/title/shark/src/main.rs +++ b/title/shark/src/main.rs @@ -29,8 +29,8 @@ use narcissus_gpu::{ }; use narcissus_image as image; use narcissus_maths::{ - clamp, perlin_noise3, sin_cos_pi_f32, sin_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4, - Point3, Vec3, + clamp, perlin_noise3, sin_cos_pi_f32, sin_pi_f32, vec2, vec3, Affine3, Deg, HalfTurn, Mat3, + Mat4, Point3, Vec2, Vec3, }; use spring::simple_spring_damper_exact; @@ -469,20 +469,28 @@ impl<'a> UiState<'a> { } } - fn rect(&mut self, x: f32, y: f32, width: f32, height: f32, background_color: u32) { - 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; + fn rect( + &mut self, + position: Vec2, + bounds: Vec2, + border_width: f32, + border_radii: [f32; 4], + border_color: u32, + background_color: u32, + ) { + let bounds_min = position; + let bounds_max = position + bounds; + + let border_width = border_width.clamp(0.0, 255.0).floor() as u8; + let border_radii = border_radii.map(|radius| radius.clamp(0.0, 255.0).floor() as u8); self.draw_cmds.push(Draw2dCmd::rect( - center_x, - center_y, - half_extent_x, - half_extent_y, - 5.0, + bounds_min, + bounds_max, + border_width, + border_radii, + border_color, background_color, - 0xffff0000, )) } @@ -526,8 +534,11 @@ impl<'a> UiState<'a> { x += advance * scale; - self.draw_cmds - .push(Draw2dCmd::glyph(touched_glyph_index, 0xff0000ff, x, y)); + self.draw_cmds.push(Draw2dCmd::glyph( + touched_glyph_index, + microshades::GRAY_RGBA8[4].rotate_right(8), + vec2(x, y), + )); x += advance_width * scale; } @@ -1799,11 +1810,12 @@ pub fn main() { for _ in 0..100 { ui_state.rect( - 100.0, - 100.0, - width as f32 - 200.0, - height as f32 - 200.0, - 0x88008800, + vec2(100.0, 100.0), + vec2(1000.0, 1000.0), + 0.0, + [25.0; 4], + 0, + 0x88442211, ); } @@ -1821,16 +1833,27 @@ pub fn main() { } for i in 0..500 { - let (rect_x, rect_y) = sin_cos_pi_f32(game_state.time * 0.1 + i as f32 * 0.01); + let (rect_x, rect_y) = sin_cos_pi_f32(game_state.time * 0.1 + i as f32 * 1.01); ui_state.rect( - (width as f32 / 2.0) - rect_x * 1000.0, - (height as f32 / 2.0) - rect_y * 1000.0, - 500.0, - 500.0, - 0xff00ff00, + (vec2(width as f32, height as f32) / 2.0) + - 250.0 + - vec2(rect_x, rect_y) * 1000.0, + vec2(500.0, 500.0), + 10.0, + [rect_x * 50.0, rect_y * 50.0, 25.0, 25.0], + 0xffffffff, + microshades::BLUE_RGBA8[4].rotate_right(8), ); } - ui_state.rect(base_x * 60.0, base_y * 60.0, 100.0, 100.0, 0xffff0000); + + ui_state.rect( + vec2(base_x, base_y) * 60.0, + vec2(400.0, 400.0), + 0.0, + [0.0; 4], + 0, + microshades::ORANGE_RGBA8[2].rotate_right(8), + ); for i in 0..10 { if i & 1 != 0 { -- 2.49.0