das.glsl (15302B)
1 /* See LICENSE for license details. */ 2 #if InputDataKind == DataKind_Float32 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 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 layout(set = ShaderResourceKind_Buffer, binding = ShaderBufferSlot_PingPong) readonly buffer RF { 35 InputDataType rf[]; 36 }; 37 38 layout(std430, buffer_reference) buffer Output { 39 OutputDataType x[]; 40 }; 41 42 layout(std430, buffer_reference) buffer IncoherentOutput { 43 f32 x[]; 44 }; 45 46 layout(std430, buffer_reference) buffer F16 { f16 x[]; }; 47 48 #define RX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 0, 4) 49 #define TX_ORIENTATION(tx_rx) bitfieldExtract((tx_rx), 4, 4) 50 51 #define C_SPLINE 0.5 52 53 #if InputDataKind == DataKind_Float32Complex 54 vec2 rotate_iq(const vec2 iq, const float time) 55 { 56 float arg = radians(360) * DemodulationFrequency * time; 57 mat2 phasor = mat2( cos(arg), sin(arg), 58 -sin(arg), cos(arg)); 59 vec2 result = phasor * iq; 60 return result; 61 } 62 #else 63 #define rotate_iq(a, b) (a) 64 #endif 65 66 /* NOTE: See: https://cubic.org/docs/hermite.htm */ 67 SAMPLE_TYPE cubic(const int offset, const float t) 68 { 69 const mat4 h = mat4( 70 2, -3, 0, 1, 71 -2, 3, 0, 0, 72 1, -2, 1, 0, 73 1, -1, 0, 0 74 ); 75 76 SAMPLE_TYPE samples[4] = { 77 rf[offset + 0], 78 rf[offset + 1], 79 rf[offset + 2], 80 rf[offset + 3], 81 }; 82 83 vec4 S = vec4(t * t * t, t * t, t, 1); 84 SAMPLE_TYPE P1 = samples[1]; 85 SAMPLE_TYPE P2 = samples[2]; 86 SAMPLE_TYPE T1 = C_SPLINE * (P2 - samples[0]); 87 SAMPLE_TYPE T2 = C_SPLINE * (samples[3] - P1); 88 89 #if InputDataKind == DataKind_Float32 90 vec4 C = vec4(P1.x, P2.x, T1.x, T2.x); 91 SAMPLE_TYPE result = dot(S, h * C); 92 #elif InputDataKind == DataKind_Float32Complex 93 mat2x4 C = mat2x4(vec4(P1.x, P2.x, T1.x, T2.x), vec4(P1.y, P2.y, T1.y, T2.y)); 94 SAMPLE_TYPE result = S * h * C; 95 #endif 96 return result; 97 } 98 99 SAMPLE_TYPE sample_rf(const int rf_offset, const float index) 100 { 101 SAMPLE_TYPE result = SAMPLE_TYPE(0); 102 103 switch (InterpolationMode) { 104 case InterpolationMode_Nearest:{ 105 if (index >= 0.f && index < (f32(SampleCount) - 0.5f)) 106 result = rotate_iq(rf[rf_offset + int(round(index))], index / SamplingFrequency); 107 }break; 108 case InterpolationMode_Linear:{ 109 if (index >= 0.f && index < f32(SampleCount - 1)) { 110 float tk, t = modf(index, tk); 111 int n = rf_offset + int(tk); 112 result = (1 - t) * rf[n] + t * rf[n + 1]; 113 result = rotate_iq(result, index / SamplingFrequency); 114 } 115 }break; 116 case InterpolationMode_Cubic:{ 117 if (index >= 1.f && index < f32(SampleCount - 2)) { 118 float tk, t = modf(index, tk); 119 result = rotate_iq(cubic(rf_offset + int(index), t), index / SamplingFrequency); 120 } 121 }break; 122 } 123 return result; 124 } 125 126 float sample_index(const float distance) 127 { 128 float time = distance / SpeedOfSound + TimeOffset; 129 return time * SamplingFrequency; 130 } 131 132 u32 output_index(const u32 x, const u32 y, const u32 z) 133 { 134 u32 result = OutputSizeX * OutputSizeY * z + OutputSizeX * y + x; 135 return result; 136 } 137 138 float apodize(const float arg) 139 { 140 /* IMPORTANT: do not move calculation of arg into this function. It will generate a 141 * conditional move resulting in cos always being evaluated causing a slowdown */ 142 143 /* NOTE: constant F# dynamic receive apodization. This is implemented as: 144 * 145 * / |x_e - x_i|\ 146 * a(x, z) = cos(F# * π * ----------- ) ^ 2 147 * \ |z_e - z_i|/ 148 * 149 * where x,z_e are transducer element positions and x,z_i are image positions. */ 150 float a = cos(radians(180) * arg); 151 return a * a; 152 } 153 154 vec2 rca_plane_projection(const vec3 point, const bool rows) 155 { 156 vec2 result = vec2(point[int(rows)], point[2]); 157 return result; 158 } 159 160 float plane_wave_transmit_distance(const vec3 point, const float transmit_angle, const bool tx_rows) 161 { 162 return dot(rca_plane_projection(point, tx_rows), vec2(sin(transmit_angle), cos(transmit_angle))); 163 } 164 165 float cylindrical_wave_transmit_distance(const vec3 point, const float focal_depth, 166 const float transmit_angle, const bool tx_rows) 167 { 168 vec2 f = focal_depth * vec2(sin(transmit_angle), cos(transmit_angle)); 169 return distance(rca_plane_projection(point, tx_rows), f); 170 } 171 172 u8 tx_rx_orientation_for_acquisition(const s32 acquisition) 173 { 174 u8 result = u8(TransmitReceiveOrientation); 175 ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); 176 if (!SingleOrientation) result = dp.transmit_receive_orientations[acquisition]; 177 return result; 178 } 179 180 f32vec2 focal_vector_for_acquisition(const s32 acquisition) 181 { 182 ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); 183 f32vec2 result = SingleFocus ? f32vec2(TransmitAngle, FocusDepth) : dp.focal_vectors[acquisition]; 184 return result; 185 } 186 187 f32 rca_transmit_distance(const vec3 world_point, const vec2 focal_vector, const u8 transmit_receive_orientation) 188 { 189 float result = 0; 190 if (TX_ORIENTATION(transmit_receive_orientation) != RCAOrientation_None) { 191 bool tx_rows = TX_ORIENTATION(transmit_receive_orientation) == RCAOrientation_Rows; 192 float transmit_angle = radians(focal_vector.x); 193 float focal_depth = focal_vector.y; 194 195 if (isinf(focal_depth)) { 196 result = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows); 197 } else { 198 result = cylindrical_wave_transmit_distance(world_point, focal_depth, transmit_angle, tx_rows); 199 } 200 } 201 return result; 202 } 203 204 RESULT_TYPE RCA(const vec3 world_point) 205 { 206 RESULT_TYPE result = RESULT_TYPE(0); 207 for (s32 acquisition = 0; acquisition < s32(AcquisitionCount); acquisition++) { 208 const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(acquisition); 209 const bool rx_rows = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows; 210 const vec2 focal_vector = focal_vector_for_acquisition(acquisition); 211 vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows); 212 float transmit_distance = rca_transmit_distance(world_point, focal_vector, tx_rx_orientation); 213 214 int rf_offset = int(rf_element_offset) + acquisition * SampleCount; 215 rf_offset -= int(InterpolationMode == InterpolationMode_Cubic); 216 for (f32 chunk_channel = 0.f; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) { 217 f32 rx_channel = f32(channel_offset) + chunk_channel; 218 vec3 rx_center = vec3(rx_channel * xdc_element_pitch, 0); 219 vec2 receive_vector = xdc_world_point - rca_plane_projection(rx_center, rx_rows); 220 f32 a_arg = abs(FNumber * receive_vector.x / abs(xdc_world_point.y)); 221 222 if (a_arg < 0.5f) { 223 float sidx = sample_index(transmit_distance + length(receive_vector)); 224 SAMPLE_TYPE value = apodize(a_arg) * sample_rf(rf_offset, sidx); 225 result += RESULT_STORE(value); 226 } 227 rf_offset += SampleCount * AcquisitionCount; 228 } 229 } 230 return result; 231 } 232 233 RESULT_TYPE HERCULES(const vec3 world_point) 234 { 235 ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); 236 237 const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0); 238 const bool rx_cols = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Columns; 239 const vec2 focal_vector = focal_vector_for_acquisition(0); 240 const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz; 241 242 const float transmit_index = sample_index(rca_transmit_distance(world_point, focal_vector, tx_rx_orientation)); 243 const float z_delta_squared = xdc_world_point.z * xdc_world_point.z; 244 const float f_number_over_z = abs(FNumber / xdc_world_point.z); 245 const vec2 xy_world_point = xdc_world_point.xy; 246 const float apodization_test = 0.25f / (f_number_over_z * f_number_over_z); 247 248 RESULT_TYPE result = RESULT_TYPE(0); 249 for (f32 chunk_channel = 0.f; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) { 250 f32 rx_channel = f32(channel_offset) + chunk_channel; 251 s32 rf_offset = s32(rf_element_offset) + s32(chunk_channel) * SampleCount * AcquisitionCount + s32(Sparse) * SampleCount; 252 rf_offset -= s32(InterpolationMode == InterpolationMode_Cubic); 253 254 // NOTE(rnp): this wouldn't be so messy if we just forced an orientation like with FORCES 255 vec2 element_receive_delta_squared = xy_world_point; 256 if (rx_cols) element_receive_delta_squared.x -= rx_channel * xdc_element_pitch.x; 257 else element_receive_delta_squared.y -= rx_channel * xdc_element_pitch.y; 258 259 if (rx_cols) element_receive_delta_squared.x *= element_receive_delta_squared.x; 260 else element_receive_delta_squared.y *= element_receive_delta_squared.y; 261 262 for (f32 transmit = f32(Sparse); transmit < f32(AcquisitionCount); transmit += 1.f) { 263 f32 tx_channel = Sparse ? f32(dp.sparse_elements[s32(transmit) - s32(Sparse)]) : transmit; 264 265 if (rx_cols) element_receive_delta_squared.y = xy_world_point.y - tx_channel * xdc_element_pitch.y; 266 else element_receive_delta_squared.x = xy_world_point.x - tx_channel * xdc_element_pitch.x; 267 268 if (rx_cols) element_receive_delta_squared.y *= element_receive_delta_squared.y; 269 else element_receive_delta_squared.x *= element_receive_delta_squared.x; 270 271 float element_delta_squared = element_receive_delta_squared.x + element_receive_delta_squared.y; 272 if (element_delta_squared < apodization_test) { 273 /* NOTE: tribal knowledge */ 274 float apodization = transmit == 0 ? inversesqrt(float(AcquisitionCount)) : 1.0f; 275 apodization *= apodize(f_number_over_z * sqrt(element_delta_squared)); 276 277 float index = transmit_index + sqrt(z_delta_squared + element_delta_squared) * SamplingFrequency / SpeedOfSound; 278 SAMPLE_TYPE value = apodization * sample_rf(rf_offset, index); 279 result += RESULT_STORE(value); 280 } 281 282 rf_offset += SampleCount; 283 } 284 } 285 return result; 286 } 287 288 RESULT_TYPE FORCES(const vec3 xdc_world_point) 289 { 290 RESULT_TYPE result = RESULT_TYPE(0); 291 292 ComputeArrayParametersReference dp = ComputeArrayParametersReference(ArrayParameters); 293 294 float z_delta_squared = xdc_world_point.z * xdc_world_point.z; 295 float transmit_y_delta = xdc_world_point.y - xdc_element_pitch.y * ChannelCount / 2; 296 float transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared; 297 298 for (f32 chunk_channel = 0; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) { 299 float rx_channel = f32(channel_offset) + chunk_channel; 300 float receive_x_delta = xdc_world_point.x - rx_channel * xdc_element_pitch.x; 301 float a_arg = abs(FNumber * receive_x_delta / xdc_world_point.z); 302 303 if (a_arg < 0.5f) { 304 s32 rf_offset = s32(rf_element_offset) + s32(chunk_channel) * SampleCount * AcquisitionCount + s32(Sparse) * SampleCount; 305 rf_offset -= s32(InterpolationMode == InterpolationMode_Cubic); 306 307 f32 receive_index = sample_index(sqrt(receive_x_delta * receive_x_delta + z_delta_squared)); 308 f32 apodization = apodize(a_arg); 309 for (f32 transmit = f32(Sparse); transmit < f32(AcquisitionCount); transmit += 1.f) { 310 f32 tx_channel = Sparse ? f32(dp.sparse_elements[s32(transmit) - s32(Sparse)]) : transmit; 311 f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_channel; 312 f32 transmit_index = sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta) * SamplingFrequency / SpeedOfSound; 313 314 SAMPLE_TYPE value = apodization * sample_rf(rf_offset, receive_index + transmit_index); 315 result += RESULT_STORE(value); 316 rf_offset += SampleCount; 317 } 318 } 319 } 320 return result; 321 } 322 323 RESULT_TYPE READI_FORCES(const vec3 xdc_world_point) 324 { 325 RESULT_TYPE result = RESULT_TYPE(0); 326 327 float z_delta_squared = xdc_world_point.z * xdc_world_point.z; 328 float transmit_y_delta = xdc_world_point.y - xdc_element_pitch.y * ChannelCount / 2; 329 float transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared; 330 331 // NOTE(tkh): The row we use matches the acquisition group, the column is the element group we are beamforming. 332 s32 hadamard_offset = s32(readi_group) * s32(ReadiGroupCount); 333 334 for (f32 chunk_channel = 0; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) { 335 f32 rx_channel = f32(channel_offset) + chunk_channel; 336 f32 receive_x_delta = xdc_world_point.x - rx_channel * xdc_element_pitch.x; 337 f32 a_arg = abs(FNumber * receive_x_delta / xdc_world_point.z); 338 339 if (a_arg < 0.5f) { 340 s32 channel_rf_offset = s32(rf_element_offset) + s32(chunk_channel) * SampleCount * AcquisitionCount; 341 channel_rf_offset -= s32(InterpolationMode == InterpolationMode_Cubic); 342 343 f32 receive_index = sample_index(sqrt(receive_x_delta * receive_x_delta + z_delta_squared)); 344 f32 apodization = apodize(a_arg); 345 346 // NOTE(tkh): Iterating over groups of tx elements, each group is AcquisitionCount 347 // sequential elements. The first element in each group is beamformed using the first 348 // acquisition, the second element in each group is beamformed using the second acquisition, etc. 349 for (s32 tx_group = 0; tx_group < s32(ReadiGroupCount); tx_group++) { 350 f32 group_apodization = apodization * F16(Hadamard).x[hadamard_offset + tx_group]; 351 s32 rf_offset = channel_rf_offset; 352 353 for (f32 tx_event = 0; tx_event < f32(AcquisitionCount); tx_event += 1.f) { 354 f32 tx_element = f32(tx_group) * f32(AcquisitionCount) + tx_event; 355 f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_element; 356 f32 transmit_index = sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta) * SamplingFrequency / SpeedOfSound; 357 358 SAMPLE_TYPE value = group_apodization * sample_rf(rf_offset, receive_index + transmit_index); 359 result += RESULT_STORE(value); 360 rf_offset += SampleCount; 361 } 362 } 363 } 364 } 365 return result; 366 } 367 368 void main() 369 { 370 uvec3 out_voxel = gl_GlobalInvocationID; 371 if (!all(lessThan(out_voxel, uvec3(OutputSizeX, OutputSizeY, OutputSizeZ)))) 372 return; 373 374 vec3 image_points = vec3(OutputSizeX, OutputSizeY, OutputSizeZ) - 1.0f; 375 vec3 point = vec3(out_voxel) / max(vec3(1.0f), image_points); 376 vec3 world_point = (voxel_transform * vec4(point, 1)).xyz; 377 378 uint32_t out_index = output_index(out_voxel.x, out_voxel.y, out_voxel.z); 379 380 RESULT_TYPE sum = RESULT_TYPE(0); 381 switch (AcquisitionKind) { 382 case AcquisitionKind_FORCES: 383 case AcquisitionKind_UFORCES: 384 { 385 sum = ReadiGroupCount > 1 ? READI_FORCES(world_point) 386 : FORCES(world_point); 387 }break; 388 case AcquisitionKind_HERCULES: 389 case AcquisitionKind_UHERCULES: 390 case AcquisitionKind_HERO_PA: 391 { 392 sum = HERCULES(world_point); 393 }break; 394 case AcquisitionKind_Flash: 395 case AcquisitionKind_RCA_TPW: 396 case AcquisitionKind_RCA_VLS: 397 { 398 sum = RCA(world_point); 399 }break; 400 } 401 402 #if CoherencyWeighting 403 IncoherentOutput(IncoherentFrame).x[out_index] += RESULT_INCOHERENT_CAST(sum); 404 #endif 405 406 Output(output_frame).x[out_index] += RESULT_COHERENT_CAST(sum); 407 }