Commit: 496fbce8d1966ad4cc434913767a5a3df6fb111d
Parent: 9848525e35a25ff68b6da1157ade4de6e673580e
Author: Randy Palamar
Date:   Sat,  9 Aug 2025 12:57:53 -0600
ui/lib: add save file name tag field to live parameters
Diffstat:
4 files changed, 95 insertions(+), 21 deletions(-)
diff --git a/beamformer_parameters.h b/beamformer_parameters.h
@@ -147,7 +147,8 @@ enum {BEAMFORMER_CONSTANTS_LIST};
 	X(beamform_plane,        int32_t,     , 1, int,     , "/* Plane to Beamform in TPW/VLS/HERCULES */")                        \
 	X(f_number,              float,       , 1, float,   , "/* F# (set to 0 to disable) */")                                     \
 	X(interpolate,           uint32_t,    , 1, bool,    , "/* Perform Cubic Interpolation of RF Samples */")                    \
-	X(coherency_weighting,   uint32_t,    , 1, bool,    , "/* Apply coherency weighting to output data */")
+	X(coherency_weighting,   uint32_t,    , 1, bool,    , "/* Apply coherency weighting to output data */")                     \
+	X(decimation_rate,       uint32_t,    , 1, uint,    , "/* Number of times to decimate */")
 
 #define BEAMFORMER_PARAMS_HEAD \
 	X(xdc_transform,     float,    [16], 16, mat4,       , "/* IMPORTANT: column major order */")                                      \
@@ -160,7 +161,6 @@ enum {BEAMFORMER_CONSTANTS_LIST};
 	X(time_offset,       float,        ,  1, float,      , "/* pulse length correction time [s] */")
 
 #define BEAMFORMER_PARAMS_TAIL \
-	X(decimation_rate,  uint32_t, , 1, uint, , "/* Number of times to decimate */")         \
 	X(readi_group_id,   uint32_t, , 1, uint, , "/* Which readi group this data is from */") \
 	X(readi_group_size, uint32_t, , 1, uint, , "/* Size of readi transmit group */")
 
@@ -191,16 +191,19 @@ _Static_assert((sizeof(BeamformerParameters) & 15) == 0, "UBO size must be a mul
 	X(TransmitPower,     1) \
 	X(TGCControlPoints,  2) \
 	X(SaveData,          3) \
-	X(StopImaging,       4)
+	X(SaveNameTag,       4) \
+	X(StopImaging,       5)
 /* NOTE(rnp): if this exceeds 32 you need to fix the flag handling code */
 
 #define BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST \
-	X(active,              uint32_t, ,                               1) \
-	X(save_enabled,        uint32_t, ,                               1) \
-	X(save_active,         uint32_t, ,                               1) \
-	X(transmit_power,      float,    ,                               1) \
-	X(image_plane_offsets, float,    [BeamformerViewPlaneTag_Count], BeamformerViewPlaneTag_Count) \
-	X(tgc_control_points,  float,    [8],                            8)
+	X(active,               uint32_t, ,                               1)   \
+	X(save_enabled,         uint32_t, ,                               1)   \
+	X(save_active,          uint32_t, ,                               1)   \
+	X(transmit_power,       float,    ,                               1)   \
+	X(image_plane_offsets,  float,    [BeamformerViewPlaneTag_Count], BeamformerViewPlaneTag_Count) \
+	X(tgc_control_points,   float,    [8],                            8)   \
+	X(save_name_tag_length, int32_t,  ,                               1)   \
+	X(save_name_tag,        char,     [128],                          128)
 
 #define X(name, type, size, ...) type name size;
 typedef struct {BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST} BeamformerLiveImagingParameters;
diff --git a/beamformer_shared_memory.c b/beamformer_shared_memory.c
@@ -1,5 +1,5 @@
 /* See LICENSE for license details. */
-#define BEAMFORMER_SHARED_MEMORY_VERSION (12UL)
+#define BEAMFORMER_SHARED_MEMORY_VERSION (13UL)
 
 typedef struct BeamformerFrame     BeamformerFrame;
 typedef struct ShaderReloadContext ShaderReloadContext;
@@ -41,9 +41,7 @@ typedef struct {
 	u32 size;
 } BeamformerExportContext;
 
-/* TODO(rnp): remove the None lock */
 #define BEAMFORMER_SHARED_MEMORY_LOCKS \
-	X(None)            \
 	X(ScratchSpace)    \
 	X(UploadRF)        \
 	X(ExportSync)      \
@@ -86,7 +84,7 @@ typedef struct {
 } BeamformWorkQueue;
 
 #define BEAMFORMER_SHARED_MEMORY_SIZE             (GB(2))
-#define BEAMFORMER_SHARED_MEMORY_MIN_SCRATCH_SIZE (BEAMFORMER_SHARED_MEMORY_SIZE - \
+#define BEAMFORMER_SHARED_MEMORY_MAX_SCRATCH_SIZE (BEAMFORMER_SHARED_MEMORY_SIZE - \
                                                    sizeof(BeamformerSharedMemory) - \
                                                    sizeof(BeamformerParameterBlock))
 
diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c
@@ -560,7 +560,7 @@ beamform_data_synchronized(void *data, u32 data_size, i32 output_points[3], f32 
 b32
 beamformer_compute_timings(BeamformerComputeStatsTable *output, i32 timeout_ms)
 {
-	static_assert(sizeof(*output) <= BEAMFORMER_SHARED_MEMORY_MIN_SCRATCH_SIZE,
+	static_assert(sizeof(*output) <= BEAMFORMER_SHARED_MEMORY_MAX_SCRATCH_SIZE,
 	              "timing table size exceeds scratch space");
 
 	b32 result = 0;
diff --git a/ui.c b/ui.c
@@ -1,5 +1,30 @@
 /* See LICENSE for license details. */
 /* TODO(rnp):
+ * [ ]: refactor: ui kind of needs to be mostly thrown away
+ *      - want all drawing to be immediate mode
+ *      - only layout information should be retained
+ *        - leaf nodes of layout have a kind associated which
+ *          instructs the builder code on how to build the view
+ *      - ui items (currently called Variables) are stored in a hash
+ *        table and are looked up for state information at frame building time
+ *        - removed/recycled when last building frame index is less than drawing frame index
+ *      - building:
+ *        - loop over tiled layout tree and floating layout tree as is currently done
+ *          - this will build current frame ui draw tree
+ *        - for each view use a stack structure with grouping, similar to how tables are made
+ *          - more general though: sub groups contain a draw axis (x or y)
+ *        - each ui item gets looked up in the hash table for previous frame drawing info
+ *          - this can then be used to construct a "ui comm" which contains relevant info
+ *            about how that ui item is being interacted with
+ *      - drawing:
+ *        - must be separated into layout constraint solving and rendering
+ *        - layout constraint solving handles sizing, clipping, etc.
+ *          - this will need multiple passes per subgroup to allow for autosizing
+ *          - pay attention to the fixed size points in the hierarchy. fixed size
+ *            items are complete once their children are complete.
+ *        - rendering simply uses the rect/clipping regions produced by layout
+ *          to send draw commands
+ * [ ]: bug: resizing live view causes texture to jump around
  * [ ]: bug: group at end of parameter listing
  * [ ]: refactor: ui should be in its own thread and that thread should only be concerned with the ui
  * [ ]: refactor: remove all the excessive measure_texts (cell drawing, hover_interaction in params table)
@@ -76,9 +101,10 @@ typedef struct BeamformerUI BeamformerUI;
 typedef struct Variable     Variable;
 
 typedef struct {
-	u8   buf[64];
+	u8   buf[128];
 	i32  count;
 	i32  cursor;
+	b32  numeric;
 	UIBlinker cursor_blink;
 	Font *font, *hot_font;
 	Variable *container;
@@ -159,6 +185,7 @@ typedef enum {
 	VT_COMPUTE_STATS_VIEW,
 	VT_COMPUTE_PROGRESS_BAR,
 	VT_LIVE_CONTROLS_VIEW,
+	VT_LIVE_CONTROLS_STRING,
 	VT_SCALE_BAR,
 	VT_UI_BUTTON,
 	VT_UI_MENU,
@@ -339,8 +366,10 @@ struct BeamformerLiveControlsView {
 	Variable tgc_control_points[countof(((BeamformerLiveImagingParameters *)0)->tgc_control_points)];
 	Variable save_button;
 	Variable stop_button;
+	Variable save_text;
 	UIBlinker save_button_blink;
 	u32      hot_field_flag;
+	u32      active_field_flag;
 };
 
 typedef enum {
@@ -595,6 +624,11 @@ stream_append_variable(Stream *s, Variable *var)
 		if (var->cycler.labels) stream_append_s8(s, var->cycler.labels[index]);
 		else                    stream_append_u64(s, index);
 	}break;
+	case VT_LIVE_CONTROLS_STRING:{
+		BeamformerLiveImagingParameters *lip = var->generic;
+		stream_append_s8(s, (s8){.data = (u8 *)lip->save_name_tag, .len = lip->save_name_tag_length});
+		if (lip->save_name_tag_length <= 0) stream_append_s8(s, s8("Tag..."));
+	}break;
 	InvalidDefaultCase;
 	}
 }
@@ -1390,6 +1424,10 @@ add_live_controls_view(BeamformerUI *ui, Variable *parent, Arena *arena)
 	              VT_CYCLER, ui->small_font);
 	fill_variable_cycler(&lv->save_button, &lip->save_active, save_labels, countof(save_labels));
 
+	fill_variable(&lv->save_text, view, s8(""), V_INPUT|V_TEXT|V_LIVE_CONTROL,
+	              VT_LIVE_CONTROLS_STRING, ui->small_font);
+	lv->save_text.generic = lip;
+
 	return result;
 }
 
@@ -2790,14 +2828,13 @@ draw_compute_stats_view(BeamformerUI *ui, Arena arena, Variable *view, Rect r, v
 }
 
 function v2
-draw_live_controls_view(BeamformerUI *ui, Variable *var, Rect r, v2 mouse)
+draw_live_controls_view(BeamformerUI *ui, Variable *var, Rect r, v2 mouse, Arena arena)
 {
 	BeamformerSharedMemory          *sm  = ui->shared_memory.region;
 	BeamformerLiveImagingParameters *lip = &sm->live_imaging_parameters;
 	BeamformerLiveControlsView      *lv  = var->generic;
 
 	TextSpec text_spec = {.font = &ui->font, .colour = FG_COLOUR, .flags = TF_LIMITED};
-	text_spec.limits.size.w = r.size.w;
 
 	v2 slider_size = {{MIN(140.0f, r.size.w), (f32)ui->font.baseSize}};
 	v2 button_size = {{MIN(r.size.w, slider_size.x + (f32)ui->font.baseSize), (f32)ui->font.baseSize * 1.5f}};
@@ -2806,6 +2843,8 @@ draw_live_controls_view(BeamformerUI *ui, Variable *var, Rect r, v2 mouse)
 	f32 slider_off = r.pos.x + 0.5f * (r.size.w - slider_size.w);
 	f32 button_off = r.pos.x + 0.5f * (r.size.w - button_size.w);
 
+	text_spec.limits.size.w = r.size.w - (text_off - r.pos.x);
+
 	v2 at = {{text_off, r.pos.y}};
 
 	v4 hsv_power_slider = {{0.35f * ease_in_out_cubic(1.0f - lip->transmit_power), 0.65f, 0.65f, 1}};
@@ -2840,10 +2879,24 @@ draw_live_controls_view(BeamformerUI *ui, Variable *var, Rect r, v2 mouse)
 		v4 border_colour = BORDER_COLOUR;
 		if (active) border_colour = v4_lerp(BORDER_COLOUR, FOCUSED_COLOUR, ease_in_out_cubic(save_t));
 
+		at.x  = text_off;
+		at.y += draw_text(s8("File Tag:"), at, &text_spec).y;
+		at.x += (f32)text_spec.font->baseSize / 2;
+		text_spec.limits.size.w -= (f32)text_spec.font->baseSize;
+
+		v4 save_text_colour = FG_COLOUR;
+		if (lip->save_name_tag_length <= 0)
+			save_text_colour.a = 0.6f;
+		at.y += draw_variable(ui, arena, &lv->save_text, at, mouse, save_text_colour, text_spec).y;
+		text_spec.limits.size.w += (f32)text_spec.font->baseSize;
+
+		at.x  = button_off;
 		at.y += (f32)ui->font.baseSize * 0.25f;
 		at.y += draw_fancy_button(ui, &lv->save_button, label, (Rect){.pos = at, .size = button_size},
 		                          border_colour, mouse, text_spec).y;
 
+		if (interaction_is_hot(ui, auto_interaction(r, &lv->save_text)))
+			lv->hot_field_flag = BeamformerLiveImagingDirtyFlags_SaveNameTag;
 		if (interaction_is_hot(ui, auto_interaction(r, &lv->save_button)))
 			lv->hot_field_flag = BeamformerLiveImagingDirtyFlags_SaveData;
 	}
@@ -3060,7 +3113,7 @@ draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec tex
 			r.pos.y += 0.5f * (r.size.h - view->rect.size.h);
 		BeamformerSharedMemory *sm = ui->shared_memory.region;
 		if (sm->live_imaging_parameters.active)
-			size = draw_live_controls_view(ui, var, r, mouse);
+			size = draw_live_controls_view(ui, var, r, mouse, ui->arena);
 	}break;
 	InvalidDefaultCase;
 	}
@@ -3254,6 +3307,13 @@ begin_text_input(InputState *is, Rect r, Variable *container, v2 mouse)
 	is->count = s.widx;
 	is->container = container;
 
+	is->numeric = container->view.child->type != VT_LIVE_CONTROLS_STRING;
+	if (container->view.child->type == VT_LIVE_CONTROLS_STRING) {
+		BeamformerLiveImagingParameters *lip = container->view.child->generic;
+		if (lip->save_name_tag_length <= 0)
+			is->count = 0;
+	}
+
 	/* NOTE: extra offset to help with putting a cursor at idx 0 */
 	f32 text_half_char_width = 10.0f;
 	f32 hover_p = CLAMP01((mouse.x - r.pos.x) / r.size.w);
@@ -3272,7 +3332,8 @@ begin_text_input(InputState *is, Rect r, Variable *container, v2 mouse)
 function void
 end_text_input(InputState *is, Variable *var)
 {
-	f32 value = (f32)parse_f64((s8){.len = is->count, .data = is->buf});
+	f32 value = 0;
+	if (is->numeric) value = (f32)parse_f64((s8){.len = is->count, .data = is->buf});
 
 	switch (var->type) {
 	case VT_SCALED_F32:{ var->scaled_real32.val = value; }break;
@@ -3282,6 +3343,11 @@ end_text_input(InputState *is, Variable *var)
 		*bv->store = CLAMP(value / bv->display_scale, bv->limits.x, bv->limits.y);
 		var->hover_t = 0;
 	}break;
+	case VT_LIVE_CONTROLS_STRING:{
+		BeamformerLiveImagingParameters *lip = var->generic;
+		mem_copy(lip->save_name_tag, is->buf, (uz)is->count % countof(lip->save_name_tag));
+		lip->save_name_tag_length = is->count % countof(lip->save_name_tag);
+	}break;
 	InvalidDefaultCase;
 	}
 }
@@ -3301,7 +3367,7 @@ update_text_input(InputState *is, Variable *var)
 	     is->count < countof(is->buf) && key > 0;
 	     key = GetCharPressed())
 	{
-		b32 allow_key = (BETWEEN(key, '0', '9') || (key == '.') ||
+		b32 allow_key = !is->numeric || (BETWEEN(key, '0', '9') || (key == '.') ||
 		                 (key == '-' && is->cursor == 0));
 		if (allow_key) {
 			mem_move(is->buf + is->cursor + 1,
@@ -3543,6 +3609,7 @@ ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll)
 				else        hot.kind = InteractionKind_Set;
 			}break;
 			case VT_BEAMFORMER_VARIABLE:
+			case VT_LIVE_CONTROLS_STRING:
 			case VT_F32:
 			case VT_SCALED_F32:
 			{
@@ -3564,6 +3631,12 @@ ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll)
 
 		ui->interaction = hot;
 
+		if (ui->interaction.var->flags & V_LIVE_CONTROL) {
+			assert(ui->interaction.var->parent->type == VT_LIVE_CONTROLS_VIEW);
+			BeamformerLiveControlsView *lv = ui->interaction.var->parent->generic;
+			lv->active_field_flag = lv->hot_field_flag;
+		}
+
 		if (ui->interaction.var->flags & V_HIDES_CURSOR) {
 			HideCursor();
 			DisableCursor();
@@ -3614,7 +3687,7 @@ ui_live_control_update(BeamformerUI *ui, Variable *controls)
 	assert(controls->type == VT_LIVE_CONTROLS_VIEW);
 	BeamformerSharedMemory *sm = ui->shared_memory.region;
 	BeamformerLiveControlsView *lv = controls->generic;
-	atomic_or_u32(&sm->live_imaging_dirty_flags, lv->hot_field_flag);
+	atomic_or_u32(&sm->live_imaging_dirty_flags, lv->active_field_flag);
 }
 
 function void