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 }