mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-08-07 02:00:45 +00:00
fix(nvenc): dynamic nv codec sdk selection (#5451)
This commit is contained in:
@@ -38,6 +38,10 @@ endif()
|
||||
# set the module path, used for includes
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
# CPM
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpm/CPM.cmake)
|
||||
CPMUsePackageLock(package-lock.cmake)
|
||||
|
||||
# export compile_commands.json
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
||||
@@ -59,11 +59,53 @@ elseif(UNIX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
BEFORE SYSTEM
|
||||
"${CMAKE_SOURCE_DIR}/third-party/build-deps/third-party/FFmpeg/nv-codec-headers/include"
|
||||
set(NVENC_PUBLIC_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_config.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_d3d11_interface.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_dynamic_factory.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_dynamic_factory.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_dynamic_factory_versions.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_encoded_frame.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_encoder.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_shared_dll.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_version.h"
|
||||
)
|
||||
file(GLOB NVENC_SOURCES CONFIGURE_DEPENDS "src/nvenc/*.cpp" "src/nvenc/*.h")
|
||||
set(NVENC_SOURCES ${NVENC_PUBLIC_SOURCES})
|
||||
|
||||
if(WIN32)
|
||||
set(NVENC_IMPLEMENTATION_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_base.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_d3d11.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_d3d11_native.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_d3d11_on_cuda.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_dynamic_factory_impl.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/nvenc/nvenc_utils.cpp"
|
||||
)
|
||||
|
||||
# Add a version-isolated NVENC implementation object library.
|
||||
# add_nvenc_sdk_implementation: args = `target_name`, `sdk_version`, `sdk_include_dir`
|
||||
function(add_nvenc_sdk_implementation target_name sdk_version sdk_include_dir)
|
||||
add_library(${target_name} OBJECT ${NVENC_IMPLEMENTATION_SOURCES})
|
||||
target_include_directories(${target_name} BEFORE PRIVATE "${sdk_include_dir}")
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
NVENC_FACTORY_SUFFIX=${sdk_version}
|
||||
NVENC_NAMESPACE=nvenc_${sdk_version}
|
||||
NVENC_SDK_VERSION=${sdk_version}
|
||||
)
|
||||
target_compile_options(${target_name} PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
||||
endfunction()
|
||||
|
||||
add_nvenc_sdk_implementation(nvenc_sdk_1100 1100 "${NV_CODEC_HEADERS_11_INCLUDE_DIR}")
|
||||
add_nvenc_sdk_implementation(nvenc_sdk_1200 1200 "${NV_CODEC_HEADERS_12_INCLUDE_DIR}")
|
||||
add_nvenc_sdk_implementation(nvenc_sdk_1300 1300 "${NV_CODEC_HEADERS_13_INCLUDE_DIR}")
|
||||
|
||||
list(APPEND NVENC_SOURCES
|
||||
$<TARGET_OBJECTS:nvenc_sdk_1100>
|
||||
$<TARGET_OBJECTS:nvenc_sdk_1200>
|
||||
$<TARGET_OBJECTS:nvenc_sdk_1300>
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_TARGET_FILES ${NVENC_SOURCES})
|
||||
|
||||
set(SUNSHINE_TARGET_FILES
|
||||
@@ -155,6 +197,10 @@ include_directories(
|
||||
${Boost_INCLUDE_DIRS} # has to be the last, or we get runtime error on macOS ffmpeg encoder
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
include_directories(BEFORE SYSTEM "${NV_CODEC_HEADERS_13_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
|
||||
${MINIUPNP_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
|
||||
+1379
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ if(SUNSHINE_ENABLE_TRAY)
|
||||
endif()
|
||||
|
||||
# common dependencies
|
||||
include("${CMAKE_MODULE_PATH}/dependencies/nv_codec_headers.cmake")
|
||||
include("${CMAKE_MODULE_PATH}/dependencies/nlohmann_json.cmake")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
if(WIN32)
|
||||
CPMGetPackage(nv_codec_headers_13)
|
||||
CPMGetPackage(nv_codec_headers_11)
|
||||
CPMGetPackage(nv_codec_headers_12)
|
||||
|
||||
set(NV_CODEC_HEADERS_11_INCLUDE_DIR "${nv_codec_headers_11_SOURCE_DIR}/include")
|
||||
set(NV_CODEC_HEADERS_12_INCLUDE_DIR "${nv_codec_headers_12_SOURCE_DIR}/include")
|
||||
set(NV_CODEC_HEADERS_13_INCLUDE_DIR "${nv_codec_headers_13_SOURCE_DIR}/include")
|
||||
endif()
|
||||
@@ -0,0 +1,67 @@
|
||||
# CPM Package Lock
|
||||
# This file should be committed to version control
|
||||
|
||||
# The first argument of CPMDeclarePackage can be freely chosen and is used as argument in CPMGetPackage.
|
||||
# The NAME argument should be package name that would also be used in a find_package call.
|
||||
# Ideally, both are the same, which might not always be possible: https://github.com/cpm-cmake/CPM.cmake/issues/603
|
||||
# This is needed to support CPM_USE_LOCAL_PACKAGES
|
||||
|
||||
# Renovate-bot will update the versions and hashes in this file when a new version of a dependency is released.
|
||||
# The comments above each dependency are used by renovate to identify the dependencies and extract the version numbers.
|
||||
# See https://github.com/LizardByte/.github/blob/master/renovate-config.json5 for the configuration of renovate.
|
||||
#
|
||||
# Expected dependency structure for new entries:
|
||||
# - Start each block with a human-readable comment, for example `# Example dependency`.
|
||||
# - Follow it with consecutive renovate metadata comments.
|
||||
# - The first metadata line must start with `# renovate:` and include `datasource=` and `depName=`.
|
||||
# - Optional metadata keys are `packageName=`, `versioning=`, `extractVersion=`, and `registryUrl=`.
|
||||
# - Optional metadata may stay on the `# renovate:` line or continue on the next consecutive `#` lines.
|
||||
# - Keep metadata keys in this order: `datasource`, `depName`, `packageName`, `versioning`,
|
||||
# `extractVersion`, `registryUrl`.
|
||||
# - After metadata, declare the tracked value with `set(NAME_VERSION ...)` or `set(NAME_TAG ...)`.
|
||||
# - If the dependency also tracks a SHA256, keep `set(NAME_SHA256 ...)` immediately after the
|
||||
# matching `NAME_VERSION` or `NAME_TAG` line with no unrelated lines between them.
|
||||
# - Keep `CPMDeclarePackage(...)` below the tracked values.
|
||||
#
|
||||
# Example layout:
|
||||
# - `# Example dependency`
|
||||
# - `# renovate: datasource=github-tags depName=owner/repo`
|
||||
# - `# versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$`
|
||||
# - `set(EXAMPLE_TAG v1.2.3)`
|
||||
# - `set(EXAMPLE_SHA256 <sha256>)`
|
||||
# - `CPMDeclarePackage(...)`
|
||||
|
||||
set(PATCH_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/patches")
|
||||
|
||||
# NVENC SDK 11.0 headers
|
||||
# renovate: datasource=github-tags depName=FFmpeg/nv-codec-headers
|
||||
# versioning=regex:^n(?<major>11)\.(?<minor>0)\.(?<patch>\d+)\.(?<build>\d+)$
|
||||
set(NV_CODEC_HEADERS_11_TAG n11.0.10.3)
|
||||
CPMDeclarePackage(nv_codec_headers_11
|
||||
NAME nv_codec_headers_11
|
||||
GIT_REPOSITORY https://github.com/FFmpeg/nv-codec-headers.git
|
||||
GIT_TAG ${NV_CODEC_HEADERS_11_TAG}
|
||||
DOWNLOAD_ONLY YES
|
||||
)
|
||||
|
||||
# NVENC SDK 12.0 headers
|
||||
# renovate: datasource=github-tags depName=FFmpeg/nv-codec-headers
|
||||
# versioning=regex:^n(?<major>12)\.(?<minor>0)\.(?<patch>\d+)\.(?<build>\d+)$
|
||||
set(NV_CODEC_HEADERS_12_TAG n12.0.16.2)
|
||||
CPMDeclarePackage(nv_codec_headers_12
|
||||
NAME nv_codec_headers_12
|
||||
GIT_REPOSITORY https://github.com/FFmpeg/nv-codec-headers.git
|
||||
GIT_TAG ${NV_CODEC_HEADERS_12_TAG}
|
||||
DOWNLOAD_ONLY YES
|
||||
)
|
||||
|
||||
# NVENC SDK 13.0 headers
|
||||
# renovate: datasource=github-tags depName=FFmpeg/nv-codec-headers
|
||||
# versioning=regex:^n(?<major>13)\.(?<minor>0)\.(?<patch>\d+)\.(?<build>\d+)$
|
||||
set(NV_CODEC_HEADERS_13_TAG n13.0.19.1)
|
||||
CPMDeclarePackage(nv_codec_headers_13
|
||||
NAME nv_codec_headers_13
|
||||
GIT_REPOSITORY https://github.com/FFmpeg/nv-codec-headers.git
|
||||
GIT_TAG ${NV_CODEC_HEADERS_13_TAG}
|
||||
DOWNLOAD_ONLY YES
|
||||
)
|
||||
+539
-395
File diff suppressed because it is too large
Load Diff
+202
-11
@@ -4,32 +4,33 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <ffnvcodec/nvEncodeAPI.h>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_colorspace.h"
|
||||
#include "nvenc_config.h"
|
||||
#include "nvenc_encoded_frame.h"
|
||||
#include "nvenc_encoder.h"
|
||||
#include "nvenc_sdk.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/video.h"
|
||||
#include "src/video_colorspace.h"
|
||||
|
||||
/**
|
||||
* @brief Standalone NVENC encoder
|
||||
*/
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
/**
|
||||
* @brief Abstract platform-agnostic base of standalone NVENC encoder.
|
||||
* Derived classes perform platform-specific operations.
|
||||
*/
|
||||
class nvenc_base {
|
||||
// Virtual inheritance is required because platform implementations also inherit their SDK-neutral interface.
|
||||
class nvenc_base: public virtual ::nvenc::nvenc_encoder { // NOSONAR(cpp:S1011)
|
||||
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() override;
|
||||
|
||||
nvenc_base(const nvenc_base &) = delete;
|
||||
nvenc_base &operator=(const nvenc_base &) = delete;
|
||||
@@ -42,13 +43,18 @@ namespace nvenc {
|
||||
* @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);
|
||||
bool create_encoder(
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
const video::sunshine_colorspace_t &colorspace,
|
||||
platf::pix_fmt_e buffer_format
|
||||
) override;
|
||||
|
||||
/**
|
||||
* @brief Destroy the encoder.
|
||||
* Derived classes classes call it in the destructor.
|
||||
*/
|
||||
void destroy_encoder();
|
||||
void destroy_encoder() override;
|
||||
|
||||
/**
|
||||
* @brief Encode the next frame using platform-specific input surface.
|
||||
@@ -58,7 +64,7 @@ namespace nvenc {
|
||||
* @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);
|
||||
::nvenc::nvenc_encoded_frame encode_frame(uint64_t frame_index, bool force_idr) override;
|
||||
|
||||
/**
|
||||
* @brief Perform reference frame invalidation (RFI) procedure.
|
||||
@@ -67,7 +73,7 @@ namespace nvenc {
|
||||
* @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);
|
||||
bool invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -137,6 +143,191 @@ namespace nvenc {
|
||||
///< Can be set in constructor or `init_library()`, must override `wait_for_async_event()`.
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Query one encoder capability.
|
||||
*
|
||||
* @param encode_guid Codec GUID to query.
|
||||
* @param cap Capability identifier.
|
||||
* @return Capability value, or zero when the query fails.
|
||||
*/
|
||||
int get_encoder_cap(const GUID &encode_guid, NV_ENC_CAPS cap) const;
|
||||
|
||||
/**
|
||||
* @brief Validate the requested input format and dimensions against encoder capabilities.
|
||||
*
|
||||
* @param encode_guid Selected codec GUID.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
* @return `true` when the request is supported, otherwise `false`.
|
||||
*/
|
||||
bool validate_encoder_capabilities(const GUID &encode_guid, NV_ENC_BUFFER_FORMAT buffer_format);
|
||||
|
||||
/**
|
||||
* @brief Configure split-frame encoding for the selected SDK.
|
||||
*
|
||||
* @param init_params Encoder initialization parameters to update.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
*/
|
||||
void configure_split_frame(
|
||||
NV_ENC_INITIALIZE_PARAMS &init_params,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config
|
||||
) const;
|
||||
|
||||
/**
|
||||
* @brief Configure rate control and VBV options.
|
||||
*
|
||||
* @param enc_config Encoder configuration to update.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
void configure_rate_control(
|
||||
NV_ENC_CONFIG &enc_config,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Configure the requested reference-frame count.
|
||||
*
|
||||
* @param ref_frames_option Codec-specific reference-frame option.
|
||||
* @param list0_option Codec-specific list-zero option.
|
||||
* @param default_count Default reference-frame count.
|
||||
* @param requested_count Client-requested reference-frame count.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
void configure_reference_frames(
|
||||
std::uint32_t &ref_frames_option,
|
||||
NV_ENC_NUM_REF_FRAMES &list0_option,
|
||||
std::uint32_t default_count,
|
||||
int requested_count,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Configure VUI metadata and intra-refresh options shared by H.264 and HEVC.
|
||||
*
|
||||
* @tparam FormatConfig Codec-specific NVENC configuration type.
|
||||
* @param format_config Codec-specific encoder configuration to update.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param colorspace NVENC colorspace metadata.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
template<typename FormatConfig>
|
||||
void configure_h264_hevc_metadata(
|
||||
FormatConfig &format_config,
|
||||
const video::config_t &client_config,
|
||||
const nvenc_colorspace_t &colorspace,
|
||||
NV_ENC_BUFFER_FORMAT buffer_format,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Configure H.264 codec options.
|
||||
*
|
||||
* @param enc_config Encoder configuration to update.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param colorspace NVENC colorspace metadata.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
void configure_h264(
|
||||
NV_ENC_CONFIG &enc_config,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
const nvenc_colorspace_t &colorspace,
|
||||
NV_ENC_BUFFER_FORMAT buffer_format,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Configure HEVC codec options.
|
||||
*
|
||||
* @param enc_config Encoder configuration to update.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param colorspace NVENC colorspace metadata.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
void configure_hevc(
|
||||
NV_ENC_CONFIG &enc_config,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
const nvenc_colorspace_t &colorspace,
|
||||
NV_ENC_BUFFER_FORMAT buffer_format,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
|
||||
#if NVENC_SDK_VERSION >= 1200
|
||||
/**
|
||||
* @brief Configure AV1 codec options.
|
||||
*
|
||||
* @param enc_config Encoder configuration to update.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param colorspace NVENC colorspace metadata.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
void configure_av1(
|
||||
NV_ENC_CONFIG &enc_config,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
const nvenc_colorspace_t &colorspace,
|
||||
NV_ENC_BUFFER_FORMAT buffer_format,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configure codec-specific encoder options.
|
||||
*
|
||||
* @param enc_config Encoder configuration to update.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param colorspace NVENC colorspace metadata.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
* @param encode_guid Selected codec GUID.
|
||||
*/
|
||||
void configure_codec(
|
||||
NV_ENC_CONFIG &enc_config,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
const nvenc_colorspace_t &colorspace,
|
||||
NV_ENC_BUFFER_FORMAT buffer_format,
|
||||
const GUID &encode_guid
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Initialize the encoder and its registered input and output resources.
|
||||
*
|
||||
* @param init_params Completed encoder initialization parameters.
|
||||
* @return `true` on success, otherwise `false`.
|
||||
*/
|
||||
bool initialize_encoder_resources(NV_ENC_INITIALIZE_PARAMS &init_params);
|
||||
|
||||
/**
|
||||
* @brief Log the selected encoder configuration.
|
||||
*
|
||||
* @param init_params Encoder initialization parameters.
|
||||
* @param enc_config Encoder configuration.
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param buffer_format Selected NVENC input format.
|
||||
*/
|
||||
void log_created_encoder(
|
||||
const NV_ENC_INITIALIZE_PARAMS &init_params,
|
||||
const NV_ENC_CONFIG &enc_config,
|
||||
const ::nvenc::nvenc_config &config,
|
||||
const video::config_t &client_config,
|
||||
NV_ENC_BUFFER_FORMAT buffer_format
|
||||
) const;
|
||||
|
||||
NV_ENC_OUTPUT_PTR output_bitstream = nullptr;
|
||||
|
||||
struct {
|
||||
@@ -147,4 +338,4 @@ namespace nvenc {
|
||||
} encoder_state;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <ffnvcodec/nvEncodeAPI.h>
|
||||
// local includes
|
||||
#include "nvenc_sdk.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
/**
|
||||
* @brief YUV colorspace and color range.
|
||||
@@ -19,4 +19,4 @@ namespace nvenc {
|
||||
bool full_range; ///< Whether the video range is full-range instead of limited.
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
|
||||
+19
-34
@@ -8,62 +8,47 @@
|
||||
#ifdef _WIN32
|
||||
#include "nvenc_d3d11.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
nvenc_d3d11::nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type):
|
||||
nvenc_base(device_type) {
|
||||
nvenc_d3d11::nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type, ::nvenc::shared_dll dll):
|
||||
nvenc_base(device_type),
|
||||
dll(std::move(dll)) {
|
||||
async_event_handle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
nvenc_d3d11::~nvenc_d3d11() {
|
||||
if (dll) {
|
||||
FreeLibrary(dll);
|
||||
dll = nullptr;
|
||||
}
|
||||
if (async_event_handle) {
|
||||
CloseHandle(async_event_handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool nvenc_d3d11::init_library() {
|
||||
if (dll) {
|
||||
if (nvenc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
constexpr auto dll_name = "nvEncodeAPI64.dll";
|
||||
#else
|
||||
constexpr auto dll_name = "nvEncodeAPI.dll";
|
||||
#endif
|
||||
|
||||
if ((dll = LoadLibraryEx(dll_name, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
|
||||
if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) {
|
||||
auto new_nvenc = std::make_unique<NV_ENCODE_API_FUNCTION_LIST>();
|
||||
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 " << dll_name;
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << dll_name;
|
||||
auto create_instance = reinterpret_cast<decltype(NvEncodeAPICreateInstance) *>(
|
||||
GetProcAddress(dll.get(), "NvEncodeAPICreateInstance")
|
||||
);
|
||||
if (!create_instance) {
|
||||
BOOST_LOG(error) << "NvEnc: No NvEncodeAPICreateInstance() in NVENC driver library";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dll) {
|
||||
FreeLibrary(dll);
|
||||
dll = nullptr;
|
||||
auto new_nvenc = std::make_unique<NV_ENCODE_API_FUNCTION_LIST>();
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
nvenc = std::move(new_nvenc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nvenc_d3d11::wait_for_async_event(uint32_t timeout_ms) {
|
||||
return WaitForSingleObject(async_event_handle, timeout_ms) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
#endif
|
||||
|
||||
+10
-7
@@ -11,8 +11,10 @@
|
||||
|
||||
// local includes
|
||||
#include "nvenc_base.h"
|
||||
#include "nvenc_d3d11_interface.h"
|
||||
#include "nvenc_shared_dll.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
#ifdef DOXYGEN
|
||||
/**
|
||||
@@ -42,29 +44,30 @@ 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: public nvenc_base, public ::nvenc::nvenc_d3d11_interface {
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize an NVENC session wrapper for D3D11 input textures.
|
||||
*
|
||||
* @param device_type NVENC device type used by the encoder session.
|
||||
* @param dll Shared NVENC driver module.
|
||||
*/
|
||||
explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type);
|
||||
~nvenc_d3d11();
|
||||
explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type, ::nvenc::shared_dll dll);
|
||||
~nvenc_d3d11() override;
|
||||
|
||||
/**
|
||||
* @brief Get input surface texture.
|
||||
* @return Input surface texture.
|
||||
*/
|
||||
virtual ID3D11Texture2D *get_input_texture() = 0;
|
||||
ID3D11Texture2D *get_input_texture() override = 0;
|
||||
|
||||
protected:
|
||||
bool init_library() override;
|
||||
bool wait_for_async_event(uint32_t timeout_ms) override;
|
||||
|
||||
private:
|
||||
HMODULE dll = nullptr;
|
||||
::nvenc::shared_dll dll;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_d3d11_interface.h
|
||||
* @brief Declarations for the SDK-neutral Direct3D11 NVENC interface.
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef _WIN32
|
||||
|
||||
// lib includes
|
||||
#include <d3d11.h>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_encoder.h"
|
||||
|
||||
namespace nvenc {
|
||||
|
||||
/**
|
||||
* @brief SDK-neutral Direct3D11 NVENC encoder interface.
|
||||
*/
|
||||
// Virtual inheritance is required because concrete encoders also inherit their SDK-specific implementation.
|
||||
class nvenc_d3d11_interface: public virtual nvenc_encoder { // NOSONAR(cpp:S1011)
|
||||
public:
|
||||
/**
|
||||
* @brief Destroy the SDK-neutral Direct3D11 encoder interface.
|
||||
*/
|
||||
~nvenc_d3d11_interface() override = default;
|
||||
|
||||
/**
|
||||
* @brief Get the input surface texture.
|
||||
*
|
||||
* @return Input surface texture.
|
||||
*/
|
||||
virtual ID3D11Texture2D *get_input_texture() = 0;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
#endif
|
||||
@@ -9,10 +9,10 @@
|
||||
// local includes
|
||||
#include "nvenc_utils.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
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, ::nvenc::shared_dll dll):
|
||||
nvenc_d3d11(NV_ENC_DEVICE_TYPE_DIRECTX, std::move(dll)),
|
||||
d3d_device(d3d_device) {
|
||||
device = d3d_device;
|
||||
}
|
||||
@@ -70,5 +70,5 @@ namespace nvenc {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// local includes
|
||||
#include "nvenc_d3d11.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
/**
|
||||
* @brief Native Direct3D11 NVENC encoder.
|
||||
@@ -20,9 +20,10 @@ namespace nvenc {
|
||||
public:
|
||||
/**
|
||||
* @param d3d_device Direct3D11 device used for encoding.
|
||||
* @param dll Shared NVENC driver module.
|
||||
*/
|
||||
explicit nvenc_d3d11_native(ID3D11Device *d3d_device);
|
||||
~nvenc_d3d11_native();
|
||||
explicit nvenc_d3d11_native(ID3D11Device *d3d_device, ::nvenc::shared_dll dll);
|
||||
~nvenc_d3d11_native() override;
|
||||
|
||||
ID3D11Texture2D *get_input_texture() override;
|
||||
|
||||
@@ -33,5 +34,5 @@ namespace nvenc {
|
||||
ID3D11Texture2DPtr d3d_input_texture;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
#endif
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
// local includes
|
||||
#include "nvenc_utils.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
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, ::nvenc::shared_dll dll):
|
||||
nvenc_d3d11(NV_ENC_DEVICE_TYPE_CUDA, std::move(dll)),
|
||||
d3d_device(d3d_device) {
|
||||
}
|
||||
|
||||
@@ -265,5 +265,5 @@ namespace nvenc {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
#endif
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef _WIN32
|
||||
// lib includes
|
||||
#include <ffnvcodec/dynlink_cuda.h>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_d3d11.h"
|
||||
#include "nvenc_sdk.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
/**
|
||||
* @brief Interop Direct3D11 on CUDA NVENC encoder.
|
||||
@@ -21,9 +19,10 @@ namespace nvenc {
|
||||
/**
|
||||
* @param d3d_device Direct3D11 device that will create input surface texture.
|
||||
* CUDA encoding device will be derived from it.
|
||||
* @param dll Shared NVENC driver module.
|
||||
*/
|
||||
explicit nvenc_d3d11_on_cuda(ID3D11Device *d3d_device);
|
||||
~nvenc_d3d11_on_cuda();
|
||||
explicit nvenc_d3d11_on_cuda(ID3D11Device *d3d_device, ::nvenc::shared_dll dll);
|
||||
~nvenc_d3d11_on_cuda() override;
|
||||
|
||||
ID3D11Texture2D *get_input_texture() override;
|
||||
|
||||
@@ -84,5 +83,5 @@ namespace nvenc {
|
||||
size_t cuda_surface_pitch = 0;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_dynamic_factory.cpp
|
||||
* @brief Definitions for runtime NVENC SDK selection on Windows.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
// this include
|
||||
#include "nvenc_dynamic_factory.h"
|
||||
|
||||
// standard includes
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_dynamic_factory_versions.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef _WIN64
|
||||
constexpr auto nvenc_dll_name = "nvEncodeAPI64.dll";
|
||||
#else
|
||||
constexpr auto nvenc_dll_name = "nvEncodeAPI.dll";
|
||||
#endif
|
||||
|
||||
constexpr auto minimum_driver_version = "456.71";
|
||||
|
||||
using get_max_supported_version_fn = std::uint32_t(WINAPI *)(std::uint32_t *);
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvenc {
|
||||
|
||||
nvenc_dynamic_factory::nvenc_dynamic_factory(
|
||||
shared_dll dll,
|
||||
nvenc_sdk_version sdk_version,
|
||||
create_encoder_fn create_native,
|
||||
create_encoder_fn create_on_cuda
|
||||
):
|
||||
dll(std::move(dll)),
|
||||
selected_sdk_version(sdk_version),
|
||||
create_native(std::move(create_native)),
|
||||
create_on_cuda(std::move(create_on_cuda)) {
|
||||
}
|
||||
|
||||
std::shared_ptr<nvenc_dynamic_factory> nvenc_dynamic_factory::get() {
|
||||
return get({
|
||||
[]() {
|
||||
return make_shared_dll(LoadLibraryEx(nvenc_dll_name, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
|
||||
},
|
||||
[](HMODULE dll, const char *symbol) {
|
||||
return GetProcAddress(dll, symbol);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<nvenc_dynamic_factory> nvenc_dynamic_factory::get(const nvenc_runtime_api &runtime_api) {
|
||||
auto dll = runtime_api.load_driver();
|
||||
if (!dll) {
|
||||
BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << nvenc_dll_name;
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto get_max_version = std::bit_cast<get_max_supported_version_fn>(
|
||||
runtime_api.get_symbol(dll.get(), "NvEncodeAPIGetMaxSupportedVersion")
|
||||
);
|
||||
if (!get_max_version) {
|
||||
BOOST_LOG(error) << "NvEnc: No NvEncodeAPIGetMaxSupportedVersion() in " << nvenc_dll_name;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::uint32_t packed_max_version = 0;
|
||||
if (get_max_version(&packed_max_version) != 0U) {
|
||||
BOOST_LOG(error) << "NvEnc: NvEncodeAPIGetMaxSupportedVersion() failed";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto max_version = decode_nvenc_driver_version(packed_max_version);
|
||||
const auto sdk_version = select_nvenc_sdk_version(max_version);
|
||||
switch (sdk_version) {
|
||||
case nvenc_sdk_version::sdk_13_0:
|
||||
return std::make_shared<nvenc_dynamic_factory>(
|
||||
std::move(dll),
|
||||
sdk_version,
|
||||
detail::create_nvenc_d3d11_native_1300,
|
||||
detail::create_nvenc_d3d11_on_cuda_1300
|
||||
);
|
||||
|
||||
case nvenc_sdk_version::sdk_12_0:
|
||||
return std::make_shared<nvenc_dynamic_factory>(
|
||||
std::move(dll),
|
||||
sdk_version,
|
||||
detail::create_nvenc_d3d11_native_1200,
|
||||
detail::create_nvenc_d3d11_on_cuda_1200
|
||||
);
|
||||
|
||||
case nvenc_sdk_version::sdk_11_0:
|
||||
return std::make_shared<nvenc_dynamic_factory>(
|
||||
std::move(dll),
|
||||
sdk_version,
|
||||
detail::create_nvenc_d3d11_native_1100,
|
||||
detail::create_nvenc_d3d11_on_cuda_1100
|
||||
);
|
||||
|
||||
case nvenc_sdk_version::unsupported:
|
||||
default:
|
||||
BOOST_LOG(error) << "NvEnc: minimum required driver version is " << minimum_driver_version;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<nvenc_d3d11_interface> nvenc_dynamic_factory::create_nvenc_d3d11_native(
|
||||
ID3D11Device *d3d_device
|
||||
) const {
|
||||
return create_native(d3d_device, dll);
|
||||
}
|
||||
|
||||
std::unique_ptr<nvenc_d3d11_interface> nvenc_dynamic_factory::create_nvenc_d3d11_on_cuda(
|
||||
ID3D11Device *d3d_device
|
||||
) const {
|
||||
return create_on_cuda(d3d_device, dll);
|
||||
}
|
||||
|
||||
nvenc_sdk_version nvenc_dynamic_factory::sdk_version() const {
|
||||
return selected_sdk_version;
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_dynamic_factory.h
|
||||
* @brief Declarations for runtime NVENC SDK selection on Windows.
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef _WIN32
|
||||
|
||||
// standard includes
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_d3d11_interface.h"
|
||||
#include "nvenc_shared_dll.h"
|
||||
#include "nvenc_version.h"
|
||||
|
||||
namespace nvenc {
|
||||
|
||||
/**
|
||||
* @brief Windows runtime operations used to discover the installed NVENC API.
|
||||
*/
|
||||
struct nvenc_runtime_api {
|
||||
using load_driver_fn = std::function<shared_dll()>; ///< Load the NVENC driver module.
|
||||
|
||||
/**
|
||||
* @brief Resolve an export from the loaded NVENC driver module.
|
||||
*/
|
||||
using get_symbol_fn = std::function<FARPROC(HMODULE, const char *)>;
|
||||
|
||||
load_driver_fn load_driver; ///< Function that loads the NVENC driver module.
|
||||
get_symbol_fn get_symbol; ///< Resolve a driver export.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Factory bound to the newest NVENC SDK supported by the installed driver.
|
||||
*/
|
||||
class nvenc_dynamic_factory {
|
||||
public:
|
||||
/**
|
||||
* @brief SDK-specific encoder constructor.
|
||||
*/
|
||||
using create_encoder_fn =
|
||||
std::function<std::unique_ptr<nvenc_d3d11_interface>(ID3D11Device *, shared_dll)>;
|
||||
|
||||
/**
|
||||
* @brief Construct a factory bound to one NVENC SDK implementation.
|
||||
*
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @param sdk_version Selected SDK version.
|
||||
* @param create_native Native Direct3D11 encoder constructor.
|
||||
* @param create_on_cuda CUDA-interoperability encoder constructor.
|
||||
*/
|
||||
nvenc_dynamic_factory(
|
||||
shared_dll dll,
|
||||
nvenc_sdk_version sdk_version,
|
||||
create_encoder_fn create_native,
|
||||
create_encoder_fn create_on_cuda
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Load the NVENC driver and select a compatible SDK implementation.
|
||||
*
|
||||
* @return Initialized factory, or an empty pointer when NVENC is unavailable.
|
||||
*/
|
||||
static std::shared_ptr<nvenc_dynamic_factory> get();
|
||||
|
||||
/**
|
||||
* @brief Select a compatible SDK implementation using the supplied runtime operations.
|
||||
*
|
||||
* @param runtime_api Runtime operations used to load and query the NVENC driver.
|
||||
* @return Initialized factory, or an empty pointer when NVENC is unavailable.
|
||||
*/
|
||||
static std::shared_ptr<nvenc_dynamic_factory> get(const nvenc_runtime_api &runtime_api);
|
||||
|
||||
/**
|
||||
* @brief Create a native Direct3D11 NVENC encoder.
|
||||
*
|
||||
* @param d3d_device Direct3D11 device used for encoding.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_native(ID3D11Device *d3d_device) const;
|
||||
|
||||
/**
|
||||
* @brief Create a CUDA NVENC encoder with Direct3D11 input surfaces.
|
||||
*
|
||||
* @param d3d_device Direct3D11 device used to create input textures.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_on_cuda(ID3D11Device *d3d_device) const;
|
||||
|
||||
/**
|
||||
* @brief Get the SDK implementation selected for this factory.
|
||||
*
|
||||
* @return Selected NVENC SDK version.
|
||||
*/
|
||||
nvenc_sdk_version sdk_version() const;
|
||||
|
||||
private:
|
||||
shared_dll dll;
|
||||
nvenc_sdk_version selected_sdk_version;
|
||||
create_encoder_fn create_native;
|
||||
create_encoder_fn create_on_cuda;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_dynamic_factory_impl.cpp
|
||||
* @brief SDK-specific constructors used by the runtime NVENC factory.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef NVENC_NAMESPACE
|
||||
#error NVENC_NAMESPACE must identify the version-specific implementation namespace
|
||||
#endif
|
||||
|
||||
#ifndef NVENC_FACTORY_SUFFIX
|
||||
#error NVENC_FACTORY_SUFFIX must identify the version-specific constructor suffix
|
||||
#endif
|
||||
|
||||
// standard includes
|
||||
#include <utility>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_d3d11_native.h"
|
||||
#include "nvenc_d3d11_on_cuda.h"
|
||||
#include "nvenc_dynamic_factory_versions.h"
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define NVENC_CONCAT_IMPL(left, right) left##right
|
||||
#define NVENC_CONCAT(left, right) NVENC_CONCAT_IMPL(left, right)
|
||||
|
||||
namespace nvenc::detail {
|
||||
|
||||
std::unique_ptr<nvenc_d3d11_interface> NVENC_CONCAT(create_nvenc_d3d11_native_, NVENC_FACTORY_SUFFIX)(
|
||||
ID3D11Device *device,
|
||||
shared_dll dll
|
||||
) {
|
||||
return std::make_unique<NVENC_NAMESPACE::nvenc_d3d11_native>(device, std::move(dll));
|
||||
}
|
||||
|
||||
std::unique_ptr<nvenc_d3d11_interface> NVENC_CONCAT(create_nvenc_d3d11_on_cuda_, NVENC_FACTORY_SUFFIX)(
|
||||
ID3D11Device *device,
|
||||
shared_dll dll
|
||||
) {
|
||||
return std::make_unique<NVENC_NAMESPACE::nvenc_d3d11_on_cuda>(device, std::move(dll));
|
||||
}
|
||||
|
||||
} // namespace nvenc::detail
|
||||
|
||||
#undef NVENC_CONCAT
|
||||
#undef NVENC_CONCAT_IMPL
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_dynamic_factory_versions.h
|
||||
* @brief Declarations for SDK-specific NVENC encoder constructors.
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef _WIN32
|
||||
|
||||
// standard includes
|
||||
#include <memory>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_d3d11_interface.h"
|
||||
#include "nvenc_shared_dll.h"
|
||||
|
||||
namespace nvenc::detail {
|
||||
|
||||
/**
|
||||
* @brief Create an SDK 11.0 native Direct3D11 encoder.
|
||||
*
|
||||
* @param device Direct3D11 device used for encoding.
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_native_1100(ID3D11Device *device, shared_dll dll);
|
||||
/**
|
||||
* @brief Create an SDK 11.0 CUDA-interoperability encoder.
|
||||
*
|
||||
* @param device Direct3D11 device used for input surfaces.
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_on_cuda_1100(ID3D11Device *device, shared_dll dll);
|
||||
/**
|
||||
* @brief Create an SDK 12.0 native Direct3D11 encoder.
|
||||
*
|
||||
* @param device Direct3D11 device used for encoding.
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_native_1200(ID3D11Device *device, shared_dll dll);
|
||||
/**
|
||||
* @brief Create an SDK 12.0 CUDA-interoperability encoder.
|
||||
*
|
||||
* @param device Direct3D11 device used for input surfaces.
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_on_cuda_1200(ID3D11Device *device, shared_dll dll);
|
||||
/**
|
||||
* @brief Create an SDK 13.0 native Direct3D11 encoder.
|
||||
*
|
||||
* @param device Direct3D11 device used for encoding.
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_native_1300(ID3D11Device *device, shared_dll dll);
|
||||
/**
|
||||
* @brief Create an SDK 13.0 CUDA-interoperability encoder.
|
||||
*
|
||||
* @param device Direct3D11 device used for input surfaces.
|
||||
* @param dll Shared NVENC driver module.
|
||||
* @return SDK-neutral encoder instance.
|
||||
*/
|
||||
std::unique_ptr<nvenc_d3d11_interface> create_nvenc_d3d11_on_cuda_1300(ID3D11Device *device, shared_dll dll);
|
||||
|
||||
} // namespace nvenc::detail
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_encoder.h
|
||||
* @brief Declarations for the SDK-neutral NVENC encoder interface.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <cstdint>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_config.h"
|
||||
#include "nvenc_encoded_frame.h"
|
||||
|
||||
namespace platf {
|
||||
enum class pix_fmt_e;
|
||||
}
|
||||
|
||||
namespace video {
|
||||
struct config_t;
|
||||
struct sunshine_colorspace_t;
|
||||
} // namespace video
|
||||
|
||||
namespace nvenc {
|
||||
|
||||
/**
|
||||
* @brief SDK-neutral standalone NVENC encoder interface.
|
||||
*/
|
||||
class nvenc_encoder {
|
||||
public:
|
||||
/**
|
||||
* @brief Destroy the SDK-neutral encoder interface.
|
||||
*/
|
||||
virtual ~nvenc_encoder() = default;
|
||||
|
||||
/**
|
||||
* @brief Create the encoder.
|
||||
*
|
||||
* @param config NVENC encoder configuration.
|
||||
* @param client_config Stream configuration requested by the client.
|
||||
* @param colorspace Sunshine colorspace metadata.
|
||||
* @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.
|
||||
*/
|
||||
virtual void destroy_encoder() = 0;
|
||||
|
||||
/**
|
||||
* @brief Encode the next frame using the platform-specific input surface.
|
||||
*
|
||||
* @param frame_index Frame index that uniquely identifies the frame.
|
||||
* @param force_idr Whether to encode the frame as a forced IDR.
|
||||
* @return Encoded frame.
|
||||
*/
|
||||
virtual nvenc_encoded_frame encode_frame(std::uint64_t frame_index, bool force_idr) = 0;
|
||||
|
||||
/**
|
||||
* @brief Invalidate reference frames in the requested range.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
virtual bool invalidate_ref_frames(std::uint64_t first_frame, std::uint64_t last_frame) = 0;
|
||||
};
|
||||
|
||||
} // namespace nvenc
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_sdk.h
|
||||
* @brief Imports one NVENC SDK into its version-specific implementation namespace.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef DOXYGEN
|
||||
/**
|
||||
* @def NVENC_NAMESPACE
|
||||
* @brief Namespace used for the documented NVENC implementation.
|
||||
*/
|
||||
#define NVENC_NAMESPACE nvenc
|
||||
|
||||
/**
|
||||
* @def NVENC_SDK_VERSION
|
||||
* @brief NVENC SDK version used while generating documentation.
|
||||
*/
|
||||
// Doxygen must select the same preprocessor interface used by versioned build targets.
|
||||
#define NVENC_SDK_VERSION 1300 // NOSONAR(cpp:S5028)
|
||||
#endif
|
||||
|
||||
#ifndef NVENC_NAMESPACE
|
||||
#error NVENC_NAMESPACE must identify the version-specific implementation namespace
|
||||
#endif
|
||||
|
||||
#ifndef NVENC_SDK_VERSION
|
||||
#error NVENC_SDK_VERSION must identify the version-specific NVENC SDK
|
||||
#endif
|
||||
|
||||
// Include common platform and integer declarations outside the SDK namespace.
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
// The version-specific namespace is populated by the SDK headers included below.
|
||||
namespace NVENC_NAMESPACE { // NOSONAR(cpp:S3261)
|
||||
#ifndef DOXYGEN
|
||||
#include <ffnvcodec/dynlink_cuda.h>
|
||||
#include <ffnvcodec/nvEncodeAPI.h>
|
||||
|
||||
#if NVENCAPI_MAJOR_VERSION * 100 + NVENCAPI_MINOR_VERSION != NVENC_SDK_VERSION
|
||||
#error NVENC_SDK_VERSION does not match the selected nv-codec-headers package
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NVENC_SDK_VERSION < 1200
|
||||
/**
|
||||
* @brief Video format values added as named types after SDK 11.0.
|
||||
*/
|
||||
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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Color-primary values added as named types after SDK 11.0.
|
||||
*/
|
||||
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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Transfer-characteristic values added as named types after SDK 11.0.
|
||||
*/
|
||||
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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Matrix-coefficient values added as named types after SDK 11.0.
|
||||
*/
|
||||
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,
|
||||
};
|
||||
#endif
|
||||
} // namespace NVENC_NAMESPACE
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_shared_dll.h
|
||||
* @brief Windows module lifetime helpers shared by NVENC factories and encoders.
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef _WIN32
|
||||
|
||||
// standard includes
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
// platform includes
|
||||
#include <Windows.h>
|
||||
|
||||
namespace nvenc {
|
||||
|
||||
/**
|
||||
* @brief Shared ownership wrapper for a loaded Windows module.
|
||||
*/
|
||||
using shared_dll = std::shared_ptr<std::remove_pointer_t<HMODULE>>;
|
||||
|
||||
/**
|
||||
* @brief Release a Windows module when its final shared owner is destroyed.
|
||||
*/
|
||||
struct shared_dll_deleter {
|
||||
/**
|
||||
* @brief Release the module.
|
||||
*
|
||||
* @param dll Module handle to release.
|
||||
*/
|
||||
void operator()(HMODULE dll) const {
|
||||
if (dll) {
|
||||
FreeLibrary(dll);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wrap a Windows module handle in shared ownership.
|
||||
*
|
||||
* @param dll Module handle returned by `LoadLibraryEx()`.
|
||||
* @return Shared module handle.
|
||||
*/
|
||||
inline shared_dll make_shared_dll(HMODULE dll) {
|
||||
return shared_dll(dll, shared_dll_deleter {});
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@
|
||||
// local includes
|
||||
#include "nvenc_utils.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
@@ -102,4 +102,4 @@ namespace nvenc {
|
||||
return colorspace;
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
#include <dxgiformat.h>
|
||||
#endif
|
||||
|
||||
// lib includes
|
||||
#include <ffnvcodec/nvEncodeAPI.h>
|
||||
|
||||
// local includes
|
||||
#include "nvenc_colorspace.h"
|
||||
#include "nvenc_sdk.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/video_colorspace.h"
|
||||
|
||||
namespace nvenc {
|
||||
namespace NVENC_NAMESPACE {
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
@@ -45,4 +43,4 @@ namespace nvenc {
|
||||
*/
|
||||
nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace);
|
||||
|
||||
} // namespace nvenc
|
||||
} // namespace NVENC_NAMESPACE
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file src/nvenc/nvenc_version.h
|
||||
* @brief NVENC SDK version selection helpers.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace nvenc {
|
||||
|
||||
/**
|
||||
* @brief NVENC SDK implementations compiled into Sunshine.
|
||||
*/
|
||||
enum class nvenc_sdk_version : std::uint32_t {
|
||||
unsupported = 0U, ///< No compatible SDK implementation is available.
|
||||
sdk_11_0 = 1100U, ///< Video Codec SDK 11.0.
|
||||
sdk_12_0 = 1200U, ///< Video Codec SDK 12.0.
|
||||
sdk_13_0 = 1300U, ///< Video Codec SDK 13.0.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert the packed driver API version to a comparable integer.
|
||||
*
|
||||
* @param version Version returned by `NvEncodeAPIGetMaxSupportedVersion()`.
|
||||
* @return Version encoded as `major * 100 + minor`.
|
||||
*/
|
||||
constexpr std::uint32_t decode_nvenc_driver_version(std::uint32_t version) {
|
||||
return (version >> 4U) * 100U + (version & 0x0FU);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select the newest compiled SDK supported by the installed driver.
|
||||
*
|
||||
* @param max_version Maximum driver API version encoded as `major * 100 + minor`.
|
||||
* @return Selected SDK implementation, or `unsupported` when the driver is too old.
|
||||
*/
|
||||
constexpr nvenc_sdk_version select_nvenc_sdk_version(std::uint32_t max_version) {
|
||||
using enum nvenc_sdk_version;
|
||||
if (max_version >= std::to_underlying(sdk_13_0)) {
|
||||
return sdk_13_0;
|
||||
}
|
||||
if (max_version >= std::to_underlying(sdk_12_0)) {
|
||||
return sdk_12_0;
|
||||
}
|
||||
if (max_version >= std::to_underlying(sdk_11_0)) {
|
||||
return sdk_11_0;
|
||||
}
|
||||
return unsupported;
|
||||
}
|
||||
|
||||
} // namespace nvenc
|
||||
@@ -69,7 +69,7 @@ namespace video {
|
||||
} // namespace video
|
||||
|
||||
namespace nvenc {
|
||||
class nvenc_base;
|
||||
class nvenc_encoder;
|
||||
}
|
||||
|
||||
namespace platf {
|
||||
@@ -638,7 +638,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 encoder instance owned by the encode device.
|
||||
nvenc::nvenc_encoder *nvenc = nullptr; ///< NVENC encoder instance owned by the encode device.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,9 +24,7 @@ extern "C" {
|
||||
#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/nvenc_dynamic_factory.h"
|
||||
#include "src/video.h"
|
||||
#include "utf_utils.h"
|
||||
|
||||
@@ -1240,21 +1238,25 @@ namespace platf::dxgi {
|
||||
* @return True when the D3D11 device resources are initialized.
|
||||
*/
|
||||
bool init_device(std::shared_ptr<platf::display_t> 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;
|
||||
}
|
||||
|
||||
if (pix_fmt == pix_fmt_e::yuv444p16) {
|
||||
nvenc_d3d = std::make_unique<nvenc::nvenc_d3d11_on_cuda>(base.device.get());
|
||||
} else {
|
||||
nvenc_d3d = std::make_unique<nvenc::nvenc_d3d11_native>(base.device.get());
|
||||
auto factory = nvenc::nvenc_dynamic_factory::get();
|
||||
if (!factory) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pix_fmt == pix_fmt_e::yuv444p16) {
|
||||
nvenc_d3d = factory->create_nvenc_d3d11_on_cuda(base.device.get());
|
||||
} else {
|
||||
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;
|
||||
@@ -1272,8 +1274,7 @@ namespace platf::dxgi {
|
||||
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)) {
|
||||
if (!nvenc_d3d->create_encoder(config::video.nv, client_config, colorspace, buffer_format)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1293,8 +1294,8 @@ namespace platf::dxgi {
|
||||
|
||||
private:
|
||||
d3d_base_encode_device base;
|
||||
std::unique_ptr<nvenc::nvenc_d3d11> nvenc_d3d;
|
||||
NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED;
|
||||
std::unique_ptr<nvenc::nvenc_d3d11_interface> nvenc_d3d;
|
||||
platf::pix_fmt_e buffer_format = platf::pix_fmt_e::unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -18,6 +18,9 @@ extern "C" {
|
||||
#include <libavutil/mastering_display_metadata.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <ffnvcodec/nvEncodeAPI.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
// local includes
|
||||
@@ -27,7 +30,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"
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* @file tests/unit/test_nvenc_dynamic_factory.cpp
|
||||
* @brief Tests for the Windows runtime NVENC SDK factory.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
// standard includes
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
// lib includes
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// local includes
|
||||
#include "src/nvenc/nvenc_dynamic_factory.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::uint32_t reported_version; ///< Version returned by the fake NVENC driver.
|
||||
std::uint32_t reported_status; ///< Status returned by the fake NVENC driver.
|
||||
|
||||
/**
|
||||
* @brief Fake implementation of `NvEncodeAPIGetMaxSupportedVersion()`.
|
||||
*
|
||||
* @param version Receives the configured packed API version.
|
||||
* @return Configured NVENC status code.
|
||||
*/
|
||||
std::uint32_t WINAPI get_fake_max_supported_version(std::uint32_t *version) {
|
||||
*version = reported_version;
|
||||
return reported_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Minimal SDK-neutral encoder used to verify factory callbacks.
|
||||
*/
|
||||
class fake_nvenc_encoder final: public nvenc::nvenc_d3d11_interface {
|
||||
public:
|
||||
bool create_encoder(
|
||||
const nvenc::nvenc_config &,
|
||||
const video::config_t &,
|
||||
const video::sunshine_colorspace_t &,
|
||||
platf::pix_fmt_e
|
||||
) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroy_encoder() override {
|
||||
}
|
||||
|
||||
nvenc::nvenc_encoded_frame encode_frame(std::uint64_t frame_index, bool force_idr) override {
|
||||
return {{}, frame_index, force_idr, false};
|
||||
}
|
||||
|
||||
bool invalidate_ref_frames(std::uint64_t, std::uint64_t) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
ID3D11Texture2D *get_input_texture() override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Make a non-owning fake Windows module handle for factory discovery tests.
|
||||
*
|
||||
* @return Shared fake module handle.
|
||||
*/
|
||||
nvenc::shared_dll make_fake_dll() {
|
||||
constexpr std::uintptr_t fake_handle_value = 1U;
|
||||
return {
|
||||
reinterpret_cast<HMODULE>(fake_handle_value),
|
||||
[](HMODULE) {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make runtime operations that expose the fake version query function.
|
||||
*
|
||||
* @return Runtime operations for a successfully loaded fake NVENC driver.
|
||||
*/
|
||||
nvenc::nvenc_runtime_api make_fake_runtime_api() {
|
||||
return {
|
||||
[]() {
|
||||
return make_fake_dll();
|
||||
},
|
||||
[](HMODULE dll, const char *symbol) {
|
||||
EXPECT_EQ(dll, make_fake_dll().get());
|
||||
EXPECT_EQ(std::string_view {symbol}, "NvEncodeAPIGetMaxSupportedVersion");
|
||||
return std::bit_cast<FARPROC>(&get_fake_max_supported_version);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, HandlesUnavailableDriver) {
|
||||
bool resolved_symbol = false;
|
||||
const nvenc::nvenc_runtime_api runtime_api {
|
||||
[]() {
|
||||
return nvenc::shared_dll {};
|
||||
},
|
||||
[&resolved_symbol](HMODULE, const char *) {
|
||||
resolved_symbol = true;
|
||||
return FARPROC {};
|
||||
},
|
||||
};
|
||||
|
||||
EXPECT_FALSE(nvenc::nvenc_dynamic_factory::get(runtime_api));
|
||||
EXPECT_FALSE(resolved_symbol);
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, HandlesMissingVersionQuery) {
|
||||
const nvenc::nvenc_runtime_api runtime_api {
|
||||
[]() {
|
||||
return make_fake_dll();
|
||||
},
|
||||
[](HMODULE, const char *) {
|
||||
return FARPROC {};
|
||||
},
|
||||
};
|
||||
|
||||
EXPECT_FALSE(nvenc::nvenc_dynamic_factory::get(runtime_api));
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, HandlesFailedVersionQuery) {
|
||||
reported_version = 1300U;
|
||||
reported_status = 1U;
|
||||
|
||||
EXPECT_FALSE(nvenc::nvenc_dynamic_factory::get(make_fake_runtime_api()));
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, RejectsUnsupportedDriver) {
|
||||
reported_version = 10U << 4U;
|
||||
reported_status = 0U;
|
||||
|
||||
EXPECT_FALSE(nvenc::nvenc_dynamic_factory::get(make_fake_runtime_api()));
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, SelectsSupportedSdkImplementations) {
|
||||
using enum nvenc::nvenc_sdk_version;
|
||||
constexpr std::array test_cases {
|
||||
std::pair {11U << 4U, sdk_11_0},
|
||||
std::pair {12U << 4U, sdk_12_0},
|
||||
std::pair {13U << 4U, sdk_13_0},
|
||||
std::pair {14U << 4U, sdk_13_0},
|
||||
};
|
||||
reported_status = 0U;
|
||||
|
||||
for (const auto &[version, expected] : test_cases) {
|
||||
reported_version = version;
|
||||
const auto factory = nvenc::nvenc_dynamic_factory::get(make_fake_runtime_api());
|
||||
ASSERT_TRUE(factory);
|
||||
EXPECT_EQ(factory->sdk_version(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, UsesConfiguredEncoderConstructors) {
|
||||
bool created_native = false;
|
||||
bool created_on_cuda = false;
|
||||
const auto dll = make_fake_dll();
|
||||
nvenc::nvenc_dynamic_factory factory {
|
||||
dll,
|
||||
nvenc::nvenc_sdk_version::sdk_13_0,
|
||||
[&created_native, &dll](ID3D11Device *, nvenc::shared_dll callback_dll) {
|
||||
created_native = true;
|
||||
EXPECT_EQ(callback_dll, dll);
|
||||
return std::make_unique<fake_nvenc_encoder>();
|
||||
},
|
||||
[&created_on_cuda, &dll](ID3D11Device *, nvenc::shared_dll callback_dll) {
|
||||
created_on_cuda = true;
|
||||
EXPECT_EQ(callback_dll, dll);
|
||||
return std::make_unique<fake_nvenc_encoder>();
|
||||
},
|
||||
};
|
||||
|
||||
EXPECT_EQ(factory.sdk_version(), nvenc::nvenc_sdk_version::sdk_13_0);
|
||||
EXPECT_TRUE(factory.create_nvenc_d3d11_native(nullptr));
|
||||
EXPECT_TRUE(factory.create_nvenc_d3d11_on_cuda(nullptr));
|
||||
EXPECT_TRUE(created_native);
|
||||
EXPECT_TRUE(created_on_cuda);
|
||||
}
|
||||
|
||||
TEST(NvencDynamicFactoryTest, UsesDefaultWindowsRuntime) {
|
||||
const auto factory = nvenc::nvenc_dynamic_factory::get();
|
||||
EXPECT_TRUE(!factory || factory->sdk_version() != nvenc::nvenc_sdk_version::unsupported);
|
||||
}
|
||||
|
||||
TEST(NvencSharedDllTest, OwnsLoadedModuleUntilLastReference) {
|
||||
const auto handle = LoadLibraryEx("version.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
|
||||
auto dll = nvenc::make_shared_dll(handle);
|
||||
EXPECT_EQ(dll.get(), handle);
|
||||
auto copy = dll;
|
||||
EXPECT_EQ(copy.use_count(), 2);
|
||||
|
||||
dll.reset();
|
||||
EXPECT_EQ(copy.use_count(), 1);
|
||||
copy.reset();
|
||||
}
|
||||
|
||||
TEST(NvencSharedDllTest, AcceptsNullModule) {
|
||||
auto dll = nvenc::make_shared_dll(nullptr);
|
||||
EXPECT_FALSE(dll);
|
||||
dll.reset();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file tests/unit/test_nvenc_version.cpp
|
||||
* @brief Tests for runtime NVENC SDK version selection.
|
||||
*/
|
||||
|
||||
// standard includes
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
// lib includes
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// local includes
|
||||
#include "src/nvenc/nvenc_version.h"
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief Expected SDK selection for a driver API version.
|
||||
*/
|
||||
struct nvenc_version_test_case {
|
||||
std::uint32_t max_version; ///< Maximum API version reported by the driver.
|
||||
nvenc::nvenc_sdk_version expected; ///< SDK implementation Sunshine should select.
|
||||
};
|
||||
|
||||
TEST(NvencVersionTest, DecodesPackedDriverVersion) {
|
||||
EXPECT_EQ(nvenc::decode_nvenc_driver_version((11U << 4U) | 0U), 1100U);
|
||||
EXPECT_EQ(nvenc::decode_nvenc_driver_version((13U << 4U) | 1U), 1301U);
|
||||
}
|
||||
|
||||
TEST(NvencVersionTest, SelectsNewestCompatibleSdk) {
|
||||
using enum nvenc::nvenc_sdk_version;
|
||||
constexpr std::array test_cases {
|
||||
nvenc_version_test_case {1000U, unsupported},
|
||||
nvenc_version_test_case {1099U, unsupported},
|
||||
nvenc_version_test_case {1100U, sdk_11_0},
|
||||
nvenc_version_test_case {1101U, sdk_11_0},
|
||||
nvenc_version_test_case {1199U, sdk_11_0},
|
||||
nvenc_version_test_case {1200U, sdk_12_0},
|
||||
nvenc_version_test_case {1201U, sdk_12_0},
|
||||
nvenc_version_test_case {1299U, sdk_12_0},
|
||||
nvenc_version_test_case {1300U, sdk_13_0},
|
||||
nvenc_version_test_case {1301U, sdk_13_0},
|
||||
nvenc_version_test_case {1400U, sdk_13_0},
|
||||
};
|
||||
|
||||
for (const auto &[max_version, expected] : test_cases) {
|
||||
EXPECT_EQ(nvenc::select_nvenc_sdk_version(max_version), expected);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user