ogl_beamforming

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

decode.c (9073B)


      1 /* See LICENSE for license details. */
      2 #define BASE_EXPORT           function
      3 #define BASE_IMPORT           function
      4 #define BEAMFORMER_LIB_EXPORT function
      5 #include "base_platform.h"
      6 #include "ogl_beamformer_lib.c"
      7 
      8 #include <signal.h>
      9 #include <stdarg.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 
     13 #define AVERAGE_SAMPLES countof(((BeamformerComputeStatsTable *)0)->times)
     14 //#define RF_TIME_SAMPLES 2432
     15 #define RF_TIME_SAMPLES 4096
     16 
     17 read_only global u32 decode_transmit_counts[] = {
     18 	2, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96, 128, 160, 192, 256
     19 };
     20 
     21 typedef struct {
     22 	b32 loop;
     23 	b32 once;
     24 	b32 dump;
     25 	b32 full_aperture;
     26 
     27 	u32 warmup_count;
     28 
     29 	char *outdir;
     30 
     31 	char **remaining;
     32 	i32    remaining_count;
     33 } Options;
     34 
     35 global b32 g_should_exit;
     36 
     37 #define die(...) die_((char *)__func__, __VA_ARGS__)
     38 function no_return void
     39 die_(char *function_name, char *format, ...)
     40 {
     41 	if (function_name)
     42 		fprintf(stderr, "%s: ", function_name);
     43 
     44 	va_list ap;
     45 
     46 	va_start(ap, format);
     47 	vfprintf(stderr, format, ap);
     48 	va_end(ap);
     49 
     50 	os_exit(1);
     51 }
     52 
     53 #if OS_LINUX
     54 
     55 function void
     56 os_make_directory(char *name)
     57 {
     58 	mkdir(name, 0770);
     59 }
     60 
     61 #elif OS_WINDOWS
     62 
     63 W32(b32) CreateDirectoryA(c8 *, void *);
     64 
     65 function void
     66 os_make_directory(char *name)
     67 {
     68 	CreateDirectoryA(name, 0);
     69 }
     70 
     71 #else
     72 #error Unsupported Platform
     73 #endif
     74 
     75 #define shift_n(v, c, n) v += n, c -= n
     76 #define shift(v, c)   shift_n(v, c, 1)
     77 #define unshift(v, c) shift_n(v, c, -1)
     78 
     79 function void
     80 usage(char *argv0)
     81 {
     82 	die("%s [--loop] [--once] [--full-aperture] [--warmup n] [--dump dir]\n"
     83 	    "    --loop:          reupload data forever\n"
     84 	    "    --once:          only run a single frame\n"
     85 	    "    --full-aperture: recieve on full 256 channel aperture\n"
     86 	    "    --warmup:        warmup with n runs\n"
     87 	    "    --dump:          dump output stats files to dir\n",
     88 	    argv0);
     89 }
     90 
     91 function Options
     92 parse_argv(i32 argc, char *argv[])
     93 {
     94 	Options result = {0};
     95 
     96 	char *argv0 = argv[0];
     97 	shift(argv, argc);
     98 
     99 	while (argc > 0) {
    100 		str8 arg = str8_from_c_str(*argv);
    101 		shift(argv, argc);
    102 
    103 		if (str8_equal(arg, str8("--loop"))) {
    104 			result.loop = 1;
    105 		} else if (str8_equal(arg, str8("--full-aperture"))) {
    106 			result.full_aperture = 1;
    107 		} else if (str8_equal(arg, str8("--dump"))) {
    108 			if (argc) {
    109 				result.outdir = *argv;
    110 				result.dump   = 1;
    111 				shift(argv, argc);
    112 			} else {
    113 				die("expected directory to dump to\n");
    114 			}
    115 		} else if (str8_equal(arg, str8("--once"))) {
    116 			result.once = 1;
    117 		} else if (str8_equal(arg, str8("--warmup"))) {
    118 			if (argc) {
    119 				result.warmup_count = (u32)atoi(*argv);
    120 				shift(argv, argc);
    121 			}
    122 		} else if (arg.length > 0 && arg.data[0] == '-') {
    123 			usage(argv0);
    124 		} else {
    125 			unshift(argv, argc);
    126 			break;
    127 		}
    128 	}
    129 
    130 	result.remaining       = argv;
    131 	result.remaining_count = argc;
    132 
    133 	return result;
    134 }
    135 
    136 function b32
    137 send_frame(i16 *restrict i16_data, u32 data_size)
    138 {
    139 	b32 result = beamformer_push_data_with_compute(i16_data, data_size, BeamformerViewPlaneTag_XZ, 0);
    140 	if (!result && !g_should_exit) printf("lib error: %s\n", beamformer_get_last_error_string());
    141 	return result;
    142 }
    143 
    144 function uv4
    145 decoded_data_dim(u32 transmit_count, b32 full_aperture)
    146 {
    147 	u32 max_transmits = decode_transmit_counts[countof(decode_transmit_counts) - 1];
    148 	uv4 result = {{RF_TIME_SAMPLES, full_aperture? max_transmits: transmit_count, transmit_count, 1}};
    149 	return result;
    150 }
    151 
    152 function uv2
    153 raw_data_dim(u32 transmit_count, b32 full_aperture)
    154 {
    155 	uv4 dec = decoded_data_dim(transmit_count, full_aperture);
    156 	uv2 result = {{dec.x * transmit_count,  256}};
    157 	return result;
    158 }
    159 
    160 function u32
    161 data_size_for_transmit_count(u32 transmit_count, b32 full_aperture)
    162 {
    163 	uv2 rf_dim = raw_data_dim(transmit_count, full_aperture);
    164 	u32 result = rf_dim.x * rf_dim.y * sizeof(i16);
    165 	return result;
    166 }
    167 
    168 function i16 *
    169 generate_test_data_for_transmit_count(u32 transmit_count, b32 full_aperture)
    170 {
    171 	u64  rf_size = data_size_for_transmit_count(transmit_count, full_aperture);
    172 	i16 *result  = malloc(rf_size);
    173 	if (!result) die("malloc\n");
    174 	return result;
    175 }
    176 
    177 function void
    178 dump_stats(BeamformerComputeStatsTable *stats, Options *options, u32 transmit_count)
    179 {
    180 	char path_buffer[1024];
    181 	Stream sb = {.data = (u8 *)path_buffer, .cap = sizeof(path_buffer)};
    182 	stream_append_str8s(&sb, str8_from_c_str(options->outdir), str8(OS_PATH_SEPARATOR "decode_"));
    183 	stream_append_u64(&sb, transmit_count);
    184 	stream_append_str8(&sb, str8(".bin"));
    185 	stream_append_byte(&sb, 0);
    186 	os_write_new_file(path_buffer, str8_struct(stats));
    187 }
    188 
    189 function void
    190 send_parameters(Options *options, u32 transmit_count)
    191 {
    192 	BeamformerParameters bp = {0};
    193 	bp.decode_mode    = BeamformerDecodeMode_Hadamard;
    194 	b32 full_aperture = options->full_aperture;
    195 	uv3 dec_data_dim  = decoded_data_dim(transmit_count, full_aperture).xyz;
    196 	bp.sample_count      = dec_data_dim.x;
    197 	bp.channel_count     = dec_data_dim.y;
    198 	bp.acquisition_count = dec_data_dim.z;
    199 
    200 	bp.raw_data_dimensions = raw_data_dim(transmit_count, full_aperture);
    201 	beamformer_push_parameters(&bp);
    202 
    203 	/* NOTE(rnp): use real channel mapping so that we still get ~random~ access pattern */
    204 	read_only local_persist i16 channel_mapping[] = {
    205 		217, 129, 212, 188, 255, 131, 237, 190, 241, 130, 248, 187, 219, 128, 218, 181,
    206 		216, 134, 247, 180, 220, 132, 238, 178, 246, 133, 240, 179, 221, 135, 239, 173,
    207 		231, 137, 211, 172, 222, 139, 213, 170, 249, 138, 210, 171, 223, 136, 232, 189,
    208 		233, 142, 209, 164, 224, 140, 214, 186, 254, 141, 208, 163, 225, 143, 215, 185,
    209 		230, 145, 204, 162, 226, 147, 206, 165, 229, 146, 207, 161, 227, 144, 205, 182,
    210 		234, 150, 203, 160, 228, 148, 201, 166, 236, 149, 200, 159, 235, 175, 202, 177,
    211 		242, 151, 196, 191, 243, 155, 198, 167, 245, 154, 199, 158, 244, 176, 197, 174,
    212 		250, 168, 195, 184, 251, 156, 193, 152, 253, 153, 192, 157, 252, 183, 194, 169,
    213 		102,  62,  71,   3, 100,  60,  82,   1,  78,  61,  72,   4,  64,  63, 101,  10,
    214 		103,  57, 107,  11,  99,  59,  81,  13,  73,  58,  79,  12,  98,  56,  80,  18,
    215 		 88,  54, 108,  19,  97,  52, 106,  21,  70,  53, 109,  20,  96,  55,  87,   2,
    216 		 86,  49, 110,  27,  95,  51, 105,   5,  65,  50, 111,  28,  94,  48, 104,   6,
    217 		 89,  46, 115,  29,  93,  44, 113,  26,  90,  45, 112,  30,  92,  47, 114,   9,
    218 		 85,  41, 116,  31,  91,  43, 118,  25,  83,  42, 119,  32,  84,  16, 117,  14,
    219 		 77,  40, 123,   0,  76,  36, 121,  24,  74,  37, 120,  33,  75,  15, 122,  17,
    220 		 69,  23, 124,   7,  68,  35, 126,  39,  66,  38, 127,  34,  67,   8, 125,  22,
    221 	};
    222 	beamformer_push_channel_mapping(channel_mapping, countof(channel_mapping));
    223 
    224 	i32 shader_stages = BeamformerShaderKind_Decode;
    225 	beamformer_push_pipeline(&shader_stages, 1, BeamformerDataKind_Int16);
    226 	beamformer_set_global_timeout(1000);
    227 }
    228 
    229 function f32
    230 execute_study(Options *options, u32 transmit_count, i16 *restrict data)
    231 {
    232 	send_parameters(options, transmit_count);
    233 	u32 data_size = data_size_for_transmit_count(transmit_count, options->full_aperture);
    234 	for (u32 i = 0; !g_should_exit && i < options->warmup_count; i++)
    235 		send_frame(data, data_size);
    236 
    237 	u64 start     = os_timer_count();
    238 	f64 frequency = os_timer_frequency();
    239 	for (u32 i = 0; !g_should_exit && i < AVERAGE_SAMPLES; i++)
    240 		send_frame(data, data_size);
    241 	f32 result = (os_timer_count() - start) / frequency / (f32)AVERAGE_SAMPLES;
    242 
    243 	return result;
    244 }
    245 
    246 function void
    247 print_result(u32 transmit_count, f32 time)
    248 {
    249 	printf("decode %3u | %uF Average: %8.3f [ms]\n", transmit_count, (u32)AVERAGE_SAMPLES, time * 1e3);
    250 }
    251 
    252 function void
    253 sigint(i32 _signo)
    254 {
    255 	g_should_exit = 1;
    256 }
    257 
    258 BASE_IMPORT void
    259 entry_point(i32 argc, char *argv[])
    260 {
    261 	Options options = parse_argv(argc, argv);
    262 
    263 	if (options.remaining_count)
    264 		usage(argv[0]);
    265 
    266 	if (options.dump) os_make_directory(options.outdir);
    267 
    268 	signal(SIGINT, sigint);
    269 
    270 	BeamformerLiveImagingParameters lip = {.active = 1, .save_enabled = 1};
    271 	str8 short_name = str8("Decode Bench");
    272 	memory_copy(lip.save_name_tag, short_name.data, (u64)short_name.length);
    273 	lip.save_name_tag_length = (i32)short_name.length;
    274 	beamformer_set_live_parameters(&lip);
    275 
    276 	u32 max_transmit_count = decode_transmit_counts[countof(decode_transmit_counts) - 1];
    277 	i16 *data = generate_test_data_for_transmit_count(max_transmit_count, options.full_aperture);
    278 	if (options.loop) {
    279 		for (;!g_should_exit;) {
    280 			u32 transmit_count = decode_transmit_counts[0];
    281 			f32 time = execute_study(&options, transmit_count, data);
    282 			if (!g_should_exit) print_result(transmit_count, time);
    283 		}
    284 	} else if (options.once) {
    285 		u32 transmit_count = decode_transmit_counts[0];
    286 		u32 data_size = data_size_for_transmit_count(transmit_count, options.full_aperture);
    287 		send_parameters(&options, transmit_count);
    288 		send_frame(data, data_size);
    289 	} else {
    290 		BeamformerComputeStatsTable stats = {0};
    291 		for (i64 i = 0; i < countof(decode_transmit_counts); i++) {
    292 			u32 transmit_count = decode_transmit_counts[i];
    293 			f32 time = execute_study(&options, transmit_count, data);
    294 			if (options.dump) {
    295 				beamformer_compute_timings(&stats, 1000);
    296 				dump_stats(&stats, &options, transmit_count);
    297 			}
    298 			if (!g_should_exit) print_result(transmit_count, time);
    299 		}
    300 	}
    301 
    302 	lip.active = 0;
    303 	beamformer_set_live_parameters(&lip);
    304 }