ogl_beamforming

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

das.glsl (17934B)


      1 /* See LICENSE for license details. */
      2 #if   InputDataKind == DataKind_Float32 || InputDataKind == DataKind_Float16
      3   #if CoherencyWeighting
      4     #define RESULT_TYPE               vec2
      5     #define RESULT_COHERENT_CAST(a)   (a).x
      6     #define RESULT_INCOHERENT_CAST(a) (a).y
      7   #endif
      8   #define SAMPLE_TYPE f32
      9 #elif InputDataKind == DataKind_Float32Complex || InputDataKind == DataKind_Float16Complex
     10   #if CoherencyWeighting
     11     #define RESULT_TYPE               vec3
     12     #define RESULT_COHERENT_CAST(a)   (a).xy
     13     #define RESULT_INCOHERENT_CAST(a) (a).z
     14   #endif
     15   #define SAMPLE_TYPE f32vec2
     16 #else
     17   #error InputDataKind unsupported for DAS
     18 #endif
     19 
     20 #ifndef RESULT_TYPE
     21   #define RESULT_TYPE SAMPLE_TYPE
     22 #endif
     23 
     24 #ifndef RESULT_COHERENT_CAST
     25   #define RESULT_COHERENT_CAST(a) (a)
     26 #endif
     27 
     28 #if CoherencyWeighting
     29   #define RESULT_STORE(a) RESULT_TYPE(RESULT_COHERENT_CAST(a), length(a))
     30 #else
     31   #define RESULT_STORE(a) (a)
     32 #endif
     33 
     34 // NOTE(rnp): we don't want das to get recompiled when it isn't actually using the Heap
     35 // but we also don't want to check everywhere in here for the existence of the Heap
     36 #ifndef HeapBase
     37   #define HeapBase u64(0)
     38 #endif
     39 
     40 layout(std430, buffer_reference) readonly buffer Input  { InputDataType  x[]; };
     41 layout(std430, buffer_reference)          buffer Output { OutputDataType x[]; };
     42 
     43 layout(std430, buffer_reference) buffer IncoherentOutput { f32 x[]; };
     44 
     45 layout(std430, buffer_reference) readonly buffer F16   { f16     x[]; };
     46 layout(std430, buffer_reference) readonly buffer F32   { f32     x[]; };
     47 layout(std430, buffer_reference) readonly buffer S16   { s16     x[]; };
     48 layout(std430, buffer_reference) readonly buffer U8    { u8      x[]; };
     49 layout(std430, buffer_reference) readonly buffer U32V4 { u32vec4 x[]; };
     50 layout(std430, buffer_reference) readonly buffer F32V2 { f32vec2 x[]; };
     51 layout(std430, buffer_reference) readonly buffer F32V4 { f32vec4 x[]; };
     52 layout(std430, buffer_reference) readonly buffer F16V2 { f16vec2 x[]; };
     53 layout(std430, buffer_reference) readonly buffer F16V4 { f16vec4 x[]; };
     54 
     55 #define RX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 0, 4)
     56 #define TX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 4, 4)
     57 
     58 #define C_SPLINE 0.5
     59 
     60 #if InputDataKind == DataKind_Float32Complex || InputDataKind == DataKind_Float16Complex
     61 vec2 rotate_iq(const vec2 iq, const float time)
     62 {
     63 	float arg    = radians(360) * DemodulationFrequency * time;
     64 	mat2  phasor = mat2( cos(arg), sin(arg),
     65 	                    -sin(arg), cos(arg));
     66 	vec2 result = phasor * iq;
     67 	return result;
     68 }
     69 #else
     70   #define rotate_iq(a, b) (a)
     71 #endif
     72 
     73 // NOTE(rnp): while the input RF buffer is padded such that we could continue reading
     74 // DAS is very expensive so we want to avoid any extra work possible.
     75 u32 batch_channel_count()
     76 {
     77 	const bool safe   = (ReceiveChannelCount % ChunkChannelCount) == 0;
     78 	const u32  result = safe ? ChunkChannelCount : min(ReceiveChannelCount - channel_offset, ChunkChannelCount);
     79 	return result;
     80 }
     81 
     82 u64 rf_data_pointer(const u32 channel, const u32 acquisition)
     83 {
     84 	u64 result  = rf_data + ChannelByteStride * channel + AcquisitionByteStride * acquisition;
     85 	result     -= InputDataKindByteSize * u32(InterpolationMode == InterpolationMode_Cubic);
     86 	return result;
     87 }
     88 
     89 /* NOTE: See: https://cubic.org/docs/hermite.htm */
     90 SAMPLE_TYPE cubic(const u64 rf_pointer, const f32 t)
     91 {
     92 	const mat4 h = mat4(
     93 		 2, -3,  0, 1,
     94 		-2,  3,  0, 0,
     95 		 1, -2,  1, 0,
     96 		 1, -1,  0, 0
     97 	);
     98 
     99 	#if InputDataKind == DataKind_Float32
    100 		f32vec4 samples = F32V4(rf_pointer).x[0];
    101 	#elif InputDataKind == DataKind_Float16
    102 		f16vec4 samples = F16V4(rf_pointer).x[0];
    103 	#elif InputDataKind == DataKind_Float16Complex
    104 		f32vec2 samples[4];
    105 		uvec4 load = U32V4(rf_pointer).x[0];
    106 		samples[0] = unpackHalf2x16(load[0]);
    107 		samples[1] = unpackHalf2x16(load[1]);
    108 		samples[2] = unpackHalf2x16(load[2]);
    109 		samples[3] = unpackHalf2x16(load[3]);
    110 	#else
    111 		f32vec2 samples[4];
    112 		vec4 load1 = F32V4(rf_pointer).x[0];
    113 		vec4 load2 = F32V4(rf_pointer).x[1];
    114 		samples[0] = load1.xy;
    115 		samples[1] = load1.zw;
    116 		samples[2] = load2.xy;
    117 		samples[3] = load2.zw;
    118 	#endif
    119 
    120 	vec4        Sh = vec4(t * t * t, t * t, t, 1) * h;
    121 	SAMPLE_TYPE P1 = samples[1];
    122 	SAMPLE_TYPE P2 = samples[2];
    123 	SAMPLE_TYPE T1 = C_SPLINE * (P2 - samples[0]);
    124 	SAMPLE_TYPE T2 = C_SPLINE * (samples[3] - P1);
    125 
    126 	#if   InputDataKind == DataKind_Float32 || InputDataKind == DataKind_Float16
    127 		SAMPLE_TYPE result = dot(Sh, vec4(P1, P2, T1, T2));
    128 	#else
    129 		mat2x4 C = mat2x4(vec4(P1.x, P2.x, T1.x, T2.x), vec4(P1.y, P2.y, T1.y, T2.y));
    130 		SAMPLE_TYPE result = Sh * C;
    131 	#endif
    132 	return result;
    133 }
    134 
    135 SAMPLE_TYPE sample_rf(const u64 rf_pointer, const f32 index)
    136 {
    137 	SAMPLE_TYPE result = SAMPLE_TYPE(0);
    138 
    139 	switch (InterpolationMode) {
    140 	case InterpolationMode_Nearest:{
    141 		if (index >= 0.f && index < (f32(SampleCount) - 0.5f))
    142 			result = rotate_iq(Input(rf_pointer + InputDataKindByteSize * u32(round(index))).x[0], index / SamplingFrequency);
    143 	}break;
    144 	case InterpolationMode_Linear:{
    145 		if (index >= 0.f && index < f32(SampleCount - 1)) {
    146 			#if InputDataKind == DataKind_Float32
    147 				f32vec2 rf = F32V2(rf_pointer + InputDataKindByteSize * u32(index)).x[0];
    148 			#elif InputDataKind == DataKind_Float16
    149 				f16vec2 rf = F16V2(rf_pointer + InputDataKindByteSize * u32(index)).x[0];
    150 			#elif InputDataKind == DataKind_Float16Complex
    151 				f16vec4 load  = F16V4(rf_pointer + InputDataKindByteSize * u32(index)).x[0];
    152 				f16vec2 rf[2] = {load.xy, load.zw};
    153 			#else
    154 				f32vec4 load  = F32V4(rf_pointer + InputDataKindByteSize * u32(index)).x[0];
    155 				f32vec2 rf[2] = {load.xy, load.zw};
    156 			#endif
    157 
    158 			f32 t  = fract(index);
    159 			result = (1 - t) * rf[0] + t * rf[1];
    160 			result = rotate_iq(result, index / SamplingFrequency);
    161 		}
    162 	}break;
    163 	case InterpolationMode_Cubic:{
    164 		if (index >= 1.f && index < f32(SampleCount - 2))
    165 			result = rotate_iq(cubic(rf_pointer + InputDataKindByteSize * u32(index), fract(index)), index / SamplingFrequency);
    166 	}break;
    167 	}
    168 	return result;
    169 }
    170 
    171 float sample_index(const float distance)
    172 {
    173 	float  time = distance / SpeedOfSound + TimeOffset;
    174 	return time * SamplingFrequency;
    175 }
    176 
    177 u32 output_index(const u32 x, const u32 y, const u32 z)
    178 {
    179 	u32 result = OutputSizeX * OutputSizeY * z + OutputSizeX * y + x;
    180 	return result;
    181 }
    182 
    183 float apodize(const float arg)
    184 {
    185 	/* IMPORTANT: do not move calculation of arg into this function. It will generate a
    186 	 * conditional move resulting in cos always being evaluated causing a slowdown */
    187 
    188 	/* NOTE: constant F# dynamic receive apodization. This is implemented as:
    189 	 *
    190 	 *                  /        |x_e - x_i|\
    191 	 *    a(x, z) = cos(F# * π * ----------- ) ^ 2
    192 	 *                  \        |z_e - z_i|/
    193 	 *
    194 	 * where x,z_e are transducer element positions and x,z_i are image positions. */
    195 	float a = cos(radians(180) * arg);
    196 	return a * a;
    197 }
    198 
    199 vec2 rca_plane_projection(const vec3 point, const bool rows)
    200 {
    201 	vec2 result = vec2(point[int(rows)], point[2]);
    202 	return result;
    203 }
    204 
    205 float plane_wave_transmit_distance(const vec3 point, const float transmit_angle, const bool tx_rows)
    206 {
    207 	return dot(rca_plane_projection(point, tx_rows), vec2(sin(transmit_angle), cos(transmit_angle)));
    208 }
    209 
    210 float cylindrical_wave_transmit_distance(const vec3 point, const float focal_depth,
    211                                          const float transmit_angle, const bool tx_rows)
    212 {
    213 	vec2 f = focal_depth * vec2(sin(transmit_angle), cos(transmit_angle));
    214 	return distance(rca_plane_projection(point, tx_rows), f);
    215 }
    216 
    217 u8 tx_rx_orientation_for_acquisition(const s32 acquisition)
    218 {
    219 	u8 result = u8(TransmitReceiveOrientation);
    220 	if (!SingleOrientation) result = U8(HeapBase + TransmitReceiveOrientations).x[acquisition];
    221 	return result;
    222 }
    223 
    224 f32vec2 focal_vector_for_acquisition(const s32 acquisition)
    225 {
    226 	f32vec2 result = SingleFocus ? f32vec2(TransmitAngle, FocusDepth) : F32V2(HeapBase + FocalVectors).x[acquisition];
    227 	return result;
    228 }
    229 
    230 f32 rca_transmit_distance(const vec3 world_point, const vec2 focal_vector, const u8 transmit_receive_orientation)
    231 {
    232 	float result = 0;
    233 	if (TX_ORIENTATION(transmit_receive_orientation) != RCAOrientation_None) {
    234 		bool  tx_rows        = TX_ORIENTATION(transmit_receive_orientation) == RCAOrientation_Rows;
    235 		float transmit_angle = radians(focal_vector.x);
    236 		float focal_depth    = focal_vector.y;
    237 
    238 		if (isinf(focal_depth)) {
    239 			result = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    240 		} else {
    241 			result = cylindrical_wave_transmit_distance(world_point, focal_depth, transmit_angle, tx_rows);
    242 		}
    243 	}
    244 	return result;
    245 }
    246 
    247 RESULT_TYPE RCA(const vec3 world_point)
    248 {
    249 	RESULT_TYPE result = RESULT_TYPE(0);
    250 	for (s32 acquisition = 0; acquisition < s32(AcquisitionCount); acquisition++) {
    251 		const u8   tx_rx_orientation = tx_rx_orientation_for_acquisition(acquisition);
    252 		const bool rx_rows           = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows;
    253 		const vec2 focal_vector      = focal_vector_for_acquisition(acquisition);
    254 		vec2  xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
    255 		f32   transmit_index  = sample_index(rca_transmit_distance(world_point, focal_vector, tx_rx_orientation));
    256 
    257 		u64 rf_pointer = rf_data_pointer(0u, u32(acquisition));
    258 
    259 		for (f32 chunk_channel = 0.f; chunk_channel < f32(batch_channel_count()); chunk_channel += 1.f) {
    260 			f32  rx_channel     = f32(channel_offset) + chunk_channel;
    261 			vec3 rx_center      = vec3(rx_channel * xdc_element_pitch, 0);
    262 			vec2 receive_vector = xdc_world_point - rca_plane_projection(rx_center, rx_rows);
    263 			f32  a_arg          = abs(FNumber * receive_vector.x / abs(xdc_world_point.y));
    264 
    265 			if (a_arg < 0.5f) {
    266 				f32         index = transmit_index + length(receive_vector) * SamplingFrequency / SpeedOfSound;
    267 				SAMPLE_TYPE value = apodize(a_arg) * sample_rf(rf_pointer, index);
    268 				result += RESULT_STORE(value);
    269 			}
    270 			rf_pointer += ChannelByteStride;
    271 		}
    272 	}
    273 	return result;
    274 }
    275 
    276 RESULT_TYPE HERCULES(const vec3 world_point)
    277 {
    278 	const u8   tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
    279 	const bool rx_cols           = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Columns;
    280 	const vec2 focal_vector      = focal_vector_for_acquisition(0);
    281 	const vec3 xdc_world_point   = (xdc_transform * vec4(world_point, 1)).xyz;
    282 
    283 	const f32 transmit_index   = sample_index(rca_transmit_distance(world_point, focal_vector, tx_rx_orientation));
    284 	const f32 z_delta_squared  = xdc_world_point.z * xdc_world_point.z;
    285 	const f32 f_number_over_z  = abs(FNumber / xdc_world_point.z);
    286 	const f32 apodization_test = 0.25f / (f_number_over_z * f_number_over_z);
    287 
    288 	const f32 rx_world_point   = xdc_world_point[s32(!rx_cols)];
    289 	const f32 tx_world_point   = xdc_world_point[s32(rx_cols)];
    290 	const f32 rx_pitch         = xdc_element_pitch[s32(!rx_cols)];
    291 	const f32 tx_pitch         = xdc_element_pitch[s32(rx_cols)];
    292 
    293 	RESULT_TYPE result = RESULT_TYPE(0);
    294 	for (f32 chunk_channel = 0.f; chunk_channel < f32(batch_channel_count()); chunk_channel += 1.f) {
    295 		f32 rx_channel  = f32(channel_offset) + chunk_channel;
    296 
    297 		f32 element_receive_delta_squared = rx_world_point - rx_channel * rx_pitch;
    298 		element_receive_delta_squared *= element_receive_delta_squared;
    299 
    300 		u64 rf_pointer = rf_data_pointer(u32(chunk_channel), u32(Sparse));
    301 
    302 		for (f32 transmit = f32(Sparse); transmit < f32(AcquisitionCount); transmit += 1.f) {
    303 			f32 tx_channel = Sparse ? f32(S16(HeapBase + SparseElements - 2 * u32(Sparse)).x[s32(transmit)]) : transmit;
    304 
    305 			f32 element_transmit_delta_squared = tx_world_point - tx_channel * tx_pitch;
    306 			element_transmit_delta_squared *= element_transmit_delta_squared;
    307 
    308 			f32 element_delta_squared = element_transmit_delta_squared + element_receive_delta_squared;
    309 			if (element_delta_squared < apodization_test) {
    310 				/* NOTE: tribal knowledge */
    311 				float apodization = transmit == 0 ? inversesqrt(float(AcquisitionCount)) : 1.0f;
    312 				apodization *= apodize(f_number_over_z * sqrt(element_delta_squared));
    313 
    314 				float index = transmit_index + sqrt(z_delta_squared + element_delta_squared) * SamplingFrequency / SpeedOfSound;
    315 				SAMPLE_TYPE value = apodization * sample_rf(rf_pointer, index);
    316 				result += RESULT_STORE(value);
    317 			}
    318 
    319 			rf_pointer += AcquisitionByteStride;
    320 		}
    321 	}
    322 	return result;
    323 }
    324 
    325 RESULT_TYPE FORCES(const vec3 world_point)
    326 {
    327 	RESULT_TYPE result = RESULT_TYPE(0);
    328 
    329 	const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    330 
    331 	// TODO(rnp): the sign of the origin offset might be flipped
    332 	f32 origin_offset       = FocusDepth * tan(radians(TransmitAngle));
    333 	f32 transmit_y_delta    = world_point.y + origin_offset;
    334 	f32 z_delta_squared     = xdc_world_point.z * xdc_world_point.z;
    335 	f32 transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared;
    336 
    337 	#define AcquisitionChunkSize 16
    338 	const u32 AcquisitionChunkCount = (AcquisitionCount + (AcquisitionChunkSize - 1)) / AcquisitionChunkSize;
    339 	for (u32 acquisition_chunk = 0; acquisition_chunk < AcquisitionChunkCount; acquisition_chunk++) {
    340 		u32 acquisition = acquisition_chunk * AcquisitionChunkSize;
    341 		if (Sparse && acquisition_chunk == 0) acquisition++;
    342 
    343 		f32 transmit_indices[AcquisitionChunkSize];
    344 		for (u32 transmit = 0; transmit < AcquisitionChunkSize; transmit++) {
    345 			u32 index = acquisition + transmit;
    346 			if ((AcquisitionCount % AcquisitionChunkSize) != 0 && (index >= AcquisitionCount))
    347 				break;
    348 			f32 tx_channel = Sparse ? f32(S16(HeapBase + SparseElements - 2 * u32(Sparse)).x[index]) : f32(index);
    349 			f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_channel;
    350 			transmit_indices[transmit] = sample_index(sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta));
    351 		}
    352 
    353 		for (u32 channel = 0; channel < batch_channel_count(); channel += 1u) {
    354 			f32 rx_channel      = f32(channel_offset + channel);
    355 			f32 receive_x_delta = xdc_world_point.x - rx_channel * xdc_element_pitch.x;
    356 			f32 a_arg           = abs(FNumber * receive_x_delta / xdc_world_point.z);
    357 
    358 			if (a_arg < 0.5f) {
    359 				u64 rf_pointer    = rf_data_pointer(channel, acquisition);
    360 				f32 receive_index = sqrt(receive_x_delta * receive_x_delta + z_delta_squared) * SamplingFrequency / SpeedOfSound;
    361 				f32 apodization   = apodize(a_arg);
    362 				for (u32 transmit = 0; transmit < AcquisitionChunkSize; transmit += 1u, rf_pointer += AcquisitionByteStride) {
    363 					if ((AcquisitionCount % AcquisitionChunkSize) != 0 &&
    364 					    (acquisition + transmit >= AcquisitionCount))
    365 						break;
    366 
    367 					SAMPLE_TYPE value = apodization * sample_rf(rf_pointer, receive_index + transmit_indices[transmit]);
    368 					result += RESULT_STORE(value);
    369 				}
    370 			}
    371 		}
    372 	}
    373 	return result;
    374 }
    375 
    376 RESULT_TYPE READI_FORCES(const vec3 world_point)
    377 {
    378 	RESULT_TYPE result = RESULT_TYPE(0);
    379 
    380 	const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    381 
    382 	// TODO(rnp): the sign of the origin offset might be flipped
    383 	f32 origin_offset       = FocusDepth * tan(radians(TransmitAngle));
    384 	f32 transmit_y_delta    = world_point.y + origin_offset;
    385 	f32 z_delta_squared     = xdc_world_point.z * xdc_world_point.z;
    386 	f32 transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared;
    387 
    388 	// NOTE(tkh): The row we use matches the acquisition group, the column is the element group we are beamforming.
    389 	s32 hadamard_offset = s32(readi_group) * s32(ReadiGroupCount);
    390 
    391 	for (f32 chunk_channel = 0; chunk_channel < f32(batch_channel_count()); chunk_channel += 1.f) {
    392 		f32 rx_channel      = f32(channel_offset) + chunk_channel;
    393 		f32 receive_x_delta = xdc_world_point.x - rx_channel * xdc_element_pitch.x;
    394 		f32 a_arg           = abs(FNumber * receive_x_delta / xdc_world_point.z);
    395 
    396 		if (a_arg < 0.5f) {
    397 			u64 channel_rf_pointer = rf_data_pointer(u32(chunk_channel), 0);
    398 
    399 			f32 receive_index = sample_index(sqrt(receive_x_delta * receive_x_delta + z_delta_squared));
    400 			f32 apodization   = apodize(a_arg);
    401 
    402 			// NOTE(tkh): Iterating over groups of tx elements, each group is AcquisitionCount
    403 			// sequential elements. The first element in each group is beamformed using the first
    404 			// acquisition, the second element in each group is beamformed using the second acquisition, etc.
    405 			for (s32 tx_group = 0; tx_group < s32(ReadiGroupCount); tx_group++) {
    406 				f32 group_apodization = apodization * F16(HeapBase + Hadamard).x[hadamard_offset + tx_group];
    407 				u64 rf_pointer = channel_rf_pointer;
    408 
    409 				for (f32 tx_event = 0; tx_event < f32(AcquisitionCount); tx_event += 1.f) {
    410 					f32 tx_element       = f32(tx_group) * f32(AcquisitionCount) + tx_event;
    411 					f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_element;
    412 					f32 transmit_index   = sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta) * SamplingFrequency / SpeedOfSound;
    413 
    414 					SAMPLE_TYPE value = group_apodization * sample_rf(rf_pointer, receive_index + transmit_index);
    415 					result     += RESULT_STORE(value);
    416 					rf_pointer += AcquisitionByteStride;
    417 				}
    418 			}
    419 		}
    420 	}
    421 	return result;
    422 }
    423 
    424 void main()
    425 {
    426 	uvec3 out_voxel = gl_GlobalInvocationID;
    427 	if (!all(lessThan(out_voxel, uvec3(OutputSizeX, OutputSizeY, OutputSizeZ))))
    428 		return;
    429 
    430 	vec3 image_points = vec3(OutputSizeX, OutputSizeY, OutputSizeZ) - 1.0f;
    431 	vec3 point        = vec3(out_voxel) / max(vec3(1.0f), image_points);
    432 	vec3 world_point  = (voxel_transform * vec4(point, 1)).xyz;
    433 
    434 	uint32_t out_index = output_index(out_voxel.x, out_voxel.y, out_voxel.z);
    435 
    436 	RESULT_TYPE sum = RESULT_TYPE(0);
    437 	switch (AcquisitionKind) {
    438 	case AcquisitionKind_FORCES:
    439 	case AcquisitionKind_UFORCES:
    440 	{
    441 		sum = ReadiGroupCount > 1 ? READI_FORCES(world_point)
    442 		                          : FORCES(world_point);
    443 	}break;
    444 	case AcquisitionKind_HERCULES:
    445 	case AcquisitionKind_UHERCULES:
    446 	case AcquisitionKind_HERO_PA:
    447 	{
    448 		sum = HERCULES(world_point);
    449 	}break;
    450 	case AcquisitionKind_Flash:
    451 	case AcquisitionKind_RCA_TPW:
    452 	case AcquisitionKind_RCA_VLS:
    453 	{
    454 		sum = RCA(world_point);
    455 	}break;
    456 	}
    457 
    458 	#if CoherencyWeighting
    459 	IncoherentOutput(HeapBase + IncoherentFrame).x[out_index] += RESULT_INCOHERENT_CAST(sum);
    460 	#endif
    461 
    462 	Output(output_frame).x[out_index] += RESULT_COHERENT_CAST(sum);
    463 }