]> git.nega.tv - josh/narcissus/commitdiff
shark: Move ui into its own module
authorJoshua Simmons <josh@nega.tv>
Sun, 19 Oct 2025 19:03:02 +0000 (21:03 +0200)
committerJoshua Simmons <josh@nega.tv>
Sun, 19 Oct 2025 19:03:13 +0000 (21:03 +0200)
title/shark/src/draw.rs
title/shark/src/main.rs
title/shark/src/ui.rs [new file with mode: 0644]

index 05db1bacf284645346f322376aee58d28b9d348d..dad79a239aa603a54796f17301e7e2a4d4e93187 100644 (file)
@@ -767,6 +767,13 @@ impl<'gpu> DrawState<'gpu> {
                     microshades::PURPLE_RGBA_F32[3],
                 );
 
+                let glyph_buffer = gpu.request_transient_buffer_with_data(
+                    frame,
+                    thread_token,
+                    BufferUsageFlags::STORAGE,
+                    touched_glyphs,
+                );
+
                 let tile_buffer = gpu.request_transient_buffer(
                     frame,
                     thread_token,
@@ -778,27 +785,22 @@ impl<'gpu> DrawState<'gpu> {
                 );
                 let tile_buffer_address = gpu.get_buffer_address(tile_buffer.to_arg());
 
+                let draw_cmds = ui_state.draw_cmds();
                 let draw_buffer = gpu.request_transient_buffer_with_data(
                     frame,
                     thread_token,
                     BufferUsageFlags::STORAGE,
-                    ui_state.draw_cmds.as_slice(),
+                    draw_cmds,
                 );
 
-                let draw_buffer_len = ui_state.draw_cmds.len() as u32;
+                let draw_buffer_len = draw_cmds.len() as u32;
 
+                let scissors = ui_state.scissors();
                 let scissor_buffer = gpu.request_transient_buffer_with_data(
                     frame,
                     thread_token,
                     BufferUsageFlags::STORAGE,
-                    ui_state.scissors.as_slice(),
-                );
-
-                let glyph_buffer = gpu.request_transient_buffer_with_data(
-                    frame,
-                    thread_token,
-                    BufferUsageFlags::STORAGE,
-                    touched_glyphs,
+                    scissors,
                 );
 
                 const COARSE_BUFFER_LEN: usize = 1 << 20;
index 12a1e8a8e039a47d31f7834634aa345fc5b83dec..8632d5c9ec74c07c72056a32f0344202dfe47665 100644 (file)
@@ -1,23 +1,17 @@
-use std::fmt::Write;
 use std::time::{Duration, Instant};
 
-use draw::DrawState;
-use game::{Action, ActionEvent, GameState};
-use narcissus_core::Widen;
-
-use shark_shaders::pipelines::{Draw2dCmd, Draw2dScissor};
-
 use renderdoc_sys as rdoc;
 
-use fonts::{FontFamily, Fonts};
 use narcissus_app::{Event, Key, WindowDesc, create_app};
-use narcissus_core::default;
-use narcissus_font::{FontCollection, GlyphCache, HorizontalMetrics};
 use narcissus_gpu::{
     ColorSpace, ImageFormat, ImageUsageFlags, PresentMode, SwapchainConfigurator, SwapchainImage,
     ThreadToken, create_device,
 };
-use narcissus_maths::{Vec2, vec2};
+
+use draw::DrawState;
+use fonts::Fonts;
+use game::{Action, ActionEvent, GameState};
+use ui::UiState;
 
 mod draw;
 mod fonts;
@@ -25,175 +19,7 @@ mod game;
 mod helpers;
 pub mod microshades;
 mod spring;
-
-const GLYPH_CACHE_SIZE: usize = 1024;
-
-struct UiState<'a> {
-    fonts: &'a Fonts<'a>,
-    glyph_cache: GlyphCache<'a, Fonts<'a>>,
-
-    width: f32,
-    height: f32,
-    scale: f32,
-
-    tmp_string: String,
-
-    scissors: Vec<Draw2dScissor>,
-    scissor_stack: Vec<u32>,
-
-    draw_cmds: Vec<Draw2dCmd>,
-}
-
-#[allow(unused)]
-impl<'a> UiState<'a> {
-    fn new(fonts: &'a Fonts<'a>) -> Self {
-        let glyph_cache = GlyphCache::new(fonts, GLYPH_CACHE_SIZE, GLYPH_CACHE_SIZE, 1);
-
-        Self {
-            fonts,
-            glyph_cache,
-            width: 0.0,
-            height: 0.0,
-            scale: 1.0,
-            tmp_string: default(),
-            scissors: vec![],
-            scissor_stack: vec![],
-
-            draw_cmds: vec![],
-        }
-    }
-
-    fn begin_frame(&mut self, width: f32, height: f32, scale: f32) {
-        self.width = width;
-        self.height = height;
-        self.scale = scale;
-
-        self.draw_cmds.clear();
-
-        self.scissor_stack.clear();
-        self.scissors.clear();
-
-        // Scissor 0 is always the screen bounds.
-        self.scissors.push(Draw2dScissor {
-            offset_min: vec2(0.0, 0.0),
-            offset_max: vec2(width, height),
-        });
-    }
-
-    fn push_scissor(
-        &mut self,
-        mut offset_min: Vec2,
-        mut offset_max: Vec2,
-        intersect_with_current: bool,
-    ) {
-        if intersect_with_current {
-            let current_scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
-            let current_scissor = &self.scissors[current_scissor_index.widen()];
-            offset_min = Vec2::max(offset_min, current_scissor.offset_min);
-            offset_max = Vec2::min(offset_max, current_scissor.offset_max);
-        }
-
-        let scissor_index = self.scissors.len() as u32;
-        self.scissors.push(Draw2dScissor {
-            offset_min,
-            offset_max,
-        });
-        self.scissor_stack.push(scissor_index);
-    }
-
-    fn push_fullscreen_scissor(&mut self) {
-        // The fullscreen scissor is always at index 0
-        self.scissor_stack.push(0);
-    }
-
-    fn pop_scissor(&mut self) {
-        // It's invalid to pop more than we've pushed.
-        self.scissor_stack.pop().expect("unbalanced push / pop");
-    }
-
-    fn rect(
-        &mut self,
-        x: f32,
-        y: f32,
-        width: f32,
-        height: f32,
-        border_width: f32,
-        border_radii: [f32; 4],
-        border_color: u32,
-        background_color: u32,
-    ) {
-        let scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
-
-        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(
-            scissor_index,
-            vec2(x, y),
-            vec2(width, height),
-            border_width,
-            border_radii,
-            border_color,
-            background_color,
-        ))
-    }
-
-    fn text_fmt(
-        &mut self,
-        mut x: f32,
-        y: f32,
-        font_family: FontFamily,
-        font_size_px: f32,
-        color: u32,
-        args: std::fmt::Arguments,
-    ) -> f32 {
-        let scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
-
-        let font = self.fonts.font(font_family);
-        let font_size_px = font_size_px * self.scale;
-        let scale = font.scale_for_size_px(font_size_px);
-
-        let mut prev_index = None;
-
-        self.tmp_string.clear();
-        self.tmp_string.write_fmt(args).unwrap();
-
-        for c in self.tmp_string.chars() {
-            let glyph_index = font
-                .glyph_index(c)
-                .unwrap_or_else(|| font.glyph_index('□').unwrap());
-
-            let touched_glyph_index =
-                self.glyph_cache
-                    .touch_glyph(font_family, glyph_index, font_size_px);
-
-            let HorizontalMetrics {
-                advance_width,
-                left_side_bearing: _,
-            } = font.horizontal_metrics(glyph_index);
-
-            let advance = if let Some(prev_index) = prev_index {
-                font.kerning_advance(prev_index, glyph_index)
-            } else {
-                0.0
-            };
-            prev_index = Some(glyph_index);
-
-            x += advance * scale;
-
-            self.draw_cmds.push(Draw2dCmd::glyph(
-                scissor_index,
-                touched_glyph_index,
-                color,
-                vec2(x, y),
-            ));
-
-            x += advance_width * scale;
-        }
-
-        (font.ascent() - font.descent() + font.line_gap()) * scale
-    }
-}
+mod ui;
 
 pub fn main() {
     #[cfg(debug_assertions)]
@@ -366,49 +192,13 @@ pub fn main() {
             let draw_start = Instant::now();
             let tick_duration = draw_start - tick_start;
 
-            ui_state.begin_frame(
-                width as f32,
-                height as f32,
+            ui_state.draw(
+                width,
+                height,
                 ui_scale_override.unwrap_or(window_display_scale),
+                tick_duration,
             );
 
-            {
-                let width = width as f32;
-
-                let font_size_px = 20.0;
-
-                let font = ui_state.fonts.font(FontFamily::NotoSansJapanese);
-                let font_size_px_scaled = font_size_px * ui_state.scale;
-                let font_scale = font.scale_for_size_px(font_size_px_scaled);
-
-                let y = font.ascent() * font_scale;
-                let h = (font.ascent() - font.descent()) * font_scale;
-
-                ui_state.rect(0.0, 0.0, width, h, 0.0, [0.0; 4], 0x0, 0x40000000);
-
-                ui_state.text_fmt(
-                    10.0,
-                    y,
-                    FontFamily::NotoSansJapanese,
-                    font_size_px,
-                    0xffffffff,
-                    format_args!("tick: {:?}", tick_duration),
-                );
-
-                for i in 1..=3 {
-                    ui_state.rect(
-                        width - font_size_px_scaled * i as f32,
-                        0.0,
-                        font_size_px_scaled * 0.75,
-                        font_size_px_scaled * 0.75,
-                        4.0 * ui_state.scale,
-                        [5.0 * ui_state.scale; 4],
-                        microshades::GREEN_RGBA8[3].rotate_right(8),
-                        microshades::GRAY_RGBA8[0].rotate_right(8),
-                    );
-                }
-            }
-
             draw_state.draw(
                 thread_token,
                 frame,
diff --git a/title/shark/src/ui.rs b/title/shark/src/ui.rs
new file mode 100644 (file)
index 0000000..ecc7bbe
--- /dev/null
@@ -0,0 +1,229 @@
+use std::{fmt::Write as _, time::Duration};
+
+use narcissus_core::{Widen as _, default};
+use narcissus_font::{FontCollection as _, GlyphCache, HorizontalMetrics};
+use narcissus_maths::{Vec2, vec2};
+use shark_shaders::pipelines::{Draw2dCmd, Draw2dScissor};
+
+use crate::{
+    fonts::{FontFamily, Fonts},
+    microshades,
+};
+
+const GLYPH_CACHE_SIZE: usize = 1024;
+
+pub struct UiState<'a> {
+    fonts: &'a Fonts<'a>,
+    pub glyph_cache: GlyphCache<'a, Fonts<'a>>,
+
+    width: f32,
+    height: f32,
+    scale: f32,
+
+    tmp_string: String,
+
+    scissors: Vec<Draw2dScissor>,
+    scissor_stack: Vec<u32>,
+
+    draw_cmds: Vec<Draw2dCmd>,
+}
+
+#[allow(unused)]
+impl<'a> UiState<'a> {
+    pub fn new(fonts: &'a Fonts<'a>) -> Self {
+        let glyph_cache = GlyphCache::new(fonts, GLYPH_CACHE_SIZE, GLYPH_CACHE_SIZE, 1);
+
+        Self {
+            fonts,
+            glyph_cache,
+            width: 0.0,
+            height: 0.0,
+            scale: 1.0,
+            tmp_string: default(),
+            scissors: vec![],
+            scissor_stack: vec![],
+
+            draw_cmds: vec![],
+        }
+    }
+
+    pub fn draw_cmds(&self) -> &[Draw2dCmd] {
+        &self.draw_cmds
+    }
+
+    pub fn scissors(&self) -> &[Draw2dScissor] {
+        &self.scissors
+    }
+
+    fn begin_frame(&mut self, width: f32, height: f32, scale: f32) {
+        self.width = width;
+        self.height = height;
+        self.scale = scale;
+
+        self.draw_cmds.clear();
+
+        self.scissor_stack.clear();
+        self.scissors.clear();
+
+        // Scissor 0 is always the screen bounds.
+        self.scissors.push(Draw2dScissor {
+            offset_min: vec2(0.0, 0.0),
+            offset_max: vec2(width, height),
+        });
+    }
+
+    fn push_scissor(
+        &mut self,
+        mut offset_min: Vec2,
+        mut offset_max: Vec2,
+        intersect_with_current: bool,
+    ) {
+        if intersect_with_current {
+            let current_scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
+            let current_scissor = &self.scissors[current_scissor_index.widen()];
+            offset_min = Vec2::max(offset_min, current_scissor.offset_min);
+            offset_max = Vec2::min(offset_max, current_scissor.offset_max);
+        }
+
+        let scissor_index = self.scissors.len() as u32;
+        self.scissors.push(Draw2dScissor {
+            offset_min,
+            offset_max,
+        });
+        self.scissor_stack.push(scissor_index);
+    }
+
+    fn push_fullscreen_scissor(&mut self) {
+        // The fullscreen scissor is always at index 0
+        self.scissor_stack.push(0);
+    }
+
+    fn pop_scissor(&mut self) {
+        // It's invalid to pop more than we've pushed.
+        self.scissor_stack.pop().expect("unbalanced push / pop");
+    }
+
+    fn rect(
+        &mut self,
+        x: f32,
+        y: f32,
+        width: f32,
+        height: f32,
+        border_width: f32,
+        border_radii: [f32; 4],
+        border_color: u32,
+        background_color: u32,
+    ) {
+        let scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
+
+        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(
+            scissor_index,
+            vec2(x, y),
+            vec2(width, height),
+            border_width,
+            border_radii,
+            border_color,
+            background_color,
+        ))
+    }
+
+    fn text_fmt(
+        &mut self,
+        mut x: f32,
+        y: f32,
+        font_family: FontFamily,
+        font_size_px: f32,
+        color: u32,
+        args: std::fmt::Arguments,
+    ) -> f32 {
+        let scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
+
+        let font = self.fonts.font(font_family);
+        let font_size_px = font_size_px * self.scale;
+        let scale = font.scale_for_size_px(font_size_px);
+
+        let mut prev_index = None;
+
+        self.tmp_string.clear();
+        self.tmp_string.write_fmt(args).unwrap();
+
+        for c in self.tmp_string.chars() {
+            let glyph_index = font
+                .glyph_index(c)
+                .unwrap_or_else(|| font.glyph_index('□').unwrap());
+
+            let touched_glyph_index =
+                self.glyph_cache
+                    .touch_glyph(font_family, glyph_index, font_size_px);
+
+            let HorizontalMetrics {
+                advance_width,
+                left_side_bearing: _,
+            } = font.horizontal_metrics(glyph_index);
+
+            let advance = if let Some(prev_index) = prev_index {
+                font.kerning_advance(prev_index, glyph_index)
+            } else {
+                0.0
+            };
+            prev_index = Some(glyph_index);
+
+            x += advance * scale;
+
+            self.draw_cmds.push(Draw2dCmd::glyph(
+                scissor_index,
+                touched_glyph_index,
+                color,
+                vec2(x, y),
+            ));
+
+            x += advance_width * scale;
+        }
+
+        (font.ascent() - font.descent() + font.line_gap()) * scale
+    }
+
+    pub fn draw(&mut self, width: u32, height: u32, scale: f32, tick_duration: Duration) {
+        self.begin_frame(width as f32, height as f32, scale);
+
+        {
+            let width = width as f32;
+
+            let font_size_px = 20.0;
+
+            let font = self.fonts.font(FontFamily::NotoSansJapanese);
+            let font_size_px_scaled = font_size_px * self.scale;
+            let font_scale = font.scale_for_size_px(font_size_px_scaled);
+
+            let y = font.ascent() * font_scale;
+            let h = (font.ascent() - font.descent()) * font_scale;
+
+            self.rect(0.0, 0.0, width, h, 0.0, [0.0; 4], 0x0, 0x40000000);
+
+            self.text_fmt(
+                10.0,
+                y,
+                FontFamily::NotoSansJapanese,
+                font_size_px,
+                0xffffffff,
+                format_args!("tick: {:?}", tick_duration),
+            );
+
+            for i in 1..=3 {
+                self.rect(
+                    width - font_size_px_scaled * i as f32,
+                    0.0,
+                    font_size_px_scaled * 0.75,
+                    font_size_px_scaled * 0.75,
+                    4.0 * self.scale,
+                    [5.0 * self.scale; 4],
+                    microshades::GREEN_RGBA8[3].rotate_right(8),
+                    microshades::GRAY_RGBA8[0].rotate_right(8),
+                );
+            }
+        }
+    }
+}