/** * @file tests/unit/test_nvenc_dynamic_factory.cpp * @brief Tests for the Windows runtime NVENC SDK factory. */ #ifdef _WIN32 // standard includes #include #include #include #include #include #include // lib includes #include // 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(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(&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(); }, [&created_on_cuda, &dll](ID3D11Device *, nvenc::shared_dll callback_dll) { created_on_cuda = true; EXPECT_EQ(callback_dll, dll); return std::make_unique(); }, }; 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