mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-08-07 10:20:46 +00:00
fix(nvenc): dynamic nv codec sdk selection (#5451)
This commit is contained in:
@@ -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