ogl_beamforming

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

Commit: 5ac2a90a2b3883eb96737f47db6edbdbd1bdff33
Parent: e545a88bf8f442a63a96d52d221b082fff119f1b
Author: Randy Palamar
Date:   Wed,  9 Jul 2025 22:45:30 -0600

ui: fancier live control power slider

Diffstat:
Mintrinsics.c | 20++++++++++++++++----
Mmath.c | 44++++++++++++++++++++++++++++++++++++++++++--
Mui.c | 5+++--
3 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/intrinsics.c b/intrinsics.c @@ -138,31 +138,43 @@ ctz_u32(u32 a) typedef float32x4_t f32x4; typedef int32x4_t i32x4; +#define add_f32x4(a, b) vaddq_f32(a, b) #define cvt_i32x4_f32x4(a) vcvtq_f32_s32(a) #define cvt_f32x4_i32x4(a) vcvtq_s32_f32(a) +#define div_f32x4(a, b) vdivq_f32(a, b) #define dup_f32x4(f) vdupq_n_f32(f) +#define floor_f32x4(a) vrndmq_f32(a) #define load_f32x4(a) vld1q_f32(a) #define load_i32x4(a) vld1q_s32(a) +#define max_f32x4(a, b) vmaxq_f32(a, b) +#define min_f32x4(a, b) vminq_f32(a, b) #define mul_f32x4(a, b) vmulq_f32(a, b) #define set_f32x4(a, b, c, d) vld1q_f32((f32 []){d, c, b, a}) #define sqrt_f32x4(a) vsqrtq_f32(a) -#define store_f32x4(a, o) vst1q_f32(o, a) -#define store_i32x4(a, o) vst1q_s32(o, a) +#define store_f32x4(o, a) vst1q_f32(o, a) +#define store_i32x4(o, a) vst1q_s32(o, a) +#define sub_f32x4(a, b) vsubq_f32(a, b) #elif ARCH_X64 #include <immintrin.h> typedef __m128 f32x4; typedef __m128i i32x4; +#define add_f32x4(a, b) _mm_add_ps(a, b) #define cvt_i32x4_f32x4(a) _mm_cvtepi32_ps(a) #define cvt_f32x4_i32x4(a) _mm_cvtps_epi32(a) +#define div_f32x4(a, b) _mm_div_ps(a, b) #define dup_f32x4(f) _mm_set1_ps(f) +#define floor_f32x4(a) _mm_floor_ps(a) #define load_f32x4(a) _mm_loadu_ps(a) #define load_i32x4(a) _mm_loadu_si128((i32x4 *)a) +#define max_f32x4(a, b) _mm_max_ps(a, b) +#define min_f32x4(a, b) _mm_min_ps(a, b) #define mul_f32x4(a, b) _mm_mul_ps(a, b) #define set_f32x4(a, b, c, d) _mm_set_ps(a, b, c, d) #define sqrt_f32x4(a) _mm_sqrt_ps(a) -#define store_f32x4(a, o) _mm_storeu_ps(o, a) -#define store_i32x4(a, o) _mm_storeu_si128((i32x4 *)o, a) +#define store_f32x4(o, a) _mm_storeu_ps(o, a) +#define store_i32x4(o, a) _mm_storeu_si128((i32x4 *)o, a) +#define sub_f32x4(a, b) _mm_sub_ps(a, b) #endif diff --git a/math.c b/math.c @@ -5,7 +5,7 @@ fill_kronecker_sub_matrix(i32 *out, i32 out_stride, i32 scale, i32 *b, uv2 b_dim for (u32 i = 0; i < b_dim.y; i++) { for (u32 j = 0; j < b_dim.x; j += 4, b += 4) { f32x4 vb = cvt_i32x4_f32x4(load_i32x4(b)); - store_i32x4(cvt_f32x4_i32x4(mul_f32x4(vscale, vb)), out + j); + store_i32x4(out + j, cvt_f32x4_i32x4(mul_f32x4(vscale, vb))); } out += out_stride; } @@ -199,7 +199,6 @@ v2_magnitude(v2 a) return result; } - function v3 cross(v3 a, v3 b) { @@ -562,3 +561,44 @@ obb_raycast(m4 obb_orientation, v3 obb_size, v3 obb_center, ray ray) return result; } + +function v4 +hsv_to_rgb(v4 hsv) +{ + /* f(k(n)) = V - V*S*max(0, min(k, min(4 - k, 1))) + * k(n) = fmod((n + H * 6), 6) + * (R, G, B) = (f(n = 5), f(n = 3), f(n = 1)) + */ + align_as(16) f32 nval[4] = {5.0f, 3.0f, 1.0f, 0.0f}; + f32x4 n = load_f32x4(nval); + f32x4 H = dup_f32x4(hsv.x); + f32x4 S = dup_f32x4(hsv.y); + f32x4 V = dup_f32x4(hsv.z); + f32x4 six = dup_f32x4(6); + + f32x4 t = add_f32x4(n, mul_f32x4(six, H)); + f32x4 rem = floor_f32x4(div_f32x4(t, six)); + f32x4 k = sub_f32x4(t, mul_f32x4(rem, six)); + + t = min_f32x4(sub_f32x4(dup_f32x4(4), k), dup_f32x4(1)); + t = max_f32x4(dup_f32x4(0), min_f32x4(k, t)); + t = mul_f32x4(t, mul_f32x4(S, V)); + + v4 rgba; + store_f32x4(rgba.E, sub_f32x4(V, t)); + rgba.a = hsv.a; + return rgba; +} + +function f32 +ease_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; + } + return result; +} diff --git a/ui.c b/ui.c @@ -2727,10 +2727,11 @@ draw_live_controls_view(BeamformerUI *ui, Arena arena, Variable *var, Rect r, v2 v2 at = {{text_off, r.pos.y}}; + v4 hsv_power_slider = {{0.35 * ease_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}, - lip->transmit_power, g_colour_palette[2], mouse); + lip->transmit_power, hsv_to_rgb(hsv_power_slider), mouse); at.x = text_off; at.y += draw_text(s8("TGC:"), at, &text_spec).y; @@ -2759,7 +2760,7 @@ draw_live_controls_view(BeamformerUI *ui, Arena arena, Variable *var, Rect r, v2 if (lv->save_button_blink_t <= 0.0f) lv->save_button_blink_scale = BLINK_SPEED; v4 border_colour = BORDER_COLOUR; - if (active) border_colour = v4_lerp(border_colour, FOCUSED_COLOUR, lv->save_button_blink_t); + if (active) border_colour = v4_lerp(border_colour, FOCUSED_COLOUR, ease_cubic(lv->save_button_blink_t)); at.y += ui->font.baseSize * 0.25; at.y += draw_fancy_button(ui, &lv->save_button, label, (Rect){.pos = at, .size = button_size},