PR #24647 opened by Lynne URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24647 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24647.patch
Ditto. Saves a lot of lines, and reduces headaches and issues the encoders had. The reason hw_base_encode isn't used is that its simply too heavy and would require far more logic to glue the encoders to it, than to write a small 100-line piece of logic. >From 1ae61f6abf3e746df235d9b134a79d8fb3dba645 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Wed, 23 Sep 2026 08:39:03 +0900 Subject: [PATCH 1/9] proresenc_kostya_vulkan: fix constant quantizer mode The estimate stored its results at index 0, where the tables held the constant quantizer's matrix, but the encoder looked both up at the quantizer itself. The slice sizes never matched the coded slices, and below 16 every coefficient was divided by an empty matrix. Fill the tables with every quantizer's matrix, estimate with the constant quantizer while keeping index 0, and skip the overquant search. --- libavcodec/proresenc_kostya_vulkan.c | 16 ++++++---------- .../vulkan/prores_ks_encode_slice.comp.glsl | 3 ++- .../vulkan/prores_ks_estimate_slice.comp.glsl | 7 +++++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c index c07bca1426..ff4df910c5 100644 --- a/libavcodec/proresenc_kostya_vulkan.c +++ b/libavcodec/proresenc_kostya_vulkan.c @@ -253,7 +253,7 @@ static int init_estimate_slice_pipeline(ProresVulkanContext *pv, FFVulkanShader* pv->estimate_slices_per_wg = dim_x / pv->ctx.num_planes; - SPEC_LIST_CREATE(sl, 8, 8 * sizeof(uint32_t)) + SPEC_LIST_CREATE(sl, 9, 9 * sizeof(uint32_t)) SPEC_LIST_ADD(sl, 0, 32, pv->ctx.mbs_per_slice); SPEC_LIST_ADD(sl, 1, 32, pv->ctx.chroma_factor); SPEC_LIST_ADD(sl, 2, 32, pv->ctx.alpha_bits); @@ -262,6 +262,7 @@ static int init_estimate_slice_pipeline(ProresVulkanContext *pv, FFVulkanShader* SPEC_LIST_ADD(sl, 5, 32, pv->ctx.force_quant ? 0 : pv->ctx.profile_info->min_quant); SPEC_LIST_ADD(sl, 6, 32, pv->ctx.force_quant ? 0 : pv->ctx.profile_info->max_quant); SPEC_LIST_ADD(sl, 7, 32, pv->ctx.bits_per_mb); + SPEC_LIST_ADD(sl, 8, 32, pv->ctx.force_quant); ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl, (uint32_t []) { dim_x, 1, 1 }, required ? dim_x : 0); @@ -346,13 +347,14 @@ static int init_encode_slice_pipeline(ProresVulkanContext *pv, FFVulkanShader* s FFVulkanContext *vkctx = &pv->vkctx; FFVulkanDescriptorSetBinding *desc; - SPEC_LIST_CREATE(sl, 6, 6 * sizeof(uint32_t)) + SPEC_LIST_CREATE(sl, 7, 7 * sizeof(uint32_t)) SPEC_LIST_ADD(sl, 0, 32, pv->ctx.mbs_per_slice); SPEC_LIST_ADD(sl, 1, 32, pv->ctx.chroma_factor); SPEC_LIST_ADD(sl, 2, 32, pv->ctx.alpha_bits); SPEC_LIST_ADD(sl, 3, 32, pv->ctx.num_planes); SPEC_LIST_ADD(sl, 4, 32, pv->ctx.slices_per_picture); - SPEC_LIST_ADD(sl, 5, 32, pv->ctx.force_quant ? pv->ctx.force_quant : pv->ctx.profile_info->max_quant); + SPEC_LIST_ADD(sl, 5, 32, pv->ctx.profile_info->max_quant); + SPEC_LIST_ADD(sl, 6, 32, pv->ctx.force_quant); ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl, (uint32_t []) { 64, 1, 1 }, 0); @@ -1066,13 +1068,7 @@ static av_cold int encode_init(AVCodecContext *avctx) VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); RET(ff_vk_map_buffer(vkctx, &pv->prores_data_tables_buf, (void *)&pv->tables, 0)); - for (q = 0; q < MAX_STORED_Q; ++q) { - for (i = 0; i < 64; i++) { - pv->tables->qmat[q][i] = ctx->quants[q][ctx->scantable[i]]; - pv->tables->qmat_chroma[q][i] = ctx->quants_chroma[q][ctx->scantable[i]]; - } - } - for (q = MAX_STORED_Q; q < 128; ++q) { + for (q = 0; q < 128; ++q) { for (i = 0; i < 64; i++) { pv->tables->qmat[q][i] = ctx->quant_mat[ctx->scantable[i]] * q; pv->tables->qmat_chroma[q][i] = ctx->quant_chroma_mat[ctx->scantable[i]] * q; diff --git a/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl b/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl index a974e51d9e..c3f3c7b8f2 100644 --- a/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl +++ b/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl @@ -32,6 +32,7 @@ layout (constant_id = 2) const int alpha_bits = 0; layout (constant_id = 3) const int num_planes = 0; layout (constant_id = 4) const int slices_per_picture = 0; layout (constant_id = 5) const int max_quant = 0; +layout (constant_id = 6) const int force_quant = 0; struct SliceData { uint32_t mbs_per_slice; @@ -242,7 +243,7 @@ void main() uint plane = gl_GlobalInvocationID.y; int q = scores[slice].quant; - int q_idx = min(q, max_quant + 1); + int q_idx = force_quant != 0 ? 0 : min(q, max_quant + 1); ivec4 bits = scores[slice].bits[q_idx]; int slice_hdr_size = 2 * num_planes; int slice_size = slice_hdr_size + ((bits.x + bits.y + bits.z + bits.w) / 8); diff --git a/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl b/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl index be0f95a9bd..28807c5186 100644 --- a/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl +++ b/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl @@ -37,6 +37,7 @@ layout (constant_id = 4) const int slices_per_picture = 0; layout (constant_id = 5) const int min_quant = 0; layout (constant_id = 6) const int max_quant = 0; layout (constant_id = 7) const int bits_per_mb = 0; +layout (constant_id = 8) const int force_quant = 0; struct SliceData { uint32_t mbs_per_slice; @@ -242,6 +243,8 @@ void main() [[maximally_reconverges]] uint slice = gl_WorkGroupID.x * slices_per_wg + gl_LocalInvocationID.x / num_planes; uint plane = gl_LocalInvocationID.x % num_planes; uint q = min_quant + gl_GlobalInvocationID.y; + /* A constant quantiser is estimated once, and stored at index 0 */ + uint quant = force_quant != 0 ? uint(force_quant) : q; if (gl_LocalInvocationID.x >= slices_per_wg * num_planes || slice >= slices_per_picture) return; @@ -251,7 +254,7 @@ void main() [[maximally_reconverges]] if (plane == 3) bits = estimate_alpha_plane(slice); else - bits = estimate_slice_plane(error, slice, plane, q); + bits = estimate_slice_plane(error, slice, plane, quant); /* Write results to score buffer */ scores[slice].bits[q][plane] = FFALIGN(bits, 8); @@ -275,7 +278,7 @@ void main() [[maximally_reconverges]] scores[slice].total_bits[q] = total_bits; scores[slice].total_score[q] = total_score; - if (q != max_quant) + if (force_quant != 0 || q != max_quant) return; /* Task threads that computed max_quant to also compute overquant if necessary */ -- 2.52.0 >From fc87955889fb7bcd16b63a4163f14124fe0f9eee Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Wed, 23 Sep 2026 02:09:36 +0900 Subject: [PATCH 2/9] vulkan_video: add a shared segment gather The FFv1, APV and ProRes encoders each carried a copy of the same segment gather. Move it into ff_vk_seg_gather(), which also writes the packed size after the segment sizes, all that FFv1 needed from them, and makes the output visible to the host, which none of them did. Gather straight into the host-visible output, as ProRes already did. FFv1 and APV gathered into VRAM and copied the worst case out, but on a compute queue that copy is just another shader writing to system memory, after a round trip through VRAM. This also drops a worst-case VRAM buffer per frame in flight. At depth 1 on RADV, APV goes from 12 to 40 fps at 4K and from 44 to 175 fps at 1080p, and FFv1 from 42 to 131 fps at 1080p. --- libavcodec/apv_encode_vulkan.c | 126 +++-------------------- libavcodec/ffv1enc_vulkan.c | 136 +++---------------------- libavcodec/proresenc_kostya_vulkan.c | 96 ++--------------- libavcodec/vulkan/Makefile | 8 +- libavcodec/vulkan/seg_gather.comp.glsl | 7 +- libavcodec/vulkan_video.c | 86 ++++++++++++++++ libavcodec/vulkan_video.h | 17 ++++ 7 files changed, 149 insertions(+), 327 deletions(-) diff --git a/libavcodec/apv_encode_vulkan.c b/libavcodec/apv_encode_vulkan.c index c0b3999e43..0e0fc1abc7 100644 --- a/libavcodec/apv_encode_vulkan.c +++ b/libavcodec/apv_encode_vulkan.c @@ -30,6 +30,7 @@ #include "codec_internal.h" #include "encode.h" #include "hwconfig.h" +#include "vulkan_video.h" #include "internal.h" #include "apv.h" @@ -42,9 +43,6 @@ extern const unsigned int ff_apv_encode_dct_comp_spv_len; extern const unsigned char ff_apv_encode_tiles_comp_spv_data[]; extern const unsigned int ff_apv_encode_tiles_comp_spv_len; -extern const unsigned char ff_seg_gather_comp_spv_data[]; -extern const unsigned int ff_seg_gather_comp_spv_len; - #define APV_DEFAULT_QMAT 16 #define APV_MAX_NUM_COMP 4 @@ -71,12 +69,6 @@ typedef struct EntropyPushData { uint32_t blocks_per_mb; /* blocks per MB of this dispatch's components */ } EntropyPushData; -typedef struct CompactPushData { - VkDeviceAddress sparse; - VkDeviceAddress compacted; - uint32_t slot_size; -} CompactPushData; - typedef struct VulkanEncodeAPVFrameData { FFVkBuffer *coeffs_ref; FFVkBuffer *bytestream_ref; @@ -104,7 +96,6 @@ typedef struct VulkanEncodeAPVContext { /* Per-frame buffer pools */ AVRefStructPool *coeffs_pool; AVRefStructPool *bytestream_pool; - AVRefStructPool *gathered_pool; AVRefStructPool *compacted_pool; AVRefStructPool *sizes_pool; @@ -316,37 +307,6 @@ fail: return err; } -static int init_compact_shader(AVCodecContext *avctx) -{ - int err; - VulkanEncodeAPVContext *ev = avctx->priv_data; - FFVulkanShader *shd = &ev->shd_compact; - - ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, - (uint32_t []) { 256, 1, 1 }, 0); - - ff_vk_shader_add_push_const(shd, 0, sizeof(CompactPushData), - VK_SHADER_STAGE_COMPUTE_BIT); - - const FFVulkanDescriptorSetBinding desc_set[] = { - { - .name = "sizes_buf", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - }, - }; - ff_vk_shader_add_descriptor_set(&ev->s, shd, desc_set, 1, 0); - - RET(ff_vk_shader_link(&ev->s, shd, - ff_seg_gather_comp_spv_data, - ff_seg_gather_comp_spv_len, "main")); - - RET(ff_vk_shader_register_exec(&ev->s, &ev->exec_pool, shd)); - -fail: - return err; -} - /* * The DCT/quantize shader's push constants are entirely encoder-constant: * frame geometry, the per-component quant scale qf, and the quantisation @@ -410,7 +370,6 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, FFVkBuffer *coeffs_buf; FFVkBuffer *bytestream_buf; - FFVkBuffer *gathered_buf = NULL; FFVkBuffer *compacted_buf; FFVkBuffer *sizes_buf; @@ -437,22 +396,10 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); bytestream_buf = fd->bytestream_ref; - /* The compaction shader gathers the sparse slots into here, contiguous. - * Device-local: shader stores over the bus are unreliably slow on some - * drivers, so the transfer to the host is left to the copy engine. */ - RET(ff_vk_get_pooled_buffer(&ev->s, &ev->gathered_pool, - &gathered_buf, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - NULL, ev->bytestream_size, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); - - /* Copy-engine destination the CPU assembles the packet from. - * Host-visible + host-cached so the readback is a fast cached copy. */ + /* The compaction shader gathers the sparse slots into here, and the CPU + * assembles the packet from it. */ RET(ff_vk_get_pooled_buffer(&ev->s, &ev->compacted_pool, &fd->compacted_ref, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL, ev->bytestream_size, @@ -571,61 +518,14 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, vk->CmdDispatch(exec->buf, ev->tile_cols, ev->tile_rows, z_comps); } - /* Compaction pass: gather the sparse per-tile-component slots into one - * contiguous device-local buffer, then read it back with the copy - * engine. */ - if (!ev->headers_only) { - ff_vk_buf_barrier(buf_bar[nb_buf_bar++], bytestream_buf, - COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE, - COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE, - 0, bytestream_buf->size); - ff_vk_buf_barrier(buf_bar[nb_buf_bar++], sizes_buf, - COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE, - COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE, - 0, sizes_buf->size); - vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pBufferMemoryBarriers = buf_bar, - .bufferMemoryBarrierCount = nb_buf_bar, - }); - nb_buf_bar = 0; + /* Compaction pass: gather the sparse per-tile-component slots into the + * output buffer. */ + if (!ev->headers_only) + RET(ff_vk_seg_gather(&ev->s, exec, &ev->shd_compact, + sizes_buf, 0, ev->tile_count * ev->num_comp, + bytestream_buf, ev->slot_size, + compacted_buf, 0)); - CompactPushData pd = { - .sparse = bytestream_buf->address, - .compacted = gathered_buf->address, - .slot_size = (uint32_t)ev->slot_size, - }; - - ff_vk_shader_update_desc_buffer(&ev->s, exec, &ev->shd_compact, - 0, 0, 0, - sizes_buf, 0, sizes_buf->size, - VK_FORMAT_UNDEFINED); - ff_vk_exec_bind_shader(&ev->s, exec, &ev->shd_compact); - ff_vk_shader_update_push_const(&ev->s, exec, &ev->shd_compact, - VK_SHADER_STAGE_COMPUTE_BIT, - 0, sizeof(pd), &pd); - - vk->CmdDispatch(exec->buf, ev->tile_count * ev->num_comp, 1, 1); - - /* The gathered size is only known once the encode is done, so the - * whole buffer is copied; the slots are sized to the entropy coder's - * worst case, which keeps this close to the payload size. */ - ff_vk_buf_barrier(buf_bar[nb_buf_bar++], gathered_buf, - COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE, - TRANSFER_BIT, TRANSFER_READ_BIT, NONE, - 0, gathered_buf->size); - vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pBufferMemoryBarriers = buf_bar, - .bufferMemoryBarrierCount = nb_buf_bar, - }); - nb_buf_bar = 0; - - vk->CmdCopyBuffer(exec->buf, gathered_buf->buf, compacted_buf->buf, - 1, &(VkBufferCopy) { .size = ev->bytestream_size }); - } - - ff_vk_exec_move_dep_refstruct(&ev->s, exec, &gathered_buf); err = ff_vk_exec_submit(&ev->s, exec); if (err < 0) return err; @@ -633,7 +533,6 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, return 0; fail: - av_refstruct_unref(&gathered_buf); ff_vk_exec_discard(&ev->s, exec); return err; } @@ -869,7 +768,6 @@ static av_cold int vulkan_encode_apv_close(AVCodecContext *avctx) av_refstruct_pool_uninit(&ev->coeffs_pool); av_refstruct_pool_uninit(&ev->bytestream_pool); - av_refstruct_pool_uninit(&ev->gathered_pool); av_refstruct_pool_uninit(&ev->compacted_pool); av_refstruct_pool_uninit(&ev->sizes_pool); @@ -1002,7 +900,7 @@ static av_cold int vulkan_encode_apv_init(AVCodecContext *avctx) ev->slot_size = blocks_per_tile * APV_BLK_COEFFS * 8; ev->slot_size = FFALIGN(ev->slot_size, 64); ev->bytestream_size = (size_t)ev->tile_count * ev->num_comp * ev->slot_size; - ev->sizes_size = (size_t)ev->tile_count * ev->num_comp * sizeof(uint32_t); + ev->sizes_size = ((size_t)ev->tile_count * ev->num_comp + 1) * sizeof(uint32_t); av_log(avctx, AV_LOG_VERBOSE, "APV Vulkan encoder: %dx%d, %d tiles (%dx%d MBs each), " @@ -1056,7 +954,7 @@ static av_cold int vulkan_encode_apv_init(AVCodecContext *avctx) &ev->shd_entropy[1]); if (err < 0) return err; - err = init_compact_shader(avctx); + err = ff_vk_seg_gather_init(&ev->s, &ev->exec_pool, &ev->shd_compact); if (err < 0) return err; diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index 14f7d2e5dc..f3e0a61ff7 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -31,6 +31,7 @@ #include "ffv1.h" #include "ffv1enc.h" #include "ffv1_vulkan.h" +#include "vulkan_video.h" /* Parallel Golomb alignment */ #define LG_ALIGN_W 32 @@ -92,9 +93,6 @@ typedef struct VulkanEncodeFFv1Context { /* Output data buffer */ AVRefStructPool *out_data_pool; - /* Gathered (contiguous) device-local buffer pool */ - AVRefStructPool *gathered_data_pool; - /* Host-visible readback buffer pool */ AVRefStructPool *compacted_data_pool; @@ -113,12 +111,6 @@ typedef struct VulkanEncodeFFv1Context { int chunks; } VulkanEncodeFFv1Context; -typedef struct SegGatherPushData { - VkDeviceAddress sparse; - VkDeviceAddress compacted; - uint32_t slot_size; -} SegGatherPushData; - extern const char *ff_source_common_comp; extern const char *ff_source_rangecoder_comp; extern const char *ff_source_ffv1_vlc_comp; @@ -167,9 +159,6 @@ extern const unsigned int ff_ffv1_enc_bayer_comp_spv_len; extern const unsigned char ff_ffv1_enc_bayer_golomb_comp_spv_data[]; extern const unsigned int ff_ffv1_enc_bayer_golomb_comp_spv_len; -extern const unsigned char ff_seg_gather_comp_spv_data[]; -extern const unsigned int ff_seg_gather_comp_spv_len; - /* Size output slots with the v4 worst case; v3's ~(2*bits+5) bytes/sample runs * to gigabytes on large frames. The bitstream version is unaffected. */ static size_t ffv1_vk_buffer_size(AVCodecContext *avctx) @@ -303,7 +292,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, /* Output data */ size_t maxsize; FFVkBuffer *out_data_buf; - FFVkBuffer *gathered_buf = NULL; FFVkBuffer *compacted_buf; int has_inter = avctx->gop_size > 1; @@ -388,19 +376,10 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); out_data_buf = fd->out_data_ref; - /* Device-local gather destination */ - RET(ff_vk_get_pooled_buffer(&fv->s, &fv->gathered_data_pool, - &gathered_buf, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - NULL, maxsize, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); - /* Contiguous output, read back by the CPU. */ RET(ff_vk_get_pooled_buffer(&fv->s, &fv->compacted_data_pool, &fd->compacted_data_ref, - VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL, maxsize, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | fv->s.host_cached_flag)); @@ -673,7 +652,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->enc, 1, 1, 0, &fv->results_buf, - fd->idx*f->max_slice_count*sizeof(uint32_t), + fd->idx*(f->max_slice_count + 1)*sizeof(uint32_t), f->slice_count*sizeof(uint32_t), VK_FORMAT_UNDEFINED); ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->enc, @@ -706,60 +685,16 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, 0, sizeof(FFv1ShaderParams), &pd); vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1); - /* Gather the per-slice slots into one contiguous buffer, in the same + /* Gather the per-slice slots into the packet buffer, in the same * submission. */ - FFVkBuffer *results_buf = &fv->results_buf; - ff_vk_buf_barrier(buf_bar[nb_buf_bar++], out_data_buf, - COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR, - COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE_KHR, - 0, VK_WHOLE_SIZE); - ff_vk_buf_barrier(buf_bar[nb_buf_bar++], results_buf, - COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR, - COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE_KHR, - 0, VK_WHOLE_SIZE); - vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pBufferMemoryBarriers = buf_bar, - .bufferMemoryBarrierCount = nb_buf_bar, - }); - nb_buf_bar = 0; - - SegGatherPushData gather_pd = { - .sparse = out_data_buf->address, - .compacted = gathered_buf->address, - .slot_size = (uint32_t)((out_data_buf->size / f->slice_count) & ~(size_t)15), - }; - ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->gather, 0, 0, 0, - &fv->results_buf, - fd->idx*f->max_slice_count*sizeof(uint32_t), - f->slice_count*sizeof(uint32_t), - VK_FORMAT_UNDEFINED); - ff_vk_exec_bind_shader(&fv->s, exec, &fv->gather); - ff_vk_shader_update_push_const(&fv->s, exec, &fv->gather, - VK_SHADER_STAGE_COMPUTE_BIT, - 0, sizeof(gather_pd), &gather_pd); - vk->CmdDispatch(exec->buf, f->slice_count, 1, 1); - - /* Read the gathered bitstream back with the copy engine. The size is - * only known once the encode is done, so the whole buffer is copied; - * with the version-4 slot sizing this is close to the payload size. */ - ff_vk_buf_barrier(buf_bar[nb_buf_bar++], gathered_buf, - COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR, - TRANSFER_BIT, TRANSFER_READ_BIT, NONE_KHR, - 0, VK_WHOLE_SIZE); - vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pBufferMemoryBarriers = buf_bar, - .bufferMemoryBarrierCount = nb_buf_bar, - }); - nb_buf_bar = 0; - - vk->CmdCopyBuffer(exec->buf, gathered_buf->buf, compacted_buf->buf, - 1, &(VkBufferCopy) { .size = maxsize }); + RET(ff_vk_seg_gather(&fv->s, exec, &fv->gather, + &fv->results_buf, fd->idx*(f->max_slice_count + 1)*sizeof(uint32_t), + f->slice_count, + out_data_buf, (out_data_buf->size / f->slice_count) & ~(size_t)15, + compacted_buf, 0)); /* Submit */ ff_vk_exec_move_dep_refstruct(&fv->s, exec, &slice_data_buf); - ff_vk_exec_move_dep_refstruct(&fv->s, exec, &gathered_buf); if (remap_data_buf) ff_vk_exec_move_dep_refstruct(&fv->s, exec, &remap_data_buf); err = ff_vk_exec_submit(&fv->s, exec); @@ -778,7 +713,6 @@ fail: av_frame_free(&tmp); av_refstruct_unref(&slice_data_buf); av_refstruct_unref(&remap_data_buf); - av_refstruct_unref(&gathered_buf); return err; } @@ -802,27 +736,20 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, /* Make sure the encode + gather submission is done */ ff_vk_exec_wait(&fv->s, exec); - /* Invalidate the per-slice sizes if needed */ - uint32_t rb_off = fd->idx*f->max_slice_count*sizeof(uint32_t); + /* Invalidate the packed size if needed */ + size_t total_off = (fd->idx*(f->max_slice_count + 1) + f->slice_count)*sizeof(uint32_t); if (!(fv->results_buf.flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { VkMappedMemoryRange invalidate_data = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = fv->results_buf.mem, - .offset = rb_off, - .size = f->slice_count*sizeof(uint32_t), + .offset = total_off, + .size = sizeof(uint32_t), }; vk->InvalidateMappedMemoryRanges(fv->s.hwctx->act_dev, 1, &invalidate_data); } - /* The gather packed the slices tight and in order; sum their lengths. */ - pkt->size = 0; - uint8_t *rb = fv->results_buf.mapped_mem + rb_off; - for (int i = 0; i < f->slice_count; i++) { - uint32_t sl_len = AV_RN32(rb + i*4); - av_log(avctx, AV_LOG_DEBUG, "Slice %i size = %u\n", i, sl_len); - pkt->size += sl_len; - } + pkt->size = AV_RN32(fv->results_buf.mapped_mem + total_off); av_log(avctx, AV_LOG_VERBOSE, "Encoded data: %iMiB\n", pkt->size / (1024*1024)); /* Invalidate the gathered bitstream if needed */ @@ -1287,36 +1214,6 @@ fail: return err; } -static int init_gather_shader(AVCodecContext *avctx) -{ - int err; - VulkanEncodeFFv1Context *fv = avctx->priv_data; - FFVulkanShader *shd = &fv->gather; - - ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, - (uint32_t []) { 256, 1, 1 }, 0); - - ff_vk_shader_add_push_const(shd, 0, sizeof(SegGatherPushData), - VK_SHADER_STAGE_COMPUTE_BIT); - - const FFVulkanDescriptorSetBinding desc_set[] = { - { /* sizes_buf */ - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - }, - }; - ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 1, 0); - - RET(ff_vk_shader_link(&fv->s, shd, - ff_seg_gather_comp_spv_data, - ff_seg_gather_comp_spv_len, "main")); - - RET(ff_vk_shader_register_exec(&fv->s, &fv->exec_pool, shd)); - -fail: - return err; -} - static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) { int err; @@ -1520,7 +1417,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) return err; /* Gather shader */ - err = init_gather_shader(avctx); + err = ff_vk_seg_gather_init(&fv->s, &fv->exec_pool, &fv->gather); if (err < 0) return err; @@ -1571,7 +1468,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) /* Buffers */ RET(ff_vk_create_buf(&fv->s, &fv->results_buf, - fv->async_depth*f->max_slice_count*sizeof(uint32_t), + fv->async_depth*(f->max_slice_count + 1)*sizeof(uint32_t), NULL, NULL, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | @@ -1609,7 +1506,6 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx) av_buffer_unref(&fv->intermediate_frames_ref); av_refstruct_pool_uninit(&fv->out_data_pool); - av_refstruct_pool_uninit(&fv->gathered_data_pool); av_refstruct_pool_uninit(&fv->compacted_data_pool); av_refstruct_unref(&fv->keyframe_slice_data_ref); diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c index ff4df910c5..9449e88c4e 100644 --- a/libavcodec/proresenc_kostya_vulkan.c +++ b/libavcodec/proresenc_kostya_vulkan.c @@ -42,6 +42,7 @@ #include "proresdata.h" #include "proresenc_kostya_common.h" #include "hwconfig.h" +#include "vulkan_video.h" #define DCTSIZE 8 @@ -62,12 +63,6 @@ typedef struct EncodeSliceInfo { uint32_t slot_size; } EncodeSliceInfo; -typedef struct SegGatherPushData { - VkDeviceAddress sparse; - VkDeviceAddress compacted; - uint32_t slot_size; -} SegGatherPushData; - typedef struct SliceData { uint32_t mbs_per_slice; int16_t rows[MAX_PLANES * MAX_MBS_PER_SLICE * 256]; @@ -143,9 +138,6 @@ typedef struct ProresVulkanContext { extern const unsigned char ff_prores_ks_alpha_data_comp_spv_data[]; extern const unsigned int ff_prores_ks_alpha_data_comp_spv_len; -extern const unsigned char ff_seg_gather_comp_spv_data[]; -extern const unsigned int ff_seg_gather_comp_spv_len; - extern const unsigned char ff_prores_ks_slice_data_comp_spv_data[]; extern const unsigned int ff_prores_ks_slice_data_comp_spv_len; @@ -390,37 +382,6 @@ fail: return err; } -static int init_gather_pipeline(ProresVulkanContext *pv, FFVulkanShader *shd) -{ - int err = 0; - FFVulkanContext *vkctx = &pv->vkctx; - FFVulkanDescriptorSetBinding *desc; - - ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, - (uint32_t []) { 256, 1, 1 }, 0); - - desc = (FFVulkanDescriptorSetBinding []) { - { - .name = "sizes_buf", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - }, - }; - ff_vk_shader_add_descriptor_set(vkctx, shd, desc, 1, 0); - - ff_vk_shader_add_push_const(shd, 0, sizeof(SegGatherPushData), - VK_SHADER_STAGE_COMPUTE_BIT); - - RET(ff_vk_shader_link(vkctx, shd, - ff_seg_gather_comp_spv_data, - ff_seg_gather_comp_spv_len, "main")); - - RET(ff_vk_shader_register_exec(vkctx, &pv->e, shd)); - -fail: - return err; -} - static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, AVFrame *frame, int picture_idx) { @@ -454,7 +415,7 @@ static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecCont RET(ff_vk_get_pooled_buffer(vkctx, &pv->slice_sizes_buf_pool, &pd->slice_sizes_ref[picture_idx], VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL, - ctx->slices_per_picture * sizeof(uint32_t), + (ctx->slices_per_picture + 1) * sizeof(uint32_t), VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); @@ -655,52 +616,11 @@ static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecCont /* Gather the sparse slots into the contiguous bitstream, in the same * submission. */ - VkBufferMemoryBarrier2 gather_bar[2] = { - { - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = pkt_vk_buf->buf, - .offset = 0, - .size = pkt_vk_buf->size, - }, { - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = pd->slice_sizes_ref[picture_idx]->buf, - .offset = 0, - .size = pd->slice_sizes_ref[picture_idx]->size, - }, - }; - vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pBufferMemoryBarriers = gather_bar, - .bufferMemoryBarrierCount = 2, - }); - - SegGatherPushData gather_pd = { - .sparse = pkt_vk_buf->address, - .compacted = pd->gathered_ref[picture_idx]->address + - (picture_idx == 0 ? pv->payload_off : 0), - .slot_size = pv->slice_slot_size, - }; - ff_vk_shader_update_desc_buffer(vkctx, exec, &pv->gather_shd, 0, 0, 0, - pd->slice_sizes_ref[picture_idx], - 0, ctx->slices_per_picture * sizeof(uint32_t), - VK_FORMAT_UNDEFINED); - ff_vk_exec_bind_shader(vkctx, exec, &pv->gather_shd); - ff_vk_shader_update_push_const(vkctx, exec, &pv->gather_shd, - VK_SHADER_STAGE_COMPUTE_BIT, - 0, sizeof(gather_pd), &gather_pd); - vk->CmdDispatch(exec->buf, ctx->slices_per_picture, 1, 1); + RET(ff_vk_seg_gather(vkctx, exec, &pv->gather_shd, + pd->slice_sizes_ref[picture_idx], 0, ctx->slices_per_picture, + pkt_vk_buf, pv->slice_slot_size, + pd->gathered_ref[picture_idx], + picture_idx == 0 ? pv->payload_off : 0)); fail: return err; @@ -1033,7 +953,7 @@ static av_cold int encode_init(AVCodecContext *avctx) init_estimate_slice_pipeline(pv, &pv->estimate_slice_shd); init_trellis_node_pipeline(pv, &pv->trellis_node_shd); init_encode_slice_pipeline(pv, &pv->encode_slice_shd); - init_gather_pipeline(pv, &pv->gather_shd); + RET(ff_vk_seg_gather_init(vkctx, &pv->e, &pv->gather_shd)); /* Size slots for the entropy coder's worst case; bits_per_mb is only a rate-control average */ { diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile index 48afd639f4..a2ff2a4d6d 100644 --- a/libavcodec/vulkan/Makefile +++ b/libavcodec/vulkan/Makefile @@ -4,9 +4,10 @@ clean:: OBJS-$(CONFIG_APV_VULKAN_HWACCEL) += vulkan/apv_decode.comp.spv.o \ vulkan/apv_idct.comp.spv.o +OBJS-$(CONFIG_VULKAN) += vulkan/seg_gather.comp.spv.o + OBJS-$(CONFIG_APV_VULKAN_ENCODER) += vulkan/apv_encode_dct.comp.spv.o \ - vulkan/apv_encode_tiles.comp.spv.o \ - vulkan/seg_gather.comp.spv.o + vulkan/apv_encode_tiles.comp.spv.o OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_setup.comp.spv.o \ vulkan/ffv1_enc_reset.comp.spv.o \ @@ -21,8 +22,7 @@ OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_setup.comp.spv.o \ vulkan/ffv1_enc_rgb_float_golomb.comp.spv.o \ vulkan/ffv1_enc_sort32.comp.spv.o \ vulkan/ffv1_enc_bayer.comp.spv.o \ - vulkan/ffv1_enc_bayer_golomb.comp.spv.o \ - vulkan/seg_gather.comp.spv.o + vulkan/ffv1_enc_bayer_golomb.comp.spv.o OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \ vulkan/ffv1_dec_reset.comp.spv.o \ diff --git a/libavcodec/vulkan/seg_gather.comp.glsl b/libavcodec/vulkan/seg_gather.comp.glsl index 5ee28097d6..0a3c6cbf42 100644 --- a/libavcodec/vulkan/seg_gather.comp.glsl +++ b/libavcodec/vulkan/seg_gather.comp.glsl @@ -37,9 +37,11 @@ * * One workgroup per segment. Inputs: the sparse slot buffer, the per-segment * sizes, and the slot stride. + * + * The last workgroup writes the packed size after the segment sizes. */ -layout (set = 0, binding = 0, scalar) readonly buffer sizes_buf { +layout (set = 0, binding = 0, scalar) buffer sizes_buf { uint32_t seg_sizes[]; }; @@ -141,4 +143,7 @@ void main(void) for (uint i = head + (nsafe << 4u) + b; i < n; i += wg) dst8[i].v = src8[i].v; + + if (seg == gl_NumWorkGroups.x - 1u && b == 0u) + seg_sizes[gl_NumWorkGroups.x] = s_dst_off + n; } diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c index 44ad8e9233..838d681e64 100644 --- a/libavcodec/vulkan_video.c +++ b/libavcodec/vulkan_video.c @@ -543,3 +543,89 @@ fail: ff_vk_video_common_uninit(s, common); return err; } + +typedef struct SegGatherPushData { + VkDeviceAddress sparse; + VkDeviceAddress compacted; + uint32_t slot_size; +} SegGatherPushData; + +extern const unsigned char ff_seg_gather_comp_spv_data[]; +extern const unsigned int ff_seg_gather_comp_spv_len; + +int ff_vk_seg_gather_init(FFVulkanContext *s, FFVkExecPool *pool, + FFVulkanShader *shd) +{ + int err; + FFVulkanDescriptorSetBinding desc_set[] = { + { + .name = "sizes_buf", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 256, 1, 1 }, 0); + ff_vk_shader_add_push_const(shd, 0, sizeof(SegGatherPushData), + VK_SHADER_STAGE_COMPUTE_BIT); + ff_vk_shader_add_descriptor_set(s, shd, desc_set, 1, 0); + + RET(ff_vk_shader_link(s, shd, ff_seg_gather_comp_spv_data, + ff_seg_gather_comp_spv_len, "main")); + RET(ff_vk_shader_register_exec(s, pool, shd)); + +fail: + return err; +} + +int ff_vk_seg_gather(FFVulkanContext *s, FFVkExecContext *exec, FFVulkanShader *shd, + FFVkBuffer *sizes, size_t sizes_offset, uint32_t nb_segs, + FFVkBuffer *sparse, uint32_t slot_size, + FFVkBuffer *compacted, size_t compacted_offset) +{ + int err; + FFVulkanFunctions *vk = &s->vkfn; + SegGatherPushData pd = { + .sparse = sparse->address, + .compacted = compacted->address + compacted_offset, + .slot_size = slot_size, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pMemoryBarriers = &(VkMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | + VK_ACCESS_2_SHADER_WRITE_BIT, + }, + .memoryBarrierCount = 1, + }); + + RET(ff_vk_shader_update_desc_buffer(s, exec, shd, 0, 0, 0, + sizes, sizes_offset, (nb_segs + 1)*sizeof(uint32_t), + VK_FORMAT_UNDEFINED)); + ff_vk_exec_bind_shader(s, exec, shd); + ff_vk_shader_update_push_const(s, exec, shd, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(pd), &pd); + vk->CmdDispatch(exec->buf, nb_segs, 1, 1); + + /* For the host to read the output and the packed size */ + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pMemoryBarriers = &(VkMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT, + .dstAccessMask = VK_ACCESS_2_HOST_READ_BIT, + }, + .memoryBarrierCount = 1, + }); + +fail: + return err; +} diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h index 15aefc7136..cc3fe41640 100644 --- a/libavcodec/vulkan_video.h +++ b/libavcodec/vulkan_video.h @@ -136,4 +136,21 @@ int ff_vk_video_common_init(AVCodecContext *avctx, FFVulkanContext *s, */ void ff_vk_video_common_uninit(FFVulkanContext *s, FFVkVideoCommon *common); +/** + * Packs fixed-stride segment slots back to back into a contiguous buffer. + */ +int ff_vk_seg_gather_init(FFVulkanContext *s, FFVkExecPool *pool, + FFVulkanShader *shd); + +/** + * Gathers nb_segs slots of slot_size bytes from sparse into compacted, with + * the segment sizes given as nb_segs uint32_t in sizes at sizes_offset, + * followed by one more that receives the packed size. Both are made visible + * to the host. + */ +int ff_vk_seg_gather(FFVulkanContext *s, FFVkExecContext *exec, FFVulkanShader *shd, + FFVkBuffer *sizes, size_t sizes_offset, uint32_t nb_segs, + FFVkBuffer *sparse, uint32_t slot_size, + FFVkBuffer *compacted, size_t compacted_offset); + #endif /* AVCODEC_VULKAN_VIDEO_H */ -- 2.52.0 >From e2d4c9f15acca294c5620a62dbf3652ce4bb098f Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Tue, 22 Sep 2026 01:17:40 +0900 Subject: [PATCH 3/9] vulkan: enable VK_KHR_maintenance11 Among other things, lifts the 4-byte alignment that transfer-only queue families require of buffer offsets in buffer-image copies. --- libavutil/hwcontext_vulkan.c | 13 +++++++++++++ libavutil/vulkan.c | 4 ++++ libavutil/vulkan.h | 3 +++ libavutil/vulkan_functions.h | 1 + libavutil/vulkan_loader.h | 3 +++ 5 files changed, 24 insertions(+) diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c index 736b44a05a..02f7ff6ce4 100644 --- a/libavutil/hwcontext_vulkan.c +++ b/libavutil/hwcontext_vulkan.c @@ -107,6 +107,9 @@ typedef struct VulkanDeviceFeatures { #ifdef VK_KHR_maintenance9 VkPhysicalDeviceMaintenance9FeaturesKHR maintenance_9; #endif +#ifdef VK_KHR_maintenance11 + VkPhysicalDeviceMaintenance11FeaturesKHR maintenance_11; +#endif #ifdef VK_KHR_unified_image_layouts VkPhysicalDeviceUnifiedImageLayoutsFeaturesKHR unified_layouts; #endif @@ -287,6 +290,10 @@ static void device_features_init(AVHWDeviceContext *ctx, VulkanDeviceFeatures *f FF_VK_STRUCT_EXT(s, &feats->device, &feats->maintenance_9, FF_VK_EXT_MAINTENANCE_9, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_9_FEATURES_KHR); #endif +#ifdef VK_KHR_maintenance11 + FF_VK_STRUCT_EXT(s, &feats->device, &feats->maintenance_11, FF_VK_EXT_MAINTENANCE_11, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_11_FEATURES_KHR); +#endif #ifdef VK_KHR_unified_image_layouts FF_VK_STRUCT_EXT(s, &feats->device, &feats->unified_layouts, FF_VK_EXT_UNIFIED_IMAGE_LAYOUTS, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFIED_IMAGE_LAYOUTS_FEATURES_KHR); @@ -440,6 +447,9 @@ static void device_features_copy_needed(VulkanDeviceFeatures *dst, VulkanDeviceF #ifdef VK_KHR_maintenance9 COPY_VAL(maintenance_9.maintenance9); #endif +#ifdef VK_KHR_maintenance11 + COPY_VAL(maintenance_11.maintenance11); +#endif #ifdef VK_KHR_unified_image_layouts COPY_VAL(unified_layouts.unifiedImageLayouts); COPY_VAL(unified_layouts.unifiedImageLayoutsVideo); @@ -773,6 +783,9 @@ static const VulkanOptExtension optional_device_exts[] = { #ifdef VK_KHR_maintenance9 { VK_KHR_MAINTENANCE_9_EXTENSION_NAME, FF_VK_EXT_MAINTENANCE_9 }, #endif +#ifdef VK_KHR_maintenance11 + { VK_KHR_MAINTENANCE_11_EXTENSION_NAME, FF_VK_EXT_MAINTENANCE_11 }, +#endif #ifdef VK_KHR_unified_image_layouts { VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME, FF_VK_EXT_UNIFIED_IMAGE_LAYOUTS }, #endif diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c index e9e914dab8..de7f836341 100644 --- a/libavutil/vulkan.c +++ b/libavutil/vulkan.c @@ -191,6 +191,10 @@ int ff_vk_load_props(FFVulkanContext *s) FF_VK_STRUCT_EXT(s, &s->feats, &s->unified_layout_feats, FF_VK_EXT_UNIFIED_IMAGE_LAYOUTS, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFIED_IMAGE_LAYOUTS_FEATURES_KHR); #endif +#ifdef VK_KHR_maintenance11 + FF_VK_STRUCT_EXT(s, &s->feats, &s->maintenance_11_feats, FF_VK_EXT_MAINTENANCE_11, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_11_FEATURES_KHR); +#endif if (!s->imageviews_pool) { s->imageviews_pool = av_refstruct_pool_alloc_ext(sizeof(FFVkImageViews), 0, diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h index e92b8dffb9..7d2e89c09d 100644 --- a/libavutil/vulkan.h +++ b/libavutil/vulkan.h @@ -329,6 +329,9 @@ typedef struct FFVulkanContext { VkPhysicalDeviceVulkan12Features feats_12; #ifdef VK_KHR_unified_image_layouts VkPhysicalDeviceUnifiedImageLayoutsFeaturesKHR unified_layout_feats; +#endif +#ifdef VK_KHR_maintenance11 + VkPhysicalDeviceMaintenance11FeaturesKHR maintenance_11_feats; #endif VkPhysicalDeviceFeatures2 feats; diff --git a/libavutil/vulkan_functions.h b/libavutil/vulkan_functions.h index c7d91b445a..90512691f9 100644 --- a/libavutil/vulkan_functions.h +++ b/libavutil/vulkan_functions.h @@ -57,6 +57,7 @@ typedef uint64_t FFVulkanExtensions; #define FF_VK_EXT_MAXIMAL_RECONVERGENCE (1ULL << 24) /* VK_KHR_shader_maximal_reconvergence */ #define FF_VK_EXT_MAINTENANCE_9 (1ULL << 25) /* VK_KHR_maintenance9 */ #define FF_VK_EXT_UNIFIED_IMAGE_LAYOUTS (1ULL << 26) /* VK_KHR_unified_image_layouts */ +#define FF_VK_EXT_MAINTENANCE_11 (1ULL << 27) /* VK_KHR_maintenance11 */ /* Video extensions */ #define FF_VK_EXT_VIDEO_QUEUE (1ULL << 36) /* VK_KHR_video_queue */ diff --git a/libavutil/vulkan_loader.h b/libavutil/vulkan_loader.h index f1dbd7fee1..d4be1ffb9b 100644 --- a/libavutil/vulkan_loader.h +++ b/libavutil/vulkan_loader.h @@ -107,6 +107,9 @@ static inline uint64_t ff_vk_extensions_to_mask(const char * const *extensions, #endif #ifdef VK_KHR_internally_synchronized_queues { VK_KHR_INTERNALLY_SYNCHRONIZED_QUEUES_EXTENSION_NAME, FF_VK_EXT_INTERNAL_QUEUE_SYNC }, +#endif +#ifdef VK_KHR_maintenance11 + { VK_KHR_MAINTENANCE_11_EXTENSION_NAME, FF_VK_EXT_MAINTENANCE_11 }, #endif }; -- 2.52.0 >From d95758de159beb1dfb118388bb3e945b33c610a2 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Tue, 22 Sep 2026 01:19:31 +0900 Subject: [PATCH 4/9] hwcontext_vulkan: stage host-mapped planes with unaligned offsets Host-mapped planes start wherever the software frame's do, but buffer-image copies need offsets aligned to the texel block size, and on transfer-only queue families to 4 bytes unless maintenance11 is enabled. Fall back to the staging buffer when they are not. --- libavutil/hwcontext_vulkan.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c index 02f7ff6ce4..47c685c6e6 100644 --- a/libavutil/hwcontext_vulkan.c +++ b/libavutil/hwcontext_vulkan.c @@ -185,6 +185,9 @@ typedef struct VulkanDevicePriv { /* Prefer memcpy over dynamic host pointer imports */ int avoid_host_import; + /* Alignment the transfer queue needs for buffer offsets in image copies */ + int transfer_offset_align; + /* Maximum queues */ int limit_queues; } VulkanDevicePriv; @@ -2143,6 +2146,18 @@ FF_ENABLE_DEPRECATION_WARNINGS p->compute_qf = ff_vk_qf_find(&p->vkctx, VK_QUEUE_COMPUTE_BIT, 0); p->transfer_qf = ff_vk_qf_find(&p->vkctx, VK_QUEUE_TRANSFER_BIT, 0); + /* Transfer-only queues need 4-byte aligned buffer offsets in image copies */ + p->transfer_offset_align = 1; + if (p->transfer_qf) { + VkQueueFlags flags = p->vkctx.qf_props[p->transfer_qf->idx].queueFamilyProperties.queueFlags; + if (!(flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT))) + p->transfer_offset_align = 4; + } +#ifdef VK_KHR_maintenance11 + if (p->vkctx.maintenance_11_feats.maintenance11) + p->transfer_offset_align = 1; +#endif + /* Re-query device capabilities, in case the device was created externally */ vk->GetPhysicalDeviceMemoryProperties(hwctx->phys_dev, &p->mprops); @@ -4775,6 +4790,7 @@ static int host_map_frame(AVHWFramesContext *hwfc, FFVkBuffer **dst, int *nb_buf int nb_src_bufs; const int planes = av_pix_fmt_count_planes(swf->format); + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(swf->format); VkBufferUsageFlags buf_usage = upload ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : VK_BUFFER_USAGE_TRANSFER_DST_BIT; @@ -4811,6 +4827,17 @@ static int host_map_frame(AVHWFramesContext *hwfc, FFVkBuffer **dst, int *nb_buf return AVERROR_PATCHWELCOME; } + /* Buffer-image copies need offsets aligned to the texel block, and + * transfer-only queues to 4 bytes; stage anything else, including + * texel blocks that are not a power of two */ + for (int i = 0; i < planes; i++) { + int align = FFMAX(desc->comp[i].step, p->transfer_offset_align); + if ((align & (align - 1)) || (region[i].bufferOffset & (align - 1))) { + err = AVERROR(EINVAL); + goto fail; + } + } + return 0; fail: -- 2.52.0 >From 40390cb787c3fddea0ef9ff7031f54f873a265e0 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Tue, 22 Sep 2026 02:42:01 +0900 Subject: [PATCH 5/9] ffv1enc_vulkan: zero the packet padding The packet wraps the pooled readback buffer, so the bytes after the payload were whatever the previous frame left there, and at the worst case size the padding fell outside the buffer. --- libavcodec/ffv1enc_vulkan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index f3e0a61ff7..0b3e527a8e 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -380,7 +380,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, RET(ff_vk_get_pooled_buffer(&fv->s, &fv->compacted_data_pool, &fd->compacted_data_ref, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - NULL, maxsize, + NULL, maxsize + AV_INPUT_BUFFER_PADDING_SIZE, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | fv->s.host_cached_flag)); compacted_buf = fd->compacted_data_ref; @@ -764,6 +764,8 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, 1, &invalidate_data); } + memset(compacted_buf->mapped_mem + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + /* Hand the gathered buffer to the packet with no copy: pkt->buf references * the pooled Vulkan buffer, returned to its pool when the packet is freed. */ pkt->buf = av_buffer_create(compacted_buf->mapped_mem, compacted_buf->size, -- 2.52.0 >From 4c2c56231d08e8bb2ca2912dd77d2913d3feeb21 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Wed, 23 Sep 2026 00:41:44 +0900 Subject: [PATCH 6/9] ffv1enc: pass the context to the encoder helpers They read it from avctx->priv_data, which forces every encoder using them to keep FFV1Context first in its private data. --- libavcodec/ffv1enc.c | 27 ++++++++++----------------- libavcodec/ffv1enc.h | 10 +++++----- libavcodec/ffv1enc_vulkan.c | 10 +++++----- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index e6b7506557..eaff15fc03 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -444,10 +444,8 @@ static void set_micro_version(FFV1Context *f) av_assert0(f->micro_version == 0); } -av_cold int ff_ffv1_write_extradata(AVCodecContext *avctx) +av_cold int ff_ffv1_write_extradata(AVCodecContext *avctx, FFV1Context *f) { - FFV1Context *f = avctx->priv_data; - RangeCoder c; uint8_t state[CONTEXT_SIZE]; int i, j, k; @@ -567,9 +565,8 @@ static int sort_stt(FFV1Context *s, uint8_t stt[256]) } -int ff_ffv1_encode_determine_slices(AVCodecContext *avctx) +int ff_ffv1_encode_determine_slices(AVCodecContext *avctx, FFV1Context *s) { - FFV1Context *s = avctx->priv_data; int plane_count = 1 + 2*s->chroma_planes + s->bayer + s->transparency; int max_h_slices = AV_CEIL_RSHIFT(avctx->width , s->bayer ? 1 : s->chroma_h_shift); int max_v_slices = AV_CEIL_RSHIFT(avctx->height, s->bayer ? 1 : s->chroma_v_shift); @@ -602,9 +599,8 @@ int ff_ffv1_encode_determine_slices(AVCodecContext *avctx) return AVERROR(ENOSYS); } -av_cold int ff_ffv1_encode_init(AVCodecContext *avctx) +av_cold int ff_ffv1_encode_init(AVCodecContext *avctx, FFV1Context *s) { - FFV1Context *s = avctx->priv_data; int i, j, k, m, ret; if ((avctx->flags & (AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2)) || @@ -804,10 +800,9 @@ av_cold int ff_ffv1_encode_init(AVCodecContext *avctx) return 0; } -av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx, +av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx, FFV1Context *s, enum AVPixelFormat pix_fmt) { - FFV1Context *s = avctx->priv_data; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); s->bayer = 0; @@ -1012,7 +1007,7 @@ static av_cold int encode_init_internal(AVCodecContext *avctx) else if (s->ac == AC_RANGE_DEFAULT_TAB_FORCE) s->ac = AC_RANGE_DEFAULT_TAB; - ret = ff_ffv1_encode_setup_plane_info(avctx, avctx->pix_fmt); + ret = ff_ffv1_encode_setup_plane_info(avctx, s, avctx->pix_fmt); if (ret < 0) return ret; @@ -1030,15 +1025,15 @@ static av_cold int encode_init_internal(AVCodecContext *avctx) } - ret = ff_ffv1_encode_init(avctx); + ret = ff_ffv1_encode_init(avctx, s); if (ret < 0) return ret; if (s->version > 1) { - if ((ret = ff_ffv1_encode_determine_slices(avctx)) < 0) + if ((ret = ff_ffv1_encode_determine_slices(avctx, s)) < 0) return ret; - if ((ret = ff_ffv1_write_extradata(avctx)) < 0) + if ((ret = ff_ffv1_write_extradata(avctx, s)) < 0) return ret; } @@ -1860,10 +1855,8 @@ retry: return 0; } -size_t ff_ffv1_encode_buffer_size(AVCodecContext *avctx) +size_t ff_ffv1_encode_buffer_size(AVCodecContext *avctx, FFV1Context *f) { - FFV1Context *f = avctx->priv_data; - int w = avctx->width + f->num_h_slices; int h = avctx->height + f->num_v_slices; size_t maxsize = w*h * (1 + f->transparency); @@ -1941,7 +1934,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, } /* Maximum packet size */ - maxsize = ff_ffv1_encode_buffer_size(avctx); + maxsize = ff_ffv1_encode_buffer_size(avctx, f); if (maxsize > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - 32) { FFV1Context *f = avctx->priv_data; diff --git a/libavcodec/ffv1enc.h b/libavcodec/ffv1enc.h index 42d521a747..0322702664 100644 --- a/libavcodec/ffv1enc.h +++ b/libavcodec/ffv1enc.h @@ -31,12 +31,12 @@ enum { QTABLE_GT8BIT, }; -av_cold int ff_ffv1_encode_init(AVCodecContext *avctx); -av_cold int ff_ffv1_encode_determine_slices(AVCodecContext *avctx); -av_cold int ff_ffv1_write_extradata(AVCodecContext *avctx); -av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx, +av_cold int ff_ffv1_encode_init(AVCodecContext *avctx, FFV1Context *s); +av_cold int ff_ffv1_encode_determine_slices(AVCodecContext *avctx, FFV1Context *s); +av_cold int ff_ffv1_write_extradata(AVCodecContext *avctx, FFV1Context *f); +av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx, FFV1Context *s, enum AVPixelFormat pix_fmt); -size_t ff_ffv1_encode_buffer_size(AVCodecContext *avctx); +size_t ff_ffv1_encode_buffer_size(AVCodecContext *avctx, FFV1Context *f); #endif /* AVCODEC_FFV1ENC_H */ diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index 0b3e527a8e..42020ccab3 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -169,7 +169,7 @@ static size_t ffv1_vk_buffer_size(AVCodecContext *avctx) size_t size; f->version = 4; - size = ff_ffv1_encode_buffer_size(avctx); + size = ff_ffv1_encode_buffer_size(avctx, f); f->version = version; return size; @@ -1229,7 +1229,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) if (f->ac == 1) f->ac = AC_RANGE_CUSTOM_TAB; - err = ff_ffv1_encode_setup_plane_info(avctx, avctx->sw_pix_fmt); + err = ff_ffv1_encode_setup_plane_info(avctx, f, avctx->sw_pix_fmt); if (err < 0) return err; @@ -1237,7 +1237,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) * v3 bits-per-plane increment would mismatch the shaders' c_bits-1). */ f->version = f->bayer ? 4 : 3; - err = ff_ffv1_encode_init(avctx); + err = ff_ffv1_encode_init(avctx, f); if (err < 0) return err; @@ -1269,7 +1269,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) if (f->num_h_slices <= 0 && f->num_v_slices <= 0) { if (avctx->slices) { - err = ff_ffv1_encode_determine_slices(avctx); + err = ff_ffv1_encode_determine_slices(avctx, f); if (err < 0) return err; } else { @@ -1294,7 +1294,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) f->max_slice_count = f->num_h_slices * f->num_v_slices; - if ((err = ff_ffv1_write_extradata(avctx)) < 0) + if ((err = ff_ffv1_write_extradata(avctx, f)) < 0) return err; if (f->version < 4) { -- 2.52.0 >From 29fc7ecfb8411a14e9c410149268100009439ae2 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Wed, 23 Sep 2026 00:56:02 +0900 Subject: [PATCH 7/9] vulkan_video: add a shared frame loop for the compute encoders The FFv1, APV and ProRes encoders each drove their execution pool by hand, and either submitted a frame or returned a packet on each call, never both. With a depth of N that filled N contexts and then drained them, leaving the GPU idle for the drain, and the default depth of 1 was fully synchronous. FFVkEncodeLoop keeps up to pool_size frames in flight, and returns the oldest packet as soon as its execution completes, or once the pool is full. It also carries the packet metadata, and flushes what is in flight, which the encoders advertised but did not implement. ProRes now honours async_depth and no longer leaks its per-context data on close. The encoders keep it first in their private data, as the video encoders do with FFHWBaseEncodeContext, so its receive_packet and flush are their callbacks. 1080p 4:2:2 10-bit, depth 2: ProRes on RADV goes from 44 to 61 fps and APV on ANV from 194 to 235 fps. Output is unchanged at every depth. --- libavcodec/apv_encode_vulkan.c | 86 ++---------------- libavcodec/ffv1enc_vulkan.c | 92 +++---------------- libavcodec/proresenc_kostya_vulkan.c | 130 +++++++++------------------ libavcodec/vulkan_video.c | 118 ++++++++++++++++++++++++ libavcodec/vulkan_video.h | 41 +++++++++ 5 files changed, 220 insertions(+), 247 deletions(-) diff --git a/libavcodec/apv_encode_vulkan.c b/libavcodec/apv_encode_vulkan.c index 0e0fc1abc7..cb9efc8130 100644 --- a/libavcodec/apv_encode_vulkan.c +++ b/libavcodec/apv_encode_vulkan.c @@ -74,16 +74,10 @@ typedef struct VulkanEncodeAPVFrameData { FFVkBuffer *bytestream_ref; FFVkBuffer *compacted_ref; FFVkBuffer *sizes_ref; - - int64_t pts; - int64_t duration; - void *frame_opaque; - AVBufferRef *frame_opaque_ref; - int flags; } VulkanEncodeAPVFrameData; typedef struct VulkanEncodeAPVContext { - const AVClass *class; + FFVkEncodeLoop loop; FFVulkanContext s; AVVulkanDeviceQueueFamily *qf; @@ -106,11 +100,8 @@ typedef struct VulkanEncodeAPVContext { CodedBitstreamContext *cbc; CodedBitstreamFragment au; - AVFrame *frame; - /* Async machinery */ int async_depth; - int in_flight; VulkanEncodeAPVFrameData *exec_ctx_info; /* Derived per-encoder state */ @@ -671,17 +662,6 @@ static int build_packet(AVCodecContext *avctx, FFVkExecContext *exec, if (err < 0) return err; - pkt->pts = fd->pts; - pkt->dts = fd->pts; - pkt->duration = fd->duration; - pkt->flags |= AV_PKT_FLAG_KEY; /* APV is all intra */ - - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - pkt->opaque = fd->frame_opaque; - pkt->opaque_ref = fd->frame_opaque_ref; - fd->frame_opaque_ref = NULL; - } - av_log(avctx, AV_LOG_VERBOSE, "Encoded APV frame: %i bytes (%.2f MiB)\n", pkt->size, pkt->size / (1024.0 * 1024.0)); @@ -693,56 +673,6 @@ static int build_packet(AVCodecContext *avctx, FFVkExecContext *exec, return 0; } -static int vulkan_encode_apv_receive_packet(AVCodecContext *avctx, - AVPacket *pkt) -{ - int err; - VulkanEncodeAPVContext *ev = avctx->priv_data; - VulkanEncodeAPVFrameData *fd; - FFVkExecContext *exec; - AVFrame *frame; - - while (1) { - exec = ff_vk_exec_get(&ev->s, &ev->exec_pool); - - if (exec->had_submission) { - exec->had_submission = 0; - ev->in_flight--; - return build_packet(avctx, exec, pkt); - } - - frame = ev->frame; - err = ff_encode_get_frame(avctx, frame); - if (err < 0 && err != AVERROR_EOF) - return err; - else if (err == AVERROR_EOF) { - if (!ev->in_flight) - return err; - continue; - } - - fd = exec->opaque; - fd->pts = frame->pts; - fd->duration = frame->duration; - fd->flags = frame->flags; - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - fd->frame_opaque = frame->opaque; - fd->frame_opaque_ref = frame->opaque_ref; - frame->opaque_ref = NULL; - } - - err = submit_frame(avctx, exec, frame); - av_frame_unref(frame); - if (err < 0) - return err; - - ev->in_flight++; - if (ev->in_flight < ev->async_depth) - return AVERROR(EAGAIN); - } - return 0; -} - static av_cold int vulkan_encode_apv_close(AVCodecContext *avctx) { VulkanEncodeAPVContext *ev = avctx->priv_data; @@ -761,7 +691,6 @@ static av_cold int vulkan_encode_apv_close(AVCodecContext *avctx) av_refstruct_unref(&fd->bytestream_ref); av_refstruct_unref(&fd->compacted_ref); av_refstruct_unref(&fd->sizes_ref); - av_buffer_unref(&fd->frame_opaque_ref); } av_freep(&ev->exec_ctx_info); } @@ -774,7 +703,7 @@ static av_cold int vulkan_encode_apv_close(AVCodecContext *avctx) ff_cbs_fragment_free(&ev->au); ff_cbs_close(&ev->cbc); - av_frame_free(&ev->frame); + ff_vk_encode_loop_uninit(&ev->loop); ff_vk_uninit(&ev->s); return 0; @@ -962,9 +891,9 @@ static av_cold int vulkan_encode_apv_init(AVCodecContext *avctx) * build them once. */ build_dct_push_const(avctx); - ev->frame = av_frame_alloc(); - if (!ev->frame) - return AVERROR(ENOMEM); + err = ff_vk_encode_loop_init(&ev->s, &ev->exec_pool, &ev->loop, submit_frame, build_packet); + if (err < 0) + return err; /* Async data pool */ ev->async_depth = ev->exec_pool.pool_size; @@ -1027,7 +956,8 @@ const FFCodec ff_apv_vulkan_encoder = { .p.id = AV_CODEC_ID_APV, .priv_data_size = sizeof(VulkanEncodeAPVContext), .init = &vulkan_encode_apv_init, - FF_CODEC_RECEIVE_PACKET_CB(&vulkan_encode_apv_receive_packet), + FF_CODEC_RECEIVE_PACKET_CB(&ff_vk_encode_loop_receive_packet), + .flush = &ff_vk_encode_loop_flush, .close = &vulkan_encode_apv_close, .p.priv_class = &vulkan_encode_apv_class, .p.capabilities = AV_CODEC_CAP_DELAY | @@ -1035,7 +965,7 @@ const FFCodec ff_apv_vulkan_encoder = { AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_EOF_FLUSH, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vulkan_encode_apv_defaults, CODEC_PIXFMTS(AV_PIX_FMT_VULKAN), .hw_configs = vulkan_encode_apv_hw_configs, diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index 42020ccab3..a235be214e 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -45,26 +45,19 @@ typedef struct VulkanEncodeFFv1FrameData { FFVkBuffer *out_data_ref; FFVkBuffer *compacted_data_ref; - /* Copied from the source */ - int64_t pts; - int64_t duration; - void *frame_opaque; - AVBufferRef *frame_opaque_ref; - int key_frame; int idx; } VulkanEncodeFFv1FrameData; typedef struct VulkanEncodeFFv1Context { + FFVkEncodeLoop loop; FFV1Context ctx; - AVFrame *frame; FFVulkanContext s; AVVulkanDeviceQueueFamily *qf; FFVkExecPool exec_pool; VulkanEncodeFFv1FrameData *exec_ctx_info; - int in_flight; int async_depth; FFVulkanShader rct_search; @@ -270,7 +263,7 @@ static int run_sort32(AVCodecContext *avctx, FFVkExecContext *exec, static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, - const AVFrame *pict) + AVFrame *pict) { int err; VulkanEncodeFFv1Context *fv = avctx->priv_data; @@ -386,7 +379,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, compacted_buf = fd->compacted_data_ref; /* Image views */ - AVFrame *src = (AVFrame *)pict; + AVFrame *src = pict; VkImageView src_views[AV_NUM_DATA_POINTERS]; AVFrame *tmp = NULL; @@ -777,75 +770,18 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, } fd->compacted_data_ref = NULL; /* ownership passed to pkt->buf */ pkt->data = compacted_buf->mapped_mem; - - pkt->pts = fd->pts; - pkt->dts = fd->pts; - pkt->duration = fd->duration; - pkt->flags |= AV_PKT_FLAG_KEY * fd->key_frame; - - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - pkt->opaque = fd->frame_opaque; - pkt->opaque_ref = fd->frame_opaque_ref; - fd->frame_opaque_ref = NULL; - } + pkt->flags |= AV_PKT_FLAG_KEY * fd->key_frame; av_refstruct_unref(&fd->out_data_ref); return 0; } -static int vulkan_encode_ffv1_receive_packet(AVCodecContext *avctx, - AVPacket *pkt) +static av_cold void vulkan_encode_ffv1_flush(AVCodecContext *avctx) { - int err; VulkanEncodeFFv1Context *fv = avctx->priv_data; - VulkanEncodeFFv1FrameData *fd; - FFVkExecContext *exec; - AVFrame *frame; - - while (1) { - /* Roll an execution context */ - exec = ff_vk_exec_get(&fv->s, &fv->exec_pool); - - /* If it had a frame, immediately output it */ - if (exec->had_submission) { - exec->had_submission = 0; - fv->in_flight--; - return get_packet(avctx, exec, pkt); - } - - /* Get next frame to encode */ - frame = fv->frame; - err = ff_encode_get_frame(avctx, frame); - if (err < 0 && err != AVERROR_EOF) { - return err; - } else if (err == AVERROR_EOF) { - if (!fv->in_flight) - return err; - continue; - } - - /* Encode frame */ - fd = exec->opaque; - fd->pts = frame->pts; - fd->duration = frame->duration; - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - fd->frame_opaque = frame->opaque; - fd->frame_opaque_ref = frame->opaque_ref; - frame->opaque_ref = NULL; - } - - err = vulkan_encode_ffv1_submit_frame(avctx, exec, frame); - av_frame_unref(frame); - if (err < 0) - return err; - - fv->in_flight++; - if (fv->in_flight < fv->async_depth) - return AVERROR(EAGAIN); - } - - return 0; + ff_vk_encode_loop_flush(avctx); + fv->ctx.picture_number = 0; } static int init_indirect(AVCodecContext *avctx, enum AVPixelFormat sw_format) @@ -1453,10 +1389,8 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) 0, 256*sizeof(uint32_t), VK_FORMAT_UNDEFINED)); - /* Temporary frame */ - fv->frame = av_frame_alloc(); - if (!fv->frame) - return AVERROR(ENOMEM); + RET(ff_vk_encode_loop_init(&fv->s, &fv->exec_pool, &fv->loop, + vulkan_encode_ffv1_submit_frame, get_packet)); /* Async data pool */ fv->async_depth = fv->exec_pool.pool_size; @@ -1500,7 +1434,6 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx) VulkanEncodeFFv1FrameData *fd = &fv->exec_ctx_info[i]; av_refstruct_unref(&fd->out_data_ref); av_refstruct_unref(&fd->compacted_data_ref); - av_buffer_unref(&fd->frame_opaque_ref); } } av_free(fv->exec_ctx_info); @@ -1518,7 +1451,7 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx) ff_vk_free_buf(&fv->s, &fv->consts_buf); - av_frame_free(&fv->frame); + ff_vk_encode_loop_uninit(&fv->loop); ff_vk_uninit(&fv->s); return 0; @@ -1600,7 +1533,8 @@ const FFCodec ff_ffv1_vulkan_encoder = { .p.id = AV_CODEC_ID_FFV1, .priv_data_size = sizeof(VulkanEncodeFFv1Context), .init = &vulkan_encode_ffv1_init, - FF_CODEC_RECEIVE_PACKET_CB(&vulkan_encode_ffv1_receive_packet), + FF_CODEC_RECEIVE_PACKET_CB(&ff_vk_encode_loop_receive_packet), + .flush = &vulkan_encode_ffv1_flush, .close = &vulkan_encode_ffv1_close, .p.priv_class = &vulkan_encode_ffv1_class, .p.capabilities = AV_CODEC_CAP_DELAY | @@ -1608,7 +1542,7 @@ const FFCodec ff_ffv1_vulkan_encoder = { AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_EOF_FLUSH, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vulkan_encode_ffv1_defaults, CODEC_PIXFMTS(AV_PIX_FMT_VULKAN), .hw_configs = vulkan_encode_ffv1_hw_configs, diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c index 9449e88c4e..17f24ba5fc 100644 --- a/libavcodec/proresenc_kostya_vulkan.c +++ b/libavcodec/proresenc_kostya_vulkan.c @@ -88,18 +88,14 @@ typedef struct VulkanEncodeProresFrameData { FFVkBuffer *frame_size_ref[2]; /* Copied from the source */ - int64_t pts; - int64_t duration; - void *frame_opaque; - AVBufferRef *frame_opaque_ref; enum AVColorTransferCharacteristic color_trc; enum AVColorSpace colorspace; enum AVColorPrimaries color_primaries; - int key_frame; int flags; } VulkanEncodeProresFrameData; typedef struct ProresVulkanContext { + FFVkEncodeLoop loop; ProresContext ctx; /* Vulkan state */ @@ -125,13 +121,9 @@ typedef struct ProresVulkanContext { FFVulkanShader gather_shd; FFVkBuffer prores_data_tables_buf; - int *slice_quants; - SliceScore *slice_scores; ProresDataTables *tables; - int in_flight; int async_depth; - AVFrame *frame; VulkanEncodeProresFrameData *exec_ctx_info; } ProresVulkanContext; @@ -382,8 +374,8 @@ fail: return err; } -static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, - AVFrame *frame, int picture_idx) +static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, + AVFrame *frame, int picture_idx) { ProresVulkanContext *pv = avctx->priv_data; ProresContext *ctx = &pv->ctx; @@ -790,93 +782,36 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk pkt->data = wrap_buf->mapped_mem; pkt->size = frame_size; - pkt->pts = pd->pts; - pkt->dts = pd->pts; - pkt->duration = pd->duration; - pkt->flags |= AV_PKT_FLAG_KEY * pd->key_frame; - - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - pkt->opaque = pd->frame_opaque; - pkt->opaque_ref = pd->frame_opaque_ref; - pd->frame_opaque_ref = NULL; - } - av_log(avctx, AV_LOG_VERBOSE, "Encoded data: %iMiB\n", pkt->size / (1024*1024)); return 0; } -static int vulkan_encode_prores_receive_packet(AVCodecContext *avctx, AVPacket *pkt) +static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, + AVFrame *frame) { int err; ProresVulkanContext *pv = avctx->priv_data; - ProresContext *ctx = &pv->ctx; - VulkanEncodeProresFrameData *pd; - FFVkExecContext *exec; - AVFrame *frame; + VulkanEncodeProresFrameData *pd = exec->opaque; - while (1) { - /* Roll an execution context */ - exec = ff_vk_exec_get(&pv->vkctx, &pv->e); + pd->color_primaries = frame->color_primaries; + pd->color_trc = frame->color_trc; + pd->colorspace = frame->colorspace; + pd->flags = frame->flags; - /* If it had a frame, immediately output it */ - if (exec->had_submission) { - exec->had_submission = 0; - pv->in_flight--; - return get_packet(avctx, exec, pkt); - } + err = ff_vk_exec_start(&pv->vkctx, exec); + if (err < 0) + return err; - /* Get next frame to encode */ - frame = pv->frame; - err = ff_encode_get_frame(avctx, frame); - if (err < 0 && err != AVERROR_EOF) { - return err; - } else if (err == AVERROR_EOF) { - if (!pv->in_flight) - return err; - continue; - } - - /* Encode frame */ - pd = exec->opaque; - pd->color_primaries = frame->color_primaries; - pd->color_trc = frame->color_trc; - pd->colorspace = frame->colorspace; - pd->pts = frame->pts; - pd->duration = frame->duration; - pd->flags = frame->flags; - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - pd->frame_opaque = frame->opaque; - pd->frame_opaque_ref = frame->opaque_ref; - frame->opaque_ref = NULL; - } - - err = ff_vk_exec_start(&pv->vkctx, exec); + for (int i = 0; i < pv->ctx.pictures_per_frame; i++) { + err = encode_picture(avctx, exec, frame, i); if (err < 0) { - av_frame_unref(frame); + ff_vk_exec_discard(&pv->vkctx, exec); return err; } - - for (int i = 0; i < ctx->pictures_per_frame; i++) { - err = vulkan_encode_prores_submit_frame(avctx, exec, frame, i); - if (err < 0) { - ff_vk_exec_discard(&pv->vkctx, exec); - av_frame_unref(frame); - return err; - } - } - - err = ff_vk_exec_submit(&pv->vkctx, exec); - av_frame_unref(frame); - if (err < 0) - return err; - - pv->in_flight++; - if (pv->in_flight < pv->async_depth) - return AVERROR(EAGAIN); } - return 0; + return ff_vk_exec_submit(&pv->vkctx, exec); } static av_cold int encode_close(AVCodecContext *avctx) @@ -899,6 +834,22 @@ static av_cold int encode_close(AVCodecContext *avctx) ff_vk_free_buf(vkctx, &pv->prores_data_tables_buf); + if (pv->exec_ctx_info) { + for (int i = 0; i < pv->async_depth; i++) { + VulkanEncodeProresFrameData *pd = &pv->exec_ctx_info[i]; + for (int j = 0; j < 2; j++) { + av_refstruct_unref(&pd->out_data_ref[j]); + av_refstruct_unref(&pd->slice_sizes_ref[j]); + av_refstruct_unref(&pd->gathered_ref[j]); + av_refstruct_unref(&pd->slice_data_ref[j]); + av_refstruct_unref(&pd->slice_score_ref[j]); + av_refstruct_unref(&pd->frame_size_ref[j]); + } + } + av_freep(&pv->exec_ctx_info); + } + ff_vk_encode_loop_uninit(&pv->loop); + av_refstruct_pool_uninit(&pv->pkt_buf_pool); av_refstruct_pool_uninit(&pv->slice_sizes_buf_pool); av_refstruct_pool_uninit(&pv->gathered_buf_pool); @@ -927,18 +878,13 @@ static av_cold int encode_init(AVCodecContext *avctx) return AVERROR(ENOTSUP); } - RET(ff_vk_exec_pool_init(vkctx, pv->qf, &pv->e, 1, 0, 0, 0, NULL)); + RET(ff_vk_exec_pool_init(vkctx, pv->qf, &pv->e, pv->async_depth, 0, 0, 0, NULL)); /* Init common prores structures */ err = ff_prores_kostya_encode_init(avctx, ctx, vkctx->frames->sw_format); if (err < 0) return err; - /* Temporary frame */ - pv->frame = av_frame_alloc(); - if (!pv->frame) - return AVERROR(ENOMEM); - /* Async data pool */ pv->async_depth = pv->e.pool_size; pv->exec_ctx_info = av_calloc(pv->async_depth, sizeof(*pv->exec_ctx_info)); @@ -947,6 +893,9 @@ static av_cold int encode_init(AVCodecContext *avctx) for (int i = 0; i < pv->async_depth; i++) pv->e.contexts[i].opaque = &pv->exec_ctx_info[i]; + RET(ff_vk_encode_loop_init(vkctx, &pv->e, &pv->loop, + vulkan_encode_prores_submit_frame, get_packet)); + /* Compile shaders used by encoder */ init_slice_data_pipeline(pv, &pv->slice_data_shd[0], 2); init_slice_data_pipeline(pv, &pv->slice_data_shd[1], 4); @@ -1068,7 +1017,8 @@ const FFCodec ff_prores_ks_vulkan_encoder = { .priv_data_size = sizeof(ProresVulkanContext), .init = encode_init, .close = encode_close, - FF_CODEC_RECEIVE_PACKET_CB(&vulkan_encode_prores_receive_packet), + FF_CODEC_RECEIVE_PACKET_CB(&ff_vk_encode_loop_receive_packet), + .flush = &ff_vk_encode_loop_flush, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_ENCODER_FLUSH | @@ -1078,5 +1028,5 @@ const FFCodec ff_prores_ks_vulkan_encoder = { .color_ranges = AVCOL_RANGE_MPEG, .p.priv_class = &proresenc_class, .p.profiles = NULL_IF_CONFIG_SMALL(ff_prores_profiles), - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_EOF_FLUSH, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c index 838d681e64..6a496898cc 100644 --- a/libavcodec/vulkan_video.c +++ b/libavcodec/vulkan_video.c @@ -17,6 +17,7 @@ */ #include "libavutil/mem.h" +#include "encode.h" #include "vulkan_video.h" #define ASPECT_2PLANE (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT) @@ -629,3 +630,120 @@ int ff_vk_seg_gather(FFVulkanContext *s, FFVkExecContext *exec, FFVulkanShader * fail: return err; } + +int ff_vk_encode_loop_init(FFVulkanContext *s, FFVkExecPool *pool, FFVkEncodeLoop *l, + int (*submit_frame)(AVCodecContext *avctx, FFVkExecContext *exec, AVFrame *frame), + int (*get_packet)(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pkt)) +{ + l->s = s; + l->pool = pool; + l->submit_frame = submit_frame; + l->get_packet = get_packet; + l->head = 0; + l->in_flight = 0; + + l->frames = av_calloc(pool->pool_size, sizeof(*l->frames)); + l->frame = av_frame_alloc(); + l->pkt = av_packet_alloc(); + if (!l->frames || !l->frame || !l->pkt) + return AVERROR(ENOMEM); + + return 0; +} + +static int loop_get_packet(AVCodecContext *avctx, FFVkEncodeLoop *l, AVPacket *pkt) +{ + int err; + int idx = (l->head + l->pool->pool_size - l->in_flight) % l->pool->pool_size; + + l->in_flight--; + + err = l->get_packet(avctx, &l->pool->contexts[idx], pkt); + if (err < 0) { + av_buffer_unref(&l->frames[idx].opaque_ref); + return err; + } + + pkt->pts = l->frames[idx].pts; + pkt->dts = l->frames[idx].pts; + pkt->duration = l->frames[idx].duration; + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = l->frames[idx].opaque; + pkt->opaque_ref = l->frames[idx].opaque_ref; + l->frames[idx].opaque_ref = NULL; + } + + return 0; +} + +static int loop_oldest_done(FFVkEncodeLoop *l) +{ + FFVulkanFunctions *vk = &l->s->vkfn; + int idx = (l->head + l->pool->pool_size - l->in_flight) % l->pool->pool_size; + FFVkExecContext *e = &l->pool->contexts[idx]; + uint64_t val; + + return vk->GetSemaphoreCounterValue(l->s->hwctx->act_dev, e->sem, &val) == VK_SUCCESS && + val >= e->sem_value; +} + +int ff_vk_encode_loop_receive_packet(AVCodecContext *avctx, AVPacket *pkt) +{ + FFVkEncodeLoop *l = avctx->priv_data; + int err, eof = 0; + FFVkExecPool *pool = l->pool; + + err = ff_encode_get_frame(avctx, l->frame); + if (err == AVERROR_EOF) { + eof = 1; + } else if (err >= 0) { + l->frames[l->head].pts = l->frame->pts; + l->frames[l->head].duration = l->frame->duration; + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + l->frames[l->head].opaque = l->frame->opaque; + l->frames[l->head].opaque_ref = l->frame->opaque_ref; + l->frame->opaque_ref = NULL; + } + + err = l->submit_frame(avctx, &pool->contexts[l->head], l->frame); + av_frame_unref(l->frame); + if (err < 0) { + av_buffer_unref(&l->frames[l->head].opaque_ref); + return err; + } + + l->head = (l->head + 1) % pool->pool_size; + l->in_flight++; + } else if (err != AVERROR(EAGAIN)) { + return err; + } + + if (!l->in_flight) + return eof ? AVERROR_EOF : AVERROR(EAGAIN); + if (l->in_flight < pool->pool_size && !eof && !loop_oldest_done(l)) + return AVERROR(EAGAIN); + + return loop_get_packet(avctx, l, pkt); +} + +void ff_vk_encode_loop_flush(AVCodecContext *avctx) +{ + FFVkEncodeLoop *l = avctx->priv_data; + + while (l->in_flight) { + if (loop_get_packet(avctx, l, l->pkt) >= 0) + av_packet_unref(l->pkt); + } + l->head = 0; +} + +void ff_vk_encode_loop_uninit(FFVkEncodeLoop *l) +{ + if (l->frames) { + for (int i = 0; i < l->pool->pool_size; i++) + av_buffer_unref(&l->frames[i].opaque_ref); + av_freep(&l->frames); + } + av_frame_free(&l->frame); + av_packet_free(&l->pkt); +} diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h index cc3fe41640..af16e5ed6a 100644 --- a/libavcodec/vulkan_video.h +++ b/libavcodec/vulkan_video.h @@ -153,4 +153,45 @@ int ff_vk_seg_gather(FFVulkanContext *s, FFVkExecContext *exec, FFVulkanShader * FFVkBuffer *sparse, uint32_t slot_size, FFVkBuffer *compacted, size_t compacted_offset); +/** + * Frame loop for compute encoders. Keeps up to pool_size frames in flight, + * submitting each into its own execution context, and returns their packets + * in order as soon as they complete, or once the pool is full. + */ +typedef struct FFVkEncodeLoop { + const AVClass *class; /* Encoders keep this struct first in their context */ + FFVulkanContext *s; + FFVkExecPool *pool; + int (*submit_frame)(AVCodecContext *avctx, FFVkExecContext *exec, AVFrame *frame); + int (*get_packet)(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pkt); + + AVFrame *frame; + AVPacket *pkt; + struct { + int64_t pts; + int64_t duration; + void *opaque; + AVBufferRef *opaque_ref; + } *frames; + int head; + int in_flight; +} FFVkEncodeLoop; + +int ff_vk_encode_loop_init(FFVulkanContext *s, FFVkExecPool *pool, FFVkEncodeLoop *l, + int (*submit_frame)(AVCodecContext *avctx, FFVkExecContext *exec, AVFrame *frame), + int (*get_packet)(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pkt)); + +/** + * FFCodec.cb.receive_packet; the packet metadata is carried from the + * submitted frame to its packet. + */ +int ff_vk_encode_loop_receive_packet(AVCodecContext *avctx, AVPacket *pkt); + +/** + * Waits for and discards every frame in flight. + */ +void ff_vk_encode_loop_flush(AVCodecContext *avctx); + +void ff_vk_encode_loop_uninit(FFVkEncodeLoop *l); + #endif /* AVCODEC_VULKAN_VIDEO_H */ -- 2.52.0 >From eec112f7f5bbcabd867c9faf476afaa4caf6f0c3 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Wed, 23 Sep 2026 02:15:58 +0900 Subject: [PATCH 8/9] proresenc_kostya_vulkan: gather the second field in place The second field follows the first field's payload, whose size is only known on the GPU, so it was gathered into a scratch buffer and copied into the packet by the CPU. Let the gather add an offset it reads from GPU memory, and point it at the size the first field's gather wrote. --- libavcodec/apv_encode_vulkan.c | 2 +- libavcodec/ffv1enc_vulkan.c | 2 +- libavcodec/proresenc_kostya_vulkan.c | 62 ++++++++++---------------- libavcodec/vulkan/seg_gather.comp.glsl | 10 +++-- libavcodec/vulkan_video.c | 11 +++-- libavcodec/vulkan_video.h | 6 ++- 6 files changed, 43 insertions(+), 50 deletions(-) diff --git a/libavcodec/apv_encode_vulkan.c b/libavcodec/apv_encode_vulkan.c index cb9efc8130..7aa3fa36f8 100644 --- a/libavcodec/apv_encode_vulkan.c +++ b/libavcodec/apv_encode_vulkan.c @@ -515,7 +515,7 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, RET(ff_vk_seg_gather(&ev->s, exec, &ev->shd_compact, sizes_buf, 0, ev->tile_count * ev->num_comp, bytestream_buf, ev->slot_size, - compacted_buf, 0)); + compacted_buf, 0, 0)); err = ff_vk_exec_submit(&ev->s, exec); if (err < 0) diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index a235be214e..ea8ddecb85 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -684,7 +684,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, &fv->results_buf, fd->idx*(f->max_slice_count + 1)*sizeof(uint32_t), f->slice_count, out_data_buf, (out_data_buf->size / f->slice_count) & ~(size_t)15, - compacted_buf, 0)); + compacted_buf, 0, 0)); /* Submit */ ff_vk_exec_move_dep_refstruct(&fv->s, exec, &slice_data_buf); diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c index 17f24ba5fc..a1dd1ace77 100644 --- a/libavcodec/proresenc_kostya_vulkan.c +++ b/libavcodec/proresenc_kostya_vulkan.c @@ -82,7 +82,7 @@ typedef struct VulkanEncodeProresFrameData { /* Intermediate buffers */ FFVkBuffer *out_data_ref[2]; FFVkBuffer *slice_sizes_ref[2]; - FFVkBuffer *gathered_ref[2]; + FFVkBuffer *gathered_ref; FFVkBuffer *slice_data_ref[2]; FFVkBuffer *slice_score_ref[2]; FFVkBuffer *frame_size_ref[2]; @@ -413,17 +413,16 @@ static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); ff_vk_exec_add_dep_refstruct(vkctx, exec, pd->slice_sizes_ref[picture_idx]); - /* Picture 0 is gathered directly into the packet buffer at its static - * offset; picture 1's offset depends on picture 0's encoded size, so it - * is gathered to a scratch buffer and moved into place by the CPU. */ - RET(ff_vk_get_pooled_buffer(vkctx, &pv->gathered_buf_pool, &pd->gathered_ref[picture_idx], - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL, - picture_idx == 0 ? ctx->frame_size_upper_bound + FF_INPUT_BUFFER_MIN_SIZE - : ctx->slices_per_picture * pv->slice_slot_size, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - vkctx->host_cached_flag)); - ff_vk_exec_add_dep_refstruct(vkctx, exec, pd->gathered_ref[picture_idx]); + /* Both pictures are gathered directly into the packet buffer */ + if (!picture_idx) { + RET(ff_vk_get_pooled_buffer(vkctx, &pv->gathered_buf_pool, &pd->gathered_ref, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL, + ctx->frame_size_upper_bound + FF_INPUT_BUFFER_MIN_SIZE, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + vkctx->host_cached_flag)); + ff_vk_exec_add_dep_refstruct(vkctx, exec, pd->gathered_ref); + } /* Allocate buffer for writing slice data */ RET(ff_vk_get_pooled_buffer(vkctx, &pv->slice_data_buf_pool, &pd->slice_data_ref[picture_idx], @@ -607,12 +606,14 @@ static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, ctx->num_planes, 1); /* Gather the sparse slots into the contiguous bitstream, in the same - * submission. */ + * submission. Picture 1 follows the header and seek table after + * picture 0's payload, whose size the gather of picture 0 wrote. */ RET(ff_vk_seg_gather(vkctx, exec, &pv->gather_shd, pd->slice_sizes_ref[picture_idx], 0, ctx->slices_per_picture, - pkt_vk_buf, pv->slice_slot_size, - pd->gathered_ref[picture_idx], - picture_idx == 0 ? pv->payload_off : 0)); + pkt_vk_buf, pv->slice_slot_size, pd->gathered_ref, + pv->payload_off + picture_idx * (8 + ctx->slices_per_picture * 2), + picture_idx ? pd->slice_sizes_ref[0]->address + + ctx->slices_per_picture * sizeof(uint32_t) : 0)); fail: return err; @@ -675,8 +676,8 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk VulkanEncodeProresFrameData *pd = exec->opaque; FFVulkanContext *vkctx = &pv->vkctx; FFVulkanFunctions *vk = &vkctx->vkfn; - FFVkBuffer *wrap_buf = pd->gathered_ref[0]; - uint8_t *orig_buf, *buf, *slice_sizes; + FFVkBuffer *wrap_buf = pd->gathered_ref; + uint8_t *orig_buf, *buf; uint8_t *picture_size_pos; int picture_idx; int frame_size, picture_size; @@ -728,29 +729,14 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk /* Write the seek table from the per-slice sizes; the payload itself * was already packed to match by the gather pass. */ - slice_sizes = buf; sizes = (const uint32_t *)slice_sizes_buf->mapped_mem; for (int i = 0; i < ctx->slices_per_picture; i++) bytestream_put_be16(&buf, sizes[i]); + av_assert1(picture_idx || buf - wrap_buf->mapped_mem == pv->payload_off); /* Calculate final size */ buf += *(int*)frame_size_buf->mapped_mem; - if (picture_idx == 0) { - av_assert1(((slice_sizes + ctx->slices_per_picture * 2) - - wrap_buf->mapped_mem) == pv->payload_off); - } else { - /* Relocate the second picture's payload from its scratch buffer */ - FFVkBuffer *scratch = pd->gathered_ref[1]; - if (!(scratch->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { - invalidate_data.memory = scratch->mem; - vk->InvalidateMappedMemoryRanges(vkctx->hwctx->act_dev, 1, &invalidate_data); - } - memcpy(slice_sizes + ctx->slices_per_picture * 2, scratch->mapped_mem, - buf - (slice_sizes + ctx->slices_per_picture * 2)); - av_refstruct_unref(&pd->gathered_ref[1]); - } - /* Write picture size with header */ picture_size = buf - (picture_size_pos - 1); bytestream_put_be32(&picture_size_pos, picture_size); @@ -773,12 +759,12 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk /* Hand the buffer to the packet with no copy: pkt->buf references the * pooled Vulkan buffer, returned to its pool when the packet is freed. */ pkt->buf = av_buffer_create(wrap_buf->mapped_mem, wrap_buf->size, - prores_vk_packet_free, pd->gathered_ref[0], 0); + prores_vk_packet_free, pd->gathered_ref, 0); if (!pkt->buf) { - av_refstruct_unref(&pd->gathered_ref[0]); + av_refstruct_unref(&pd->gathered_ref); return AVERROR(ENOMEM); } - pd->gathered_ref[0] = NULL; /* ownership passed to pkt->buf */ + pd->gathered_ref = NULL; /* ownership passed to pkt->buf */ pkt->data = wrap_buf->mapped_mem; pkt->size = frame_size; @@ -840,11 +826,11 @@ static av_cold int encode_close(AVCodecContext *avctx) for (int j = 0; j < 2; j++) { av_refstruct_unref(&pd->out_data_ref[j]); av_refstruct_unref(&pd->slice_sizes_ref[j]); - av_refstruct_unref(&pd->gathered_ref[j]); av_refstruct_unref(&pd->slice_data_ref[j]); av_refstruct_unref(&pd->slice_score_ref[j]); av_refstruct_unref(&pd->frame_size_ref[j]); } + av_refstruct_unref(&pd->gathered_ref); } av_freep(&pv->exec_ctx_info); } diff --git a/libavcodec/vulkan/seg_gather.comp.glsl b/libavcodec/vulkan/seg_gather.comp.glsl index 0a3c6cbf42..073c43536b 100644 --- a/libavcodec/vulkan/seg_gather.comp.glsl +++ b/libavcodec/vulkan/seg_gather.comp.glsl @@ -46,9 +46,10 @@ layout (set = 0, binding = 0, scalar) buffer sizes_buf { }; layout (push_constant, scalar) uniform pushConstants { - u8buf sparse; /* one slot per segment */ - u8buf compacted; /* contiguous output */ - uint slot_size; /* stride between sparse slots */ + u8buf sparse; /* one slot per segment */ + u8buf compacted; /* contiguous output */ + uint64_t offset_addr; /* extra output offset, or 0 */ + uint slot_size; /* stride between sparse slots */ }; shared uint s_dst_off; @@ -110,7 +111,8 @@ void main(void) const uint n = seg_sizes[seg]; const uint64_t src_base = uint64_t(sparse) + seg * slot_size; - const uint64_t dst_base = uint64_t(compacted) + s_dst_off; + const uint base = offset_addr != 0ul ? u32buf(offset_addr).v : 0u; + const uint64_t dst_base = uint64_t(compacted) + base + s_dst_off; u8buf src8 = u8buf(src_base); u8buf dst8 = u8buf(dst_base); diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c index 6a496898cc..8942f509d5 100644 --- a/libavcodec/vulkan_video.c +++ b/libavcodec/vulkan_video.c @@ -548,6 +548,7 @@ fail: typedef struct SegGatherPushData { VkDeviceAddress sparse; VkDeviceAddress compacted; + VkDeviceAddress offset_addr; uint32_t slot_size; } SegGatherPushData; @@ -583,14 +584,16 @@ fail: int ff_vk_seg_gather(FFVulkanContext *s, FFVkExecContext *exec, FFVulkanShader *shd, FFVkBuffer *sizes, size_t sizes_offset, uint32_t nb_segs, FFVkBuffer *sparse, uint32_t slot_size, - FFVkBuffer *compacted, size_t compacted_offset) + FFVkBuffer *compacted, size_t compacted_offset, + VkDeviceAddress offset_addr) { int err; FFVulkanFunctions *vk = &s->vkfn; SegGatherPushData pd = { - .sparse = sparse->address, - .compacted = compacted->address + compacted_offset, - .slot_size = slot_size, + .sparse = sparse->address, + .compacted = compacted->address + compacted_offset, + .offset_addr = offset_addr, + .slot_size = slot_size, }; vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h index af16e5ed6a..f4e3844304 100644 --- a/libavcodec/vulkan_video.h +++ b/libavcodec/vulkan_video.h @@ -146,12 +146,14 @@ int ff_vk_seg_gather_init(FFVulkanContext *s, FFVkExecPool *pool, * Gathers nb_segs slots of slot_size bytes from sparse into compacted, with * the segment sizes given as nb_segs uint32_t in sizes at sizes_offset, * followed by one more that receives the packed size. Both are made visible - * to the host. + * to the host. If offset_addr is non-zero, the uint32_t it points to is added + * to compacted_offset. */ int ff_vk_seg_gather(FFVulkanContext *s, FFVkExecContext *exec, FFVulkanShader *shd, FFVkBuffer *sizes, size_t sizes_offset, uint32_t nb_segs, FFVkBuffer *sparse, uint32_t slot_size, - FFVkBuffer *compacted, size_t compacted_offset); + FFVkBuffer *compacted, size_t compacted_offset, + VkDeviceAddress offset_addr); /** * Frame loop for compute encoders. Keeps up to pool_size frames in flight, -- 2.52.0 >From 10ce0d7e1d8b77d1155d4d0abf3926fba353254c Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Wed, 23 Sep 2026 02:32:36 +0900 Subject: [PATCH 9/9] proresenc_kostya_vulkan: take the picture size from the gather The trellis summed the estimated slice sizes into a separate host buffer for the CPU to step over the payload with, and wrote each slice's start offset, which nothing has read since slices went into fixed slots. The gather writes the packed size of the slices after their sizes; use that, and drop the buffer and the prefix sums. The barrier after the trellis now covers the quantisers the encoder reads, rather than the frame size buffer. --- libavcodec/proresenc_kostya_vulkan.c | 48 ++---------- .../vulkan/prores_ks_encode_slice.comp.glsl | 1 - .../vulkan/prores_ks_estimate_slice.comp.glsl | 1 - .../vulkan/prores_ks_trellis_node.comp.glsl | 77 +++---------------- 4 files changed, 16 insertions(+), 111 deletions(-) diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c index a1dd1ace77..1b94742c39 100644 --- a/libavcodec/proresenc_kostya_vulkan.c +++ b/libavcodec/proresenc_kostya_vulkan.c @@ -74,7 +74,6 @@ typedef struct SliceScore { int total_bits[MAX_STORED_Q]; int total_error[MAX_STORED_Q]; int overquant; - int buf_start; int quant; } SliceScore; @@ -85,7 +84,6 @@ typedef struct VulkanEncodeProresFrameData { FFVkBuffer *gathered_ref; FFVkBuffer *slice_data_ref[2]; FFVkBuffer *slice_score_ref[2]; - FFVkBuffer *frame_size_ref[2]; /* Copied from the source */ enum AVColorTransferCharacteristic color_trc; @@ -107,7 +105,6 @@ typedef struct ProresVulkanContext { AVRefStructPool *gathered_buf_pool; AVRefStructPool *slice_data_buf_pool; AVRefStructPool *slice_score_buf_pool; - AVRefStructPool *frame_size_buf_pool; size_t slice_slot_size; int estimate_slices_per_wg; @@ -284,13 +281,9 @@ static int init_trellis_node_pipeline(ProresVulkanContext *pv, FFVulkanShader* s int err = 0; FFVulkanContext *vkctx = &pv->vkctx; FFVulkanDescriptorSetBinding *desc; - /* The driver picks the subgroup size; size for the most it may use */ - int subgroup_size = vkctx->subgroup_props.minSubgroupSize; - int num_subgroups = FFALIGN(pv->ctx.mb_height, subgroup_size) / subgroup_size; - SPEC_LIST_CREATE(sl, 8, 8 * sizeof(uint32_t)) + SPEC_LIST_CREATE(sl, 7, 7 * sizeof(uint32_t)) SPEC_LIST_ADD(sl, 0, 32, pv->ctx.slices_width); - SPEC_LIST_ADD(sl, 1, 32, num_subgroups); SPEC_LIST_ADD(sl, 2, 32, pv->ctx.num_planes); SPEC_LIST_ADD(sl, 3, 32, pv->ctx.force_quant); SPEC_LIST_ADD(sl, 4, 32, pv->ctx.profile_info->min_quant); @@ -302,18 +295,13 @@ static int init_trellis_node_pipeline(ProresVulkanContext *pv, FFVulkanShader* s (uint32_t []) { pv->ctx.mb_height, 1, 1 }, 0); desc = (FFVulkanDescriptorSetBinding []) { - { - .name = "FrameSize", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - }, { .name = "SliceScores", .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .stages = VK_SHADER_STAGE_COMPUTE_BIT, }, }; - ff_vk_shader_add_descriptor_set(vkctx, shd, desc, 2, 0); + ff_vk_shader_add_descriptor_set(vkctx, shd, desc, 1, 0); RET(ff_vk_shader_link(vkctx, shd, ff_prores_ks_trellis_node_comp_spv_data, @@ -388,7 +376,7 @@ static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(vkctx->frames->sw_format); VkImageView views[AV_NUM_DATA_POINTERS]; VkImageMemoryBarrier2 img_bar[AV_NUM_DATA_POINTERS]; - FFVkBuffer *pkt_vk_buf, *slice_data_buf, *slice_score_buf, *frame_size_buf; + FFVkBuffer *pkt_vk_buf, *slice_data_buf, *slice_score_buf; SliceDataInfo slice_data_info; EncodeSliceInfo encode_info; FFVulkanShader *shd; @@ -442,17 +430,6 @@ static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, slice_score_buf = pd->slice_score_ref[picture_idx]; ff_vk_exec_add_dep_refstruct(vkctx, exec, pd->slice_score_ref[picture_idx]); - /* Allocate buffer for writing frame size */ - RET(ff_vk_get_pooled_buffer(vkctx, &pv->frame_size_buf_pool, &pd->frame_size_ref[picture_idx], - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL, - sizeof(int), - VK_MEMORY_PROPERTY_HOST_CACHED_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - frame_size_buf = pd->frame_size_ref[picture_idx]; - ff_vk_exec_add_dep_refstruct(vkctx, exec, pd->frame_size_ref[picture_idx]); - /* Generate barriers and image views for frame images. */ RET(ff_vk_exec_add_dep_frame(vkctx, exec, frame, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -557,9 +534,6 @@ static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, /* Compute optimal quant value for each slice */ ff_vk_shader_update_desc_buffer(vkctx, exec, &pv->trellis_node_shd, 0, 0, 0, - frame_size_buf, 0, frame_size_buf->size, - VK_FORMAT_UNDEFINED); - ff_vk_shader_update_desc_buffer(vkctx, exec, &pv->trellis_node_shd, 0, 1, 0, slice_score_buf, 0, slice_score_buf->size, VK_FORMAT_UNDEFINED); ff_vk_exec_bind_shader(vkctx, exec, &pv->trellis_node_shd); @@ -577,9 +551,9 @@ static int encode_picture(AVCodecContext *avctx, FFVkExecContext *exec, .dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_SHADER_READ_BIT, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = frame_size_buf->buf, + .buffer = slice_score_buf->buf, .offset = 0, - .size = frame_size_buf->size, + .size = slice_score_buf->size, }, .bufferMemoryBarrierCount = 1, }); @@ -681,7 +655,6 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk uint8_t *picture_size_pos; int picture_idx; int frame_size, picture_size; - FFVkBuffer *frame_size_buf; VkMappedMemoryRange invalidate_data; /* Make sure encoding's done */ @@ -708,13 +681,7 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk FFVkBuffer *slice_sizes_buf = pd->slice_sizes_ref[picture_idx]; const uint32_t *sizes; - frame_size_buf = pd->frame_size_ref[picture_idx]; - /* Invalidate slice sizes if needed */ - if (!(frame_size_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { - invalidate_data.memory = frame_size_buf->mem; - vk->InvalidateMappedMemoryRanges(vkctx->hwctx->act_dev, 1, &invalidate_data); - } if (!(slice_sizes_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { invalidate_data.memory = slice_sizes_buf->mem; vk->InvalidateMappedMemoryRanges(vkctx->hwctx->act_dev, 1, &invalidate_data); @@ -735,7 +702,7 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk av_assert1(picture_idx || buf - wrap_buf->mapped_mem == pv->payload_off); /* Calculate final size */ - buf += *(int*)frame_size_buf->mapped_mem; + buf += sizes[ctx->slices_per_picture]; /* Write picture size with header */ picture_size = buf - (picture_size_pos - 1); @@ -746,7 +713,6 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec, AVPacket *pk av_refstruct_unref(&pd->slice_sizes_ref[picture_idx]); av_refstruct_unref(&pd->slice_data_ref[picture_idx]); av_refstruct_unref(&pd->slice_score_ref[picture_idx]); - av_refstruct_unref(&pd->frame_size_ref[picture_idx]); } /* Write frame size in header */ @@ -828,7 +794,6 @@ static av_cold int encode_close(AVCodecContext *avctx) av_refstruct_unref(&pd->slice_sizes_ref[j]); av_refstruct_unref(&pd->slice_data_ref[j]); av_refstruct_unref(&pd->slice_score_ref[j]); - av_refstruct_unref(&pd->frame_size_ref[j]); } av_refstruct_unref(&pd->gathered_ref); } @@ -841,7 +806,6 @@ static av_cold int encode_close(AVCodecContext *avctx) av_refstruct_pool_uninit(&pv->gathered_buf_pool); av_refstruct_pool_uninit(&pv->slice_data_buf_pool); av_refstruct_pool_uninit(&pv->slice_score_buf_pool); - av_refstruct_pool_uninit(&pv->frame_size_buf_pool); ff_vk_uninit(vkctx); diff --git a/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl b/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl index c3f3c7b8f2..456b0416c3 100644 --- a/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl +++ b/libavcodec/vulkan/prores_ks_encode_slice.comp.glsl @@ -45,7 +45,6 @@ struct SliceScore { int total_bits[16]; int total_score[16]; int overquant; - int buf_start; int quant; }; diff --git a/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl b/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl index 28807c5186..26720229cf 100644 --- a/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl +++ b/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl @@ -50,7 +50,6 @@ struct SliceScore { int total_bits[16]; int total_score[16]; int overquant; - int buf_start; int quant; }; diff --git a/libavcodec/vulkan/prores_ks_trellis_node.comp.glsl b/libavcodec/vulkan/prores_ks_trellis_node.comp.glsl index a7c6fb9bcd..26f2c76d34 100644 --- a/libavcodec/vulkan/prores_ks_trellis_node.comp.glsl +++ b/libavcodec/vulkan/prores_ks_trellis_node.comp.glsl @@ -22,13 +22,10 @@ #extension GL_EXT_control_flow_attributes : require #extension GL_EXT_scalar_block_layout : require #extension GL_EXT_shader_explicit_arithmetic_types : require -#extension GL_KHR_shader_subgroup_arithmetic : require -#extension GL_EXT_maximal_reconvergence : require layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; layout (constant_id = 0) const int slices_per_row = 1; -layout (constant_id = 1) const int num_subgroups = 1; layout (constant_id = 2) const int num_planes = 0; layout (constant_id = 3) const int force_quant = 0; layout (constant_id = 4) const int min_quant = 0; @@ -42,14 +39,10 @@ struct SliceScore { int total_bits[16]; int total_score[16]; int overquant; - int buf_start; int quant; }; -layout (set = 0, binding = 0, scalar) writeonly buffer FrameSize { - int frame_size; -}; -layout (set = 0, binding = 1, scalar) buffer SliceScores { +layout (set = 0, binding = 0, scalar) buffer SliceScores { SliceScore scores[]; }; @@ -63,10 +56,6 @@ struct TrellisNode { int score; }; -shared int subgroup_sizes[num_subgroups]; - -int slice_sizes[slices_per_row]; - TrellisNode nodes[(slices_per_row + 1) * TRELLIS_WIDTH]; int find_slice_quant(int slice_x) @@ -117,7 +106,7 @@ int find_slice_quant(int slice_x) return pq; } -int find_slice_row_quants() +void find_slice_row_quants() { for (int i = min_quant; i < max_quant + 2; i++) { nodes[i].prev_node = -1; @@ -130,67 +119,21 @@ int find_slice_row_quants() q = find_slice_quant(slice_x); } - int slice_hdr_size = 2 * num_planes; - int slice_row_size = slice_hdr_size * slices_per_row; int y = int(gl_LocalInvocationID.x); for (int x = slices_per_row - 1; x >= 0; x--) { int slice = x + y * slices_per_row; - int quant = nodes[q].quant; - int q_idx = min(quant, max_quant + 1); - ivec4 bits = scores[slice].bits[q_idx]; - slice_sizes[x] = (bits.x + bits.y + bits.z + bits.w) / 8; - slice_row_size += slice_sizes[x]; - scores[slice].quant = quant; + scores[slice].quant = nodes[q].quant; q = nodes[q].prev_node; } - - return slice_row_size; } -int force_slice_row_quants() +void main() { - int slice_hdr_size = 2 * num_planes; - int slice_row_size = slice_hdr_size * slices_per_row; - int y = int(gl_LocalInvocationID.x); - for (int x = slices_per_row - 1; x >= 0; x--) { - int slice = x + y * slices_per_row; - ivec4 bits = scores[slice].bits[0]; - slice_sizes[x] = (bits.x + bits.y + bits.z + bits.w) / 8; - slice_row_size += slice_sizes[x]; - scores[slice].quant = force_quant; + if (force_quant == 0) { + find_slice_row_quants(); + } else { + int y = int(gl_LocalInvocationID.x); + for (int x = 0; x < slices_per_row; x++) + scores[x + y * slices_per_row].quant = force_quant; } - - return slice_row_size; -} - -void main() [[maximally_reconverges]] -{ - int slice_row_size; - if (force_quant == 0) - slice_row_size = find_slice_row_quants(); - else - slice_row_size = force_slice_row_quants(); - - int subgroup_sum = subgroupAdd(slice_row_size); - if (subgroupElect()) - subgroup_sizes[gl_SubgroupID] = subgroup_sum; - barrier(); - - int buf_start = subgroupExclusiveAdd(slice_row_size); - [[unroll]] for (int i = 0; i < num_subgroups; ++i) { - if (i >= gl_SubgroupID) - break; - buf_start += subgroup_sizes[i]; - } - - int slice_hdr_size = 2 * num_planes; - int y = int(gl_LocalInvocationID.x); - [[unroll]] for (int x = 0; x < slices_per_row; ++x) { - int slice = x + y * slices_per_row; - scores[slice].buf_start = buf_start; - buf_start += slice_hdr_size + slice_sizes[x]; - } - - if (y == gl_WorkGroupSize.x - 1) - frame_size = buf_start; } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
