diff --git a/.gitmodules b/.gitmodules index 88198314dd0..0852e2652e1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,10 +26,18 @@ path = third-party/nanors url = https://github.com/sleepybishop/nanors.git branch = master -[submodule "third-party/nv-codec-headers"] - path = third-party/nv-codec-headers - url = https://github.com/FFmpeg/nv-codec-headers +[submodule "third-party/nvenc-headers/1100"] + path = third-party/nvenc-headers/1100 + url = https://github.com/FFmpeg/nv-codec-headers.git + branch = sdk/11.0 +[submodule "third-party/nvenc-headers/1200"] + path = third-party/nvenc-headers/1200 + url = https://github.com/FFmpeg/nv-codec-headers.git branch = sdk/12.0 +[submodule "third-party/nvenc-headers/1202"] + path = third-party/nvenc-headers/1202 + url = https://github.com/FFmpeg/nv-codec-headers.git + branch = master [submodule "third-party/nvapi-open-source-sdk"] path = third-party/nvapi-open-source-sdk url = https://github.com/LizardByte/nvapi-open-source-sdk diff --git a/cmake/compile_definitions/common.cmake b/cmake/compile_definitions/common.cmake index a2260595c9a..ad63b6c2671 100644 --- a/cmake/compile_definitions/common.cmake +++ b/cmake/compile_definitions/common.cmake @@ -48,10 +48,6 @@ elseif(UNIX) endif() endif() -include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/nv-codec-headers/include") -file(GLOB NVENC_SOURCES CONFIGURE_DEPENDS "src/nvenc/*.cpp" "src/nvenc/*.h") -list(APPEND PLATFORM_TARGET_FILES ${NVENC_SOURCES}) - configure_file("${CMAKE_SOURCE_DIR}/src/version.h.in" version.h @ONLY) include_directories("${CMAKE_CURRENT_BINARY_DIR}") # required for importing version.h diff --git a/cmake/compile_definitions/windows.cmake b/cmake/compile_definitions/windows.cmake index 7643d1d9efc..1bb2088e2c1 100644 --- a/cmake/compile_definitions/windows.cmake +++ b/cmake/compile_definitions/windows.cmake @@ -30,6 +30,11 @@ file(GLOB NVPREFS_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/src/platform/windows/nvprefs/*.cpp" "${CMAKE_SOURCE_DIR}/src/platform/windows/nvprefs/*.h") +include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/nvenc-headers") +file(GLOB_RECURSE NVENC_SOURCES CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/src/nvenc/*.h" + "${CMAKE_SOURCE_DIR}/src/nvenc/*.cpp") + # vigem include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include") @@ -57,7 +62,8 @@ set(PLATFORM_TARGET_FILES "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/Common.h" "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/Util.h" "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/km/BusShared.h" - ${NVPREFS_FILES}) + ${NVPREFS_FILES} + ${NVENC_SOURCES}) set(OPENSSL_LIBRARIES libssl.a diff --git a/docs/configuration.md b/docs/configuration.md index 98bab79a060..5b2bba679d7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1774,6 +1774,46 @@ editing the `conf` file in a text editor. Use the examples as reference. +### [nvenc_split_encode](https://localhost:47990/config/#nvenc_split_encode) + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Description + Split the encoding of each video frame over multiple NVENC hardware units. + Significantly reduces encoding latency with a marginal compression efficiency penalty. + This option is ignored if your GPU has a singular NVENC unit. + @note{This option only applies when using NVENC [encoder](#encoderhttpslocalhost47990configencoder).} + @note{Applies to Windows only.} +
Default@code{} + driver_decides + @endcode
Example@code{} + nvenc_split_encode = driver_decides + @endcode
ChoicesdisabledDisabled
driver_decidesNVIDIA driver makes the decision whether to enable it
enabledEnabled
+ ### [nvenc_latency_over_power](https://localhost:47990/config/#nvenc_latency_over_power) diff --git a/src/config.cpp b/src/config.cpp index e8accb15a57..aed7875b065 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -56,6 +56,16 @@ namespace config { return nvenc::nvenc_two_pass::quarter_resolution; } + nvenc::nvenc_split_frame_encoding + split_encode_from_view(const std::string_view &preset) { + using enum nvenc::nvenc_split_frame_encoding; + if (preset == "disabled") return disabled; + if (preset == "driver_decides") return driver_decides; + if (preset == "enabled") return force_enabled; + BOOST_LOG(warning) << "config: unknown nvenc_split_encode value: " << preset; + return driver_decides; + } + } // namespace nv namespace amd { @@ -960,6 +970,7 @@ namespace config { bool_f(vars, "nvenc_spatial_aq", video.nv.adaptive_quantization); generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view); bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc); + generic_f(vars, "nvenc_split_encode", video.nv.split_frame_encoding, nv::split_encode_from_view); bool_f(vars, "nvenc_realtime_hags", video.nv_realtime_hags); bool_f(vars, "nvenc_opengl_vulkan_on_dxgi", video.nv_opengl_vulkan_on_dxgi); bool_f(vars, "nvenc_latency_over_power", video.nv_sunshine_high_power_mode); diff --git a/src/nvenc/nvenc_base.cpp b/src/nvenc/common_impl/nvenc_base.cpp similarity index 84% rename from src/nvenc/nvenc_base.cpp rename to src/nvenc/common_impl/nvenc_base.cpp index b69d6f26bd6..594ae10d796 100644 --- a/src/nvenc/nvenc_base.cpp +++ b/src/nvenc/common_impl/nvenc_base.cpp @@ -1,26 +1,21 @@ /** - * @file src/nvenc/nvenc_base.cpp + * @file src/nvenc/common_impl/nvenc_base.cpp * @brief Definitions for abstract platform-agnostic base of standalone NVENC encoder. */ #include "nvenc_base.h" -#include "src/config.h" -#include "src/logging.h" -#include "src/utility.h" +#include "nvenc_utils.h" -#define MAKE_NVENC_VER(major, minor) ((major) | ((minor) << 24)) +#include "src/utility.h" -// Make sure we check backwards compatibility when bumping the Video Codec SDK version -// Things to look out for: -// - NV_ENC_*_VER definitions where the value inside NVENCAPI_STRUCT_VERSION() was increased -// - Incompatible struct changes in nvEncodeAPI.h (fields removed, semantics changed, etc.) -// - Test both old and new drivers with all supported codecs -#if NVENCAPI_VERSION != MAKE_NVENC_VER(12U, 0U) - #error Check and update NVENC code for backwards compatibility! -#endif +#define NVENC_INT_VERSION (NVENCAPI_MAJOR_VERSION * 100 + NVENCAPI_MINOR_VERSION) namespace { +#ifdef NVENC_NAMESPACE + using namespace NVENC_NAMESPACE; +#endif + GUID quality_preset_guid_from_number(unsigned number) { if (number > 7) number = 7; @@ -83,7 +78,11 @@ namespace { } // namespace +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else namespace nvenc { +#endif nvenc_base::nvenc_base(NV_ENC_DEVICE_TYPE device_type): device_type(device_type) { @@ -94,25 +93,28 @@ namespace nvenc { } bool - nvenc_base::create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format) { - // Pick the minimum NvEncode API version required to support the specified codec - // to maximize driver compatibility. AV1 was introduced in SDK v12.0. - minimum_api_version = (client_config.videoFormat <= 1) ? MAKE_NVENC_VER(11U, 0U) : MAKE_NVENC_VER(12U, 0U); - + nvenc_base::create_encoder( + const nvenc_config &config, + const video::config_t &client_config, + const video::sunshine_colorspace_t &sunshine_colorspace, + platf::pix_fmt_e sunshine_buffer_format) { if (!nvenc && !init_library()) return false; if (encoder) destroy_encoder(); auto fail_guard = util::fail_guard([this] { destroy_encoder(); }); + auto colorspace = nvenc_colorspace_from_sunshine_colorspace(sunshine_colorspace); + auto buffer_format = nvenc_format_from_sunshine_format(sunshine_buffer_format); + encoder_params.width = client_config.width; encoder_params.height = client_config.height; encoder_params.buffer_format = buffer_format; encoder_params.rfi = true; - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { min_struct_version(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER) }; + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER }; session_params.device = device; session_params.deviceType = device_type; - session_params.apiVersion = minimum_api_version; + session_params.apiVersion = NVENCAPI_VERSION; if (nvenc_failed(nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) { BOOST_LOG(error) << "NvEnc: NvEncOpenEncodeSessionEx() failed: " << last_nvenc_error_string; return false; @@ -130,7 +132,7 @@ namespace nvenc { return false; } - NV_ENC_INITIALIZE_PARAMS init_params = { min_struct_version(NV_ENC_INITIALIZE_PARAMS_VER) }; + NV_ENC_INITIALIZE_PARAMS init_params = { NV_ENC_INITIALIZE_PARAMS_VER }; switch (client_config.videoFormat) { case 0: @@ -143,10 +145,12 @@ namespace nvenc { init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID; break; +#if NVENC_INT_VERSION >= 1200 case 2: // AV1 init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID; break; +#endif default: BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat; @@ -164,7 +168,8 @@ namespace nvenc { } auto get_encoder_cap = [&](NV_ENC_CAPS cap) { - NV_ENC_CAPS_PARAM param = { min_struct_version(NV_ENC_CAPS_PARAM_VER), cap }; + NV_ENC_CAPS_PARAM param = { NV_ENC_CAPS_PARAM_VER }; + param.capsToQuery = cap; int value = 0; nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, ¶m, &value); return value; @@ -182,7 +187,8 @@ namespace nvenc { auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX); auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX); if (encoder_params.width > supported_width || encoder_params.height > supported_height) { - BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height; + BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height + << ", requested " << encoder_params.width << "x" << encoder_params.height; return false; } } @@ -217,7 +223,19 @@ namespace nvenc { init_params.frameRateNum = client_config.framerate; init_params.frameRateDen = 1; - NV_ENC_PRESET_CONFIG preset_config = { min_struct_version(NV_ENC_PRESET_CONFIG_VER), { min_struct_version(NV_ENC_CONFIG_VER, 7, 8) } }; +#if NVENC_INT_VERSION >= 1202 + { + using enum nvenc_split_frame_encoding; + init_params.splitEncodeMode = config.split_frame_encoding == disabled ? NV_ENC_SPLIT_DISABLE_MODE : + config.split_frame_encoding == force_enabled ? NV_ENC_SPLIT_AUTO_FORCED_MODE : + NV_ENC_SPLIT_AUTO_MODE; + } +#endif + + NV_ENC_PRESET_CONFIG preset_config = { + .version = NV_ENC_PRESET_CONFIG_VER, + .presetCfg = { .version = NV_ENC_CONFIG_VER }, + }; if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) { BOOST_LOG(error) << "NvEnc: NvEncGetEncodePresetConfigEx() failed: " << last_nvenc_error_string; return false; @@ -248,8 +266,12 @@ namespace nvenc { auto set_h264_hevc_common_format_config = [&](auto &format_config) { format_config.repeatSPSPPS = 1; format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; - format_config.sliceMode = 3; - format_config.sliceModeData = client_config.slicesPerFrame; + if (client_config.slicesPerFrame > 1 || + NVENC_INT_VERSION < 1202 || + config.split_frame_encoding == nvenc_split_frame_encoding::disabled) { + format_config.sliceMode = 3; + format_config.sliceModeData = client_config.slicesPerFrame; + } if (buffer_is_yuv444()) { format_config.chromaFormatIDC = 3; } @@ -316,7 +338,12 @@ namespace nvenc { auto &format_config = enc_config.encodeCodecConfig.hevcConfig; set_h264_hevc_common_format_config(format_config); if (buffer_is_10bit()) { +#if NVENC_INT_VERSION >= 1202 + format_config.inputBitDepth = NV_ENC_BIT_DEPTH_10; + format_config.outputBitDepth = NV_ENC_BIT_DEPTH_10; +#else format_config.pixelBitDepthMinus8 = 2; +#endif } set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numRefL0, 5); set_minqp_if_enabled(config.min_qp_hevc); @@ -324,6 +351,7 @@ namespace nvenc { break; } +#if NVENC_INT_VERSION >= 1200 case 2: { // AV1 auto &format_config = enc_config.encodeCodecConfig.av1Config; @@ -334,8 +362,13 @@ namespace nvenc { } format_config.enableBitstreamPadding = config.insert_filler_data; if (buffer_is_10bit()) { + #if NVENC_INT_VERSION >= 1202 + format_config.inputBitDepth = NV_ENC_BIT_DEPTH_10; + format_config.outputBitDepth = NV_ENC_BIT_DEPTH_10; + #else format_config.inputPixelBitDepthMinus8 = 2; format_config.pixelBitDepthMinus8 = 2; + #endif } format_config.colorPrimaries = colorspace.primaries; format_config.transferCharacteristics = colorspace.tranfer_function; @@ -353,6 +386,7 @@ namespace nvenc { } break; } +#endif } init_params.encodeConfig = &enc_config; @@ -363,7 +397,7 @@ namespace nvenc { } if (async_event_handle) { - NV_ENC_EVENT_PARAMS event_params = { min_struct_version(NV_ENC_EVENT_PARAMS_VER) }; + NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER }; event_params.completionEvent = async_event_handle; if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) { BOOST_LOG(error) << "NvEnc: NvEncRegisterAsyncEvent() failed: " << last_nvenc_error_string; @@ -371,7 +405,7 @@ namespace nvenc { } } - NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { min_struct_version(NV_ENC_CREATE_BITSTREAM_BUFFER_VER) }; + NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER }; if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) { BOOST_LOG(error) << "NvEnc: NvEncCreateBitstreamBuffer() failed: " << last_nvenc_error_string; return false; @@ -397,14 +431,17 @@ namespace nvenc { if (buffer_is_yuv444()) extra += " yuv444"; if (buffer_is_10bit()) extra += " 10-bit"; if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) extra += " two-pass"; - if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) extra += " vbv+" + std::to_string(config.vbv_percentage_increase); + if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) { + extra += " vbv+" + std::to_string(config.vbv_percentage_increase); + } if (encoder_params.rfi) extra += " rfi"; if (init_params.enableWeightedPrediction) extra += " weighted-prediction"; if (enc_config.rcParams.enableAQ) extra += " spatial-aq"; if (enc_config.rcParams.enableMinQP) extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP); if (config.insert_filler_data) extra += " filler-data"; - BOOST_LOG(info) << "NvEnc: created encoder " << video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra; + BOOST_LOG(info) << "NvEnc: created encoder v" << NVENC_INT_VERSION << " " + << video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra; } encoder_state = {}; @@ -421,7 +458,7 @@ namespace nvenc { output_bitstream = nullptr; } if (encoder && async_event_handle) { - NV_ENC_EVENT_PARAMS event_params = { min_struct_version(NV_ENC_EVENT_PARAMS_VER) }; + NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER }; event_params.completionEvent = async_event_handle; if (nvenc_failed(nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params))) { BOOST_LOG(error) << "NvEnc: NvEncUnregisterAsyncEvent() failed: " << last_nvenc_error_string; @@ -458,7 +495,7 @@ namespace nvenc { return {}; } - NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { min_struct_version(NV_ENC_MAP_INPUT_RESOURCE_VER) }; + NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { NV_ENC_MAP_INPUT_RESOURCE_VER }; mapped_input_buffer.registeredResource = registered_input_buffer; if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) { @@ -471,7 +508,7 @@ namespace nvenc { } }); - NV_ENC_PIC_PARAMS pic_params = { min_struct_version(NV_ENC_PIC_PARAMS_VER, 4, 6) }; + NV_ENC_PIC_PARAMS pic_params = { NV_ENC_PIC_PARAMS_VER }; pic_params.inputWidth = encoder_params.width; pic_params.inputHeight = encoder_params.height; pic_params.encodePicFlags = force_idr ? NV_ENC_PIC_FLAG_FORCEIDR : 0; @@ -487,7 +524,7 @@ namespace nvenc { return {}; } - NV_ENC_LOCK_BITSTREAM lock_bitstream = { min_struct_version(NV_ENC_LOCK_BITSTREAM_VER, 1, 2) }; + NV_ENC_LOCK_BITSTREAM lock_bitstream = { NV_ENC_LOCK_BITSTREAM_VER }; lock_bitstream.outputBitstream = output_bitstream; lock_bitstream.doNotWait = 0; @@ -546,7 +583,8 @@ namespace nvenc { return false; } - BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index; + BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame + << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index; last_frame = encoder_state.last_encoded_frame_index; encoder_state.last_rfi_range = { first_frame, last_frame }; @@ -620,21 +658,4 @@ namespace nvenc { return false; } - - uint32_t - nvenc_base::min_struct_version(uint32_t version, uint32_t v11_struct_version, uint32_t v12_struct_version) { - assert(minimum_api_version); - - // Mask off and replace the original NVENCAPI_VERSION - version &= ~NVENCAPI_VERSION; - version |= minimum_api_version; - - // If there's a struct version override, apply that too - if (v11_struct_version || v12_struct_version) { - version &= ~(0xFFu << 16); - version |= (((minimum_api_version & 0xFF) >= 12) ? v12_struct_version : v11_struct_version) << 16; - } - - return version; - } -} // namespace nvenc +} diff --git a/src/nvenc/nvenc_base.h b/src/nvenc/common_impl/nvenc_base.h similarity index 56% rename from src/nvenc/nvenc_base.h rename to src/nvenc/common_impl/nvenc_base.h index c49aa4010e7..256faa845ef 100644 --- a/src/nvenc/nvenc_base.h +++ b/src/nvenc/common_impl/nvenc_base.h @@ -1,77 +1,50 @@ /** - * @file src/nvenc/nvenc_base.h + * @file src/nvenc/common_impl/nvenc_base.h * @brief Declarations for abstract platform-agnostic base of standalone NVENC encoder. */ #pragma once -#include "nvenc_colorspace.h" -#include "nvenc_config.h" -#include "nvenc_encoded_frame.h" +#include "../nvenc_config.h" +#include "../nvenc_encoded_frame.h" +#include "../nvenc_encoder.h" +#include "src/config.h" #include "src/logging.h" #include "src/video.h" -#include - -/** - * @brief Standalone NVENC encoder - */ +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else + #include namespace nvenc { +#endif /** * @brief Abstract platform-agnostic base of standalone NVENC encoder. * Derived classes perform platform-specific operations. */ - class nvenc_base { + class nvenc_base: virtual public nvenc_encoder { public: /** * @param device_type Underlying device type used by derived class. */ explicit nvenc_base(NV_ENC_DEVICE_TYPE device_type); - virtual ~nvenc_base(); + ~nvenc_base(); - nvenc_base(const nvenc_base &) = delete; - nvenc_base & - operator=(const nvenc_base &) = delete; - - /** - * @brief Create the encoder. - * @param config NVENC encoder configuration. - * @param client_config Stream configuration requested by the client. - * @param colorspace YUV colorspace. - * @param buffer_format Platform-agnostic input surface format. - * @return `true` on success, `false` on error - */ bool - create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format); + create_encoder(const nvenc_config &config, + const video::config_t &client_config, + const video::sunshine_colorspace_t &sunshine_colorspace, + platf::pix_fmt_e sunshine_buffer_format) override; - /** - * @brief Destroy the encoder. - * Derived classes classes call it in the destructor. - */ void - destroy_encoder(); + destroy_encoder() override; - /** - * @brief Encode the next frame using platform-specific input surface. - * @param frame_index Frame index that uniquely identifies the frame. - * Afterwards serves as parameter for `invalidate_ref_frames()`. - * No restrictions on the first frame index, but later frame indexes must be subsequent. - * @param force_idr Whether to encode frame as forced IDR. - * @return Encoded frame. - */ nvenc_encoded_frame - encode_frame(uint64_t frame_index, bool force_idr); + encode_frame(uint64_t frame_index, bool force_idr) override; - /** - * @brief Perform reference frame invalidation (RFI) procedure. - * @param first_frame First frame index of the invalidation range. - * @param last_frame Last frame index of the invalidation range. - * @return `true` on success, `false` on error. - * After error next frame must be encoded with `force_idr = true`. - */ bool - invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame); + invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame) override; protected: /** @@ -111,17 +84,6 @@ namespace nvenc { bool nvenc_failed(NVENCSTATUS status); - /** - * @brief This function returns the corresponding struct version for the minimum API required by the codec. - * @details Reducing the struct versions maximizes driver compatibility by avoiding needless API breaks. - * @param version The raw structure version from `NVENCAPI_STRUCT_VERSION()`. - * @param v11_struct_version Optionally specifies the struct version to use with v11 SDK major versions. - * @param v12_struct_version Optionally specifies the struct version to use with v12 SDK major versions. - * @return A suitable struct version for the active codec. - */ - uint32_t - min_struct_version(uint32_t version, uint32_t v11_struct_version = 0, uint32_t v12_struct_version = 0); - const NV_ENC_DEVICE_TYPE device_type; void *encoder = nullptr; @@ -148,7 +110,6 @@ namespace nvenc { private: NV_ENC_OUTPUT_PTR output_bitstream = nullptr; - uint32_t minimum_api_version = 0; struct { uint64_t last_encoded_frame_index = 0; @@ -157,5 +118,4 @@ namespace nvenc { logging::min_max_avg_periodic_logger frame_size_logger = { debug, "NvEnc: encoded frame sizes in kB", "" }; } encoder_state; }; - -} // namespace nvenc +} diff --git a/src/nvenc/nvenc_utils.cpp b/src/nvenc/common_impl/nvenc_utils.cpp similarity index 92% rename from src/nvenc/nvenc_utils.cpp rename to src/nvenc/common_impl/nvenc_utils.cpp index 26e2dc30d24..5e1a3e084bc 100644 --- a/src/nvenc/nvenc_utils.cpp +++ b/src/nvenc/common_impl/nvenc_utils.cpp @@ -1,12 +1,16 @@ /** - * @file src/nvenc/nvenc_utils.cpp + * @file src/nvenc/common_impl/nvenc_utils.cpp * @brief Definitions for NVENC utilities. */ -#include - #include "nvenc_utils.h" +#include + +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else namespace nvenc { +#endif #ifdef _WIN32 DXGI_FORMAT @@ -90,5 +94,4 @@ namespace nvenc { return colorspace; } - -} // namespace nvenc +} diff --git a/src/nvenc/nvenc_utils.h b/src/nvenc/common_impl/nvenc_utils.h similarity index 53% rename from src/nvenc/nvenc_utils.h rename to src/nvenc/common_impl/nvenc_utils.h index db42867679a..c56d0792cbf 100644 --- a/src/nvenc/nvenc_utils.h +++ b/src/nvenc/common_impl/nvenc_utils.h @@ -1,5 +1,5 @@ /** - * @file src/nvenc/nvenc_utils.h + * @file src/nvenc/common_impl/nvenc_utils.h * @brief Declarations for NVENC utilities. */ #pragma once @@ -8,14 +8,25 @@ #include #endif -#include "nvenc_colorspace.h" - #include "src/platform/common.h" #include "src/video_colorspace.h" -#include - +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else + #include namespace nvenc { +#endif + + /** + * @brief YUV colorspace and color range. + */ + struct nvenc_colorspace_t { + NV_ENC_VUI_COLOR_PRIMARIES primaries; + NV_ENC_VUI_TRANSFER_CHARACTERISTIC tranfer_function; + NV_ENC_VUI_MATRIX_COEFFS matrix; + bool full_range; + }; #ifdef _WIN32 DXGI_FORMAT @@ -27,5 +38,4 @@ namespace nvenc { nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace); - -} // namespace nvenc +} diff --git a/src/nvenc/nvenc_colorspace.h b/src/nvenc/nvenc_colorspace.h deleted file mode 100644 index c9ed519318d..00000000000 --- a/src/nvenc/nvenc_colorspace.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file src/nvenc/nvenc_colorspace.h - * @brief Declarations for NVENC YUV colorspace. - */ -#pragma once - -#include - -namespace nvenc { - - /** - * @brief YUV colorspace and color range. - */ - struct nvenc_colorspace_t { - NV_ENC_VUI_COLOR_PRIMARIES primaries; - NV_ENC_VUI_TRANSFER_CHARACTERISTIC tranfer_function; - NV_ENC_VUI_MATRIX_COEFFS matrix; - bool full_range; - }; - -} // namespace nvenc diff --git a/src/nvenc/nvenc_config.h b/src/nvenc/nvenc_config.h index 213a0d289fa..03922e52902 100644 --- a/src/nvenc/nvenc_config.h +++ b/src/nvenc/nvenc_config.h @@ -12,6 +12,12 @@ namespace nvenc { full_resolution, ///< Better overall statistics, slower and uses more extra vram }; + enum class nvenc_split_frame_encoding { + disabled, ///< Disable + driver_decides, ///< Let driver decide + force_enabled, ///< Force-enable + }; + /** * @brief NVENC encoder configuration. */ @@ -48,6 +54,9 @@ namespace nvenc { // Add filler data to encoded frames to stay at target bitrate, mainly for testing bool insert_filler_data = false; + + // Enable split-frame encoding if the gpu has multiple NVENC hardware clusters + nvenc_split_frame_encoding split_frame_encoding = nvenc_split_frame_encoding::driver_decides; }; } // namespace nvenc diff --git a/src/nvenc/nvenc_d3d11.cpp b/src/nvenc/nvenc_d3d11.cpp deleted file mode 100644 index 7dd545b4b90..00000000000 --- a/src/nvenc/nvenc_d3d11.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file src/nvenc/nvenc_d3d11.cpp - * @brief Definitions for abstract Direct3D11 NVENC encoder. - */ -#include "src/logging.h" - -#ifdef _WIN32 - #include "nvenc_d3d11.h" - -namespace nvenc { - - nvenc_d3d11::~nvenc_d3d11() { - if (dll) { - FreeLibrary(dll); - dll = NULL; - } - } - - bool - nvenc_d3d11::init_library() { - if (dll) return true; - - #ifdef _WIN64 - constexpr auto dll_name = "nvEncodeAPI64.dll"; - #else - constexpr auto dll_name = "nvEncodeAPI.dll"; - #endif - - if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { - if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) { - auto new_nvenc = std::make_unique(); - new_nvenc->version = min_struct_version(NV_ENCODE_API_FUNCTION_LIST_VER); - if (nvenc_failed(create_instance(new_nvenc.get()))) { - BOOST_LOG(error) << "NvEnc: NvEncodeAPICreateInstance() failed: " << last_nvenc_error_string; - } - else { - nvenc = std::move(new_nvenc); - return true; - } - } - else { - BOOST_LOG(error) << "NvEnc: No NvEncodeAPICreateInstance() in " << dll_name; - } - } - else { - BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << dll_name; - } - - if (dll) { - FreeLibrary(dll); - dll = NULL; - } - - return false; - } - -} // namespace nvenc -#endif diff --git a/src/nvenc/nvenc_encoder.h b/src/nvenc/nvenc_encoder.h new file mode 100644 index 00000000000..f5728db4202 --- /dev/null +++ b/src/nvenc/nvenc_encoder.h @@ -0,0 +1,69 @@ +/** + * @file src/nvenc/nvenc_encoder.h + * @brief Declarations for NVENC encoder interface. + */ +#pragma once + +#include "nvenc_config.h" +#include "nvenc_encoded_frame.h" + +#include "src/platform/common.h" +#include "src/video.h" +#include "src/video_colorspace.h" + +/** + * @brief Standalone NVENC encoder + */ +namespace nvenc { + + /** + * @brief Standalone NVENC encoder interface. + */ + class nvenc_encoder { + public: + virtual ~nvenc_encoder() = default; + + /** + * @brief Create the encoder. + * @param config NVENC encoder configuration. + * @param client_config Stream configuration requested by the client. + * @param colorspace YUV colorspace. + * @param buffer_format Platform-agnostic input surface format. + * @return `true` on success, `false` on error + */ + virtual bool + create_encoder(const nvenc_config &config, + const video::config_t &client_config, + const video::sunshine_colorspace_t &colorspace, + platf::pix_fmt_e buffer_format) = 0; + + /** + * @brief Destroy the encoder. + * Also called in the destructor. + */ + virtual void + destroy_encoder() = 0; + + /** + * @brief Encode the next frame using platform-specific input surface. + * @param frame_index Frame index that uniquely identifies the frame. + * Afterwards serves as parameter for `invalidate_ref_frames()`. + * No restrictions on the first frame index, but later frame indexes must be subsequent. + * @param force_idr Whether to encode frame as forced IDR. + * @return Encoded frame. + */ + virtual nvenc_encoded_frame + encode_frame(uint64_t frame_index, bool force_idr) = 0; + + /** + * @brief Perform reference frame invalidation (RFI) procedure. + * @param first_frame First frame index of the invalidation range. + * @param last_frame Last frame index of the invalidation range. + * @return `true` on success, `false` on error. + * After error next frame must be encoded with `force_idr = true`. + */ + virtual bool + invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame) = 0; + }; + +} // namespace nvenc diff --git a/src/nvenc/win/impl/nvenc_d3d11_base.cpp b/src/nvenc/win/impl/nvenc_d3d11_base.cpp new file mode 100644 index 00000000000..d8f872e2dc3 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_d3d11_base.cpp @@ -0,0 +1,35 @@ +/** + * @file src/nvenc/win/impl/nvenc_d3d11_base.cpp + * @brief Definitions for abstract Direct3D11 NVENC encoder. + */ +#include "nvenc_d3d11_base.h" + +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else +namespace nvenc { +#endif + + bool + nvenc_d3d11_base::init_library() { + if (nvenc) return true; + if (!dll) return false; + + if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll.get(), "NvEncodeAPICreateInstance")) { + auto new_nvenc = std::make_unique(); + new_nvenc->version = NV_ENCODE_API_FUNCTION_LIST_VER; + if (nvenc_failed(create_instance(new_nvenc.get()))) { + BOOST_LOG(error) << "NvEnc: NvEncodeAPICreateInstance() failed: " << last_nvenc_error_string; + } + else { + nvenc = std::move(new_nvenc); + return true; + } + } + else { + BOOST_LOG(error) << "NvEnc: No NvEncodeAPICreateInstance() in dynamic library"; + } + + return false; + } +} diff --git a/src/nvenc/nvenc_d3d11.h b/src/nvenc/win/impl/nvenc_d3d11_base.h similarity index 52% rename from src/nvenc/nvenc_d3d11.h rename to src/nvenc/win/impl/nvenc_d3d11_base.h index 2d4d4fe77dc..9c93e382dbf 100644 --- a/src/nvenc/nvenc_d3d11.h +++ b/src/nvenc/win/impl/nvenc_d3d11_base.h @@ -1,16 +1,21 @@ /** - * @file src/nvenc/nvenc_d3d11.h + * @file src/nvenc/win/impl/nvenc_d3d11_base.h * @brief Declarations for abstract Direct3D11 NVENC encoder. */ #pragma once -#ifdef _WIN32 - #include - #include +#include "../../common_impl/nvenc_base.h" +#include "../nvenc_d3d11.h" +#include "nvenc_shared_dll.h" - #include "nvenc_base.h" +#include +#include +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else namespace nvenc { +#endif _COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); _COM_SMARTPTR_TYPEDEF(ID3D11Texture2D, IID_ID3D11Texture2D); @@ -21,27 +26,17 @@ namespace nvenc { * @brief Abstract Direct3D11 NVENC encoder. * Encapsulates common code used by native and interop implementations. */ - class nvenc_d3d11: public nvenc_base { + class nvenc_d3d11_base: public nvenc_base, virtual public nvenc_d3d11 { public: - explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type): - nvenc_base(device_type) {} - - ~nvenc_d3d11(); - - /** - * @brief Get input surface texture. - * @return Input surface texture. - */ - virtual ID3D11Texture2D * - get_input_texture() = 0; + nvenc_d3d11_base(NV_ENC_DEVICE_TYPE device_type, shared_dll dll): + nvenc_base(device_type), + dll(dll) {} protected: bool init_library() override; private: - HMODULE dll = NULL; + shared_dll dll; }; - -} // namespace nvenc -#endif +} diff --git a/src/nvenc/nvenc_d3d11_native.cpp b/src/nvenc/win/impl/nvenc_d3d11_native.cpp similarity index 83% rename from src/nvenc/nvenc_d3d11_native.cpp rename to src/nvenc/win/impl/nvenc_d3d11_native.cpp index a563b33d86e..b8b911b3e17 100644 --- a/src/nvenc/nvenc_d3d11_native.cpp +++ b/src/nvenc/win/impl/nvenc_d3d11_native.cpp @@ -1,16 +1,19 @@ /** - * @file src/nvenc/nvenc_d3d11_native.cpp + * @file src/nvenc/win/impl/nvenc_d3d11_native.cpp * @brief Definitions for native Direct3D11 NVENC encoder. */ -#ifdef _WIN32 - #include "nvenc_d3d11_native.h" +#include "nvenc_d3d11_native.h" - #include "nvenc_utils.h" +#include "../../common_impl/nvenc_utils.h" +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else namespace nvenc { +#endif - nvenc_d3d11_native::nvenc_d3d11_native(ID3D11Device *d3d_device): - nvenc_d3d11(NV_ENC_DEVICE_TYPE_DIRECTX), + nvenc_d3d11_native::nvenc_d3d11_native(ID3D11Device *d3d_device, shared_dll dll): + nvenc_d3d11_base(NV_ENC_DEVICE_TYPE_DIRECTX, dll), d3d_device(d3d_device) { device = d3d_device; } @@ -48,7 +51,7 @@ namespace nvenc { } if (!registered_input_buffer) { - NV_ENC_REGISTER_RESOURCE register_resource = { min_struct_version(NV_ENC_REGISTER_RESOURCE_VER, 3, 4) }; + NV_ENC_REGISTER_RESOURCE register_resource = { NV_ENC_REGISTER_RESOURCE_VER }; register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; register_resource.width = encoder_params.width; register_resource.height = encoder_params.height; @@ -66,6 +69,4 @@ namespace nvenc { return true; } - -} // namespace nvenc -#endif +} diff --git a/src/nvenc/nvenc_d3d11_native.h b/src/nvenc/win/impl/nvenc_d3d11_native.h similarity index 62% rename from src/nvenc/nvenc_d3d11_native.h rename to src/nvenc/win/impl/nvenc_d3d11_native.h index f9d49b18631..1e514b16631 100644 --- a/src/nvenc/nvenc_d3d11_native.h +++ b/src/nvenc/win/impl/nvenc_d3d11_native.h @@ -1,26 +1,26 @@ /** - * @file src/nvenc/nvenc_d3d11_native.h + * @file src/nvenc/win/impl/nvenc_d3d11_native.h * @brief Declarations for native Direct3D11 NVENC encoder. */ #pragma once -#ifdef _WIN32 - #include - #include - - #include "nvenc_d3d11.h" +#include "nvenc_d3d11_base.h" +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else namespace nvenc { +#endif /** * @brief Native Direct3D11 NVENC encoder. */ - class nvenc_d3d11_native final: public nvenc_d3d11 { + class nvenc_d3d11_native final: public nvenc_d3d11_base { public: /** * @param d3d_device Direct3D11 device used for encoding. */ - explicit nvenc_d3d11_native(ID3D11Device *d3d_device); + nvenc_d3d11_native(ID3D11Device *d3d_device, shared_dll dll); ~nvenc_d3d11_native(); ID3D11Texture2D * @@ -33,6 +33,4 @@ namespace nvenc { const ID3D11DevicePtr d3d_device; ID3D11Texture2DPtr d3d_input_texture; }; - -} // namespace nvenc -#endif +} diff --git a/src/nvenc/nvenc_d3d11_on_cuda.cpp b/src/nvenc/win/impl/nvenc_d3d11_on_cuda.cpp similarity index 90% rename from src/nvenc/nvenc_d3d11_on_cuda.cpp rename to src/nvenc/win/impl/nvenc_d3d11_on_cuda.cpp index 37fe896329c..2b69906792d 100644 --- a/src/nvenc/nvenc_d3d11_on_cuda.cpp +++ b/src/nvenc/win/impl/nvenc_d3d11_on_cuda.cpp @@ -1,16 +1,19 @@ /** - * @file src/nvenc/nvenc_d3d11_on_cuda.cpp + * @file src/nvenc/win/impl/nvenc_d3d11_on_cuda.cpp * @brief Definitions for CUDA NVENC encoder with Direct3D11 input surfaces. */ -#ifdef _WIN32 - #include "nvenc_d3d11_on_cuda.h" +#include "nvenc_d3d11_on_cuda.h" - #include "nvenc_utils.h" +#include "../../common_impl/nvenc_utils.h" +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else namespace nvenc { +#endif - nvenc_d3d11_on_cuda::nvenc_d3d11_on_cuda(ID3D11Device *d3d_device): - nvenc_d3d11(NV_ENC_DEVICE_TYPE_CUDA), + nvenc_d3d11_on_cuda::nvenc_d3d11_on_cuda(ID3D11Device *d3d_device, shared_dll dll): + nvenc_d3d11_base(NV_ENC_DEVICE_TYPE_CUDA, dll), d3d_device(d3d_device) { } @@ -41,11 +44,6 @@ namespace nvenc { } cuda_context = nullptr; } - - if (cuda_functions.dll) { - FreeLibrary(cuda_functions.dll); - cuda_functions = {}; - } } ID3D11Texture2D * @@ -55,13 +53,13 @@ namespace nvenc { bool nvenc_d3d11_on_cuda::init_library() { - if (!nvenc_d3d11::init_library()) return false; + if (!nvenc_d3d11_base::init_library()) return false; constexpr auto dll_name = "nvcuda.dll"; - if ((cuda_functions.dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { + if ((cuda_functions.dll = make_shared_dll(LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)))) { auto load_function = [&](T &location, auto symbol) -> bool { - location = (T) GetProcAddress(cuda_functions.dll, symbol); + location = (T) GetProcAddress(cuda_functions.dll.get(), symbol); return location != nullptr; }; if (!load_function(cuda_functions.cuInit, "cuInit") || @@ -79,7 +77,6 @@ namespace nvenc { !load_function(cuda_functions.cuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray") || !load_function(cuda_functions.cuMemcpy2D, "cuMemcpy2D_v2")) { BOOST_LOG(error) << "NvEnc: missing CUDA functions in " << dll_name; - FreeLibrary(cuda_functions.dll); cuda_functions = {}; } } @@ -164,7 +161,7 @@ namespace nvenc { } if (!registered_input_buffer) { - NV_ENC_REGISTER_RESOURCE register_resource = { min_struct_version(NV_ENC_REGISTER_RESOURCE_VER, 3, 4) }; + NV_ENC_REGISTER_RESOURCE register_resource = { NV_ENC_REGISTER_RESOURCE_VER }; register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; register_resource.width = encoder_params.width; register_resource.height = encoder_params.height; @@ -262,6 +259,4 @@ namespace nvenc { return { *this, nullptr }; } } - -} // namespace nvenc -#endif +} diff --git a/src/nvenc/nvenc_d3d11_on_cuda.h b/src/nvenc/win/impl/nvenc_d3d11_on_cuda.h similarity index 85% rename from src/nvenc/nvenc_d3d11_on_cuda.h rename to src/nvenc/win/impl/nvenc_d3d11_on_cuda.h index 81114321947..2ca11a58c71 100644 --- a/src/nvenc/nvenc_d3d11_on_cuda.h +++ b/src/nvenc/win/impl/nvenc_d3d11_on_cuda.h @@ -1,27 +1,29 @@ /** - * @file src/nvenc/nvenc_d3d11_on_cuda.h + * @file src/nvenc/win/impl/nvenc_d3d11_on_cuda.h * @brief Declarations for CUDA NVENC encoder with Direct3D11 input surfaces. */ #pragma once -#ifdef _WIN32 - #include "nvenc_d3d11.h" +#include "nvenc_d3d11_base.h" +#ifdef NVENC_NAMESPACE +namespace NVENC_NAMESPACE { +#else #include - namespace nvenc { +#endif /** * @brief Interop Direct3D11 on CUDA NVENC encoder. * Input surface is Direct3D11, encoding is performed by CUDA. */ - class nvenc_d3d11_on_cuda final: public nvenc_d3d11 { + class nvenc_d3d11_on_cuda final: public nvenc_d3d11_base { public: /** * @param d3d_device Direct3D11 device that will create input surface texture. * CUDA encoding device will be derived from it. */ - explicit nvenc_d3d11_on_cuda(ID3D11Device *d3d_device); + nvenc_d3d11_on_cuda(ID3D11Device *d3d_device, shared_dll dll); ~nvenc_d3d11_on_cuda(); ID3D11Texture2D * @@ -82,7 +84,7 @@ namespace nvenc { tcuGraphicsUnmapResources *cuGraphicsUnmapResources; tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray; tcuMemcpy2D_v2 *cuMemcpy2D; - HMODULE dll; + shared_dll dll; } cuda_functions = {}; CUresult last_cuda_error = CUDA_SUCCESS; @@ -91,6 +93,4 @@ namespace nvenc { CUdeviceptr cuda_surface = 0; size_t cuda_surface_pitch = 0; }; - -} // namespace nvenc -#endif +} diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_1100.cpp b/src/nvenc/win/impl/nvenc_dynamic_factory_1100.cpp new file mode 100644 index 00000000000..7fd0919bce3 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_1100.cpp @@ -0,0 +1,65 @@ + +namespace nvenc_1100 { + enum NV_ENC_VUI_VIDEO_FORMAT { + NV_ENC_VUI_VIDEO_FORMAT_COMPONENT = 0, + NV_ENC_VUI_VIDEO_FORMAT_PAL = 1, + NV_ENC_VUI_VIDEO_FORMAT_NTSC = 2, + NV_ENC_VUI_VIDEO_FORMAT_SECAM = 3, + NV_ENC_VUI_VIDEO_FORMAT_MAC = 4, + NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED = 5, + }; + enum NV_ENC_VUI_COLOR_PRIMARIES { + NV_ENC_VUI_COLOR_PRIMARIES_UNDEFINED = 0, + NV_ENC_VUI_COLOR_PRIMARIES_BT709 = 1, + NV_ENC_VUI_COLOR_PRIMARIES_UNSPECIFIED = 2, + NV_ENC_VUI_COLOR_PRIMARIES_RESERVED = 3, + NV_ENC_VUI_COLOR_PRIMARIES_BT470M = 4, + NV_ENC_VUI_COLOR_PRIMARIES_BT470BG = 5, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M = 6, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE240M = 7, + NV_ENC_VUI_COLOR_PRIMARIES_FILM = 8, + NV_ENC_VUI_COLOR_PRIMARIES_BT2020 = 9, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE428 = 10, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE431 = 11, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE432 = 12, + NV_ENC_VUI_COLOR_PRIMARIES_JEDEC_P22 = 22, + }; + enum NV_ENC_VUI_TRANSFER_CHARACTERISTIC { + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_UNDEFINED = 0, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 = 1, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_UNSPECIFIED = 2, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_RESERVED = 3, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT470M = 4, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT470BG = 5, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M = 6, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE240M = 7, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LINEAR = 8, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LOG = 9, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LOG_SQRT = 10, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_IEC61966_2_4 = 11, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT1361_ECG = 12, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB = 13, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10 = 14, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_12 = 15, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084 = 16, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE428 = 17, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_ARIB_STD_B67 = 18, + }; + enum NV_ENC_VUI_MATRIX_COEFFS { + NV_ENC_VUI_MATRIX_COEFFS_RGB = 0, + NV_ENC_VUI_MATRIX_COEFFS_BT709 = 1, + NV_ENC_VUI_MATRIX_COEFFS_UNSPECIFIED = 2, + NV_ENC_VUI_MATRIX_COEFFS_RESERVED = 3, + NV_ENC_VUI_MATRIX_COEFFS_FCC = 4, + NV_ENC_VUI_MATRIX_COEFFS_BT470BG = 5, + NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M = 6, + NV_ENC_VUI_MATRIX_COEFFS_SMPTE240M = 7, + NV_ENC_VUI_MATRIX_COEFFS_YCGCO = 8, + NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL = 9, + NV_ENC_VUI_MATRIX_COEFFS_BT2020_CL = 10, + NV_ENC_VUI_MATRIX_COEFFS_SMPTE2085 = 11, + }; +} // namespace nvenc_1100 + +#define NVENC_FACTORY_DEFINITION +#include "nvenc_dynamic_factory_1100.h" diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_1100.h b/src/nvenc/win/impl/nvenc_dynamic_factory_1100.h new file mode 100644 index 00000000000..7c0183d0267 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_1100.h @@ -0,0 +1,6 @@ +#pragma once + +#undef NVENC_FACTORY_VERSION +#define NVENC_FACTORY_VERSION 1100 + +#include "nvenc_dynamic_factory_blueprint.h" diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_1200.cpp b/src/nvenc/win/impl/nvenc_dynamic_factory_1200.cpp new file mode 100644 index 00000000000..b42a904a5ce --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_1200.cpp @@ -0,0 +1,2 @@ +#define NVENC_FACTORY_DEFINITION +#include "nvenc_dynamic_factory_1200.h" diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_1200.h b/src/nvenc/win/impl/nvenc_dynamic_factory_1200.h new file mode 100644 index 00000000000..fba3a7a8915 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_1200.h @@ -0,0 +1,6 @@ +#pragma once + +#undef NVENC_FACTORY_VERSION +#define NVENC_FACTORY_VERSION 1200 + +#include "nvenc_dynamic_factory_blueprint.h" diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_1202.cpp b/src/nvenc/win/impl/nvenc_dynamic_factory_1202.cpp new file mode 100644 index 00000000000..d5323b515f4 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_1202.cpp @@ -0,0 +1,2 @@ +#define NVENC_FACTORY_DEFINITION +#include "nvenc_dynamic_factory_1202.h" diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_1202.h b/src/nvenc/win/impl/nvenc_dynamic_factory_1202.h new file mode 100644 index 00000000000..e024243d9f3 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_1202.h @@ -0,0 +1,6 @@ +#pragma once + +#undef NVENC_FACTORY_VERSION +#define NVENC_FACTORY_VERSION 1202 + +#include "nvenc_dynamic_factory_blueprint.h" diff --git a/src/nvenc/win/impl/nvenc_dynamic_factory_blueprint.h b/src/nvenc/win/impl/nvenc_dynamic_factory_blueprint.h new file mode 100644 index 00000000000..86385d61545 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_dynamic_factory_blueprint.h @@ -0,0 +1,73 @@ +/** + * @file src/nvenc/win/impl/nvenc_dynamic_factory_blueprint.h + * @brief Special blueprint used for declaring and defining factories for specific NVENC SDK versions. + */ +#include + +#ifndef NVENC_FACTORY_VERSION + #error Missing NVENC_FACTORY_VERSION preprocessor definition +#endif + +#define NVENC_FACTORY_CLASS BOOST_PP_CAT(nvenc_dynamic_factory_, NVENC_FACTORY_VERSION) + +#include "nvenc_shared_dll.h" + +#include "../nvenc_dynamic_factory.h" + +namespace nvenc { + + class NVENC_FACTORY_CLASS: public nvenc_dynamic_factory { + public: + NVENC_FACTORY_CLASS(shared_dll dll): + dll(dll) {} + + static std::shared_ptr + get(shared_dll dll) { + return std::make_shared(dll); + } + + std::unique_ptr + create_nvenc_d3d11_native(ID3D11Device *d3d_device) override; + + std::unique_ptr + create_nvenc_d3d11_on_cuda(ID3D11Device *d3d_device) override; + + private: + shared_dll dll; + }; + +} // namespace nvenc + +#ifdef NVENC_FACTORY_DEFINITION + + #define NVENC_NAMESPACE BOOST_PP_CAT(nvenc_, NVENC_FACTORY_VERSION) + #define NVENC_FACTORY_INCLUDE(x) + +namespace NVENC_NAMESPACE { + #include NVENC_FACTORY_INCLUDE(dynlink_cuda.h) + #include NVENC_FACTORY_INCLUDE(nvEncodeAPI.h) +} // namespace NVENC_NAMESPACE + +using namespace nvenc; + + #include "../../common_impl/nvenc_base.cpp" + #include "../../common_impl/nvenc_utils.cpp" + #include "nvenc_d3d11_base.cpp" + #include "nvenc_d3d11_native.cpp" + #include "nvenc_d3d11_on_cuda.cpp" + +namespace nvenc { + + std::unique_ptr + NVENC_FACTORY_CLASS::create_nvenc_d3d11_native(ID3D11Device *d3d_device) { + return std::make_unique(d3d_device, dll); + } + + std::unique_ptr + NVENC_FACTORY_CLASS::create_nvenc_d3d11_on_cuda(ID3D11Device *d3d_device) { + return std::make_unique(d3d_device, dll); + } + +} // namespace nvenc + +#endif diff --git a/src/nvenc/win/impl/nvenc_shared_dll.h b/src/nvenc/win/impl/nvenc_shared_dll.h new file mode 100644 index 00000000000..7268983d348 --- /dev/null +++ b/src/nvenc/win/impl/nvenc_shared_dll.h @@ -0,0 +1,28 @@ +/** + * @file src/nvenc/win/impl/nvenc_shared_dll.h + * @brief Declarations for Windows HMODULE RAII helpers. + */ +#pragma once + +#include +#include + +#include + +namespace nvenc { + + using shared_dll = std::shared_ptr>; + + struct shared_dll_deleter { + void + operator()(HMODULE dll) { + if (dll) FreeLibrary(dll); + } + }; + + inline shared_dll + make_shared_dll(HMODULE dll) { + return shared_dll(dll, shared_dll_deleter()); + } + +} // namespace nvenc diff --git a/src/nvenc/win/nvenc_d3d11.h b/src/nvenc/win/nvenc_d3d11.h new file mode 100644 index 00000000000..1943af82cad --- /dev/null +++ b/src/nvenc/win/nvenc_d3d11.h @@ -0,0 +1,28 @@ +/** + * @file src/nvenc/win/nvenc_d3d11.h + * @brief Declarations for Direct3D11 NVENC encoder interface. + */ +#pragma once + +#include "../nvenc_encoder.h" + +#include + +namespace nvenc { + + /** + * @brief Direct3D11 NVENC encoder interface. + */ + class nvenc_d3d11: virtual public nvenc_encoder { + public: + virtual ~nvenc_d3d11() = default; + + /** + * @brief Get input surface texture. + * @return Input surface texture. + */ + virtual ID3D11Texture2D * + get_input_texture() = 0; + }; + +} // namespace nvenc diff --git a/src/nvenc/win/nvenc_dynamic_factory.cpp b/src/nvenc/win/nvenc_dynamic_factory.cpp new file mode 100644 index 00000000000..bd56a023523 --- /dev/null +++ b/src/nvenc/win/nvenc_dynamic_factory.cpp @@ -0,0 +1,160 @@ +/** + * @file src/nvenc/win/nvenc_dynamic_factory.cpp + * @brief Definitions for Windows NVENC encoder factory. + */ +#include "nvenc_dynamic_factory.h" + +#include "impl/nvenc_dynamic_factory_1100.h" +#include "impl/nvenc_dynamic_factory_1200.h" +#include "impl/nvenc_dynamic_factory_1202.h" + +#include "impl/nvenc_shared_dll.h" + +#include "src/logging.h" + +#include + +#include +#include + +uint32_t +NvEncodeAPIGetMaxSupportedVersion(uint32_t *version); + +namespace { + using namespace nvenc; + + constexpr std::array factory_priorities = { + std::tuple(&nvenc_dynamic_factory_1202::get, 1202), + std::tuple(&nvenc_dynamic_factory_1200::get, 1200), + std::tuple(&nvenc_dynamic_factory_1100::get, 1100), + }; + constexpr auto min_driver_version = "456.71"; + +#ifdef _WIN64 + constexpr auto dll_name = "nvEncodeAPI64.dll"; +#else + constexpr auto dll_name = "nvEncodeAPI.dll"; +#endif + + std::tuple + load_dll() { + auto dll = make_shared_dll(LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); + if (!dll) { + BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << dll_name; + return {}; + } + + auto get_max_version = (decltype(NvEncodeAPIGetMaxSupportedVersion) *) GetProcAddress(dll.get(), "NvEncodeAPIGetMaxSupportedVersion"); + if (!get_max_version) { + BOOST_LOG(error) << "NvEnc: No NvEncodeAPIGetMaxSupportedVersion() in " << dll_name; + return {}; + } + + uint32_t max_version = 0; + if (get_max_version(&max_version) != 0) { + BOOST_LOG(error) << "NvEnc: NvEncodeAPIGetMaxSupportedVersion() failed"; + return {}; + } + max_version = (max_version >> 4) * 100 + (max_version & 0xf); + + return { dll, max_version }; + } + +} // namespace + +namespace nvenc { + + std::shared_ptr + nvenc_dynamic_factory::get() { + auto [dll, max_version] = load_dll(); + if (!dll) return {}; + + for (const auto &[factory_init, version] : factory_priorities) { + if (max_version >= version) { + return factory_init(dll); + } + } + + BOOST_LOG(error) << "NvEnc: minimum required driver version is " << min_driver_version; + return {}; + } + +} // namespace nvenc + +#ifdef SUNSHINE_TESTS + #include "tests/tests_common.h" + + #include + #include + +namespace { + _COM_SMARTPTR_TYPEDEF(IDXGIFactory1, IID_IDXGIFactory1); + _COM_SMARTPTR_TYPEDEF(IDXGIAdapter, IID_IDXGIAdapter); + _COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); +} // namespace + +struct NvencVersionTests: testing::TestWithParam { + static void + SetUpTestSuite() { + std::tie(suite.dll, suite.max_version) = load_dll(); + if (!suite.dll) { + GTEST_SKIP() << "Can't load " << dll_name; + } + + IDXGIFactory1Ptr dxgi_factory; + ASSERT_HRESULT_SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory))); + + IDXGIAdapterPtr dxgi_adapter; + for (UINT i = 0; dxgi_factory->EnumAdapters(i, &dxgi_adapter) != DXGI_ERROR_NOT_FOUND; i++) { + DXGI_ADAPTER_DESC desc; + ASSERT_HRESULT_SUCCEEDED(dxgi_adapter->GetDesc(&desc)); + if (desc.VendorId == 0x10de) break; + } + if (!dxgi_adapter) GTEST_SKIP(); + + ASSERT_HRESULT_SUCCEEDED(D3D11CreateDevice(dxgi_adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, + nullptr, 0, D3D11_SDK_VERSION, &suite.device, nullptr, nullptr)); + } + + static void + TearDownTestSuite() { + suite = {}; + } + + inline static struct { + nvenc::shared_dll dll; + uint32_t max_version; + ID3D11DevicePtr device; + } suite = {}; +}; + +TEST_P(NvencVersionTests, CreateAndEncode) { + auto [factory_init, version] = GetParam(); + if (version > suite.max_version) { + GTEST_SKIP() << "Need dll version " << version << ", have " << suite.max_version; + } + + auto factory = factory_init(suite.dll); + ASSERT_TRUE(factory); + + auto nvenc = factory->create_nvenc_d3d11_native(suite.device); + ASSERT_TRUE(nvenc); + + video::config_t config = { + .width = 1920, + .height = 1080, + .framerate = 60, + .bitrate = 10 * 1000, + }; + video::sunshine_colorspace_t colorspace = { + .colorspace = video::colorspace_e::rec601, + .bit_depth = 8, + }; + ASSERT_TRUE(nvenc->create_encoder({}, config, colorspace, platf::pix_fmt_e::nv12)); + ASSERT_FALSE(nvenc->encode_frame(0, false).data.empty()); +} + +INSTANTIATE_TEST_SUITE_P(NvencFactoryTestsPrivate, NvencVersionTests, testing::ValuesIn(factory_priorities), + [](const auto &info) { return std::to_string(std::get<1>(info.param)); }); + +#endif diff --git a/src/nvenc/win/nvenc_dynamic_factory.h b/src/nvenc/win/nvenc_dynamic_factory.h new file mode 100644 index 00000000000..f4fdd542158 --- /dev/null +++ b/src/nvenc/win/nvenc_dynamic_factory.h @@ -0,0 +1,44 @@ +/** + * @file src/nvenc/win/nvenc_dynamic_factory.h + * @brief Declarations for Windows NVENC encoder factory. + */ +#pragma once + +#include "nvenc_d3d11.h" + +#include + +namespace nvenc { + + /** + * @brief Windows NVENC encoder factory. + */ + class nvenc_dynamic_factory { + public: + virtual ~nvenc_dynamic_factory() = default; + + /** + * @brief Initialize NVENC factory, depends on NVIDIA drivers present in the system. + * @return `shared_ptr` containing factory on success, empty `shared_ptr` on error. + */ + static std::shared_ptr + get(); + + /** + * @brief Create native Direct3D11 NVENC encoder. + * @param d3d_device Direct3D11 device. + * @return `unique_ptr` containing encoder on success, empty `unique_ptr` on error. + */ + virtual std::unique_ptr + create_nvenc_d3d11_native(ID3D11Device *d3d_device) = 0; + + /** + * @brief Create CUDA NVENC encoder with Direct3D11 input surfaces. + * @param d3d_device Direct3D11 device. + * @return `unique_ptr` containing encoder on success, empty `unique_ptr` on error. + */ + virtual std::unique_ptr + create_nvenc_d3d11_on_cuda(ID3D11Device *d3d_device) = 0; + }; + +} // namespace nvenc diff --git a/src/platform/common.h b/src/platform/common.h index 5009c18335a..e48e37c71e5 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -54,7 +54,7 @@ namespace video { struct config_t; } // namespace video namespace nvenc { - class nvenc_base; + class nvenc_encoder; } namespace platf { @@ -428,7 +428,7 @@ namespace platf { virtual bool init_encoder(const video::config_t &client_config, const video::sunshine_colorspace_t &colorspace) = 0; - nvenc::nvenc_base *nvenc = nullptr; + nvenc::nvenc_encoder *nvenc = nullptr; }; enum class capture_e : int { diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index 96ddff84258..4290b2f6178 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -16,10 +16,7 @@ extern "C" { #include "misc.h" #include "src/config.h" #include "src/logging.h" -#include "src/nvenc/nvenc_config.h" -#include "src/nvenc/nvenc_d3d11_native.h" -#include "src/nvenc/nvenc_d3d11_on_cuda.h" -#include "src/nvenc/nvenc_utils.h" +#include "src/nvenc/win/nvenc_dynamic_factory.h" #include "src/video.h" #include @@ -1042,20 +1039,21 @@ namespace platf::dxgi { public: bool init_device(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { - buffer_format = nvenc::nvenc_format_from_sunshine_format(pix_fmt); - if (buffer_format == NV_ENC_BUFFER_FORMAT_UNDEFINED) { - BOOST_LOG(error) << "Unexpected pixel format for NvENC ["sv << from_pix_fmt(pix_fmt) << ']'; - return false; - } - if (base.init(display, adapter_p, pix_fmt)) return false; + auto factory = nvenc::nvenc_dynamic_factory::get(); + if (!factory) return false; + if (pix_fmt == pix_fmt_e::yuv444p16) { - nvenc_d3d = std::make_unique(base.device.get()); + nvenc_d3d = factory->create_nvenc_d3d11_on_cuda(base.device.get()); } else { - nvenc_d3d = std::make_unique(base.device.get()); + nvenc_d3d = factory->create_nvenc_d3d11_native(base.device.get()); } + + if (!nvenc_d3d) return false; + + buffer_format = pix_fmt; nvenc = nvenc_d3d.get(); return true; @@ -1065,8 +1063,7 @@ namespace platf::dxgi { init_encoder(const ::video::config_t &client_config, const ::video::sunshine_colorspace_t &colorspace) override { if (!nvenc_d3d) return false; - auto nvenc_colorspace = nvenc::nvenc_colorspace_from_sunshine_colorspace(colorspace); - if (!nvenc_d3d->create_encoder(config::video.nv, client_config, nvenc_colorspace, buffer_format)) return false; + if (!nvenc_d3d->create_encoder(config::video.nv, client_config, colorspace, buffer_format)) return false; base.apply_colorspace(colorspace); return base.init_output(nvenc_d3d->get_input_texture(), client_config.width, client_config.height) == 0; @@ -1080,7 +1077,7 @@ namespace platf::dxgi { private: d3d_base_encode_device base; std::unique_ptr nvenc_d3d; - NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; + platf::pix_fmt_e buffer_format = platf::pix_fmt_e::unknown; }; bool diff --git a/src/video.cpp b/src/video.cpp index 8c5829a2afb..27edcfda050 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -21,7 +21,7 @@ extern "C" { #include "globals.h" #include "input.h" #include "logging.h" -#include "nvenc/nvenc_base.h" +#include "nvenc/nvenc_encoder.h" #include "platform/common.h" #include "sync.h" #include "video.h" diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 8386dbd3673..f6da228a472 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -222,6 +222,7 @@

{{ $t('config.configuration') }}

"nvenc_spatial_aq": "disabled", "nvenc_vbv_increase": 0, "nvenc_realtime_hags": "enabled", + "nvenc_split_encode": "driver_decides", "nvenc_latency_over_power": "enabled", "nvenc_opengl_vulkan_on_dxgi": "enabled", "nvenc_h264_cavlc": "disabled", diff --git a/src_assets/common/assets/web/configs/tabs/encoders/NvidiaNvencEncoder.vue b/src_assets/common/assets/web/configs/tabs/encoders/NvidiaNvencEncoder.vue index aa6ad003ac2..bda55e0eea4 100644 --- a/src_assets/common/assets/web/configs/tabs/encoders/NvidiaNvencEncoder.vue +++ b/src_assets/common/assets/web/configs/tabs/encoders/NvidiaNvencEncoder.vue @@ -85,6 +85,17 @@ const config = ref(props.config) + +
+ + +
{{ $t('config.nvenc_split_encode_desc') }}
+
+
diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index 12844fcf874..b369639aeaf 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -239,6 +239,9 @@ "nvenc_spatial_aq_desc": "Assign higher QP values to flat regions of the video. Recommended to enable when streaming at lower bitrates.", "nvenc_spatial_aq_disabled": "Disabled (faster, default)", "nvenc_spatial_aq_enabled": "Enabled (slower)", + "nvenc_split_encode": "Split frame encoding", + "nvenc_split_encode_desc": "Split the encoding of each video frame over multiple NVENC hardware units. Significantly reduces encoding latency with a marginal compression efficiency penalty. This option is ignored if your GPU has a singular NVENC unit.", + "nvenc_split_encode_driver_decides_def": "Driver decides (default)", "nvenc_twopass": "Two-pass mode", "nvenc_twopass_desc": "Adds preliminary encoding pass. This allows to detect more motion vectors, better distribute bitrate across the frame and more strictly adhere to bitrate limits. Disabling it is not recommended since this can lead to occasional bitrate overshoot and subsequent packet loss.", "nvenc_twopass_disabled": "Disabled (fastest, not recommended)", diff --git a/third-party/nvenc-headers/1100 b/third-party/nvenc-headers/1100 new file mode 160000 index 00000000000..20703c97d32 --- /dev/null +++ b/third-party/nvenc-headers/1100 @@ -0,0 +1 @@ +Subproject commit 20703c97d32089634f67c5f4ab680ad7bd48877e diff --git a/third-party/nv-codec-headers b/third-party/nvenc-headers/1200 similarity index 100% rename from third-party/nv-codec-headers rename to third-party/nvenc-headers/1200 diff --git a/third-party/nvenc-headers/1202 b/third-party/nvenc-headers/1202 new file mode 160000 index 00000000000..9934f17316b --- /dev/null +++ b/third-party/nvenc-headers/1202 @@ -0,0 +1 @@ +Subproject commit 9934f17316b66ce6de12f3b82203a298bc9351d8