Skip to content

Commit 19d0702

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 0e29d54 commit 19d0702

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
@@ -76,11 +76,11 @@ beamformer_compute_plan_for_block(BeamformerComputeContext *cc, u32 block, Arena
7676
#undef X
7777

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

436436
du->shader_flags = 0;
437437
if (bp->coherency_weighting) du->shader_flags |= BeamformerShaderDASFlags_CoherencyWeighting;
438-
if (bp->transmit_mode == BeamformerRCAOrientation_Columns)
439-
du->shader_flags |= BeamformerShaderDASFlags_TxColumns;
440-
if (bp->receive_mode == BeamformerRCAOrientation_Columns)
441-
du->shader_flags |= BeamformerShaderDASFlags_RxColumns;
442438
}
443439

444440
function void
@@ -693,35 +689,28 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp,
693689
alloc_beamform_frame(&ctx->gl, ctx->averaged_frames + 1, cp->output_points, gl_kind, s8("Averaged Frame"), arena);
694690
}
695691
}break;
696-
case BeamformerParameterBlockRegion_ChannelMapping:
692+
case BeamformerParameterBlockRegion_ChannelMapping:{
693+
cuda_set_channel_mapping(pb->channel_mapping);
694+
} /* FALLTHROUGH */
697695
case BeamformerParameterBlockRegion_FocalVectors:
698696
case BeamformerParameterBlockRegion_SparseElements:
697+
case BeamformerParameterBlockRegion_TransmitReceiveOrientations:
699698
{
700699
BeamformerComputeTextureKind texture_kind = 0;
701-
u32 texture_type = 0, texture_format = 0;
702-
/* TODO(rnp): this whole thing could be a table */
700+
u32 pixel_type = 0, texture_format = 0;
703701
switch (region) {
704-
case BeamformerParameterBlockRegion_ChannelMapping:{
705-
texture_kind = BeamformerComputeTextureKind_ChannelMapping;
706-
texture_type = GL_SHORT;
707-
texture_format = GL_RED_INTEGER;
708-
/* TODO(rnp): cuda lib */
709-
cuda_set_channel_mapping(pb->channel_mapping);
710-
}break;
711-
case BeamformerParameterBlockRegion_FocalVectors:{
712-
texture_kind = BeamformerComputeTextureKind_FocalVectors;
713-
texture_type = GL_FLOAT;
714-
texture_format = GL_RG;
715-
}break;
716-
case BeamformerParameterBlockRegion_SparseElements:{
717-
texture_kind = BeamformerComputeTextureKind_SparseElements;
718-
texture_type = GL_SHORT;
719-
texture_format = GL_RED_INTEGER;
702+
#define X(kind, _gl, tf, pt, ...) \
703+
case BeamformerParameterBlockRegion_## kind:{ \
704+
texture_kind = BeamformerComputeTextureKind_## kind; \
705+
texture_format = tf; \
706+
pixel_type = pt; \
720707
}break;
708+
BEAMFORMER_COMPUTE_TEXTURE_LIST
709+
#undef X
721710
InvalidDefaultCase;
722711
}
723712
glTextureSubImage1D(cp->textures[texture_kind], 0, 0, BeamformerMaxChannelCount,
724-
texture_format, texture_type,
713+
texture_format, pixel_type,
725714
(u8 *)pb + BeamformerParameterBlockRegionOffsets[region]);
726715
}break;
727716
}
@@ -836,6 +825,7 @@ do_compute_shader(BeamformerCtx *ctx, BeamformerComputePlan *cp, BeamformerFrame
836825
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, cc->ping_pong_ssbos[input_ssbo_idx], 0, cp->rf_size);
837826
glBindImageTexture(1, sparse_texture, 0, 0, 0, GL_READ_ONLY, GL_R16I);
838827
glBindImageTexture(2, cp->textures[BeamformerComputeTextureKind_FocalVectors], 0, 0, 0, GL_READ_ONLY, GL_RG32F);
828+
glBindImageTexture(3, cp->textures[BeamformerComputeTextureKind_TransmitReceiveOrientations], 0, 0, 0, GL_READ_ONLY, GL_R8I);
839829

840830
glProgramUniform1ui(program, DAS_CYCLE_T_UNIFORM_LOC, cycle_t++);
841831

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
@@ -45,7 +45,7 @@
4545

4646
@Enumeration(RCAOrientation)
4747

48-
@Flags([CoherencyWeighting RxColumns TxColumns])
48+
@Flags([CoherencyWeighting])
4949
}
5050

5151
@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
@@ -43,8 +43,6 @@ typedef enum {
4343
BeamformerShaderDASFlags_Sparse = (1 << 1),
4444
BeamformerShaderDASFlags_Interpolate = (1 << 2),
4545
BeamformerShaderDASFlags_CoherencyWeighting = (1 << 3),
46-
BeamformerShaderDASFlags_RxColumns = (1 << 4),
47-
BeamformerShaderDASFlags_TxColumns = (1 << 5),
4846
} BeamformerShaderDASFlags;
4947

5048
typedef enum {
@@ -245,8 +243,6 @@ read_only global s8 beamformer_shader_local_header_strings[] = {
245243
"#define ShaderFlags_Sparse (1 << 1)\n"
246244
"#define ShaderFlags_Interpolate (1 << 2)\n"
247245
"#define ShaderFlags_CoherencyWeighting (1 << 3)\n"
248-
"#define ShaderFlags_RxColumns (1 << 4)\n"
249-
"#define ShaderFlags_TxColumns (1 << 5)\n"
250246
"\n"),
251247
{0},
252248
{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);
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)