ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | LICENSE

Commit: 9ea8b4b8c2929c87eb9f1868d9c95bb0c5d4295c
Parent: 02da4fed6562bd7dbeae96c969d8aaff270d0f6b
Author: Randy Palamar
Date:   Thu, 14 Nov 2024 16:18:24 -0700

decouple ui interaction from ui drawing

Diffstat:
Mbeamformer.c | 6+++++-
Mbeamformer.h | 71++++++++++++++++++++++++++++++++++++++++-------------------------------
Mmain_generic.c | 6++++++
Mstatic.c | 4----
Mui.c | 771+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mutil.c | 44++++++++++++++++++++++++++++++++++++++++++++
Mutil.h | 43++++++++++++++++++++++++++++++++++++++++++-
7 files changed, 573 insertions(+), 372 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -595,6 +595,10 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) ctx->window_size.w = GetScreenWidth(); } + if (input->executable_reloaded) { + ui_init(ctx, ctx->ui_backing_store); + } + /* NOTE: Store the compute time for the last frame. */ check_compute_timers(&ctx->csctx, &ctx->partial_compute_ctx, ctx->params); @@ -695,7 +699,7 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step) ctx->flags &= ~GEN_MIPMAPS; } - draw_ui(ctx, *arena); + draw_ui(ctx, input); if (IsKeyPressed(KEY_R)) { ctx->flags |= RELOAD_SHADERS; diff --git a/beamformer.h b/beamformer.h @@ -38,39 +38,51 @@ enum gl_vendor_ids { GL_VENDOR_NVIDIA, }; -enum modifiable_value_flags { - MV_FLOAT = 1 << 0, - MV_INT = 1 << 1, - MV_CAUSES_COMPUTE = 1 << 29, - MV_GEN_MIPMAPS = 1 << 30, -}; - -typedef struct BeamformerCtx BeamformerCtx; -typedef struct BPModifiableValue BPModifiableValue; - -#define BMV_STORE_FN(name) void name(BeamformerCtx *ctx, BPModifiableValue *bmv, f32 new_val, b32 from_scroll) -typedef BMV_STORE_FN(bmv_store_fn); -BMV_STORE_FN(bmv_store_fn_stub) {} - -typedef struct BPModifiableValue { - void *value; - bmv_store_fn *store_fn; - union {v2 flimits; iv2 ilimits;}; - u32 flags; - f32 scroll_scale; - f32 display_scale; -} BPModifiableValue; - typedef struct { u8 buf[64]; - BPModifiableValue store; i32 buf_len; i32 cursor; - f32 cursor_hover_p; f32 cursor_blink_t; - f32 cursor_blink_target; + f32 cursor_blink_scale; } InputState; +enum variable_flags { + V_CAUSES_COMPUTE = 1 << 29, + V_GEN_MIPMAPS = 1 << 30, +}; + +enum interaction_states { + IS_NONE, + IS_NOP, + IS_SET, + IS_DRAG, + IS_SCROLL, + IS_TEXT, + + IS_DISPLAY, + IS_SCALE_BAR, +}; + +typedef struct { + Variable hot; + Variable next_hot; + Variable active; + u32 hot_state; + u32 state; + b32 var_changed_last_frame; +} InteractionState; + +typedef struct { + TempArena frame_temporary_arena; + Arena arena_for_frame; + + Font font; + Font small_font; + + InteractionState interaction; + InputState text_input_state; +} BeamformerUI; + #define MAX_FRAMES_IN_FLIGHT 3 #define INIT_CUDA_CONFIGURATION_FN(name) void name(u32 *input_dims, u32 *decoded_dims, u16 *channel_mapping, b32 rx_cols) @@ -246,11 +258,8 @@ typedef struct BeamformerCtx { uv2 window_size; u32 flags; - /* UI Theming */ - Font font; - Font small_font; - - InputState is; + Arena ui_backing_store; + BeamformerUI *ui; BeamformFrame beamform_frames[MAX_BEAMFORMED_SAVED_FRAMES]; u32 displayed_frame_index; diff --git a/main_generic.c b/main_generic.c @@ -38,10 +38,14 @@ main(void) Arena temp_memory = os_alloc_arena((Arena){0}, 16 * MEGABYTE); ctx.error_stream = stream_alloc(&temp_memory, 1 * MEGABYTE); + ctx.ui_backing_store = sub_arena(&temp_memory, 2 * MEGABYTE); + Pipe data_pipe = os_open_named_pipe(OS_PIPE_NAME); input.pipe_handle = data_pipe.file; ASSERT(data_pipe.file != INVALID_FILE); + input.executable_reloaded = 1; + #define X(name) ctx.platform.name = os_ ## name; PLATFORM_FNS #undef X @@ -61,6 +65,8 @@ main(void) input.pipe_data_available = os_poll_pipe(data_pipe); beamformer_frame_step(&ctx, &temp_memory, &input); + + input.executable_reloaded = 0; } /* NOTE: make sure this will get cleaned up after external diff --git a/static.c b/static.c @@ -266,10 +266,6 @@ setup_beamformer(BeamformerCtx *ctx, Arena temp_memory) dump_gl_params(&ctx->gl, temp_memory); validate_gl_requirements(&ctx->gl); - /* TODO: build these into the binary */ - ctx->font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0); - ctx->small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0); - ctx->fsctx.db = -50.0f; ctx->fsctx.threshold = 40.0f; diff --git a/ui.c b/ui.c @@ -1,18 +1,4 @@ /* See LICENSE for license details. */ -static void -ui_start_compute(BeamformerCtx *ctx) -{ - /* NOTE: we do not allow ui to start a work if no work was previously completed */ - Arena a = {0}; - beamform_work_queue_push(ctx, &a, BW_RECOMPUTE); - for (u32 i = 0; i < ARRAY_COUNT(ctx->beamform_frames); i++) { - BeamformFrame *frame = ctx->beamform_frames + i; - if (frame->dim.w && frame->textures[frame->dim.w - 1]) - glClearTexImage(frame->textures[frame->dim.w - 1], 0, GL_RED, GL_FLOAT, 0); - } - ctx->params->upload = 1; -} - static Color colour_from_normalized(v4 rgba) { @@ -28,21 +14,6 @@ fade(Color a, f32 alpha) } static f32 -move_towards_f32(f32 current, f32 target, f32 delta) -{ - if (target < current) { - current -= delta; - if (current < target) - current = target; - } else { - current += delta; - if (current > target) - current = target; - } - return current; -} - -static f32 lerp(f32 a, f32 b, f32 t) { return a + t * (b - a); @@ -131,201 +102,6 @@ center_align_text_in_rect(Rect r, s8 text, Font font) } static b32 -bmv_equal(BPModifiableValue *a, BPModifiableValue *b) -{ - b32 result = (uintptr_t)a->value == (uintptr_t)b->value; - return result; -} - -static f32 -bmv_value(BPModifiableValue *a) -{ - /* TODO: this is broken for very large integer values */ - f32 result; - if (a->flags & MV_FLOAT) result = *(f32 *)a->value; - else result = *(i32 *)a->value; - return result; -} - -static void -bmv_store_common(BeamformerCtx *ctx, BPModifiableValue *bmv) -{ - if (bmv->flags & MV_CAUSES_COMPUTE) - ui_start_compute(ctx); - if (bmv->flags & MV_GEN_MIPMAPS) - ctx->flags |= GEN_MIPMAPS; -} - -static BMV_STORE_FN(bmv_store_generic) -{ - if (bmv->flags & MV_FLOAT) { - f32 *value = bmv->value; - if (new_val == *value) - return; - *value = CLAMP(new_val, bmv->flimits.x, bmv->flimits.y); - } else { - ASSERT(bmv->flags & MV_INT); - i32 *value = bmv->value; - if (new_val == *value) - return; - *value = CLAMP(new_val, bmv->ilimits.x, bmv->ilimits.y); - } - bmv_store_common(ctx, bmv); -} - -static BMV_STORE_FN(bmv_store_power_of_two) -{ - ASSERT(bmv->flags & MV_INT); - i32 *value = bmv->value; - if (new_val == *value) - return; - if (from_scroll && new_val > *value) *value <<= 1; - else *value = round_down_power_of_2(new_val); - *value = CLAMP(*value, bmv->ilimits.x, bmv->ilimits.y); - bmv_store_common(ctx, bmv); -} - -static void -bmv_sprint(BPModifiableValue *bmv, Stream *s) -{ - if (bmv->flags & MV_FLOAT) { - f32 *value = bmv->value; - stream_append_f64(s, *value * bmv->display_scale, 100); - } else { - ASSERT(bmv->flags & MV_INT); - i32 *value = bmv->value; - stream_append_i64(s, *value * bmv->display_scale); - } -} - -static void -do_text_input(BeamformerCtx *ctx, i32 max_disp_chars, Rect r, Color colour) -{ - v2 ts = measure_text(ctx->font, (s8){.len = ctx->is.buf_len, .data = ctx->is.buf}); - v2 pos = {.x = r.pos.x, .y = r.pos.y + (r.size.y - ts.y) / 2}; - - i32 buf_delta = ctx->is.buf_len - max_disp_chars; - if (buf_delta < 0) buf_delta = 0; - s8 buf = {.len = ctx->is.buf_len - buf_delta, .data = ctx->is.buf + buf_delta}; - { - /* NOTE: drop a char if the subtext still doesn't fit */ - v2 nts = measure_text(ctx->font, buf); - if (nts.w > 0.96 * r.size.w) { - buf.data++; - buf.len--; - } - } - draw_text(ctx->font, buf, pos, 0, colour); - - ctx->is.cursor_blink_t = move_towards_f32(ctx->is.cursor_blink_t, - ctx->is.cursor_blink_target, 1.5 * dt_for_frame); - if (ctx->is.cursor_blink_t == ctx->is.cursor_blink_target) { - if (ctx->is.cursor_blink_target == 0) ctx->is.cursor_blink_target = 1; - else ctx->is.cursor_blink_target = 0; - } - - v4 bg = FOCUSED_COLOUR; - bg.a = 0; - Color cursor_colour = colour_from_normalized(lerp_v4(bg, FOCUSED_COLOUR, - ctx->is.cursor_blink_t)); - - /* NOTE: guess a cursor position */ - if (ctx->is.cursor == -1) { - /* NOTE: extra offset to help with putting a cursor at idx 0 */ - #define TEXT_HALF_CHAR_WIDTH 10 - f32 x_off = TEXT_HALF_CHAR_WIDTH, x_bounds = r.size.w * ctx->is.cursor_hover_p; - i32 i; - for (i = 0; i < ctx->is.buf_len && x_off < x_bounds; i++) { - /* NOTE: assumes font glyphs are ordered ASCII */ - i32 idx = ctx->is.buf[i] - 0x20; - x_off += ctx->font.glyphs[idx].advanceX; - if (ctx->font.glyphs[idx].advanceX == 0) - x_off += ctx->font.recs[idx].width; - } - ctx->is.cursor = i; - } - - buf.len = ctx->is.cursor - buf_delta; - v2 sts = measure_text(ctx->font, buf); - f32 cursor_x = r.pos.x + sts.x; - f32 cursor_width; - if (ctx->is.cursor == ctx->is.buf_len) cursor_width = MIN(ctx->window_size.w * 0.03, 20); - else cursor_width = MIN(ctx->window_size.w * 0.01, 6); - - Rect cursor_r = { - .pos = {.x = cursor_x, .y = pos.y}, - .size = {.w = cursor_width, .h = ts.h}, - }; - - DrawRectanglePro(cursor_r.rl, (Vector2){0}, 0, cursor_colour); - - /* NOTE: handle multiple input keys on a single frame */ - i32 key = GetCharPressed(); - while (key > 0) { - if (ctx->is.buf_len == ARRAY_COUNT(ctx->is.buf)) - break; - - b32 allow_key = ((key >= '0' && key <= '9') || (key == '.') || - (key == '-' && ctx->is.cursor == 0)); - if (allow_key) { - mem_move(ctx->is.buf + ctx->is.cursor, - ctx->is.buf + ctx->is.cursor + 1, - ctx->is.buf_len - ctx->is.cursor + 1); - - ctx->is.buf[ctx->is.cursor++] = key; - ctx->is.buf_len++; - } - key = GetCharPressed(); - } - - if ((IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) && ctx->is.cursor > 0) - ctx->is.cursor--; - - if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) && - ctx->is.cursor < ctx->is.buf_len) - ctx->is.cursor++; - - if ((IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE)) && - ctx->is.cursor > 0) { - ctx->is.cursor--; - mem_move(ctx->is.buf + ctx->is.cursor + 1, - ctx->is.buf + ctx->is.cursor, - ctx->is.buf_len - ctx->is.cursor); - ctx->is.buf_len--; - } - if ((IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE)) && - ctx->is.cursor < ctx->is.buf_len) { - mem_move(ctx->is.buf + ctx->is.cursor + 1, - ctx->is.buf + ctx->is.cursor, - ctx->is.buf_len - ctx->is.cursor); - ctx->is.buf_len--; - } -} - -static void -set_text_input_idx(BeamformerCtx *ctx, BPModifiableValue bmv, Rect r, v2 mouse) -{ - if (ctx->is.store.value && !bmv_equal(&ctx->is.store, &bmv)) { - f32 new_val = parse_f64((s8){.len = ctx->is.buf_len, .data = ctx->is.buf}); - ctx->is.store.store_fn(ctx, &ctx->is.store, new_val / ctx->is.store.display_scale, 0); - } - - ctx->is.store = bmv; - ctx->is.cursor = -1; - - if (ctx->is.store.value == NULL) - return; - - Stream s = {.cap = ARRAY_COUNT(ctx->is.buf), .data = ctx->is.buf}; - bmv_sprint(&bmv, &s); - ASSERT(!s.errors); - ctx->is.buf_len = s.widx; - - ASSERT(CheckCollisionPointRec(mouse.rl, r.rl)); - ctx->is.cursor_hover_p = CLAMP01((mouse.x - r.pos.x) / r.size.w); -} - -static b32 hover_text(v2 mouse, Rect text_rect, f32 *hover_t, b32 can_advance) { b32 hovering = CheckCollisionPointRec(mouse.rl, text_rect.rl); @@ -363,18 +139,21 @@ do_value_listing(s8 prefix, s8 suffix, f32 value, Font font, Arena a, Rect r) } static Rect -do_text_input_listing(s8 prefix, s8 suffix, BPModifiableValue bmv, BeamformerCtx *ctx, Arena a, +do_text_input_listing(s8 prefix, s8 suffix, Variable var, BeamformerCtx *ctx, Arena a, Rect r, v2 mouse, f32 *hover_t) { + BeamformerUI *ui = ctx->ui; + InputState *is = &ui->text_input_state; + b32 text_input_active = ui->interaction.active.store == var.store; + Stream buf = stream_alloc(&a, 64); v2 txt_s; - b32 bmv_active = bmv_equal(&bmv, &ctx->is.store); - if (bmv_active) { - txt_s = measure_text(ctx->font, (s8){.len = ctx->is.buf_len, .data = ctx->is.buf}); + if (text_input_active) { + txt_s = measure_text(ui->font, (s8){.len = is->buf_len, .data = is->buf}); } else { - bmv_sprint(&bmv, &buf); - txt_s = measure_text(ctx->font, stream_to_s8(&buf)); + stream_append_variable(&buf, &var); + txt_s = measure_text(ui->font, stream_to_s8(&buf)); } Rect edit_rect = { @@ -382,40 +161,73 @@ do_text_input_listing(s8 prefix, s8 suffix, BPModifiableValue bmv, BeamformerCtx .size = {.x = txt_s.w + TEXT_BOX_EXTRA_X, .y = txt_s.h} }; - b32 hovering = hover_text(mouse, edit_rect, hover_t, !bmv_active); - if (hovering) { - f32 mouse_scroll = GetMouseWheelMove(); - if (mouse_scroll) { - if (bmv_active) - set_text_input_idx(ctx, (BPModifiableValue){0}, (Rect){0}, mouse); - f32 old_val = bmv_value(&bmv); - bmv.store_fn(ctx, &bmv, old_val + bmv.scroll_scale * mouse_scroll, 1); - buf.widx = 0; - bmv_sprint(&bmv, &buf); - } - } + b32 hovering = hover_text(mouse, edit_rect, hover_t, !text_input_active); + if (hovering) + ui->interaction.hot = var; - if (!hovering && bmv_equal(&bmv, &ctx->is.store) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - set_text_input_idx(ctx, (BPModifiableValue){0}, (Rect){0}, mouse); - buf.widx = 0; - bmv_sprint(&bmv, &buf); + /* TODO: where should this go? */ + if (text_input_active && is->cursor == -1) { + /* NOTE: extra offset to help with putting a cursor at idx 0 */ + #define TEXT_HALF_CHAR_WIDTH 10 + f32 hover_p = CLAMP01((mouse.x - edit_rect.pos.x) / edit_rect.size.w); + f32 x_off = TEXT_HALF_CHAR_WIDTH, x_bounds = edit_rect.size.w * hover_p; + i32 i; + for (i = 0; i < is->buf_len && x_off < x_bounds; i++) { + /* NOTE: assumes font glyphs are ordered ASCII */ + i32 idx = is->buf[i] - 0x20; + x_off += ui->font.glyphs[idx].advanceX; + if (ui->font.glyphs[idx].advanceX == 0) + x_off += ui->font.recs[idx].width; + } + is->cursor = i; } - if (hovering && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) - set_text_input_idx(ctx, bmv, edit_rect, mouse); - Color colour = colour_from_normalized(lerp_v4(FG_COLOUR, HOVERED_COLOUR, *hover_t)); - if (!bmv_equal(&bmv, &ctx->is.store)) { - draw_text(ctx->font, stream_to_s8(&buf), edit_rect.pos, 0, colour); + if (text_input_active) { + s8 buf = {.len = is->buf_len, .data = is->buf}; + v2 ts = measure_text(ui->font, buf); + v2 pos = {.x = edit_rect.pos.x, .y = edit_rect.pos.y + (edit_rect.size.y - ts.y) / 2}; + + #define MAX_DISP_CHARS 7 + i32 buf_delta = is->buf_len - MAX_DISP_CHARS; + if (buf_delta < 0) buf_delta = 0; + buf.len -= buf_delta; + buf.data += buf_delta; + { + /* NOTE: drop a char if the subtext still doesn't fit */ + v2 nts = measure_text(ui->font, buf); + if (nts.w > 0.96 * edit_rect.size.w) { + buf.data++; + buf.len--; + } + } + draw_text(ui->font, buf, pos, 0, colour); + + v4 bg = FOCUSED_COLOUR; + bg.a = 0; + Color cursor_colour = colour_from_normalized(lerp_v4(bg, FOCUSED_COLOUR, + CLAMP01(is->cursor_blink_t))); + buf.len = is->cursor - buf_delta; + v2 sts = measure_text(ui->font, buf); + f32 cursor_x = pos.x + sts.x; + f32 cursor_width; + if (is->cursor == is->buf_len) cursor_width = MIN(ctx->window_size.w * 0.03, 20); + else cursor_width = MIN(ctx->window_size.w * 0.01, 6); + Rect cursor_r = { + .pos = {.x = cursor_x, .y = pos.y}, + .size = {.w = cursor_width, .h = ts.h}, + }; + + DrawRectanglePro(cursor_r.rl, (Vector2){0}, 0, cursor_colour); } else { - do_text_input(ctx, 7, edit_rect, colour); + draw_text(ui->font, stream_to_s8(&buf), edit_rect.pos, 0, colour); } - v2 suffix_s = measure_text(ctx->font, suffix); + v2 suffix_s = measure_text(ui->font, suffix); v2 suffix_p = {.x = r.pos.x + r.size.w - suffix_s.w, .y = r.pos.y}; - draw_text(ctx->font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR)); - draw_text(ctx->font, suffix, suffix_p, 0, colour_from_normalized(FG_COLOUR)); + draw_text(ui->font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR)); + draw_text(ui->font, suffix, suffix_p, 0, colour_from_normalized(FG_COLOUR)); r.pos.y += suffix_s.h + LISTING_LINE_PAD; r.size.y -= suffix_s.h + LISTING_LINE_PAD; @@ -424,29 +236,25 @@ do_text_input_listing(s8 prefix, s8 suffix, BPModifiableValue bmv, BeamformerCtx } static Rect -do_text_toggle_listing(s8 prefix, s8 text0, s8 text1, b32 toggle, BPModifiableValue bmv, - BeamformerCtx *ctx, Rect r, v2 mouse, f32 *hover_t) +do_text_toggle_listing(s8 prefix, s8 text0, s8 text1, Variable var, + BeamformerUI *ui, Rect r, v2 mouse, f32 *hover_t) { + b32 toggle = *(b32 *)var.store; v2 txt_s; - if (toggle) txt_s = measure_text(ctx->font, text1); - else txt_s = measure_text(ctx->font, text0); + if (toggle) txt_s = measure_text(ui->font, text1); + else txt_s = measure_text(ui->font, text0); Rect edit_rect = { .pos = {.x = r.pos.x + LISTING_LEFT_COLUMN_WIDTH, .y = r.pos.y}, .size = {.x = txt_s.w + TEXT_BOX_EXTRA_X, .y = txt_s.h} }; - b32 hovering = hover_text(mouse, edit_rect, hover_t, 1); - - b32 pressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsMouseButtonPressed(MOUSE_BUTTON_RIGHT); - if (hovering && (pressed || GetMouseWheelMove())) { - toggle = !toggle; - bmv.store_fn(ctx, &bmv, toggle, 0); - } + if (hover_text(mouse, edit_rect, hover_t, 1)) + ui->interaction.hot = var; Color colour = colour_from_normalized(lerp_v4(FG_COLOUR, HOVERED_COLOUR, *hover_t)); - draw_text(ctx->font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR)); - draw_text(ctx->font, toggle? text1: text0, edit_rect.pos, 0, colour); + draw_text(ui->font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR)); + draw_text(ui->font, toggle? text1: text0, edit_rect.pos, 0, colour); r.pos.y += txt_s.h + LISTING_LINE_PAD; r.size.y -= txt_s.h + LISTING_LINE_PAD; @@ -455,7 +263,7 @@ do_text_toggle_listing(s8 prefix, s8 text0, s8 text1, b32 toggle, BPModifiableVa } static b32 -do_text_button(BeamformerCtx *ctx, s8 text, Rect r, v2 mouse, f32 *hover_t) +do_text_button(BeamformerUI *ui, s8 text, Rect r, v2 mouse, f32 *hover_t) { b32 hovering = hover_text(mouse, r, hover_t, 1); b32 pressed = 0; @@ -472,12 +280,12 @@ do_text_button(BeamformerCtx *ctx, s8 text, Rect r, v2 mouse, f32 *hover_t) DrawRectangleRounded(sb.rl, RECT_BTN_ROUNDNESS, 0, RECT_BTN_BORDER_COLOUR); DrawRectangleRounded(sr.rl, RECT_BTN_ROUNDNESS, 0, RECT_BTN_COLOUR); - v2 tpos = center_align_text_in_rect(r, text, ctx->font); + v2 tpos = center_align_text_in_rect(r, text, ui->font); v2 spos = {.x = tpos.x + 1.75, .y = tpos.y + 2}; v4 colour = lerp_v4(FG_COLOUR, HOVERED_COLOUR, *hover_t); - draw_text(ctx->font, text, spos, 0, fade(BLACK, 0.8)); - draw_text(ctx->font, text, tpos, 0, colour_from_normalized(colour)); + draw_text(ui->font, text, spos, 0, fade(BLACK, 0.8)); + draw_text(ui->font, text, tpos, 0, colour_from_normalized(colour)); return pressed; } @@ -485,9 +293,7 @@ do_text_button(BeamformerCtx *ctx, s8 text, Rect r, v2 mouse, f32 *hover_t) static void draw_settings_ui(BeamformerCtx *ctx, Arena arena, Rect r, v2 mouse) { - if (IsKeyPressed(KEY_ENTER) && ctx->is.store.value) - set_text_input_idx(ctx, (BPModifiableValue){0}, (Rect){0}, mouse); - + BeamformerUI *ui = ctx->ui; BeamformerParameters *bp = &ctx->params->raw; f32 minx = bp->output_min_coordinate.x + 1e-6, maxx = bp->output_max_coordinate.x - 1e-6; @@ -500,64 +306,100 @@ draw_settings_ui(BeamformerCtx *ctx, Arena arena, Rect r, v2 mouse) draw_r.size.y -= 20; draw_r = do_value_listing(s8("Sampling Frequency:"), s8("[MHz]"), - bp->sampling_frequency * 1e-6, ctx->font, arena, draw_r); + bp->sampling_frequency * 1e-6, ui->font, arena, draw_r); static f32 hover_t[14]; i32 idx = 0; - BPModifiableValue bmv; - bmv = (BPModifiableValue){&bp->center_frequency, bmv_store_generic, - .flimits = (v2){.y = 100e6}, MV_FLOAT|MV_CAUSES_COMPUTE, 1e5, 1e-6}; - draw_r = do_text_input_listing(s8("Center Frequency:"), s8("[MHz]"), bmv, ctx, arena, + Variable var; + + var.store = &bp->center_frequency; + var.type = VT_F32; + var.f32_limits = (v2){.y = 100e6}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1e-6; + var.scroll_scale = 1e5; + draw_r = do_text_input_listing(s8("Center Frequency:"), s8("[MHz]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->speed_of_sound, bmv_store_generic, - .flimits = (v2){.y = 1e6}, MV_FLOAT|MV_CAUSES_COMPUTE, 10, 1}; - draw_r = do_text_input_listing(s8("Speed of Sound:"), s8("[m/s]"), bmv, ctx, arena, + var.store = &bp->speed_of_sound; + var.type = VT_F32; + var.f32_limits = (v2){.y = 1e6}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1; + var.scroll_scale = 10; + draw_r = do_text_input_listing(s8("Speed of Sound:"), s8("[m/s]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->output_min_coordinate.x, bmv_store_generic, - .flimits = (v2){.x = -1e3, .y = maxx}, MV_FLOAT|MV_CAUSES_COMPUTE, - 0.5e-3, 1e3}; - draw_r = do_text_input_listing(s8("Min Lateral Point:"), s8("[mm]"), bmv, ctx, arena, + var.store = &bp->output_min_coordinate.x; + var.type = VT_F32; + var.f32_limits = (v2){.x = -1e3, .y = maxx}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1e3; + var.scroll_scale = 0.5e-3; + draw_r = do_text_input_listing(s8("Min Lateral Point:"), s8("[mm]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->output_max_coordinate.x, bmv_store_generic, - .flimits = (v2){.x = minx, .y = 1e3}, MV_FLOAT|MV_CAUSES_COMPUTE, - 0.5e-3, 1e3}; - draw_r = do_text_input_listing(s8("Max Lateral Point:"), s8("[mm]"), bmv, ctx, arena, + var.store = &bp->output_max_coordinate.x; + var.type = VT_F32; + var.f32_limits = (v2){.x = minx, .y = 1e3}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1e3; + var.scroll_scale = 0.5e-3; + draw_r = do_text_input_listing(s8("Max Lateral Point:"), s8("[mm]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->output_min_coordinate.z, bmv_store_generic, - .flimits = (v2){.y = maxz}, MV_FLOAT|MV_CAUSES_COMPUTE, 0.5e-3, 1e3}; - draw_r = do_text_input_listing(s8("Min Axial Point:"), s8("[mm]"), bmv, ctx, arena, + var.store = &bp->output_min_coordinate.z; + var.type = VT_F32; + var.f32_limits = (v2){.y = maxz}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1e3; + var.scroll_scale = 0.5e-3; + draw_r = do_text_input_listing(s8("Min Axial Point:"), s8("[mm]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->output_max_coordinate.z, bmv_store_generic, - .flimits = (v2){.x = minz, .y = 1e3}, MV_FLOAT|MV_CAUSES_COMPUTE, - 0.5e-3, 1e3}; - draw_r = do_text_input_listing(s8("Max Axial Point:"), s8("[mm]"), bmv, ctx, arena, + var.store = &bp->output_max_coordinate.z; + var.type = VT_F32; + var.f32_limits = (v2){.x = minz, .y = 1e3}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1e3; + var.scroll_scale = 0.5e-3; + draw_r = do_text_input_listing(s8("Max Axial Point:"), s8("[mm]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->off_axis_pos, bmv_store_generic, - .flimits = (v2){.x = minx, .y = maxx}, MV_FLOAT|MV_CAUSES_COMPUTE, - 0.5e-3, 1e3}; - draw_r = do_text_input_listing(s8("Off Axis Position:"), s8("[mm]"), bmv, ctx, arena, + var.store = &bp->off_axis_pos; + var.type = VT_F32; + var.f32_limits = (v2){.x = minx, .y = maxx}; + var.flags = V_CAUSES_COMPUTE; + var.display_scale = 1e3; + var.scroll_scale = 0.5e-3; + draw_r = do_text_input_listing(s8("Off Axis Position:"), s8("[mm]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&bp->beamform_plane, bmv_store_generic, .ilimits = (iv2){.y = 1}, - MV_INT|MV_CAUSES_COMPUTE, 1, 1}; - draw_r = do_text_toggle_listing(s8("Beamform Plane:"), s8("XZ"), s8("YZ"), bp->beamform_plane, - bmv, ctx, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&ctx->fsctx.db, bmv_store_generic, .flimits = (v2){.x = -120}, - MV_FLOAT|MV_GEN_MIPMAPS, 1, 1}; - draw_r = do_text_input_listing(s8("Dynamic Range:"), s8("[dB]"), bmv, ctx, arena, + var = (Variable){0}; + var.store = &bp->beamform_plane; + var.type = VT_B32; + var.flags = V_CAUSES_COMPUTE; + draw_r = do_text_toggle_listing(s8("Beamform Plane:"), s8("XZ"), s8("YZ"), var, ui, + draw_r, mouse, hover_t + idx++); + + var.store = &ctx->fsctx.db; + var.type = VT_F32; + var.f32_limits = (v2){.x = -120}; + var.flags = V_GEN_MIPMAPS; + var.display_scale = 1; + var.scroll_scale = 1; + draw_r = do_text_input_listing(s8("Dynamic Range:"), s8("[dB]"), var, ctx, arena, draw_r, mouse, hover_t + idx++); - bmv = (BPModifiableValue){&ctx->fsctx.threshold, bmv_store_generic, .flimits = (v2){.y = 240}, - MV_FLOAT|MV_GEN_MIPMAPS, 1, 1}; - draw_r = do_text_input_listing(s8("Threshold:"), s8(""), bmv, ctx, arena, + var.store = &ctx->fsctx.threshold; + var.type = VT_F32; + var.f32_limits = (v2){.y = 240}; + var.flags = V_GEN_MIPMAPS; + var.display_scale = 1; + var.scroll_scale = 1; + draw_r = do_text_input_listing(s8("Threshold:"), s8(""), var, ctx, arena, draw_r, mouse, hover_t + idx++); draw_r.pos.y += 2 * LISTING_LINE_PAD; @@ -599,8 +441,6 @@ draw_settings_ui(BeamformerCtx *ctx, Arena arena, Rect r, v2 mouse) static void draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) { - uv2 ws = ctx->window_size; - static s8 labels[CS_LAST] = { [CS_CUDA_DECODE] = s8("CUDA Decoding:"), [CS_CUDA_HILBERT] = s8("CUDA Hilbert:"), @@ -612,7 +452,9 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) [CS_UFORCES] = s8("UFORCES:"), }; + BeamformerUI *ui = ctx->ui; ComputeShaderCtx *cs = &ctx->csctx; + uv2 ws = ctx->window_size; Stream buf = stream_alloc(&arena, 64); v2 pos = {.x = 20, .y = ws.h - 10}; @@ -621,15 +463,15 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) u32 stages = ctx->params->compute_stages_count; for (u32 i = 0; i < stages; i++) { u32 index = ctx->params->compute_stages[i]; - pos.y -= measure_text(ctx->font, labels[index]).y; - draw_text(ctx->font, labels[index], pos, 0, colour_from_normalized(FG_COLOUR)); + pos.y -= measure_text(ui->font, labels[index]).y; + draw_text(ui->font, labels[index], pos, 0, colour_from_normalized(FG_COLOUR)); buf.widx = 0; stream_append_f64_e(&buf, cs->last_frame_time[index]); stream_append_s8(&buf, s8(" [s]")); - v2 txt_fs = measure_text(ctx->font, stream_to_s8(&buf)); + v2 txt_fs = measure_text(ui->font, stream_to_s8(&buf)); v2 rpos = {.x = r.pos.x + r.size.w - txt_fs.w, .y = pos.y}; - draw_text(ctx->font, stream_to_s8(&buf), rpos, 0, colour_from_normalized(FG_COLOUR)); + draw_text(ui->font, stream_to_s8(&buf), rpos, 0, colour_from_normalized(FG_COLOUR)); compute_time_sum += cs->last_frame_time[index]; } @@ -637,15 +479,15 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) static s8 totals[2] = {s8("Compute Total:"), s8("Volume Total:")}; f32 times[2] = {compute_time_sum, ctx->partial_compute_ctx.runtime}; for (u32 i = 0; i < ARRAY_COUNT(totals); i++) { - pos.y -= measure_text(ctx->font, totals[i]).y; - draw_text(ctx->font, totals[i], pos, 0, colour_from_normalized(FG_COLOUR)); + pos.y -= measure_text(ui->font, totals[i]).y; + draw_text(ui->font, totals[i], pos, 0, colour_from_normalized(FG_COLOUR)); buf.widx = 0; stream_append_f64_e(&buf, times[i]); stream_append_s8(&buf, s8(" [s]")); - v2 txt_fs = measure_text(ctx->font, stream_to_s8(&buf)); + v2 txt_fs = measure_text(ui->font, stream_to_s8(&buf)); v2 rpos = {.x = r.pos.x + r.size.w - txt_fs.w, .y = pos.y}; - draw_text(ctx->font, stream_to_s8(&buf), rpos, 0, colour_from_normalized(FG_COLOUR)); + draw_text(ui->font, stream_to_s8(&buf), rpos, 0, colour_from_normalized(FG_COLOUR)); } { @@ -655,8 +497,8 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) static s8 txt[2] = { s8("-_-"), s8("^_^") }; static v2 ts[2]; if (ts[0].x == 0) { - ts[0] = measure_text(ctx->font, txt[0]); - ts[1] = measure_text(ctx->font, txt[1]); + ts[0] = measure_text(ui->font, txt[0]); + ts[1] = measure_text(ui->font, txt[1]); } pos.x += 130 * dt_for_frame * scale.x; @@ -674,14 +516,268 @@ draw_debug_overlay(BeamformerCtx *ctx, Arena arena, Rect r) scale.y *= -1.0; } - draw_text(ctx->font, txt[txt_idx], pos, 0, RED); + draw_text(ui->font, txt[txt_idx], pos, 0, RED); + } +} + +static void +ui_store_variable(Variable *var, void *new_value) +{ + /* TODO: special cases (eg. power of 2) */ + switch (var->type) { + case VT_F32: { + f32 f32_val = *(f32 *)new_value; + f32 *f32_var = var->store; + *f32_var = CLAMP(f32_val, var->f32_limits.x, var->f32_limits.y); + } break; + case VT_I32: { + i32 i32_val = *(i32 *)new_value; + i32 *i32_var = var->store; + *i32_var = CLAMP(i32_val, var->i32_limits.x, var->i32_limits.y); + } break; + default: INVALID_CODE_PATH; + } +} + +static void +begin_text_input(InputState *is, Variable *var) +{ + ASSERT(var->store != NULL); + + Stream s = {.cap = ARRAY_COUNT(is->buf), .data = is->buf}; + stream_append_variable(&s, var); + ASSERT(!s.errors); + is->buf_len = s.widx; + is->cursor = -1; +} + +static void +end_text_input(InputState *is, Variable *var) +{ + f32 value = parse_f64((s8){.len = is->buf_len, .data = is->buf}) / var->display_scale; + ui_store_variable(var, &value); +} + +static void +update_text_input(InputState *is) +{ + if (is->cursor == -1) + return; + + is->cursor_blink_t += is->cursor_blink_scale * dt_for_frame; + if (is->cursor_blink_t >= 1) is->cursor_blink_scale = -1.5f; + if (is->cursor_blink_t <= 0) is->cursor_blink_scale = 1.5f; + + /* NOTE: handle multiple input keys on a single frame */ + i32 key = GetCharPressed(); + while (key > 0) { + if (is->buf_len == ARRAY_COUNT(is->buf)) + break; + + b32 allow_key = ((key >= '0' && key <= '9') || (key == '.') || + (key == '-' && is->cursor == 0)); + if (allow_key) { + mem_move(is->buf + is->cursor, + is->buf + is->cursor + 1, + is->buf_len - is->cursor + 1); + + is->buf[is->cursor++] = key; + is->buf_len++; + } + key = GetCharPressed(); + } + + if ((IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) && is->cursor > 0) + is->cursor--; + + if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) && is->cursor < is->buf_len) + is->cursor++; + + if ((IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE)) && is->cursor > 0) { + is->cursor--; + mem_move(is->buf + is->cursor + 1, + is->buf + is->cursor, + is->buf_len - is->cursor); + is->buf_len--; + } + + if ((IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE)) && is->cursor < is->buf_len) { + mem_move(is->buf + is->cursor + 1, + is->buf + is->cursor, + is->buf_len - is->cursor); + is->buf_len--; + } +} + +static void +ui_start_compute(BeamformerCtx *ctx) +{ + /* NOTE: we do not allow ui to start a work if no work was previously completed */ + Arena a = {0}; + beamform_work_queue_push(ctx, &a, BW_RECOMPUTE); + for (u32 i = 0; i < ARRAY_COUNT(ctx->beamform_frames); i++) { + BeamformFrame *frame = ctx->beamform_frames + i; + if (frame->dim.w && frame->textures[frame->dim.w - 1]) + glClearTexImage(frame->textures[frame->dim.w - 1], 0, GL_RED, GL_FLOAT, 0); + } + ctx->params->upload = 1; +} + +static void +ui_gen_mipmaps(BeamformerCtx *ctx) +{ + if (ctx->fsctx.output.texture.id) + ctx->flags |= GEN_MIPMAPS; +} + +static void +ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll) +{ + InteractionState *is = &ui->interaction; + if (is->hot_state != IS_NONE) { + is->state = is->hot_state; + } else { + switch (is->hot.type) { + case VT_NULL: is->state = IS_NOP; break; + case VT_B32: is->state = IS_SET; break; + case VT_GROUP: is->state = IS_SET; break; + case VT_F32: { + if (scroll) { + is->state = IS_SCROLL; + } else { + is->state = IS_TEXT; + begin_text_input(&ui->text_input_state, &is->hot); + } + } break; + } + } + if (is->state != IS_NONE) { + is->active = is->hot; + } +} + +static void +ui_end_interact(BeamformerCtx *ctx, BeamformerUI *ui) +{ + InteractionState *is = &ui->interaction; + switch (is->state) { + case IS_NONE: break; + case IS_NOP: break; + case IS_SET: { + switch (is->active.type) { + case VT_B32: { + b32 *val = is->active.store; + *val = !(*val); + } break; + } + } break; + case IS_DISPLAY: + case IS_SCROLL: { + f32 delta = GetMouseWheelMove() * is->active.scroll_scale; + switch (is->active.type) { + case VT_B32: { + b32 *old_val = is->active.store; + b32 new_val = !(*old_val); + ui_store_variable(&is->active, &new_val); + } break; + case VT_F32: { + f32 *old_val = is->active.store; + f32 new_val = *old_val + delta; + ui_store_variable(&is->active, &new_val); + } break; + case VT_I32: { + i32 *old_val = is->active.store; + i32 new_val = *old_val + delta; + ui_store_variable(&is->active, &new_val); + } break; + } + } break; + case IS_TEXT: end_text_input(&ui->text_input_state, &is->active); break; + } + + if (is->active.flags & V_CAUSES_COMPUTE) + ui_start_compute(ctx); + + if (is->active.flags & V_GEN_MIPMAPS) + ui_gen_mipmaps(ctx); + + is->state = IS_NONE; + is->active = NULL_VARIABLE; +} + +static void +ui_interact(BeamformerCtx *ctx, BeamformerInput *input) +{ + BeamformerUI *ui = ctx->ui; + InteractionState *is = &ui->interaction; + if (is->state != IS_NONE) { + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + ui_end_interact(ctx, ui); + ui_begin_interact(ui, input, 0); + } + + if (GetMouseWheelMove()) { + ui_end_interact(ctx, ui); + ui_begin_interact(ui, input, 1); + } + + if (IsKeyPressed(KEY_ENTER)) + ui_end_interact(ctx, ui); + + } else { + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + ui_begin_interact(ui, input, 0); + } + if (GetMouseWheelMove()) { + ui_begin_interact(ui, input, 1); + } + } + + switch (is->state) { + case IS_DISPLAY: ui_end_interact(ctx, ui); break; + case IS_SCROLL: ui_end_interact(ctx, ui); break; + case IS_SET: ui_end_interact(ctx, ui); break; + case IS_TEXT: update_text_input(&ui->text_input_state); break; + case IS_DRAG: { + if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { + ui_end_interact(ctx, ui); + } else { + switch (is->active.type) { + } + } + } break; } + + is->hot_state = IS_NONE; + is->hot = NULL_VARIABLE; } static void -draw_ui(BeamformerCtx *ctx, Arena arena) +ui_init(BeamformerCtx *ctx, Arena store) +{ + /* NOTE: store the ui at the base of the passed in arena and use the rest for + * temporary allocations within the ui. If needed we can recall this function + * to completely clear the ui state */ + BeamformerUI *ui = ctx->ui = alloc(&store, typeof(*ctx->ui), 1); + ui->arena_for_frame = store; + + /* TODO: build these into the binary */ + ui->font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0); + ui->small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0); +} + +static void +draw_ui(BeamformerCtx *ctx, BeamformerInput *input) { BeamformerParameters *bp = &ctx->params->raw; + BeamformerUI *ui = ctx->ui; + + end_temp_arena(ui->frame_temporary_arena); + ui->frame_temporary_arena = begin_temp_arena(&ui->arena_for_frame); + + /* NOTE: process interactions first because the used interacted with + * the ui that was presented last frame */ + ui_interact(ctx, input); BeginDrawing(); ClearBackground(colour_from_normalized(BG_COLOUR)); @@ -702,10 +798,10 @@ draw_ui(BeamformerCtx *ctx, Arena arena) rr.pos.x = lr.pos.x + lr.size.w; if (output_dim.x > 1e-6 && output_dim.y > 1e-6) { - Stream buf = stream_alloc(&arena, 64); + Stream buf = stream_alloc(&ui->arena_for_frame, 64); stream_append_f64(&buf, -188.8f, 10); stream_append_s8(&buf, s8(" mm")); - v2 txt_s = measure_text(ctx->small_font, stream_to_s8(&buf)); + v2 txt_s = measure_text(ui->small_font, stream_to_s8(&buf)); rr.pos.x += 0.02 * rr.size.w; rr.pos.y += 0.02 * rr.size.h; @@ -734,11 +830,15 @@ draw_ui(BeamformerCtx *ctx, Arena arena) NPatchInfo tex_np = { tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH }; DrawTextureNPatch(*output, tex_np, vr.rl, (Vector2){0}, 0, WHITE); - /* NOTE: check mouse wheel for adjusting dynamic range of image */ if (CheckCollisionPointRec(mouse.rl, vr.rl)) { - f32 delta = GetMouseWheelMove(); - ctx->fsctx.db = CLAMP(ctx->fsctx.db + delta, -120, 0); - if (delta) ctx->flags |= GEN_MIPMAPS; + InteractionState *is = &ui->interaction; + is->hot_state = IS_DISPLAY; + is->hot.store = &ctx->fsctx.db; + is->hot.type = VT_F32; + is->hot.f32_limits = (v2){.x = -120}; + is->hot.flags = V_GEN_MIPMAPS; + is->hot.display_scale = 1; + is->hot.scroll_scale = 1; } static f32 txt_colour_t[2]; @@ -762,6 +862,7 @@ draw_ui(BeamformerCtx *ctx, Arena arena) /* TODO: don't do this nonsense; this code will need to get * split into a seperate function */ + /* TODO: pass this through the interaction system */ u32 coord_idx = i == 0? 0 : 2; if (hover_text(mouse, tick_rect, txt_colour_t + i, 1)) { f32 scale[2] = {0.5e-3, 1e-3}; @@ -787,7 +888,7 @@ draw_ui(BeamformerCtx *ctx, Arena arena) if (i == 0 && mm > 0) stream_append_byte(&buf, '+'); stream_append_f64(&buf, mm, 10); stream_append_s8(&buf, s8(" mm")); - draw_text(ctx->small_font, stream_to_s8(&buf), txt_pos, + draw_text(ui->small_font, stream_to_s8(&buf), txt_pos, rot[i], txt_colour); start_pos.E[i] += inc; end_pos.E[i] += inc; @@ -797,7 +898,7 @@ draw_ui(BeamformerCtx *ctx, Arena arena) } } - draw_settings_ui(ctx, arena, lr, mouse); - draw_debug_overlay(ctx, arena, lr); + draw_settings_ui(ctx, ui->arena_for_frame, lr, mouse); + draw_debug_overlay(ctx, ui->arena_for_frame, lr); EndDrawing(); } diff --git a/util.c b/util.c @@ -62,6 +62,35 @@ alloc_(Arena *a, size len, size align, size count) return mem_clear(p, 0, count * len); } +static Arena +sub_arena(Arena *a, size size) +{ + Arena result = {0}; + if ((a->end - a->beg) >= size) { + result.beg = a->beg; + result.end = a->beg + size; + a->beg += size; + } + return result; +} + +static TempArena +begin_temp_arena(Arena *a) +{ + TempArena result = {.arena = a, .old_beg = a->beg}; + return result; +} + +static void +end_temp_arena(TempArena ta) +{ + Arena *a = ta.arena; + if (a) { + ASSERT(a->beg >= ta.old_beg) + a->beg = ta.old_beg; + } +} + static Stream stream_alloc(Arena *a, size cap) { @@ -187,6 +216,21 @@ stream_append_f64_e(Stream *s, f64 f) stream_append_u64(s, ABS(scale)); } +static void +stream_append_variable(Stream *s, Variable *var) +{ + switch (var->type) { + case VT_F32: + f32 *f32_val = var->store; + stream_append_f64(s, *f32_val * var->display_scale, 100); + break; + case VT_I32: + i32 *i32_val = var->store; + stream_append_i64(s, *i32_val * var->display_scale); + default: INVALID_CODE_PATH; + } +} + static s8 cstr_to_s8(char *cstr) { diff --git a/util.h b/util.h @@ -35,6 +35,8 @@ #define ASSERT(c) #endif +#define INVALID_CODE_PATH ASSERT(0) + #define static_assert _Static_assert #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a)) @@ -105,7 +107,7 @@ typedef union { typedef union { struct { u32 x, y, z, w; }; - uv3 xyz; + struct { uv3 xyz; u32 _w; }; u32 E[4]; } uv4; @@ -161,6 +163,41 @@ typedef struct { b32 errors; } Stream; +enum variable_type { + VT_NULL, + VT_B32, + VT_F32, + VT_I32, + VT_GROUP, +}; + +enum variable_group_type { + VG_LISTING, + VG_V2, + VG_V4, + VG_UV4, +}; + +typedef struct { + void *store; + union { + v2 f32_limits; + iv2 i32_limits; + }; + f32 display_scale; + f32 scroll_scale; + u32 type; + u32 flags; +} Variable; + +typedef struct { + Variable *first; + Variable *last; + u32 type; +} VariableGroup; + +#define NULL_VARIABLE (Variable){.store = 0, .type = VT_NULL} + #define PLATFORM_ALLOC_ARENA_FN(name) Arena name(Arena old, size capacity) typedef PLATFORM_ALLOC_ARENA_FN(platform_alloc_arena_fn); @@ -194,8 +231,12 @@ typedef struct { PLATFORM_FNS } Platform; #undef X typedef struct { + b32 executable_reloaded; b32 pipe_data_available; iptr pipe_handle; + + v2 mouse; + v2 last_mouse; } BeamformerInput; #include "util.c"