Commit: 60bd6f8dcc0802f6abb4c107a0a74a6f994be86a
Parent: eced14ae419e51674f65e0346afb8babeea31d70
Author: Randy Palamar
Date: Tue, 12 Aug 2025 11:33:13 -0600
ui: visually display broken shader stage
When a shader is broken it is always because we are actively
making changes to it. It doesn't make sense to try and recover or
continue using the old shader.
closes #27
Diffstat:
M | beamformer.c | | | 12 | +++++------- |
M | math.c | | | 19 | ++++++++++++++++--- |
M | ui.c | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
3 files changed, 73 insertions(+), 26 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -926,13 +926,11 @@ DEBUG_EXPORT BEAMFORMER_RELOAD_SHADER_FN(beamformer_reload_shader)
link = link->link;
} while (link != src);
- u32 new_program = load_shader(&ctx->os, arena, shader_texts, shader_types, shader_count, shader_name);
- if (new_program) {
- glDeleteProgram(*src->shader);
- *src->shader = new_program;
- if (src->kind == BeamformerShaderKind_Render3D) ctx->frame_view_render_context.updated = 1;
- }
- return new_program != 0;
+ glDeleteProgram(*src->shader);
+ *src->shader = load_shader(&ctx->os, arena, shader_texts, shader_types, shader_count, shader_name);
+ if (src->kind == BeamformerShaderKind_Render3D) ctx->frame_view_render_context.updated = 1;
+
+ return 1;
}
function b32
diff --git a/math.c b/math.c
@@ -622,14 +622,27 @@ hsv_to_rgb(v4 hsv)
}
function f32
-ease_cubic(f32 t)
+ease_in_out_cubic(f32 t)
{
f32 result;
if (t < 0.5f) {
result = 4.0f * t * t * t;
} else {
- f32 c = -2.0f * t + 2.0f;
- result = 1.0f - c * c * c / 2.0f;
+ t = -2.0f * t + 2.0f;
+ result = 1.0f - t * t * t / 2.0f;
+ }
+ return result;
+}
+
+function f32
+ease_in_out_quartic(f32 t)
+{
+ f32 result;
+ if (t < 0.5f) {
+ result = 8.0f * t * t * t * t;
+ } else {
+ t = -2.0f * t + 2.0f;
+ result = 1.0f - t * t * t * t / 2.0f;
}
return result;
}
diff --git a/ui.c b/ui.c
@@ -20,7 +20,6 @@
* do a redraw on every refresh until completed.
* [ ]: show full non-truncated string on hover
* [ ]: refactor: hovered element type and show hovered element in full even when truncated
- * [ ]: visual indicator for broken shader stage gh#27
* [ ]: bug: cross-plane view with different dimensions for each plane
* [ ]: refactor: make table_skip_rows useful
* [ ]: refactor: better method of grouping variables for views such as FrameView/ComputeStatsView
@@ -68,6 +67,11 @@ typedef struct v2_sll {
v2 v;
} v2_sll;
+typedef struct {
+ f32 t;
+ f32 scale;
+} UIBlinker;
+
typedef struct BeamformerUI BeamformerUI;
typedef struct Variable Variable;
@@ -75,8 +79,7 @@ typedef struct {
u8 buf[64];
i32 count;
i32 cursor;
- f32 cursor_blink_t;
- f32 cursor_blink_scale;
+ UIBlinker cursor_blink;
Font *font, *hot_font;
Variable *container;
} InputState;
@@ -132,6 +135,7 @@ typedef struct {
ComputeShaderStats *compute_shader_stats;
Variable *cycler;
ComputeStatsViewKind kind;
+ UIBlinker blink;
} ComputeStatsView;
typedef struct {
@@ -335,8 +339,7 @@ struct BeamformerLiveControlsView {
Variable tgc_control_points[countof(((BeamformerLiveImagingParameters *)0)->tgc_control_points)];
Variable save_button;
Variable stop_button;
- f32 save_button_blink_t;
- f32 save_button_blink_scale;
+ UIBlinker save_button_blink;
u32 hot_field_flag;
};
@@ -506,6 +509,16 @@ typedef struct {
Rect cell_rect;
} TableIterator;
+function f32
+ui_blinker_update(UIBlinker *b, f32 scale)
+{
+ b->t += b->scale * dt_for_frame;
+ if (b->t >= 1.0f) b->scale = -scale;
+ if (b->t <= 0.0f) b->scale = scale;
+ f32 result = b->t;
+ return result;
+}
+
function v2
measure_glyph(Font font, u32 glyph)
{
@@ -2702,6 +2715,8 @@ draw_compute_stats_view(BeamformerUI *ui, Arena arena, Variable *view, Rect r, v
u32 stages = cp->pipeline.shader_count;
TextSpec text_spec = {.font = &ui->font, .colour = FG_COLOUR, .flags = TF_LIMITED};
+ ui_blinker_update(&csv->blink, BLINK_SPEED);
+
static_assert(BeamformerShaderKind_ComputeCount <= 32, "shader kind bitfield test");
u32 seen_shaders = 0;
for (u32 i = 0; i < stages; i++) {
@@ -2741,7 +2756,33 @@ draw_compute_stats_view(BeamformerUI *ui, Arena arena, Variable *view, Rect r, v
push_table_memory_size_row(table, &arena, s8("DAS RF Size:"), cp->rf_size);
result = v2_add(result, table_extent(table, arena, text_spec.font));
- draw_table(ui, arena, table, r, text_spec, (v2){0}, 0);
+
+ u32 row_index = 0;
+ u32 *programs = ui->beamformer_context->compute_context.programs;
+ TableIterator *it = table_iterator_new(table, TIK_ROWS, &arena, 0, r.pos, text_spec.font);
+ for (TableRow *row = table_iterator_next(it, &arena);
+ row;
+ row = table_iterator_next(it, &arena), row_index++)
+ {
+ Table *t = it->frame.table;
+ Rect cell_rect = it->cell_rect;
+ for (i32 column = 0; column < t->columns; column++) {
+ TableCell *cell = (TableCell *)it->row->data + column;
+ cell_rect.size.w = t->widths[column];
+ text_spec.limits.size.w = r.size.w - (cell_rect.pos.x - it->start_x);
+
+ if (column == 0 && row_index < stages && programs[cp->pipeline.shaders[row_index]] == 0) {
+ text_spec.colour = v4_lerp(FG_COLOUR, FOCUSED_COLOUR, ease_in_out_quartic(csv->blink.t));
+ } else {
+ text_spec.colour = FG_COLOUR;
+ }
+
+ draw_table_cell(ui, arena, cell, cell_rect, t->alignment[column], text_spec, mouse);
+
+ cell_rect.pos.x += cell_rect.size.w + t->cell_pad.w;
+ }
+ }
+
return result;
}
@@ -2764,7 +2805,7 @@ draw_live_controls_view(BeamformerUI *ui, Variable *var, Rect r, v2 mouse)
v2 at = {{text_off, r.pos.y}};
- v4 hsv_power_slider = {{0.35f * ease_cubic(1.0f - lip->transmit_power), 0.65f, 0.65f, 1}};
+ v4 hsv_power_slider = {{0.35f * ease_in_out_cubic(1.0f - lip->transmit_power), 0.65f, 0.65f, 1}};
at.y += draw_text(s8("Power:"), at, &text_spec).y;
at.x = slider_off;
at.y += draw_variable_slider(ui, &lv->transmit_power, (Rect){.pos = at, .size = slider_size},
@@ -2792,12 +2833,9 @@ draw_live_controls_view(BeamformerUI *ui, Variable *var, Rect r, v2 mouse)
b32 active = lip->save_active;
s8 label = lv->save_button.cycler.labels[active % lv->save_button.cycler.cycle_length];
- lv->save_button_blink_t += lv->save_button_blink_scale * dt_for_frame;
- if (lv->save_button_blink_t >= 1.0f) lv->save_button_blink_scale = -BLINK_SPEED;
- if (lv->save_button_blink_t <= 0.0f) lv->save_button_blink_scale = BLINK_SPEED;
-
+ f32 save_t = ui_blinker_update(&lv->save_button_blink, BLINK_SPEED);
v4 border_colour = BORDER_COLOUR;
- if (active) border_colour = v4_lerp(border_colour, FOCUSED_COLOUR, ease_cubic(lv->save_button_blink_t));
+ if (active) border_colour = v4_lerp(BORDER_COLOUR, FOCUSED_COLOUR, ease_in_out_cubic(save_t));
at.y += (f32)ui->font.baseSize * 0.25f;
at.y += draw_fancy_button(ui, &lv->save_button, label, (Rect){.pos = at, .size = button_size},
@@ -3160,7 +3198,7 @@ draw_floating_widgets(BeamformerUI *ui, Rect window_rect, v2 mouse)
cursor.size = (v2){{cursor_width, text_size.h}};
v4 cursor_colour = FOCUSED_COLOUR;
- cursor_colour.a = CLAMP01(is->cursor_blink_t);
+ cursor_colour.a = ease_in_out_cubic(is->cursor_blink.t);
v4 text_colour = v4_lerp(FG_COLOUR, HOVERED_COLOUR, fw->child->hover_t);
TextSpec input_text_spec = {.font = is->font, .colour = text_colour};
@@ -3250,9 +3288,7 @@ update_text_input(InputState *is, Variable *var)
{
assert(is->cursor != -1);
- is->cursor_blink_t += is->cursor_blink_scale * dt_for_frame;
- if (is->cursor_blink_t >= 1.0f) is->cursor_blink_scale = -BLINK_SPEED;
- if (is->cursor_blink_t <= 0.0f) is->cursor_blink_scale = BLINK_SPEED;
+ ui_blinker_update(&is->cursor_blink, BLINK_SPEED);
var->hover_t -= 2 * HOVER_SPEED * dt_for_frame;
var->hover_t = CLAMP01(var->hover_t);