diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp index 9aef5333fde..d53f8b93690 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.cpp @@ -297,6 +297,9 @@ d3d12_video_encoder_destroy(struct pipe_video_codec *codec) d3d12_video_encoder_sync_completion(codec, pD3D12Enc->m_spFence.Get(), curBatchFence, OS_TIMEOUT_INFINITE); } + if (pD3D12Enc->m_nalPrefixTmpBuffer) + pD3D12Enc->m_screen->resource_destroy(pD3D12Enc->m_screen, pD3D12Enc->m_nalPrefixTmpBuffer); + // Call d3d12_video_encoder dtor to make ComPtr and other member's destructors work delete pD3D12Enc; } @@ -1611,6 +1614,32 @@ d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc return true; } +static inline void +d3d12_video_encoder_reallocate_prefix_nal_buffer(struct d3d12_video_encoder *pD3D12Enc, + pipe_resource** out_buffer, + uint32_t byte_size) +{ + if (*out_buffer != NULL) + { + debug_printf("[d3d12_video_encoder_reallocate_prefix_nal_buffer] Destroying existing buffer at address: 0x%p byte size: %d\n", *out_buffer, (*out_buffer)->width0); + pD3D12Enc->m_screen->resource_destroy(pD3D12Enc->m_screen, *out_buffer); + *out_buffer = NULL; + } + + struct pipe_resource templ = { }; + memset(&templ, 0, sizeof(templ)); + templ.target = PIPE_BUFFER; + templ.usage = PIPE_USAGE_DEFAULT; + templ.format = PIPE_FORMAT_R8_UINT; + templ.width0 = byte_size; + templ.height0 = 1; + templ.depth0 = 1; + templ.array_size = 1; + *out_buffer = pD3D12Enc->m_screen->resource_create(pD3D12Enc->m_screen, &templ); + assert(*out_buffer); + debug_printf("[d3d12_video_encoder_reallocate_prefix_nal_buffer] Created new buffer at address: 0x%p byte size: %d\n", *out_buffer, (*out_buffer)->width0); +} + struct pipe_video_codec * d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *codec) { @@ -2014,6 +2043,9 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, assert(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size()); pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersBytePadding = 0; + // Save the pipe destination buffer the headers need to be written to in get_feedback if post encode headers needed or H264 SVC NAL prefixes, etc + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination = &pOutputBitstreamBuffer->base.b; + // Only upload headers now and leave prefix offset space gap in compressed bitstream if the codec builds headers before execution. if (!pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded) { @@ -2075,9 +2107,6 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec, // Headers are written after execution, have EncodeFrame write into a staging buffer // and then get_feedback will pack the finalized bitstream and copy into comp_bit_destination pOutputBufferD3D12Res = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream.Get(); - - // Save the pipe destination buffer the headers need to be written to in get_feedback - pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination = &pOutputBitstreamBuffer->base.b; } memset(&pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData, @@ -2462,6 +2491,133 @@ d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec, } else { +#if VIDEO_CODEC_H264ENC + // Add H264 temporal layers slice nal prefixes if necessary + if ((D3D12_VIDEO_ENCODER_CODEC_H264 == pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_encoderCodecDesc) + && (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_svcprefix_slice_header) + && (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_encoderCodecSpecificSequenceStateDescH264.num_temporal_layers > 1)) + { + if (!pD3D12Enc->m_nalPrefixTmpBuffer) + d3d12_video_encoder_reallocate_prefix_nal_buffer(pD3D12Enc, + &pD3D12Enc->m_nalPrefixTmpBuffer, + D3D12_DEFAULT_COMPBIT_STAGING_SIZE); + assert (pD3D12Enc->m_nalPrefixTmpBuffer); + + // + // Copy slices from driver comp_bit_destination into m_nalPrefixTmpBuffer with collated slices NAL prefixes + // + // Skip SPS, PPS, etc first preEncodeGeneratedHeadersByteSize bytes in src_driver_buffer_read_bytes + uint32_t src_driver_buffer_read_bytes = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize; + uint32_t dst_tmp_buffer_written_bytes = 0; + uint32_t num_slices = pSubregionsMetadata.size(); + std::vector > prefix_nal_bufs; + prefix_nal_bufs.resize(num_slices); + size_t written_prefix_nal_bytes = 0; + for (uint32_t cur_slice_idx = 0; cur_slice_idx < num_slices; cur_slice_idx++) + { + // At the end of the loop we're inserting at pSubregionsMetadata.insert(std::next(pSubregionsMetadata.begin() + // so we have to 2x the loop current index to index the current slice, skipping the prefix NALs of the previously processed slices + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA cur_subregion_metadata = pSubregionsMetadata[2 * cur_slice_idx]; + + d3d12_video_encoder_build_slice_svc_prefix_nalu_h264(pD3D12Enc, + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot], + prefix_nal_bufs[cur_slice_idx], + prefix_nal_bufs[cur_slice_idx].begin(), + written_prefix_nal_bytes); + + // Upload nal prefix to m_nalPrefixTmpBuffer + pD3D12Enc->base.context->buffer_subdata(pD3D12Enc->base.context, // context + pD3D12Enc->m_nalPrefixTmpBuffer, // dst buffer + PIPE_MAP_WRITE, // dst usage PIPE_MAP_x + dst_tmp_buffer_written_bytes, // dst offset + written_prefix_nal_bytes, // src size + prefix_nal_bufs[cur_slice_idx].data()); // src void* buffer + dst_tmp_buffer_written_bytes += written_prefix_nal_bytes; + + // Copy slice (padded as-is) cur_subregion_metadata.bSize at src_driver_buffer_read_bytes into m_nalPrefixTmpBuffer AFTER the prefix nal (written_prefix_nal_bytes) to m_nalPrefixTmpBuffer + struct pipe_box src_box = {}; + u_box_3d(src_driver_buffer_read_bytes, // x + 0, // y + 0, // z + static_cast(cur_subregion_metadata.bSize), // width + 1, // height + 1, // depth + &src_box + ); + + pD3D12Enc->base.context->resource_copy_region(pD3D12Enc->base.context, // ctx + pD3D12Enc->m_nalPrefixTmpBuffer, // dst + 0, // dst_level + dst_tmp_buffer_written_bytes, // dstX - Skip the other headers in the final bitstream (e.g SPS, PPS, etc) + 0, // dstY + 0, // dstZ + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination, // src + 0, // src level + &src_box); + src_driver_buffer_read_bytes += cur_subregion_metadata.bSize; + dst_tmp_buffer_written_bytes += cur_subregion_metadata.bSize; + + // Insert prefix NAL before current slice in cur_subregion_metadata so it's treated as just another NAL below for reporting metadata + pSubregionsMetadata.insert(std::next(pSubregionsMetadata.begin(), 2 * cur_slice_idx), { /* bSize */ written_prefix_nal_bytes, /* bStartOffset */ 0, /* bHeaderSize */ 0 }); + } + + // + // Copy from m_nalPrefixTmpBuffer with prefixes and slices back into comp_bit_destination + // + + // Make sure we have enough space in destination buffer + if (dst_tmp_buffer_written_bytes > + (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination->width0)) + { + opt_metadata.encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED; + debug_printf("[d3d12_video_encoder] Insufficient compressed buffer size passed from frontend while building the H264 SVC NAL prefixes.\n"); + assert(false); + if(pMetadata) + *pMetadata = opt_metadata; + return; + } + + // Do the copy + struct pipe_box src_box = {}; + u_box_3d(0, // x + 0, // y + 0, // z + static_cast(dst_tmp_buffer_written_bytes), // width + 1, // height + 1, // depth + &src_box + ); + + pD3D12Enc->base.context->resource_copy_region(pD3D12Enc->base.context, // ctx + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination, // dst + 0, // dst_level + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize,// dstX - Skip the other headers in the final bitstream (e.g SPS, PPS, etc) + 0, // dstY + 0, // dstZ + pD3D12Enc->m_nalPrefixTmpBuffer, // src + 0, // src level + &src_box); + + // + // Flush copies in batch and wait on this CPU thread for GPU work completion + // + { + struct pipe_fence_handle *pUploadGPUCompletionFence = NULL; + pD3D12Enc->base.context->flush(pD3D12Enc->base.context, + &pUploadGPUCompletionFence, + PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH); + assert(pUploadGPUCompletionFence); + pD3D12Enc->m_pD3D12Screen->base.fence_finish(&pD3D12Enc->m_pD3D12Screen->base, + NULL, + pUploadGPUCompletionFence, + OS_TIMEOUT_INFINITE); + pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base, + &pUploadGPUCompletionFence, + NULL); + } + } +#endif // VIDEO_CODEC_H264ENC + *output_buffer_size = 0; for (uint32_t i = 0; i < pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes.size() ; i++) { unpadded_frame_size += pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes[i]; diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc.h b/src/gallium/drivers/d3d12/d3d12_video_enc.h index d413dfaec0c..cc5d5bacbb3 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc.h +++ b/src/gallium/drivers/d3d12/d3d12_video_enc.h @@ -137,6 +137,7 @@ enum d3d12_video_encoder_config_dirty_flags d3d12_video_encoder_config_dirty_flag_picture_header = 0x2000, d3d12_video_encoder_config_dirty_flag_aud_header = 0x4000, d3d12_video_encoder_config_dirty_flag_sei_header = 0x8000, + d3d12_video_encoder_config_dirty_flag_svcprefix_slice_header = 0x10000, }; DEFINE_ENUM_FLAG_OPERATORS(d3d12_video_encoder_config_dirty_flags); @@ -287,6 +288,8 @@ struct D3D12EncodeConfiguration struct pipe_h265_enc_seq_param m_encoderCodecSpecificSequenceStateDescH265; struct pipe_h265_enc_vid_param m_encoderCodecSpecificVideoStateDescH265; struct pipe_h265_enc_pic_param m_encoderCodecSpecificPictureStateDescH265; + + bool m_bUsedAsReference; // Set if frame will be used as reference frame }; struct EncodedBitstreamResolvedMetadata @@ -408,6 +411,7 @@ struct d3d12_video_encoder std::shared_ptr m_upDPBStorageManager; std::unique_ptr m_upBitstreamBuilder; + pipe_resource* m_nalPrefixTmpBuffer = NULL; std::vector m_BitstreamHeadersBuffer; std::vector m_StagingHeadersBuffer; std::vector m_spEncodedFrameMetadata; diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp index b6caa54b0b5..9229a920e13 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_av1.cpp @@ -1218,7 +1218,8 @@ d3d12_video_encoder_update_current_frame_pic_params_info_av1(struct d3d12_video_ struct pipe_av1_enc_picture_desc *pAV1Pic = (struct pipe_av1_enc_picture_desc *) picture; // Output param bUsedAsReference - bUsedAsReference = (pAV1Pic->refresh_frame_flags != 0); + pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference = (pAV1Pic->refresh_frame_flags != 0); + bUsedAsReference = pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference; // D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAGS Flags; picParams.pAV1PicData->Flags = D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_NONE; diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp index 07bbe8a3f21..1b022bad26c 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.cpp @@ -356,7 +356,8 @@ d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video static_cast(pD3D12Enc->m_upBitstreamBuilder.get()); assert(pH264BitstreamBuilder != nullptr); - bUsedAsReference = !h264Pic->not_referenced; + pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference = !h264Pic->not_referenced; + bUsedAsReference = pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference; if (pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_H264CodecCaps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE_FLAG_SLICE_SUPPORT) @@ -391,6 +392,13 @@ d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video picParams.pH264PicData->pRateControlQPMap = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[h264Pic->pic_ctrl.temporal_id].m_pRateControlQPMap8Bit.data(); picParams.pH264PicData->QPMapValuesCount = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[h264Pic->pic_ctrl.temporal_id].m_pRateControlQPMap8Bit.size(); } + + // Save state snapshot from record time to resolve headers at get_feedback time + uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT); + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeCapabilities = + pD3D12Enc->m_currentEncodeCapabilities; + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig = + pD3D12Enc->m_currentEncodeConfig; } /// @@ -933,6 +941,8 @@ d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_ pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_aud_header; else if (header->type == NAL_TYPE_SEI) pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_sei_header; + else if (header->type == NAL_TYPE_PREFIX) + pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_svcprefix_slice_header; } // Set input format @@ -1262,3 +1272,36 @@ d3d12_video_encoder_build_codec_headers_h264(struct d3d12_video_encoder *pD3D12E static_cast(pD3D12Enc->m_BitstreamHeadersBuffer.size())); return pD3D12Enc->m_BitstreamHeadersBuffer.size(); } + +uint32_t +d3d12_video_encoder_build_slice_svc_prefix_nalu_h264(struct d3d12_video_encoder * pD3D12Enc, + EncodedBitstreamResolvedMetadata& associatedMetadata, + std::vector & headerBitstream, + std::vector::iterator placingPositionStart, + size_t & writtenSVCPrefixNalBytes) +{ + d3d12_video_bitstream_builder_h264 *pH264BitstreamBuilder = + static_cast(pD3D12Enc->m_upBitstreamBuilder.get()); + assert(pH264BitstreamBuilder); + + H264_SLICE_PREFIX_SVC nal_svc_prefix = {}; + memset(&nal_svc_prefix, 0, sizeof(nal_svc_prefix)); + nal_svc_prefix.idr_flag = (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_H264PicData.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME) ? 1 : 0; + nal_svc_prefix.no_inter_layer_pred_flag = 1; + nal_svc_prefix.output_flag = 1; + nal_svc_prefix.discardable_flag = 1; + nal_svc_prefix.temporal_id = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_H264PicData.TemporalLayerIndex; + nal_svc_prefix.priority_id = nal_svc_prefix.temporal_id; + nal_svc_prefix.nal_ref_idc = associatedMetadata.m_associatedEncodeConfig.m_bUsedAsReference ? NAL_REFIDC_REF : NAL_REFIDC_NONREF; + pH264BitstreamBuilder->write_slice_svc_prefix(nal_svc_prefix, + headerBitstream, + placingPositionStart, + writtenSVCPrefixNalBytes); + + // Shrink buffer to fit the headers + if (headerBitstream.size() > (writtenSVCPrefixNalBytes)) { + headerBitstream.resize(writtenSVCPrefixNalBytes); + } + + return headerBitstream.size(); +} \ No newline at end of file diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.h b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.h index a083abca885..86ccfcc57f3 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_h264.h +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_h264.h @@ -64,4 +64,11 @@ d3d12_video_encoder_compare_slice_config_h264_hevc( D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE otherMode, D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES otherConfig); +uint32_t +d3d12_video_encoder_build_slice_svc_prefix_nalu_h264(struct d3d12_video_encoder * pD3D12Enc, + EncodedBitstreamResolvedMetadata& associatedMetadata, + std::vector & headerBitstream, + std::vector::iterator placingPositionStart, + size_t & writtenSVCPrefixNalBytes); + #endif diff --git a/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp b/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp index e3a52420498..5b3ad9ac60d 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_enc_hevc.cpp @@ -354,7 +354,8 @@ d3d12_video_encoder_update_current_frame_pic_params_info_hevc(struct d3d12_video static_cast(pD3D12Enc->m_upBitstreamBuilder.get()); assert(pHEVCBitstreamBuilder != nullptr); - bUsedAsReference = !hevcPic->not_referenced; + pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference = !hevcPic->not_referenced; + bUsedAsReference = pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference; if (pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_HEVCCodecCaps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE_FLAG_SLICE_SUPPORT) @@ -492,6 +493,13 @@ d3d12_video_encoder_update_current_frame_pic_params_info_hevc(struct d3d12_video picParams.pHEVCPicData->pRateControlQPMap = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[hevcPic->pic.temporal_id].m_pRateControlQPMap8Bit.data(); picParams.pHEVCPicData->QPMapValuesCount = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[hevcPic->pic.temporal_id].m_pRateControlQPMap8Bit.size(); } + + // Save state snapshot from record time to resolve headers at get_feedback time + uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT); + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeCapabilities = + pD3D12Enc->m_currentEncodeCapabilities; + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig = + pD3D12Enc->m_currentEncodeConfig; } /// diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.cpp b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.cpp index f83f4d8b70d..9ba2d828285 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.cpp @@ -209,6 +209,15 @@ d3d12_video_bitstream_builder_h264::write_sei_messages(const std::vector & headerBitstream, + std::vector::iterator placingPositionStart, + size_t & writtenBytes) +{ + m_h264Encoder.write_slice_svc_prefix(nal_svc_prefix, headerBitstream, placingPositionStart, writtenBytes); +} + H264_PPS d3d12_video_bitstream_builder_h264::build_pps(const enum pipe_video_profile & profile, const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig, diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.h b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.h index 79613f7d390..cee0ce2bf0f 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.h +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_bitstream_builder_h264.h @@ -72,6 +72,10 @@ class d3d12_video_bitstream_builder_h264 : public d3d12_video_bitstream_builder_ std::vector::iterator placingPositionStart, size_t & writtenBytes); + void write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix, + std::vector & headerBitstream, + std::vector::iterator placingPositionStart, + size_t & writtenBytes); void print_pps(const H264_PPS &pps); void print_sps(const H264_SPS &sps); diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.cpp b/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.cpp index 4db05747842..7a569bd0ed6 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.cpp +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.cpp @@ -268,7 +268,8 @@ uint32_t d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP, uint32_t iNaluIdc, - uint32_t iNaluType) + uint32_t iNaluType, + const H264_SLICE_PREFIX_SVC* pSvcExtendedHeader) { bool isAligned = pRBSP->is_byte_aligned(); // causes side-effects in object state, don't put inside assert() assert(isAligned); @@ -285,6 +286,24 @@ d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream pNALU->put_bits(1, 0); pNALU->put_bits(2, iNaluIdc); pNALU->put_bits(5, iNaluType); + + if (iNaluType == NAL_TYPE_PREFIX) + { + assert(pSvcExtendedHeader); + pNALU->put_bits(1, 1); // svc_extension_flag u(1) + // nal_unit_header_svc_extension( ) + pNALU->put_bits(1, pSvcExtendedHeader->idr_flag); + pNALU->put_bits(6, pSvcExtendedHeader->priority_id); + pNALU->put_bits(1, pSvcExtendedHeader->no_inter_layer_pred_flag); + pNALU->put_bits(3, pSvcExtendedHeader->dependency_id); + pNALU->put_bits(4, pSvcExtendedHeader->quality_id); + pNALU->put_bits(3, pSvcExtendedHeader->temporal_id); + pNALU->put_bits(1, pSvcExtendedHeader->use_ref_base_pic_flag); + pNALU->put_bits(1, pSvcExtendedHeader->discardable_flag); + pNALU->put_bits(1, pSvcExtendedHeader->output_flag); + pNALU->put_bits(2, 3 /* reserved_three_2bits */); + } + pNALU->flush(); // NAL body @@ -648,3 +667,57 @@ d3d12_video_nalu_writer_h264::write_sei_nalu(H264_SEI_MESSAGE sei_ writtenBytes = naluByteSize; } + +void +d3d12_video_nalu_writer_h264::write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix, + std::vector & headerBitstream, + std::vector::iterator placingPositionStart, + size_t & writtenBytes) +{ + d3d12_video_encoder_bitstream rbsp, nalu; + if (!rbsp.create_bitstream(2 * MAX_COMPRESSED_PPS)) { + debug_printf("rbsp.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n"); + assert(false); + } + + if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) { + debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n"); + assert(false); + } + + rbsp.set_start_code_prevention(true); + + // prefix_nal_unit_svc ( ) + if (nal_svc_prefix.nal_ref_idc == NAL_REFIDC_REF) + { + rbsp.put_bits(1, nal_svc_prefix.store_ref_base_pic_flag); + rbsp.put_bits(1, 0 /* additional_prefix_nal_unit_extension_flag */); + } + else + { + // No more_rbsp_data( ) so we don't need to code anything else + } + + rbsp_trailing(&rbsp); + rbsp.flush(); + if (wrap_rbsp_into_nalu(&nalu, &rbsp, nal_svc_prefix.nal_ref_idc, NAL_TYPE_PREFIX, &nal_svc_prefix) <= 0u) { + + debug_printf( + "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_ACCESS_UNIT_DELIMITER) didn't write any bytes.\n"); + assert(false); + } + + // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer + // memory. + uint8_t *naluBytes = nalu.get_bitstream_buffer(); + size_t naluByteSize = nalu.get_byte_count(); + + auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart); + if (headerBitstream.size() < (startDstIndex + naluByteSize)) { + headerBitstream.resize(startDstIndex + naluByteSize); + } + + std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]); + + writtenBytes = naluByteSize; +} \ No newline at end of file diff --git a/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.h b/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.h index 17e9cf98266..70f5d59bbc6 100644 --- a/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.h +++ b/src/gallium/drivers/d3d12/d3d12_video_encoder_nalu_writer_h264.h @@ -55,6 +55,28 @@ enum H264_NALU_TYPE /* 24...31 UNSPECIFIED */ }; +typedef struct +{ + // nal header + H264_NALREF_IDC nal_ref_idc; + + // nal_unit_header_svc_extension( ) + uint32_t idr_flag; // u(1) + uint32_t priority_id; // u(6) + uint32_t no_inter_layer_pred_flag; // u(1) + uint32_t dependency_id; // u(3) + uint32_t quality_id; // u(4) + uint32_t temporal_id; // u(3) + uint32_t use_ref_base_pic_flag; // u(1) + uint32_t discardable_flag; // u(1) + uint32_t output_flag; // u(1) + // uint32_t reserved_three_2bits; // u(2) + + // prefix_nal_unit_svc ( ) + uint32_t store_ref_base_pic_flag; // u(1) + // uint32_t additional_prefix_nal_unit_extension_flag; // u(1) +} H264_SLICE_PREFIX_SVC; + enum H264_SEI_TYPE { H264_SEI_SCALABILITY_INFO = 24, @@ -215,6 +237,11 @@ class d3d12_video_nalu_writer_h264 std::vector::iterator placingPositionStart, size_t & writtenBytes); + void write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix, + std::vector & headerBitstream, + std::vector::iterator placingPositionStart, + size_t & writtenBytes); + private: // Writes from structure into bitstream with RBSP trailing but WITHOUT NAL unit wrap (eg. nal_idc_type, etc) uint32_t write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS); @@ -231,7 +258,8 @@ class d3d12_video_nalu_writer_h264 uint32_t wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP, uint32_t iNaluIdc, - uint32_t iNaluType); + uint32_t iNaluType, + const H264_SLICE_PREFIX_SVC* pSvcExtendedHeader = NULL); }; #endif