From 659039ca6df543f101c80858fe55a880645b773e Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sat, 7 Aug 2021 15:12:15 -0400 Subject: nvdec: Implement GPU accelerated decoding for all platforms Supplements the VAAPI intel gpu decoder by implementing the D3D11VA decoder for Windows, and CUVID/VDPAU for Nvidia and AMD on drivers linux respectively. --- src/video_core/command_classes/codecs/codec.cpp | 154 +++++++++++++----------- src/video_core/command_classes/codecs/codec.h | 8 +- 2 files changed, 92 insertions(+), 70 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index f798a0053..e4ee63e31 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -16,44 +16,17 @@ extern "C" { } namespace Tegra { -#if defined(LIBVA_FOUND) -// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license namespace { -constexpr std::array VAAPI_DRIVERS = { - "i915", - "amdgpu", -}; - -AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) { +AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { - if (*p == AV_PIX_FMT_VAAPI) { - return AV_PIX_FMT_VAAPI; + if (*p == av_codec_ctx->pix_fmt) { + return av_codec_ctx->pix_fmt; } } LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); - return *pix_fmts; -} - -bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) { - AVDictionary* hwdevice_options = nullptr; - av_dict_set(&hwdevice_options, "connection_type", "drm", 0); - for (const auto& driver : VAAPI_DRIVERS) { - av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); - const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI, - nullptr, hwdevice_options, 0); - if (hwdevice_error >= 0) { - LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); - av_dict_free(&hwdevice_options); - return true; - } - LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); - } - LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); - av_dict_free(&hwdevice_options); - return false; + return AV_PIX_FMT_NONE; } } // namespace -#endif void AVFrameDeleter(AVFrame* ptr) { av_frame_free(&ptr); @@ -69,26 +42,75 @@ Codec::~Codec() { } // Free libav memory avcodec_send_packet(av_codec_ctx, nullptr); - AVFrame* av_frame = av_frame_alloc(); - avcodec_receive_frame(av_codec_ctx, av_frame); + AVFramePtr av_frame{av_frame_alloc(), AVFrameDeleter}; + avcodec_receive_frame(av_codec_ctx, av_frame.get()); avcodec_flush_buffers(av_codec_ctx); - av_frame_free(&av_frame); avcodec_close(av_codec_ctx); - av_buffer_unref(&av_hw_device); + av_buffer_unref(&av_gpu_decoder); } -void Codec::InitializeHwdec() { - // Prioritize integrated GPU to mitigate bandwidth bottlenecks +bool Codec::CreateGpuAvDevice() { #if defined(LIBVA_FOUND) - if (CreateVaapiHwdevice(&av_hw_device)) { - const auto hw_device_ctx = av_buffer_ref(av_hw_device); - ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); - av_codec_ctx->hw_device_ctx = hw_device_ctx; - av_codec_ctx->get_format = GetHwFormat; - return; + static constexpr std::array VAAPI_DRIVERS = { + "i915", + "iHD", + "amdgpu", + }; + AVDictionary* hwdevice_options = nullptr; + av_dict_set(&hwdevice_options, "connection_type", "drm", 0); + for (const auto& driver : VAAPI_DRIVERS) { + av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); + const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, + nullptr, hwdevice_options, 0); + if (hwdevice_error >= 0) { + LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); + av_dict_free(&hwdevice_options); + av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI; + return true; + } + LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); } + LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); + av_dict_free(&hwdevice_options); +#endif + static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; + static constexpr std::array GPU_DECODER_TYPES{ + AV_HWDEVICE_TYPE_CUDA, +#ifdef _WIN32 + AV_HWDEVICE_TYPE_D3D11VA, +#else + AV_HWDEVICE_TYPE_VDPAU, #endif - // TODO more GPU accelerated decoders + }; + for (const auto& type : GPU_DECODER_TYPES) { + av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); + for (int i = 0;; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); + if (!config) { + LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.", + av_codec->name, av_hwdevice_get_type_name(type)); + break; + } + if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { + av_codec_ctx->pix_fmt = config->pix_fmt; + LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); + return true; + } + } + } + return false; +} + +void Codec::InitializeGpuDecoder() { + if (!CreateGpuAvDevice()) { + av_buffer_unref(&av_gpu_decoder); + return; + } + auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder); + ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); + av_codec_ctx->hw_device_ctx = hw_device_ctx; + av_codec_ctx->get_format = GetGpuFormat; + using_gpu_decode = true; } void Codec::Initialize() { @@ -107,7 +129,8 @@ void Codec::Initialize() { av_codec = avcodec_find_decoder(codec); av_codec_ctx = avcodec_alloc_context3(av_codec); av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); - InitializeHwdec(); + + InitializeGpuDecoder(); if (!av_codec_ctx->hw_device_ctx) { LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); } @@ -115,7 +138,7 @@ void Codec::Initialize() { if (av_error < 0) { LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); avcodec_close(av_codec_ctx); - av_buffer_unref(&av_hw_device); + av_buffer_unref(&av_gpu_decoder); return; } initialized = true; @@ -153,38 +176,33 @@ void Codec::Decode() { if (vp9_hidden_frame) { return; } - AVFrame* hw_frame = av_frame_alloc(); - AVFrame* sw_frame = hw_frame; - ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed"); - if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) { + AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter}; + AVFramePtr final_frame{nullptr, AVFrameDeleter}; + ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed"); + if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) { LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); - av_frame_free(&hw_frame); return; } - if (!hw_frame->width || !hw_frame->height) { + if (initial_frame->width == 0 || initial_frame->height == 0) { LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); - av_frame_free(&hw_frame); return; } -#if defined(LIBVA_FOUND) - // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license - if (hw_frame->format == AV_PIX_FMT_VAAPI) { - sw_frame = av_frame_alloc(); - ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed"); + if (using_gpu_decode) { + final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; + ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp // because Intel drivers crash unless using AV_PIX_FMT_NV12 - sw_frame->format = AV_PIX_FMT_NV12; - const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0); - ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret); - av_frame_free(&hw_frame); - } -#endif - if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) { - UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format); - av_frame_free(&sw_frame); + final_frame->format = AV_PIX_FMT_NV12; + const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); + ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); + } else { + final_frame = std::move(initial_frame); + } + if (final_frame->format != AV_PIX_FMT_YUV420P && final_frame->format != AV_PIX_FMT_NV12) { + UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); return; } - av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter}); + av_frames.push(std::move(final_frame)); if (av_frames.size() > 10) { LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); av_frames.pop(); diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h index 71936203f..abfe59221 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/command_classes/codecs/codec.h @@ -50,18 +50,22 @@ public: /// Returns the value of current_codec [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; + /// Return name of the current codec [[nodiscard]] std::string_view GetCurrentCodecName() const; private: - void InitializeHwdec(); + void InitializeGpuDecoder(); + + bool CreateGpuAvDevice(); bool initialized{}; + bool using_gpu_decode{}; NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; AVCodec* av_codec{nullptr}; - AVBufferRef* av_hw_device{nullptr}; AVCodecContext* av_codec_ctx{nullptr}; + AVBufferRef* av_gpu_decoder{nullptr}; GPU& gpu; const NvdecCommon::NvdecRegisters& state; -- cgit v1.2.3 From 356e10898f47aec113e45962ee3480353dadf3bc Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sat, 7 Aug 2021 15:31:14 -0400 Subject: codec: Replace deprecated av_init_packet usage --- src/video_core/command_classes/codecs/codec.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index e4ee63e31..0ad6162ca 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -134,9 +134,8 @@ void Codec::Initialize() { if (!av_codec_ctx->hw_device_ctx) { LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); } - const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr); - if (av_error < 0) { - LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); + if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { + LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); avcodec_close(av_codec_ctx); av_buffer_unref(&av_gpu_decoder); return; @@ -164,12 +163,17 @@ void Codec::Decode() { frame_data = vp9_decoder->ComposeFrameHeader(state); vp9_hidden_frame = vp9_decoder->WasFrameHidden(); } - AVPacket packet{}; - av_init_packet(&packet); - packet.data = frame_data.data(); - packet.size = static_cast(frame_data.size()); - if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) { - LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret); + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); + return; + } + packet->data = frame_data.data(); + packet->size = static_cast(frame_data.size()); + const int send_pkt_ret = avcodec_send_packet(av_codec_ctx, packet); + av_packet_free(&packet); + if (send_pkt_ret != 0) { + LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", send_pkt_ret); return; } // Only receive/store visible frames -- cgit v1.2.3 From 92bc51b66ab687072488b3ff33a2a090290da49e Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Sat, 7 Aug 2021 01:02:51 -0400 Subject: cmake: Add VDPAU and NVDEC support to FFmpeg Adds {h264_,vp9_}{nvdec,vdpau} hwaccels. --- src/video_core/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src/video_core') diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2f6cdd216..269db21a5 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -231,6 +231,7 @@ endif() target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) +target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) add_dependencies(video_core host_shaders) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) -- cgit v1.2.3 From bc3efb79cc11a98005a9c036d9474fbf9cb7042f Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Tue, 10 Aug 2021 22:12:45 -0400 Subject: codec: Fallback to CPU decoding if no compatible GPU format is found --- src/video_core/command_classes/codecs/codec.cpp | 53 +++++++++++++++---------- src/video_core/command_classes/codecs/codec.h | 1 - 2 files changed, 32 insertions(+), 22 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 0ad6162ca..400834129 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -17,6 +17,9 @@ extern "C" { namespace Tegra { namespace { +constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; +constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; + AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { if (*p == av_codec_ctx->pix_fmt) { @@ -24,7 +27,9 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi } } LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); - return AV_PIX_FMT_NONE; + av_buffer_unref(&av_codec_ctx->hw_device_ctx); + av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; + return PREFERRED_CPU_FMT; } } // namespace @@ -83,7 +88,12 @@ bool Codec::CreateGpuAvDevice() { #endif }; for (const auto& type : GPU_DECODER_TYPES) { - av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); + const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); + if (hwdevice_res < 0) { + LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", + av_hwdevice_get_type_name(type), hwdevice_res); + continue; + } for (int i = 0;; i++) { const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); if (!config) { @@ -110,36 +120,34 @@ void Codec::InitializeGpuDecoder() { ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); av_codec_ctx->hw_device_ctx = hw_device_ctx; av_codec_ctx->get_format = GetGpuFormat; - using_gpu_decode = true; } void Codec::Initialize() { - AVCodecID codec; - switch (current_codec) { - case NvdecCommon::VideoCodec::H264: - codec = AV_CODEC_ID_H264; - break; - case NvdecCommon::VideoCodec::Vp9: - codec = AV_CODEC_ID_VP9; - break; - default: - UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); - return; - } + const AVCodecID codec = [&] { + switch (current_codec) { + case NvdecCommon::VideoCodec::H264: + return AV_CODEC_ID_H264; + case NvdecCommon::VideoCodec::Vp9: + return AV_CODEC_ID_VP9; + default: + UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); + return AV_CODEC_ID_NONE; + } + }(); av_codec = avcodec_find_decoder(codec); av_codec_ctx = avcodec_alloc_context3(av_codec); av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); InitializeGpuDecoder(); - if (!av_codec_ctx->hw_device_ctx) { - LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); - } if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); avcodec_close(av_codec_ctx); av_buffer_unref(&av_gpu_decoder); return; } + if (!av_codec_ctx->hw_device_ctx) { + LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); + } initialized = true; } @@ -155,6 +163,9 @@ void Codec::Decode() { if (is_first_frame) { Initialize(); } + if (!initialized) { + return; + } bool vp9_hidden_frame = false; std::vector frame_data; if (current_codec == NvdecCommon::VideoCodec::H264) { @@ -191,18 +202,18 @@ void Codec::Decode() { LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); return; } - if (using_gpu_decode) { + if (av_codec_ctx->hw_device_ctx) { final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp // because Intel drivers crash unless using AV_PIX_FMT_NV12 - final_frame->format = AV_PIX_FMT_NV12; + final_frame->format = PREFERRED_GPU_FMT; const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); } else { final_frame = std::move(initial_frame); } - if (final_frame->format != AV_PIX_FMT_YUV420P && final_frame->format != AV_PIX_FMT_NV12) { + if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) { UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); return; } diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h index abfe59221..f51ab9df0 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/command_classes/codecs/codec.h @@ -60,7 +60,6 @@ private: bool CreateGpuAvDevice(); bool initialized{}; - bool using_gpu_decode{}; NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; AVCodec* av_codec{nullptr}; -- cgit v1.2.3 From a832aa699f783f6ae0a6a1468b0aa6bc7d68c5d2 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sat, 7 Aug 2021 23:57:22 -0400 Subject: codec: Improve libav memory alloc and cleanup --- src/video_core/command_classes/codecs/codec.cpp | 31 ++++++++++++++----------- src/video_core/command_classes/codecs/codec.h | 2 ++ 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 400834129..18aa40ca3 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -20,6 +20,12 @@ namespace { constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; +void AVPacketDeleter(AVPacket* ptr) { + av_packet_free(&ptr); +} + +using AVPacketPtr = std::unique_ptr; + AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { if (*p == av_codec_ctx->pix_fmt) { @@ -46,11 +52,7 @@ Codec::~Codec() { return; } // Free libav memory - avcodec_send_packet(av_codec_ctx, nullptr); - AVFramePtr av_frame{av_frame_alloc(), AVFrameDeleter}; - avcodec_receive_frame(av_codec_ctx, av_frame.get()); - avcodec_flush_buffers(av_codec_ctx); - avcodec_close(av_codec_ctx); + avcodec_free_context(&av_codec_ctx); av_buffer_unref(&av_gpu_decoder); } @@ -111,6 +113,11 @@ bool Codec::CreateGpuAvDevice() { return false; } +void Codec::InitializeAvCodecContext() { + av_codec_ctx = avcodec_alloc_context3(av_codec); + av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); +} + void Codec::InitializeGpuDecoder() { if (!CreateGpuAvDevice()) { av_buffer_unref(&av_gpu_decoder); @@ -135,13 +142,11 @@ void Codec::Initialize() { } }(); av_codec = avcodec_find_decoder(codec); - av_codec_ctx = avcodec_alloc_context3(av_codec); - av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); - + InitializeAvCodecContext(); InitializeGpuDecoder(); if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); - avcodec_close(av_codec_ctx); + avcodec_free_context(&av_codec_ctx); av_buffer_unref(&av_gpu_decoder); return; } @@ -174,17 +179,15 @@ void Codec::Decode() { frame_data = vp9_decoder->ComposeFrameHeader(state); vp9_hidden_frame = vp9_decoder->WasFrameHidden(); } - AVPacket* packet = av_packet_alloc(); + AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; if (!packet) { LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); return; } packet->data = frame_data.data(); packet->size = static_cast(frame_data.size()); - const int send_pkt_ret = avcodec_send_packet(av_codec_ctx, packet); - av_packet_free(&packet); - if (send_pkt_ret != 0) { - LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", send_pkt_ret); + if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) { + LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res); return; } // Only receive/store visible frames diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h index f51ab9df0..1508d36c2 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/command_classes/codecs/codec.h @@ -55,6 +55,8 @@ public: [[nodiscard]] std::string_view GetCurrentCodecName() const; private: + void InitializeAvCodecContext(); + void InitializeGpuDecoder(); bool CreateGpuAvDevice(); -- cgit v1.2.3 From cd016d3cb5191b9f4f2756e440a6aa67e577c414 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sun, 8 Aug 2021 16:56:40 -0400 Subject: configure_graphics: Add GPU nvdec decoding as an option Some system configurations may see visual regressions or lower performance using GPU decoding compared to CPU decoding. This setting provides the option for users to specify their decoding preference. Co-Authored-By: yzct12345 <87620833+yzct12345@users.noreply.github.com> --- src/video_core/command_classes/codecs/codec.cpp | 6 +++++- src/video_core/video_core.cpp | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 18aa40ca3..61966cbfe 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -5,6 +5,7 @@ #include #include #include "common/assert.h" +#include "common/settings.h" #include "video_core/command_classes/codecs/codec.h" #include "video_core/command_classes/codecs/h264.h" #include "video_core/command_classes/codecs/vp9.h" @@ -142,8 +143,11 @@ void Codec::Initialize() { } }(); av_codec = avcodec_find_decoder(codec); + InitializeAvCodecContext(); - InitializeGpuDecoder(); + if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) { + InitializeGpuDecoder(); + } if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); avcodec_free_context(&av_codec_ctx); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 3b575db4d..cae543a51 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -37,7 +37,8 @@ std::unique_ptr CreateRenderer( namespace VideoCore { std::unique_ptr CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { - const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); + const auto nvdec_value = Settings::values.nvdec_emulation.GetValue(); + const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off; const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); auto gpu = std::make_unique(system, use_async, use_nvdec); auto context = emu_window.CreateSharedContext(); -- cgit v1.2.3 From b384129c63c604d8087f72a880adfdc6c68ab9a0 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Tue, 10 Aug 2021 22:26:06 -0400 Subject: h264: Lower max_num_ref_frames GPU decoding seems to be more picky when it comes to the maximum number of reference frames. --- src/video_core/command_classes/codecs/h264.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/video_core') diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp index 5fb6d45ee..51ee14c13 100644 --- a/src/video_core/command_classes/codecs/h264.cpp +++ b/src/video_core/command_classes/codecs/h264.cpp @@ -95,7 +95,8 @@ const std::vector& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegister const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); - writer.WriteUe(16); + // TODO (ameerj): Where do we get this number, it seems to be particular for each stream + writer.WriteUe(6); // Max number of reference frames writer.WriteBit(false); writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); writer.WriteUe(pic_height - 1); -- cgit v1.2.3