Commit: f1bf7cc3eeaf6501014d1d07abaab54cb603f0a3
Parent: 3982c25d2f76c72a5f52935ef126620a4f0686b8
Author: Randy Palamar
Date: Sun, 27 Jul 2025 12:38:02 -0600
build: output matlab beamformer classes
these should prevent our most common pain point when interfacing
with matlab. when we try to modify the parameters and add or
remove a field and we use an incorrect name the only error we get
is "a field doesn't match one in the struct" which is obviously
completely useless trash that only mathworks could come up with.
This will allow me to modify these structs and get a reasonable
error message in matlab when a field is no longer valid.
These classes should be directly convertible to a struct via struct(obj).
Diffstat:
M | beamformer_parameters.h | | | 67 | +++++++++++++++++++++++++++++++++++-------------------------------- |
M | build.c | | | 88 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ |
M | static.c | | | 2 | +- |
3 files changed, 104 insertions(+), 53 deletions(-)
diff --git a/beamformer_parameters.h b/beamformer_parameters.h
@@ -129,36 +129,36 @@ typedef enum {
#define BEAMFORMER_FILTER_SLOTS 4
/* TODO(rnp): actually use a substruct but generate a header compatible with MATLAB */
-/* X(name, type, size, gltype, glsize, comment) */
+/* X(name, type, size, elements, gltype, glsize, comment) */
#define BEAMFORMER_UI_PARAMS \
- X(output_min_coordinate, float, [4], vec4, , "/* [m] Back-Top-Left corner of output region */") \
- X(output_max_coordinate, float, [4], vec4, , "/* [m] Front-Bottom-Right corner of output region */") \
- X(output_points, int32_t, [4], uvec4, , "/* Width * Height * Depth * (Frame Average Count) */") \
- X(sampling_frequency, float, , float, , "/* [Hz] */") \
- X(center_frequency, float, , float, , "/* [Hz] */") \
- X(speed_of_sound, float, , float, , "/* [m/s] */") \
- X(off_axis_pos, float, , float, , "/* [m] Position on screen normal to beamform in TPW/VLSHERCULES */") \
- X(beamform_plane, int32_t, , int, , "/* Plane to Beamform in TPW/VLS/HERCULES */") \
- X(f_number, float, , float, , "/* F# (set to 0 to disable) */") \
- X(interpolate, uint32_t, , bool, , "/* Perform Cubic Interpolation of RF Samples */") \
- X(coherency_weighting, uint32_t, , bool, , "/* Apply coherency weighting to output data */")
+ X(output_min_coordinate, float, [4], 4, vec4, , "/* [m] Back-Top-Left corner of output region */") \
+ X(output_max_coordinate, float, [4], 4, vec4, , "/* [m] Front-Bottom-Right corner of output region */") \
+ X(output_points, int32_t, [4], 4, uvec4, , "/* Width * Height * Depth * (Frame Average Count) */") \
+ X(sampling_frequency, float, , 1, float, , "/* [Hz] */") \
+ X(center_frequency, float, , 1, float, , "/* [Hz] */") \
+ X(speed_of_sound, float, , 1, float, , "/* [m/s] */") \
+ X(off_axis_pos, float, , 1, float, , "/* [m] Position on screen normal to beamform in TPW/VLSHERCULES */") \
+ 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 */")
#define BEAMFORMER_PARAMS_HEAD \
- X(xdc_transform, float, [16], mat4, , "/* IMPORTANT: column major order */") \
- X(dec_data_dim, uint32_t, [4] , ivec4, , "/* Samples * Channels * Acquisitions; last element ignored */") \
- X(xdc_element_pitch, float, [2] , vec2, , "/* [m] Transducer Element Pitch {row, col} */") \
- X(rf_raw_dim, uint32_t, [2] , ivec2, , "/* Raw Data Dimensions */") \
- X(transmit_mode, int32_t, , int, , "/* Method/Orientation of Transmit */") \
- X(decode, uint32_t, , uint, , "/* Decode or just reshape data */") \
- X(das_shader_id, uint32_t, , uint, , "") \
- X(time_offset, float, , float, , "/* pulse length correction time [s] */")
+ X(xdc_transform, float, [16], 16, mat4, , "/* IMPORTANT: column major order */") \
+ X(dec_data_dim, uint32_t, [4] , 4, ivec4, , "/* Samples * Channels * Acquisitions; last element ignored */") \
+ X(xdc_element_pitch, float, [2] , 2, vec2, , "/* [m] Transducer Element Pitch {row, col} */") \
+ X(rf_raw_dim, uint32_t, [2] , 2, ivec2, , "/* Raw Data Dimensions */") \
+ X(transmit_mode, int32_t, , 1, int, , "/* Method/Orientation of Transmit */") \
+ X(decode, uint32_t, , 1, uint, , "/* Decode or just reshape data */") \
+ X(das_shader_id, uint32_t, , 1, uint, , "") \
+ X(time_offset, float, , 1, float, , "/* pulse length correction time [s] */")
#define BEAMFORMER_PARAMS_TAIL \
- X(decimation_rate, uint32_t, , uint, , "/* Number of times to decimate */") \
- X(readi_group_id, uint32_t, , uint, , "/* Which readi group this data is from */") \
- X(readi_group_size, uint32_t, , uint, , "/* Size of readi transmit group */")
+ 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 */")
-#define X(name, type, size, gltype, glsize, comment) type name size;
+#define X(name, type, size, __e, gltype, glsize, comment) type name size;
typedef struct { BEAMFORMER_UI_PARAMS } BeamformerUIParameters;
typedef struct { BEAMFORMER_PARAMS_HEAD } BeamformerParametersHead;
typedef struct { BEAMFORMER_PARAMS_TAIL } BeamformerParametersTail;
@@ -188,11 +188,14 @@ _Static_assert((sizeof(BeamformerParameters) & 15) == 0, "UBO size must be a mul
X(StopImaging, 4)
/* NOTE(rnp): if this exceeds 32 you need to fix the flag handling code */
-typedef struct {
- uint32_t active;
- uint32_t save_enabled;
- uint32_t save_active;
- float transmit_power;
- float image_plane_offsets[BeamformerViewPlaneTag_Count];
- float tgc_control_points[8];
-} BeamformerLiveImagingParameters;
+#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)
+
+#define X(name, type, size, ...) type name size;
+typedef struct {BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST} BeamformerLiveImagingParameters;
+#undef X
diff --git a/build.c b/build.c
@@ -742,7 +742,7 @@ meta_begin_scope(MetaprogramContext *m, s8 line)
}
function void
-meta_append_line(MetaprogramContext *m, s8 line)
+meta_push_line(MetaprogramContext *m, s8 line)
{
meta_indent(m);
stream_append_s8s(&m->stream, line, s8("\n"));
@@ -752,12 +752,35 @@ function void
meta_end_scope(MetaprogramContext *m, s8 line)
{
m->indentation_level--;
- meta_append_line(m, line);
+ meta_push_line(m, line);
}
-#define meta_begin_matlab_class(m, name, type) \
- meta_begin_scope(m, s8("classdef " name " < " type))
-#define meta_end_matlab_scope(m) meta_end_scope(m, s8("end"))
+#define meta_begin_matlab_class_cracker(_1, _2, FN, ...) FN
+#define meta_begin_matlab_class_1(m, name) meta_begin_scope(m, s8("classdef " name))
+#define meta_begin_matlab_class_2(m, name, type) \
+ meta_begin_scope(m, s8("classdef " name " < " type))
+
+#define meta_begin_matlab_class(m, ...) \
+ meta_begin_matlab_class_cracker(__VA_ARGS__, \
+ meta_begin_matlab_class_2, \
+ meta_begin_matlab_class_1)(m, __VA_ARGS__)
+
+function void
+meta_push_matlab_property(MetaprogramContext *m, s8 name, i64 length)
+{
+ meta_indent(m);
+ stream_append_s8s(&m->stream, name, s8("(1,"));
+ stream_append_i64(&m->stream, length);
+ stream_append_s8(&m->stream, s8(")\n"));
+}
+
+function b32
+meta_end_and_write_matlab(MetaprogramContext *m, char *path)
+{
+ while (m->indentation_level > 0) meta_end_scope((m), s8("end"));
+ b32 result = meta_write_and_reset(m, path);
+ return result;
+}
function b32
build_matlab_bindings(Arena arena)
@@ -765,37 +788,62 @@ build_matlab_bindings(Arena arena)
b32 result = 1;
os_make_directory(OUTPUT("matlab"));
- char *out = OUTPUT("matlab/LiveFeedbackFlags.m");
+ char *out = OUTPUT("matlab/OGLBeamformerLiveFeedbackFlags.m");
/* NOTE(rnp): if one file is outdated all files are outdated */
if (needs_rebuild(out, "beamformer_parameters.h")) {
+ /* TODO(rnp): recreate/clear directory incase these file names change */
MetaprogramContext m = {.stream = arena_stream(arena)};
- meta_begin_matlab_class(&m, "LiveFeedbackFlags", "int32");
+ meta_begin_matlab_class(&m, "OGLBeamformerLiveFeedbackFlags", "int32");
meta_begin_scope(&m, s8("enumeration"));
- #define X(name, flag, ...) meta_append_line(&m, s8(#name " (" str(flag) ")"));
+ #define X(name, flag, ...) meta_push_line(&m, s8(#name " (" str(flag) ")"));
BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST
#undef X
- meta_end_matlab_scope(&m);
- meta_end_matlab_scope(&m);
- result &= meta_write_and_reset(&m, out);
+ result &= meta_end_and_write_matlab(&m, out);
- meta_begin_matlab_class(&m, "OGLShaderStage", "int32");
+ meta_begin_matlab_class(&m, "OGLBeamformerShaderStage", "int32");
meta_begin_scope(&m, s8("enumeration"));
- #define X(name, flag, ...) meta_append_line(&m, s8(#name " (" str(flag) ")"));
+ #define X(name, flag, ...) meta_push_line(&m, s8(#name " (" str(flag) ")"));
COMPUTE_SHADERS
#undef X
- meta_end_matlab_scope(&m);
- meta_end_matlab_scope(&m);
- result &= meta_write_and_reset(&m, OUTPUT("matlab/OGLShaderStage.m"));
+ result &= meta_end_and_write_matlab(&m, OUTPUT("matlab/OGLBeamformerShaderStage.m"));
meta_begin_matlab_class(&m, "OGLBeamformerDataKind", "int32");
meta_begin_scope(&m, s8("enumeration"));
- #define X(name, flag, ...) meta_append_line(&m, s8(#name " (" str(flag) ")"));
+ #define X(name, flag, ...) meta_push_line(&m, s8(#name " (" str(flag) ")"));
BEAMFORMER_DATA_KIND_LIST
#undef X
- meta_end_matlab_scope(&m);
- meta_end_matlab_scope(&m);
- result &= meta_write_and_reset(&m, OUTPUT("matlab/OGLBeamformerDataKind.m"));
+ result &= meta_end_and_write_matlab(&m, OUTPUT("matlab/OGLBeamformerDataKind.m"));
+
+ meta_begin_matlab_class(&m, "OGLBeamformerParameters");
+ meta_begin_scope(&m, s8("properties"));
+ #define X(name, __t, __s, elements, ...) meta_push_line(&m, s8(#name "(1," #elements ")"));
+ BEAMFORMER_PARAMS_HEAD
+ BEAMFORMER_UI_PARAMS
+ BEAMFORMER_PARAMS_TAIL
+ #undef X
+ result &= meta_end_and_write_matlab(&m, OUTPUT("matlab/OGLBeamformerParameters.m"));
+
+ meta_begin_matlab_class(&m, "OGLBeamformerParametersHead");
+ meta_begin_scope(&m, s8("properties"));
+ #define X(name, __t, __s, elements, ...) meta_push_line(&m, s8(#name "(1," #elements ")"));
+ BEAMFORMER_PARAMS_HEAD
+ #undef X
+ result &= meta_end_and_write_matlab(&m, OUTPUT("matlab/OGLBeamformerParametersHead.m"));
+
+ meta_begin_matlab_class(&m, "OGLBeamformerParametersUI");
+ meta_begin_scope(&m, s8("properties"));
+ #define X(name, __t, __s, elements, ...) meta_push_line(&m, s8(#name "(1," #elements ")"));
+ BEAMFORMER_UI_PARAMS
+ #undef X
+ result &= meta_end_and_write_matlab(&m, OUTPUT("matlab/OGLBeamformerParametersUI.m"));
+
+ meta_begin_matlab_class(&m, "OGLBeamformerLiveImagingParameters");
+ meta_begin_scope(&m, s8("properties"));
+ #define X(name, __t, __s, elements, ...) meta_push_matlab_property(&m, s8(#name), elements);
+ BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST
+ #undef X
+ result &= meta_end_and_write_matlab(&m, OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m"));
}
return result;
diff --git a/static.c b/static.c
@@ -364,7 +364,7 @@ setup_beamformer(Arena *memory, BeamformerCtx **o_ctx, BeamformerInput **o_input
#endif
read_only local_persist s8 compute_headers[BeamformerShaderKind_ComputeCount] = {
- #define X(name, type, size, gltype, glsize, comment) "\t" #gltype " " #name #glsize "; " comment "\n"
+ #define X(name, type, size, __e, gltype, glsize, comment) "\t" #gltype " " #name #glsize "; " comment "\n"
[BeamformerShaderKind_DAS] = s8_comp("layout(std140, binding = 0) uniform parameters {\n"
BEAMFORMER_PARAMS_HEAD
BEAMFORMER_UI_PARAMS