diff --git a/src/asahi/lib/cmdbuf.xml b/src/asahi/lib/cmdbuf.xml index 9d15ab320c2..c9b2564cdc4 100644 --- a/src/asahi/lib/cmdbuf.xml +++ b/src/asahi/lib/cmdbuf.xml @@ -471,13 +471,18 @@ + + + + - + + - + @@ -486,7 +491,7 @@ - + @@ -494,7 +499,7 @@ - + @@ -502,69 +507,51 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/asahi/lib/decode.c b/src/asahi/lib/decode.c index a0c3835967c..242a6ea68d9 100644 --- a/src/asahi/lib/decode.c +++ b/src/asahi/lib/decode.c @@ -305,64 +305,63 @@ agxdecode_stateful(uint64_t va, const char *label, decode_cmd decoder, bool verb } } -unsigned COUNTER = 0; static unsigned -agxdecode_pipeline(const uint8_t *map, uint64_t *link, UNUSED bool verbose) +agxdecode_usc(const uint8_t *map, UNUSED uint64_t *link, UNUSED bool verbose) { - uint8_t zeroes[16] = { 0 }; + enum agx_usc_control type = map[0]; - if (map[0] == 0x4D && (map[11] & BITFIELD_BIT(5))) { - agx_unpack(agxdecode_dump_stream, map, SET_SHADER_EXTENDED, cmd); - DUMP_UNPACKED(SET_SHADER_EXTENDED, cmd, "Set shader\n"); +#define USC_CASE(name, human) \ + case AGX_USC_CONTROL_##name: { \ + DUMP_CL(USC_##name, map, human); \ + return AGX_USC_##name##_LENGTH; \ + } - if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) { - agxdecode_log("Preshader\n"); - agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048), - 2048, agxdecode_dump_stream); - agxdecode_log("\n---\n"); + switch (type) { + case AGX_USC_CONTROL_NO_PRESHADER: { + DUMP_CL(USC_NO_PRESHADER, map, "No preshader"); + return STATE_DONE; + } + + case AGX_USC_CONTROL_PRESHADER: { + agx_unpack(agxdecode_dump_stream, map, USC_PRESHADER, ctrl); + DUMP_UNPACKED(USC_PRESHADER, ctrl, "Preshader\n"); + + agx_disassemble(agxdecode_fetch_gpu_mem(ctrl.code, 2048), + 8192, agxdecode_dump_stream); + + return STATE_DONE; + } + + case AGX_USC_CONTROL_SHADER: { + agx_unpack(agxdecode_dump_stream, map, USC_SHADER, ctrl); + DUMP_UNPACKED(USC_SHADER, ctrl, "Shader\n"); + + agxdecode_log("\n"); + agx_disassemble(agxdecode_fetch_gpu_mem(ctrl.code, 2048), + 8192, agxdecode_dump_stream); + agxdecode_log("\n"); + + return AGX_USC_SHADER_LENGTH; + } + + case AGX_USC_CONTROL_SAMPLER: { + agx_unpack(agxdecode_dump_stream, map, USC_SAMPLER, temp); + DUMP_UNPACKED(USC_SAMPLER, temp, "Sampler state\n"); + + uint8_t *samp = agxdecode_fetch_gpu_mem(temp.buffer, + AGX_SAMPLER_LENGTH * temp.count); + + for (unsigned i = 0; i < temp.count; ++i) { + DUMP_CL(SAMPLER, samp, "Sampler"); + samp += AGX_SAMPLER_LENGTH; } - agxdecode_log("\n"); - agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048), - 2048, agxdecode_dump_stream); - agxdecode_log("\n"); + return AGX_USC_SAMPLER_LENGTH; + } - char *name; - asprintf(&name, "file%u.bin", COUNTER++); - FILE *fp = fopen(name, "wb"); - fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp); - fclose(fp); - free(name); - agxdecode_log("\n"); - - return AGX_SET_SHADER_EXTENDED_LENGTH; - } else if (map[0] == 0x4D) { - agx_unpack(agxdecode_dump_stream, map, SET_SHADER, cmd); - DUMP_UNPACKED(SET_SHADER, cmd, "Set shader\n"); - fflush(agxdecode_dump_stream); - - if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) { - agxdecode_log("Preshader\n"); - agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048), - 2048, agxdecode_dump_stream); - agxdecode_log("\n---\n"); - } - - agxdecode_log("\n"); - agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048), - 2048, agxdecode_dump_stream); - char *name; - asprintf(&name, "file%u.bin", COUNTER++); - FILE *fp = fopen(name, "wb"); - fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp); - fclose(fp); - free(name); - agxdecode_log("\n"); - - return AGX_SET_SHADER_LENGTH; - } else if (map[0] == 0xDD) { - agx_unpack(agxdecode_dump_stream, map, BIND_TEXTURE, temp); - DUMP_UNPACKED(BIND_TEXTURE, temp, "Bind texture\n"); + case AGX_USC_CONTROL_TEXTURE: { + agx_unpack(agxdecode_dump_stream, map, USC_TEXTURE, temp); + DUMP_UNPACKED(USC_TEXTURE, temp, "Texture state\n"); uint8_t *tex = agxdecode_fetch_gpu_mem(temp.buffer, AGX_TEXTURE_LENGTH * temp.count); @@ -376,29 +375,22 @@ agxdecode_pipeline(const uint8_t *map, uint64_t *link, UNUSED bool verbose) tex += AGX_TEXTURE_LENGTH; } - return AGX_BIND_TEXTURE_LENGTH; - } else if (map[0] == 0x9D) { - agx_unpack(agxdecode_dump_stream, map, BIND_SAMPLER, temp); - DUMP_UNPACKED(BIND_SAMPLER, temp, "Bind sampler\n"); - - uint8_t *samp = agxdecode_fetch_gpu_mem(temp.buffer, - AGX_SAMPLER_LENGTH * temp.count); - - for (unsigned i = 0; i < temp.count; ++i) { - DUMP_CL(SAMPLER, samp, "Sampler"); - samp += AGX_SAMPLER_LENGTH; - } - - return AGX_BIND_SAMPLER_LENGTH; - } else if (map[0] == 0x1D) { - DUMP_CL(BIND_UNIFORM, map, "Bind uniform"); - return AGX_BIND_UNIFORM_LENGTH; - } else if (memcmp(map, zeroes, 16) == 0) { - /* TODO: Termination */ - return STATE_DONE; - } else { - return 0; + return AGX_USC_TEXTURE_LENGTH; } + + USC_CASE(FRAGMENT_PROPERTIES, "Fragment properties"); + USC_CASE(UNIFORM, "Uniform"); + USC_CASE(SHARED, "Shared"); + USC_CASE(REGISTERS, "Registers"); + + default: + fprintf(agxdecode_dump_stream, "Unknown USC control type: %u\n", + type); + hexdump(agxdecode_dump_stream, map, 8, false); + return 8; + } + +#undef USC_CASE } #define PPP_PRINT(map, header_name, struct_name, human) \ @@ -438,7 +430,7 @@ agxdecode_record(uint64_t va, size_t size, bool verbose) if (hdr.fragment_shader) { agx_unpack(agxdecode_dump_stream, map, FRAGMENT_SHADER, frag); - agxdecode_stateful(frag.pipeline, "Fragment pipeline", agxdecode_pipeline, verbose); + agxdecode_stateful(frag.pipeline, "Fragment pipeline", agxdecode_usc, verbose); if (frag.cf_bindings) { uint8_t *cf = agxdecode_fetch_gpu_mem(frag.cf_bindings, 128); @@ -476,7 +468,7 @@ agxdecode_cdm(const uint8_t *map, uint64_t *link, bool verbose) switch (block_type) { case AGX_CDM_BLOCK_TYPE_COMPUTE_KERNEL: { agx_unpack(agxdecode_dump_stream, map, LAUNCH, cmd); - agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose); + agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_usc, verbose); DUMP_UNPACKED(LAUNCH, cmd, "Launch\n"); return AGX_LAUNCH_LENGTH; } @@ -541,7 +533,7 @@ agxdecode_vdm(const uint8_t *map, uint64_t *link, bool verbose) agx_unpack(agxdecode_dump_stream, map, VDM_STATE_VERTEX_SHADER_WORD_1, word_1); fprintf(agxdecode_dump_stream, "Pipeline %X\n", (uint32_t) word_1.pipeline); - agxdecode_stateful(word_1.pipeline, "Pipeline", agxdecode_pipeline, verbose); + agxdecode_stateful(word_1.pipeline, "Pipeline", agxdecode_usc, verbose); } VDM_PRINT(vertex_shader_word_1, VERTEX_SHADER_WORD_1, "Vertex shader word 1"); @@ -615,24 +607,24 @@ agxdecode_gfx(uint32_t *cmdbuf, uint64_t encoder, bool verbose) if (gfx.clear_pipeline_unk) { fprintf(agxdecode_dump_stream, "Unk: %X\n", gfx.clear_pipeline_unk); agxdecode_stateful(gfx.clear_pipeline, "Clear pipeline", - agxdecode_pipeline, verbose); + agxdecode_usc, verbose); } if (gfx.store_pipeline_unk) { assert(gfx.store_pipeline_unk == 0x4); agxdecode_stateful(gfx.store_pipeline, "Store pipeline", - agxdecode_pipeline, verbose); + agxdecode_usc, verbose); } assert((gfx.partial_reload_pipeline_unk & 0xF) == 0x4); if (gfx.partial_reload_pipeline) { agxdecode_stateful(gfx.partial_reload_pipeline, - "Partial reload pipeline", agxdecode_pipeline, verbose); + "Partial reload pipeline", agxdecode_usc, verbose); } if (gfx.partial_store_pipeline) { agxdecode_stateful(gfx.partial_store_pipeline, - "Partial store pipeline", agxdecode_pipeline, verbose); + "Partial store pipeline", agxdecode_usc, verbose); } } diff --git a/src/gallium/drivers/asahi/agx_state.c b/src/gallium/drivers/asahi/agx_state.c index 23be68e6e82..7a324d87faf 100644 --- a/src/gallium/drivers/asahi/agx_state.c +++ b/src/gallium/drivers/asahi/agx_state.c @@ -1193,20 +1193,68 @@ agx_delete_shader_state(struct pipe_context *ctx, free(so); } -/* Pipeline consists of a sequence of binding commands followed by a set shader command */ +struct agx_usc_builder { + struct agx_ptr T; + uint8_t *head; + +#ifndef NDEBUG + size_t size; +#endif +}; + +static struct agx_usc_builder +agx_alloc_usc_control(struct agx_pool *pool, + unsigned num_reg_bindings) +{ + STATIC_ASSERT(AGX_USC_TEXTURE_LENGTH == AGX_USC_UNIFORM_LENGTH); + STATIC_ASSERT(AGX_USC_SAMPLER_LENGTH == AGX_USC_UNIFORM_LENGTH); + + size_t size = AGX_USC_UNIFORM_LENGTH * num_reg_bindings; + + size += AGX_USC_SHARED_LENGTH; + size += AGX_USC_SHADER_LENGTH; + size += AGX_USC_REGISTERS_LENGTH; + size += MAX2(AGX_USC_NO_PRESHADER_LENGTH, AGX_USC_PRESHADER_LENGTH); + size += AGX_USC_FRAGMENT_PROPERTIES_LENGTH; + + struct agx_usc_builder b = { + .T = agx_pool_alloc_aligned(pool, size, 64), + +#ifndef NDEBUG + .size = size, +#endif + }; + + b.head = (uint8_t *) b.T.cpu; + + return b; +} + +static bool +agx_usc_builder_validate(struct agx_usc_builder *b, size_t size) +{ +#ifndef NDEBUG + assert(((b->head - (uint8_t *) b->T.cpu) + size) <= b->size); +#endif + + return true; +} + +#define agx_usc_pack(b, struct_name, template) \ + for (bool it = agx_usc_builder_validate((b), AGX_USC_##struct_name##_LENGTH); \ + it; it = false, (b)->head += AGX_USC_##struct_name##_LENGTH) \ + agx_pack((b)->head, USC_##struct_name, template) + +static uint32_t +agx_usc_fini(struct agx_usc_builder *b) +{ + assert(b->T.gpu <= (1ull << 32) && "pipelines must be in low memory"); + return b->T.gpu; +} + static uint32_t agx_build_pipeline(struct agx_context *ctx, struct agx_compiled_shader *cs, enum pipe_shader_type stage) { - /* Pipelines must be 64-byte aligned */ - struct agx_ptr ptr = agx_pool_alloc_aligned(&ctx->batch->pipeline_pool, - (cs->info.push_ranges * AGX_BIND_UNIFORM_LENGTH) + - AGX_BIND_TEXTURE_LENGTH + - AGX_BIND_SAMPLER_LENGTH + - AGX_SET_SHADER_EXTENDED_LENGTH + 8, - 64); - - uint8_t *record = ptr.cpu; - unsigned nr_textures = ctx->stage[stage].texture_count; unsigned nr_samplers = ctx->stage[stage].sampler_count; @@ -1235,25 +1283,26 @@ agx_build_pipeline(struct agx_context *ctx, struct agx_compiled_shader *cs, enum samplers[i] = sampler->desc; } + struct agx_usc_builder b = + agx_alloc_usc_control(&ctx->batch->pipeline_pool, + cs->info.push_ranges + 2); + if (nr_textures) { - agx_pack(record, BIND_TEXTURE, cfg) { + agx_usc_pack(&b, TEXTURE, cfg) { cfg.start = 0; cfg.count = nr_textures; cfg.buffer = T_tex.gpu; } ctx->batch->textures = T_tex.gpu; - record += AGX_BIND_TEXTURE_LENGTH; } if (nr_samplers) { - agx_pack(record, BIND_SAMPLER, cfg) { + agx_usc_pack(&b, SAMPLER, cfg) { cfg.start = 0; cfg.count = nr_samplers; cfg.buffer = T_samp.gpu; } - - record += AGX_BIND_SAMPLER_LENGTH; } /* Must only upload uniforms after uploading textures so we can implement the @@ -1262,97 +1311,81 @@ agx_build_pipeline(struct agx_context *ctx, struct agx_compiled_shader *cs, enum for (unsigned i = 0; i < cs->info.push_ranges; ++i) { struct agx_push push = cs->info.push[i]; - agx_pack(record, BIND_UNIFORM, cfg) { + agx_usc_pack(&b, UNIFORM, cfg) { cfg.start_halfs = push.base; cfg.size_halfs = push.length; cfg.buffer = agx_push_location(ctx, push, stage); } - - record += AGX_BIND_UNIFORM_LENGTH; } - /* TODO: Can we prepack this? */ - if (stage == PIPE_SHADER_FRAGMENT) { - bool writes_sample_mask = ctx->fs->info.writes_sample_mask; - - agx_pack(record, SET_SHADER_EXTENDED, cfg) { - cfg.code = cs->bo->ptr.gpu; - cfg.register_quadwords = 0; - cfg.unk_3 = 0x8d; - cfg.unk_1 = 0x10bd; + agx_usc_pack(&b, SHARED, cfg) { + if (stage == PIPE_SHADER_FRAGMENT) { + cfg.uses_shared_memory = true; + cfg.unk_1 = 0x10bc; cfg.shared_memory_per_threadgroup_in_256_bytes = 32; - cfg.unk_2 = 0x0d; - cfg.loads_varyings = true; - cfg.fragment_parameters.early_z_testing = !writes_sample_mask; - cfg.unk_4 = 0x800; - cfg.preshader_unk = 0xc080; - cfg.spill_size = 0x2; + } else { + cfg.unk_1 = 0x90; } - - record += AGX_SET_SHADER_EXTENDED_LENGTH; - } else { - agx_pack(record, SET_SHADER, cfg) { - cfg.code = cs->bo->ptr.gpu; - cfg.register_quadwords = 0; - cfg.unk_2b = cs->info.varyings.vs.nr_index; - cfg.unk_2 = 0x0d; - } - - record += AGX_SET_SHADER_LENGTH; } - /* End pipeline */ - memset(record, 0, 8); - assert(ptr.gpu < (1ull << 32)); - return ptr.gpu; + agx_usc_pack(&b, SHADER, cfg) { + cfg.loads_varyings = (stage == PIPE_SHADER_FRAGMENT); + cfg.code = cs->bo->ptr.gpu; + cfg.unk_2 = (stage == PIPE_SHADER_FRAGMENT) ? 2 : 3; + } + + agx_usc_pack(&b, REGISTERS, cfg) { + cfg.register_quadwords = 0; + cfg.unk_1 = (stage == PIPE_SHADER_FRAGMENT); + } + + if (stage == PIPE_SHADER_FRAGMENT) { + agx_usc_pack(&b, FRAGMENT_PROPERTIES, cfg) { + bool writes_sample_mask = ctx->fs->info.writes_sample_mask; + cfg.early_z_testing = !writes_sample_mask; + cfg.unk_4 = 0x2; + cfg.unk_5 = 0x0; + } + } + + agx_usc_pack(&b, NO_PRESHADER, cfg); + + return agx_usc_fini(&b); } /* Internal pipelines (TODO: refactor?) */ uint64_t agx_build_clear_pipeline(struct agx_context *ctx, uint32_t code, uint64_t clear_buf) { - struct agx_ptr ptr = agx_pool_alloc_aligned(&ctx->batch->pipeline_pool, - (1 * AGX_BIND_UNIFORM_LENGTH) + - AGX_SET_SHADER_EXTENDED_LENGTH + 8, - 64); + struct agx_usc_builder b = + agx_alloc_usc_control(&ctx->batch->pipeline_pool, 1); - uint8_t *record = ptr.cpu; - - agx_pack(record, BIND_UNIFORM, cfg) { + agx_usc_pack(&b, UNIFORM, cfg) { cfg.start_halfs = (6 * 2); cfg.size_halfs = 4; cfg.buffer = clear_buf; } - record += AGX_BIND_UNIFORM_LENGTH; - - /* TODO: Can we prepack this? */ - agx_pack(record, SET_SHADER, cfg) { - cfg.code = code; - cfg.unk_1 = 0x10bd; + agx_usc_pack(&b, SHARED, cfg) { + cfg.uses_shared_memory = true; + cfg.unk_1 = 0x10bc; cfg.shared_memory_per_threadgroup_in_256_bytes = 32; - cfg.unk_2 = 0x0d; - cfg.unk_3 = 0x8d; - cfg.register_quadwords = 1; } - record += AGX_SET_SHADER_LENGTH; + agx_usc_pack(&b, SHADER, cfg) { + cfg.code = code; + cfg.unk_2 = 3; + } - /* End pipeline */ - memset(record, 0, 8); - return ptr.gpu; + agx_usc_pack(&b, REGISTERS, cfg) cfg.register_quadwords = 1; + agx_usc_pack(&b, NO_PRESHADER, cfg); + + return agx_usc_fini(&b); } uint64_t agx_build_reload_pipeline(struct agx_context *ctx, uint32_t code, struct pipe_surface *surf) { - struct agx_ptr ptr = agx_pool_alloc_aligned(&ctx->batch->pipeline_pool, - (1 * AGX_BIND_TEXTURE_LENGTH) + - (1 * AGX_BIND_SAMPLER_LENGTH) + - AGX_SET_SHADER_EXTENDED_LENGTH + 8, - 64); - - uint8_t *record = ptr.cpu; struct agx_ptr sampler = agx_pool_alloc_aligned(&ctx->batch->pool, AGX_SAMPLER_LENGTH, 64); struct agx_ptr texture = agx_pool_alloc_aligned(&ctx->batch->pool, AGX_TEXTURE_LENGTH, 64); @@ -1398,91 +1431,70 @@ agx_build_reload_pipeline(struct agx_context *ctx, uint32_t code, struct pipe_su cfg.unk_tiled = true; } - agx_pack(record, BIND_TEXTURE, cfg) { + struct agx_usc_builder b = + agx_alloc_usc_control(&ctx->batch->pipeline_pool, 2); + + agx_usc_pack(&b, TEXTURE, cfg) { cfg.start = 0; cfg.count = 1; cfg.buffer = texture.gpu; } - record += AGX_BIND_TEXTURE_LENGTH; - - agx_pack(record, BIND_SAMPLER, cfg) { + agx_usc_pack(&b, SAMPLER, cfg) { cfg.start = 0; cfg.count = 1; cfg.buffer = sampler.gpu; } - record += AGX_BIND_SAMPLER_LENGTH; - - /* TODO: Can we prepack this? */ - agx_pack(record, SET_SHADER_EXTENDED, cfg) { - cfg.code = code; - cfg.register_quadwords = 0; - cfg.unk_3 = 0x8d; - cfg.unk_2 = 0x0d; - cfg.unk_4 = 0; - cfg.fragment_parameters.unk_1 = 0x880100; - cfg.fragment_parameters.early_z_testing = false; - cfg.fragment_parameters.unk_2 = false; - cfg.fragment_parameters.unk_3 = 0; - cfg.preshader_mode = 0; // XXX + agx_usc_pack(&b, SHARED, cfg) { + cfg.uses_shared_memory = true; + cfg.unk_1 = 0x10bc; + cfg.shared_memory_per_threadgroup_in_256_bytes = 32; } - record += AGX_SET_SHADER_EXTENDED_LENGTH; + agx_usc_pack(&b, SHADER, cfg) { + cfg.code = code; + cfg.unk_2 = 3; + } - /* End pipeline */ - memset(record, 0, 8); - return ptr.gpu; + agx_usc_pack(&b, REGISTERS, cfg) cfg.register_quadwords = 0; + agx_usc_pack(&b, NO_PRESHADER, cfg); + + return agx_usc_fini(&b); } uint64_t agx_build_store_pipeline(struct agx_context *ctx, uint32_t code, uint64_t render_target) { - struct agx_ptr ptr = agx_pool_alloc_aligned(&ctx->batch->pipeline_pool, - (1 * AGX_BIND_TEXTURE_LENGTH) + - (1 * AGX_BIND_UNIFORM_LENGTH) + - AGX_SET_SHADER_EXTENDED_LENGTH + 8, - 64); + struct agx_usc_builder b = + agx_alloc_usc_control(&ctx->batch->pipeline_pool, 2); - uint8_t *record = ptr.cpu; - - agx_pack(record, BIND_TEXTURE, cfg) { + agx_usc_pack(&b, TEXTURE, cfg) { cfg.start = 0; cfg.count = 1; cfg.buffer = render_target; } - record += AGX_BIND_TEXTURE_LENGTH; - uint32_t unk[] = { 0, ~0 }; - agx_pack(record, BIND_UNIFORM, cfg) { + agx_usc_pack(&b, UNIFORM, cfg) { cfg.start_halfs = 4; cfg.size_halfs = 4; cfg.buffer = agx_pool_upload_aligned(&ctx->batch->pool, unk, sizeof(unk), 16); } - record += AGX_BIND_UNIFORM_LENGTH; - - /* TODO: Can we prepack this? */ - agx_pack(record, SET_SHADER_EXTENDED, cfg) { - cfg.code = code; - cfg.register_quadwords = 1; - cfg.unk_2 = 0xd; - cfg.unk_3 = 0x8d; - cfg.fragment_parameters.unk_1 = 0x880100; - cfg.fragment_parameters.early_z_testing = false; - cfg.fragment_parameters.unk_2 = false; - cfg.fragment_parameters.unk_3 = 0; - cfg.preshader_mode = 0; // XXX + agx_usc_pack(&b, SHARED, cfg) { + cfg.uses_shared_memory = true; + cfg.unk_1 = 0x10bc; + cfg.shared_memory_per_threadgroup_in_256_bytes = 32; } - record += AGX_SET_SHADER_EXTENDED_LENGTH; + agx_usc_pack(&b, SHADER, cfg) cfg.code = code; + agx_usc_pack(&b, REGISTERS, cfg) cfg.register_quadwords = 1; + agx_usc_pack(&b, NO_PRESHADER, cfg); - /* End pipeline */ - memset(record, 0, 8); - return ptr.gpu; + return agx_usc_fini(&b); } void