ogl_beamforming

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

decode.c (8929B)


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