Skip to content

Commit 66cdc0f

Browse files
committed
core/das: upload array of transmit/receive orientations for RCA methods
each acquistion event could utilize a different orientation pair in these methods
1 parent 4da2190 commit 66cdc0f

File tree

10 files changed

+82
-75
lines changed

10 files changed

+82
-75
lines changed

beamformer.c

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ beamformer_compute_plan_for_block(BeamformerComputeContext *cc, u32 block, Arena
8080
#undef X
8181

8282
#define X(_k, t, ...) t,
83-
GLenum gl_kind[] = {BEAMFORMER_COMPUTE_TEXTURE_LIST};
83+
GLenum gl_kind[] = {BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL};
8484
#undef X
8585
read_only local_persist s8 tex_prefix[] = {
8686
#define X(k, ...) s8_comp(#k "["),
87-
BEAMFORMER_COMPUTE_TEXTURE_LIST
87+
BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL
8888
#undef X
8989
};
9090
glCreateTextures(GL_TEXTURE_1D, BeamformerComputeTextureKind_Count - 1, result->textures);
@@ -439,10 +439,6 @@ das_ubo_from_beamformer_parameters(BeamformerDASUBO *du, BeamformerParameters *b
439439

440440
du->shader_flags = 0;
441441
if (bp->coherency_weighting) du->shader_flags |= BeamformerShaderDASFlags_CoherencyWeighting;
442-
if (bp->transmit_mode == BeamformerRCAOrientation_Columns)
443-
du->shader_flags |= BeamformerShaderDASFlags_TxColumns;
444-
if (bp->receive_mode == BeamformerRCAOrientation_Columns)
445-
du->shader_flags |= BeamformerShaderDASFlags_RxColumns;
446442
}
447443

448444
function void
@@ -698,35 +694,28 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp,
698694
alloc_beamform_frame(&ctx->gl, ctx->averaged_frames + 1, cp->output_points, gl_kind, s8("Averaged Frame"), arena);
699695
}
700696
}break;
701-
case BeamformerParameterBlockRegion_ChannelMapping:
697+
case BeamformerParameterBlockRegion_ChannelMapping:{
698+
cuda_set_channel_mapping(pb->channel_mapping);
699+
} /* FALLTHROUGH */
702700
case BeamformerParameterBlockRegion_FocalVectors:
703701
case BeamformerParameterBlockRegion_SparseElements:
702+
case BeamformerParameterBlockRegion_TransmitReceiveOrientations:
704703
{
705704
BeamformerComputeTextureKind texture_kind = 0;
706-
u32 texture_type = 0, texture_format = 0;
707-
/* TODO(rnp): this whole thing could be a table */
705+
u32 pixel_type = 0, texture_format = 0;
708706
switch (region) {
709-
case BeamformerParameterBlockRegion_ChannelMapping:{
710-
texture_kind = BeamformerComputeTextureKind_ChannelMapping;
711-
texture_type = GL_SHORT;
712-
texture_format = GL_RED_INTEGER;
713-
/* TODO(rnp): cuda lib */
714-
cuda_set_channel_mapping(pb->channel_mapping);
715-
}break;
716-
case BeamformerParameterBlockRegion_FocalVectors:{
717-
texture_kind = BeamformerComputeTextureKind_FocalVectors;
718-
texture_type = GL_FLOAT;
719-
texture_format = GL_RG;
720-
}break;
721-
case BeamformerParameterBlockRegion_SparseElements:{
722-
texture_kind = BeamformerComputeTextureKind_SparseElements;
723-
texture_type = GL_SHORT;
724-
texture_format = GL_RED_INTEGER;
707+
#define X(kind, _gl, tf, pt, ...) \
708+
case BeamformerParameterBlockRegion_## kind:{ \
709+
texture_kind = BeamformerComputeTextureKind_## kind; \
710+
texture_format = tf; \
711+
pixel_type = pt; \
725712
}break;
713+
BEAMFORMER_COMPUTE_TEXTURE_LIST
714+
#undef X
726715
InvalidDefaultCase;
727716
}
728717
glTextureSubImage1D(cp->textures[texture_kind], 0, 0, BeamformerMaxChannelCount,
729-
texture_format, texture_type,
718+
texture_format, pixel_type,
730719
(u8 *)pb + BeamformerParameterBlockRegionOffsets[region]);
731720
}break;
732721
}
@@ -841,6 +830,7 @@ do_compute_shader(BeamformerCtx *ctx, BeamformerComputePlan *cp, BeamformerFrame
841830
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, cc->ping_pong_ssbos[input_ssbo_idx], 0, cp->rf_size);
842831
glBindImageTexture(1, sparse_texture, 0, 0, 0, GL_READ_ONLY, GL_R16I);
843832
glBindImageTexture(2, cp->textures[BeamformerComputeTextureKind_FocalVectors], 0, 0, 0, GL_READ_ONLY, GL_RG32F);
833+
glBindImageTexture(3, cp->textures[BeamformerComputeTextureKind_TransmitReceiveOrientations], 0, 0, 0, GL_READ_ONLY, GL_R8I);
844834

845835
glProgramUniform1ui(program, DAS_CYCLE_T_UNIFORM_LOC, cycle_t++);
846836

beamformer.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,20 @@ static_assert((sizeof(BeamformerDASUBO) & 15) == 0, "UBO size must be a multiple
182182
typedef enum {BEAMFORMER_COMPUTE_UBO_LIST BeamformerComputeUBOKind_Count} BeamformerComputeUBOKind;
183183
#undef X
184184

185+
// X(kind, gl_kind, texture_format, pixel_type)
185186
#define BEAMFORMER_COMPUTE_TEXTURE_LIST \
186-
X(ChannelMapping, GL_R16I) \
187-
X(FocalVectors, GL_RG32F) \
188-
X(SparseElements, GL_R16I) \
187+
X(ChannelMapping, GL_R16I, GL_RED_INTEGER, GL_SHORT) \
188+
X(FocalVectors, GL_RG32F, GL_RG, GL_FLOAT) \
189+
X(SparseElements, GL_R16I, GL_RED_INTEGER, GL_SHORT) \
190+
X(TransmitReceiveOrientations, GL_R8I, GL_RED_INTEGER, GL_BYTE)
191+
192+
#define BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL \
193+
BEAMFORMER_COMPUTE_TEXTURE_LIST \
189194
X(Hadamard, GL_R8I)
190195

191196
typedef enum {
192197
#define X(k, ...) BeamformerComputeTextureKind_##k,
193-
BEAMFORMER_COMPUTE_TEXTURE_LIST
198+
BEAMFORMER_COMPUTE_TEXTURE_LIST_FULL
194199
#undef X
195200
BeamformerComputeTextureKind_Count
196201
} BeamformerComputeTextureKind;

beamformer.meta

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
@Enumeration(RCAOrientation)
4242

43-
@Flags([CoherencyWeighting RxColumns TxColumns])
43+
@Flags([CoherencyWeighting])
4444
}
4545

4646
@Shader(min_max.glsl) MinMax

beamformer_parameters.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,8 @@ typedef enum {BEAMFORMER_CONSTANTS_LIST} BeamformerConstants;
105105
X(acquisition_count, uint32_t, , uint32, 1, "") \
106106
X(das_shader_id, uint32_t, , uint32, 1, "") \
107107
X(time_offset, float, , single, 1, "pulse length correction time [s]") \
108-
X(decode, uint8_t, , uint8, 1, "Decode or just reshape data") \
109-
X(transmit_mode, uint8_t, , uint8, 1, "Method/Orientation of Transmit") \
110-
X(receive_mode, uint8_t, , uint8, 1, "Method/Orientation of Receive") \
111-
X(sampling_mode, uint8_t, , uint8, 1, "")
108+
X(decode, uint16_t, , uint16, 1, "Decode or just reshape data") \
109+
X(sampling_mode, uint16_t, , uint16, 1, "")
112110

113111
#define BEAMFORMER_UI_PARAMS \
114112
X(output_min_coordinate, float, [3], single, 3, "[m] Back-Top-Left corner of output region") \
@@ -125,14 +123,15 @@ typedef enum {BEAMFORMER_CONSTANTS_LIST} BeamformerConstants;
125123
X(decimation_rate, uint32_t, , uint32, 1, "Number of times to decimate")
126124

127125
#define BEAMFORMER_SIMPLE_PARAMS \
128-
X(channel_mapping, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \
129-
X(sparse_elements, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \
130-
X(steering_angles, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \
131-
X(focal_depths, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \
132-
X(compute_stages, int32_t, [BeamformerMaxComputeShaderStages], int32, BeamformerMaxComputeShaderStages) \
133-
X(compute_stage_parameters, int16_t, [BeamformerMaxComputeShaderStages], int16, BeamformerMaxComputeShaderStages) \
134-
X(compute_stages_count, uint32_t, , uint32, 1) \
135-
X(data_kind, int32_t, , int32, 1)
126+
X(channel_mapping, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \
127+
X(sparse_elements, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \
128+
X(transmit_receive_orientations, uint8_t, [BeamformerMaxChannelCount], uint8, BeamformerMaxChannelCount) \
129+
X(steering_angles, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \
130+
X(focal_depths, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \
131+
X(compute_stages, int32_t, [BeamformerMaxComputeShaderStages], int32, BeamformerMaxComputeShaderStages) \
132+
X(compute_stage_parameters, int16_t, [BeamformerMaxComputeShaderStages], int16, BeamformerMaxComputeShaderStages) \
133+
X(compute_stages_count, uint32_t, , uint32, 1) \
134+
X(data_kind, int32_t, , int32, 1)
136135

137136
#define X(name, type, size, ...) type name size;
138137
typedef struct {BEAMFORMER_PARAMS_HEAD} BeamformerParametersHead;

beamformer_shared_memory.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* See LICENSE for license details. */
2-
#define BEAMFORMER_SHARED_MEMORY_VERSION (14UL)
2+
#define BEAMFORMER_SHARED_MEMORY_VERSION (15UL)
33

44
typedef struct BeamformerFrame BeamformerFrame;
55
typedef struct ShaderReloadContext ShaderReloadContext;
@@ -96,11 +96,12 @@ typedef enum {BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST} BeamformerLiveImagingDirt
9696
#undef X
9797

9898
#define BEAMFORMER_PARAMETER_BLOCK_REGION_LIST \
99-
X(ComputePipeline, pipeline) \
100-
X(ChannelMapping, channel_mapping) \
101-
X(FocalVectors, focal_vectors) \
102-
X(Parameters, parameters) \
103-
X(SparseElements, sparse_elements)
99+
X(ComputePipeline, pipeline) \
100+
X(ChannelMapping, channel_mapping) \
101+
X(FocalVectors, focal_vectors) \
102+
X(Parameters, parameters) \
103+
X(SparseElements, sparse_elements) \
104+
X(TransmitReceiveOrientations, transmit_receive_orientations)
104105

105106
typedef enum {
106107
#define X(k, ...) BeamformerParameterBlockRegion_##k,
@@ -138,6 +139,7 @@ typedef struct {
138139

139140
alignas(16) i16 channel_mapping[BeamformerMaxChannelCount];
140141
alignas(16) i16 sparse_elements[BeamformerMaxChannelCount];
142+
alignas(16) u8 transmit_receive_orientations[BeamformerMaxChannelCount];
141143
/* NOTE(rnp): interleaved transmit angle, focal depth pairs */
142144
alignas(16) v2 focal_vectors[BeamformerMaxChannelCount];
143145
} BeamformerParameterBlock;

generated/beamformer.meta.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ typedef enum {
3939
BeamformerShaderDASFlags_Sparse = (1 << 1),
4040
BeamformerShaderDASFlags_Interpolate = (1 << 2),
4141
BeamformerShaderDASFlags_CoherencyWeighting = (1 << 3),
42-
BeamformerShaderDASFlags_RxColumns = (1 << 4),
43-
BeamformerShaderDASFlags_TxColumns = (1 << 5),
4442
} BeamformerShaderDASFlags;
4543

4644
typedef enum {
@@ -238,8 +236,6 @@ read_only global s8 beamformer_shader_local_header_strings[] = {
238236
"#define ShaderFlags_Sparse (1 << 1)\n"
239237
"#define ShaderFlags_Interpolate (1 << 2)\n"
240238
"#define ShaderFlags_CoherencyWeighting (1 << 3)\n"
241-
"#define ShaderFlags_RxColumns (1 << 4)\n"
242-
"#define ShaderFlags_TxColumns (1 << 5)\n"
243239
"\n"),
244240
{0},
245241
{0},

helpers/ogl_beamformer_lib.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,10 @@ beamformer_wait_for_compute_dispatch(i32 timeout_ms)
422422
}
423423

424424
#define BEAMFORMER_UPLOAD_FNS \
425-
X(channel_mapping, i16, 1, ChannelMapping) \
426-
X(sparse_elements, i16, 1, SparseElements) \
427-
X(focal_vectors, f32, 2, FocalVectors)
425+
X(channel_mapping, i16, 1, ChannelMapping) \
426+
X(focal_vectors, f32, 2, FocalVectors) \
427+
X(sparse_elements, i16, 1, SparseElements) \
428+
X(transmit_receive_orientations, u8, 1, TransmitReceiveOrientations)
428429

429430
#define X(name, dtype, elements, region_name) \
430431
b32 beamformer_push_##name ##_at(dtype *data, u32 count, u32 block) { \
@@ -503,17 +504,20 @@ beamformer_push_simple_parameters_at(BeamformerSimpleParameters *bp, u32 block)
503504
{
504505
b32 result = validate_simple_parameters(bp);
505506
if (result) {
507+
alignas(64) v2 focal_vectors[countof(bp->steering_angles)];
508+
for (u32 i = 0; i < countof(bp->steering_angles); i++)
509+
focal_vectors[i] = (v2){{bp->steering_angles[i], bp->focal_depths[i]}};
510+
506511
result &= beamformer_push_parameters_at((BeamformerParameters *)bp, block);
507512
result &= beamformer_push_pipeline_at(bp->compute_stages, bp->compute_stages_count, (BeamformerDataKind)bp->data_kind, block);
508513
result &= beamformer_push_channel_mapping_at(bp->channel_mapping, bp->channel_count, block);
514+
result &= beamformer_push_focal_vectors_at((f32 *)focal_vectors, countof(focal_vectors), block);
515+
result &= beamformer_push_transmit_receive_orientations_at(bp->transmit_receive_orientations,
516+
bp->acquisition_count, block);
517+
509518
if (bp->das_shader_id == BeamformerDASKind_UFORCES || bp->das_shader_id == BeamformerDASKind_UHERCULES)
510519
result &= beamformer_push_sparse_elements_at(bp->sparse_elements, bp->acquisition_count, block);
511520

512-
alignas(64) v2 focal_vectors[countof(bp->steering_angles)];
513-
for (u32 i = 0; i < countof(bp->steering_angles); i++)
514-
focal_vectors[i] = (v2){{bp->steering_angles[i], bp->focal_depths[i]}};
515-
result &= beamformer_push_focal_vectors_at((f32 *)focal_vectors, countof(focal_vectors), block);
516-
517521
for (u32 stage = 0; stage < bp->compute_stages_count; stage++)
518522
result &= beamformer_set_pipeline_stage_parameters_at(stage, bp->compute_stage_parameters[stage], block);
519523
}

helpers/ogl_beamformer_lib_base.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ LIB_FN uint32_t beamformer_push_sparse_elements_at(int16_t *elements, uint32_t c
112112
LIB_FN uint32_t beamformer_push_focal_vectors(float *vectors, uint32_t count);
113113
LIB_FN uint32_t beamformer_push_focal_vectors_at(float *vectors, uint32_t count, uint32_t parameter_slot);
114114

115+
LIB_FN uint32_t beamformer_push_transmit_receive_orientations(uint8_t *values, uint32_t count, uint32_t parameter_slot);
116+
LIB_FN uint32_t beamformer_push_transmit_receive_orientations_at(uint8_t *values, uint32_t count, uint32_t parameter_slot);
117+
115118
////////////////////
116119
// Filter Creation
117120

shaders/das.glsl

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ layout(TEXTURE_KIND, binding = 0) writeonly restrict uniform image3D u_out_data
4040

4141
layout(r16i, binding = 1) readonly restrict uniform iimage1D sparse_elements;
4242
layout(rg32f, binding = 2) readonly restrict uniform image1D focal_vectors;
43+
layout(r8i, binding = 3) readonly restrict uniform iimage1D transmit_receive_orientations;
44+
45+
#define RX_ORIENTATION_MASK (1 << 0)
46+
#define TX_ORIENTATION_MASK (1 << 1)
4347

4448
#define C_SPLINE 0.5
4549

@@ -139,8 +143,8 @@ float cylindrical_wave_transmit_distance(vec3 point, float focal_depth, float tr
139143
#if (ShaderFlags & ShaderFlags_Fast)
140144
RESULT_TYPE RCA(vec3 world_point)
141145
{
142-
bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0);
143-
bool rx_rows = bool((shader_flags & ShaderFlags_RxColumns) == 0);
146+
bool tx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & TX_ORIENTATION_MASK) == 0;
147+
bool rx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & RX_ORIENTATION_MASK) == 0;
144148
vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
145149
vec2 focal_vector = imageLoad(focal_vectors, u_channel).xy;
146150
float transmit_angle = radians(focal_vector.x);
@@ -170,15 +174,14 @@ RESULT_TYPE RCA(vec3 world_point)
170174
#else
171175
RESULT_TYPE RCA(vec3 world_point)
172176
{
173-
bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0);
174-
bool rx_rows = bool((shader_flags & ShaderFlags_RxColumns) == 0);
175-
vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
176-
177177
RESULT_TYPE result = RESULT_TYPE(0);
178178
for (int transmit = 0; transmit < acquisition_count; transmit++) {
179179
vec2 focal_vector = imageLoad(focal_vectors, transmit).xy;
180180
float transmit_angle = radians(focal_vector.x);
181181
float focal_depth = focal_vector.y;
182+
bool tx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & TX_ORIENTATION_MASK) == 0;
183+
bool rx_rows = (imageLoad(transmit_receive_orientations, u_channel).x & RX_ORIENTATION_MASK) == 0;
184+
vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
182185

183186
float transmit_distance;
184187
if (isinf(focal_depth)) {
@@ -208,8 +211,8 @@ RESULT_TYPE RCA(vec3 world_point)
208211
RESULT_TYPE HERCULES(vec3 world_point)
209212
{
210213
vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
211-
bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0);
212-
bool rx_cols = bool((shader_flags & ShaderFlags_RxColumns));
214+
bool tx_rows = (imageLoad(transmit_receive_orientations, 0).x & TX_ORIENTATION_MASK) == 0;
215+
bool rx_cols = (imageLoad(transmit_receive_orientations, 0).x & RX_ORIENTATION_MASK) != 0;
213216
vec2 focal_vector = imageLoad(focal_vectors, 0).xy;
214217
float transmit_angle = radians(focal_vector.x);
215218
float focal_depth = focal_vector.y;
@@ -245,8 +248,8 @@ RESULT_TYPE HERCULES(vec3 world_point)
245248
RESULT_TYPE HERCULES(vec3 world_point)
246249
{
247250
vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
248-
bool tx_rows = bool((shader_flags & ShaderFlags_TxColumns) == 0);
249-
bool rx_cols = bool((shader_flags & ShaderFlags_RxColumns));
251+
bool tx_rows = (imageLoad(transmit_receive_orientations, 0).x & TX_ORIENTATION_MASK) == 0;
252+
bool rx_cols = (imageLoad(transmit_receive_orientations, 0).x & RX_ORIENTATION_MASK) != 0;
250253
vec2 focal_vector = imageLoad(focal_vectors, 0).xy;
251254
float transmit_angle = radians(focal_vector.x);
252255
float focal_depth = focal_vector.y;

tests/throughput.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,6 @@ beamformer_parameters_from_zemp_bp_v1(zemp_bp_v1 *zbp, BeamformerParameters *out
205205
out->sample_count = zbp->decoded_data_dim[0];
206206
out->channel_count = zbp->decoded_data_dim[1];
207207
out->acquisition_count = zbp->decoded_data_dim[2];
208-
out->transmit_mode = (u8)((zbp->transmit_mode & 2) >> 1);
209-
out->receive_mode = (u8)((zbp->transmit_mode & 1) >> 0);
210208
out->decode = (u8)zbp->decode_mode;
211209
out->das_shader_id = zbp->beamform_mode;
212210
out->time_offset = zbp->time_offset;
@@ -368,11 +366,18 @@ execute_study(s8 study, Arena arena, Stream path, Options *options)
368366
}
369367

370368
{
371-
alignas(64) v2 focal_vectors[countof(zbp->focal_depths)];
372-
for (u32 i = 0; i < countof(zbp->focal_depths); i++)
369+
alignas(64) v2 focal_vectors[BeamformerMaxChannelCount];
370+
for (u32 i = 0; i < countof(focal_vectors); i++)
373371
focal_vectors[i] = (v2){{zbp->transmit_angles[i], zbp->focal_depths[i]}};
374372
beamformer_push_focal_vectors((f32 *)focal_vectors, countof(focal_vectors));
375373
}
374+
{
375+
alignas(64) u8 transmit_receive_orientations[BeamformerMaxChannelCount];
376+
for (u32 i = 0; i < countof(transmit_receive_orientations); i++)
377+
transmit_receive_orientations[i] = (u8)zbp->transmit_mode;
378+
beamformer_push_transmit_receive_orientations(transmit_receive_orientations,
379+
countof(transmit_receive_orientations));
380+
}
376381

377382
beamformer_push_channel_mapping(zbp->channel_mapping, countof(zbp->channel_mapping));
378383
beamformer_push_sparse_elements(zbp->sparse_elements, countof(zbp->sparse_elements));

0 commit comments

Comments
 (0)