实现自动根据显卡类型设置NvidiaNativePresent,但需要重启应用程序

This commit is contained in:
daiqingshuang
2025-12-23 11:49:28 +08:00
parent ebc872ed30
commit 18e7ef0113
12 changed files with 341 additions and 26 deletions

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "nvapi/sdk"]
path = nvapi/sdk
url = https://github.com/NVIDIA/nvapi.git
[submodule "nvapi/sdk/nvapi"]
path = nvapi/sdk/nvapi
url = https://github.com/NVIDIA/nvapi.git

View File

@@ -63,6 +63,7 @@ else ()
add_definitions(-DDEBUG=0)
endif ()
add_subdirectory(nvapi)
add_subdirectory(src)
# 构建示例

View File

@@ -37,8 +37,8 @@ int main(int argc, char* argv[]) {
// 获取上下文提供者来访问资源管理器
auto* ctx = app.get_context_provider();
auto texture_id = ctx->get_texture_manager()->load_texture("D:\\G2uY1fJa8AAOucs.jpg").value();
// const auto texture_id = ctx->get_texture_manager()->load_texture("D:\\screenshot-20251128-165627.png").value();
// auto texture_id = ctx->get_texture_manager()->load_texture("D:\\G2uY1fJa8AAOucs.jpg").value();
const auto texture_id = ctx->get_texture_manager()->load_texture("D:\\screenshot-20251128-165627.png").value();
const auto text_size_i = ctx->get_texture_manager()->get_texture(texture_id)->size();
const auto tex_size = text_size_i.cast<float>();

4
nvapi/CMakeLists.txt Normal file
View File

@@ -0,0 +1,4 @@
if (WIN32)
add_subdirectory(api)
add_subdirectory(sdk)
endif()

8
nvapi/api/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
project(nvapi_api)
simple_library(SHARED)
# 设置导出宏定义
target_compile_definitions(${PROJECT_NAME} PRIVATE NVAPI_API_EXPORTS)
target_link_libraries(${PROJECT_NAME} PUBLIC nvapi_sdk)

49
nvapi/api/export.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "export.h"
#include <windows.h>
#include "nvapi.h"
#include "NvApiDriverSettings.h"
#include <iostream>
extern "C" bool ForceNvidiaNativePresent() {
// 初始化NvAPI
auto status = NvAPI_Initialize();
if (status != NVAPI_OK) {
return false; // 不是N卡或初始化失败
}
NvDRSSessionHandle hSession = 0;
status = NvAPI_DRS_CreateSession(&hSession);
if (status != NVAPI_OK) {
return false;
}
status = NvAPI_DRS_LoadSettings(hSession);
if (status != NVAPI_OK) {
NvAPI_DRS_DestroySession(hSession);
return false;
}
NvDRSProfileHandle hProfile = 0;
// 获取当前应用程序的配置(如果没有会自动创建默认配置)
status = NvAPI_DRS_GetBaseProfile(hSession, &hProfile);
if (status != NVAPI_OK) {
NvAPI_DRS_DestroySession(hSession);
return false;
}
NVDRS_SETTING setting = {0};
setting.version = NVDRS_SETTING_VER;
setting.settingId = OGL_CPL_PREFER_DXPRESENT_ID;
setting.u32CurrentValue = OGL_CPL_PREFER_DXPRESENT_PREFER_DISABLED; // 强制设为 Native
// 应用设置
status = NvAPI_DRS_SetSetting(hSession, hProfile, &setting);
if (status == NVAPI_OK) {
NvAPI_DRS_SaveSettings(hSession);
NvAPI_DRS_DestroySession(hSession);
return true;
}
NvAPI_DRS_DestroySession(hSession);
return false;
}

25
nvapi/api/export.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#ifdef NVAPI_API_EXPORTS
#define NVAPI_API __declspec(dllexport)
#else
#define NVAPI_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* 强制NVIDIA驱动使用Native模式进行Vulkan/OpenGL呈现
*
* 这个函数通过NvAPI Driver Settings API设置"Vulkan/OpenGL present method"为Native模式
* 避免驱动自动升级到DXGI Flip Model从而保持透明度效果。
*
* @return true 如果成功设置false 如果失败非N卡、初始化失败等
*/
NVAPI_API bool ForceNvidiaNativePresent();
#ifdef __cplusplus
}
#endif

10
nvapi/sdk/CMakeLists.txt Normal file
View File

@@ -0,0 +1,10 @@
project(nvapi_sdk)
set(NVAPI_SOURCES "")
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/nvapi NVAPI_SOURCES)
add_library(${PROJECT_NAME} INTERFACE ${NVAPI_SOURCES})
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/nvapi)
target_link_libraries(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/nvapi/amd64/nvapi64.lib)

1
nvapi/sdk/nvapi Submodule

Submodule nvapi/sdk/nvapi added at 832a3673d6

View File

@@ -0,0 +1,101 @@
#include "gpu_vendor_detector.h"
#include "render/vulkan/vulkan_common.h"
#include <iostream>
#include <vector>
namespace mirage {
// ============================================================================
// 全局函数实现
// ============================================================================
const char* get_gpu_vendor_name(gpu_vendor vendor) noexcept {
switch (vendor) {
case gpu_vendor::nvidia: return "NVIDIA";
case gpu_vendor::amd: return "AMD";
case gpu_vendor::intel: return "Intel";
default: return "Unknown";
}
}
// ============================================================================
// nvapi_loader 实现
// ============================================================================
nvapi_loader::nvapi_loader() = default;
nvapi_loader::~nvapi_loader() = default;
bool nvapi_loader::apply_nvidia_native_present() {
return load_and_call_function();
}
bool nvapi_loader::load_and_call_function() {
// 创建临时lib_handle
auto lib_h = std::make_unique<lib_handle>();
// 加载nvapi_api动态库
#ifdef _WIN32
std::string lib_name = "nvapi_api.dll";
#else
std::string lib_name = "libnvapi_api.so";
#endif
if (!lib_h->open(lib_name)) {
std::cerr << "[nvapi_loader] Failed to load " << lib_name << std::endl;
return false;
}
// 获取函数指针
auto force_nvidia_native_present_func = lib_h->get_function_by_name<bool()>("ForceNvidiaNativePresent");
if (!force_nvidia_native_present_func) {
std::cerr << "[nvapi_loader] Failed to get ForceNvidiaNativePresent function" << std::endl;
lib_h->close();
return false;
}
// 调用函数
bool result = force_nvidia_native_present_func();
// 立即释放库
lib_h->close();
if (result) {
std::cout << "[nvapi_loader] Successfully forced NVIDIA Native present mode" << std::endl;
} else {
std::cerr << "[nvapi_loader] Failed to force NVIDIA Native present mode" << std::endl;
}
return result;
}
// ============================================================================
// gpu_vendor_detector 实现
// ============================================================================
gpu_vendor_detector::gpu_vendor_detector() = default;
gpu_vendor gpu_vendor_detector::detect_primary_gpu_vendor() const {
// 这里需要Vulkan实例来枚举物理设备
// 由于我们在vulkan_context_provider初始化之前调用我们需要一个临时的Vulkan实例
// 或者我们可以延迟到vulkan_context_provider初始化后再检测
// 暂时返回unknown实际检测将在vulkan_context_provider中进行
return gpu_vendor::unknown;
}
bool gpu_vendor_detector::detect_and_load_nvapi() {
// 这个方法将在vulkan_context_provider中选择物理设备后调用
// 直接调用apply_nvidia_native_present会自动加载和释放
if (!nvapi_loader_.apply_nvidia_native_present()) {
std::cout << "[gpu_vendor_detector] NvAPI library not available or failed to apply settings" << std::endl;
return false;
}
return true;
}
} // namespace mirage

View File

@@ -0,0 +1,72 @@
#pragma once
/// @file gpu_vendor_detector.h
/// @brief GPU厂商检测和NvAPI动态库加载
#include <string>
#include <memory>
#include <functional>
#include "common/lib_handle.h"
namespace mirage {
/// @brief GPU厂商枚举
enum class gpu_vendor {
unknown = 0,
nvidia = 0x10DE, // NVIDIA厂商ID
amd = 0x1002, // AMD厂商ID
intel = 0x8086 // Intel厂商ID
};
/// @brief 获取GPU厂商名称
[[nodiscard]] const char* get_gpu_vendor_name(gpu_vendor vendor) noexcept;
/// @brief NvAPI动态库加载器
class nvapi_loader {
public:
nvapi_loader();
~nvapi_loader();
// 禁止拷贝和移动
nvapi_loader(const nvapi_loader&) = delete;
nvapi_loader& operator=(const nvapi_loader&) = delete;
nvapi_loader(nvapi_loader&&) = delete;
nvapi_loader& operator=(nvapi_loader&&) = delete;
/// @brief 加载nvapi_api动态库并调用ForceNvidiaNativePresent然后立即释放
/// @return 是否成功设置
[[nodiscard]] bool apply_nvidia_native_present();
private:
[[nodiscard]] bool load_and_call_function();
};
/// @brief GPU厂商检测器
class gpu_vendor_detector {
public:
gpu_vendor_detector();
~gpu_vendor_detector() = default;
// 禁止拷贝和移动
gpu_vendor_detector(const gpu_vendor_detector&) = delete;
gpu_vendor_detector& operator=(const gpu_vendor_detector&) = delete;
gpu_vendor_detector(gpu_vendor_detector&&) = delete;
gpu_vendor_detector& operator=(gpu_vendor_detector&&) = delete;
/// @brief 检测主要GPU厂商
/// @return GPU厂商枚举
[[nodiscard]] gpu_vendor detect_primary_gpu_vendor() const;
/// @brief 获取NvAPI加载器
/// @return NvAPI加载器引用
[[nodiscard]] nvapi_loader& get_nvapi_loader() { return nvapi_loader_; }
/// @brief 检测并加载NvAPI如果是NVIDIA GPU
/// @return 是否成功检测并加载
[[nodiscard]] bool detect_and_load_nvapi();
private:
nvapi_loader nvapi_loader_;
};
} // namespace mirage

View File

@@ -13,6 +13,7 @@
#include "threading/thread_coordinator.h"
#include "ui/window/desktop/glfw_window.h"
#include "ui/window/desktop/glfw_utils.h"
#include "core/gpu_vendor_detector.h"
#include <iostream>
@@ -24,12 +25,12 @@ namespace mirage {
pool_config.enable_work_stealing = true;
pool_config.enable_statistics = true;
pool_config.thread_name_prefix = "mirage_worker";
thread_pool_ = std::make_unique<thread_pool>(pool_config);
thread_pool_->start();
std::cout << "[vulkan_context_provider] Thread pool started with "
<< thread_pool_->thread_count() << " workers" << std::endl;
<< thread_pool_->thread_count() << " workers" << std::endl;
}
vulkan_context_provider::~vulkan_context_provider() {
@@ -54,18 +55,18 @@ namespace mirage {
auto required_extensions = glfw_window::get_required_instance_extensions();
render_context::create_info context_info{
.app_name = config.app_name.c_str(),
.app_version = VK_MAKE_VERSION(1, 0, 0),
.engine_name = "Mirage Engine",
.engine_version = VK_MAKE_VERSION(1, 0, 0),
.enable_validation = config.enable_validation,
.app_name = config.app_name.c_str(),
.app_version = VK_MAKE_VERSION(1, 0, 0),
.engine_name = "Mirage Engine",
.engine_version = VK_MAKE_VERSION(1, 0, 0),
.enable_validation = config.enable_validation,
.required_extensions = required_extensions
};
auto context_result = render_context::create(context_info);
if (!context_result.has_value()) {
std::cerr << "[vulkan_context_provider] Failed to create Vulkan context: "
<< vk::to_string(context_result.error()) << std::endl;
<< vk::to_string(context_result.error()) << std::endl;
return false;
}
@@ -95,16 +96,49 @@ namespace mirage {
auto physical_device_result = dev_mgr.select_primary_device(surface);
if (!physical_device_result.has_value()) {
std::cerr << "[vulkan_context_provider] Failed to select physical device: "
<< vk::to_string(physical_device_result.error()) << std::endl;
<< vk::to_string(physical_device_result.error()) << std::endl;
return false;
}
physical_device_ = physical_device_result.value();
// ========================================================================
// 检测GPU厂商并加载NvAPI如果是NVIDIA GPU
// ========================================================================
auto device_props = physical_device_.getProperties();
uint32_t vendor_id = device_props.vendorID;
std::cout << "[vulkan_context_provider] Selected GPU: "
<< device_props.deviceName << " (Vendor ID: 0x"
<< std::hex << vendor_id << std::dec << ")" << std::endl;
if (vendor_id == static_cast<uint32_t>(gpu_vendor::nvidia)) {
std::cout << "[vulkan_context_provider] NVIDIA GPU detected, applying NvAPI settings..." << std::endl;
// 临时创建GPU厂商检测器
auto temp_gpu_detector = std::make_unique<gpu_vendor_detector>();
// 应用NVIDIA Native present模式设置
if (temp_gpu_detector->get_nvapi_loader().apply_nvidia_native_present()) {
std::cout << "[vulkan_context_provider] Successfully applied NVIDIA Native present mode" << std::endl;
}
else {
std::cerr << "[vulkan_context_provider] Failed to apply NVIDIA Native present mode" << std::endl;
}
// temp_gpu_detector 自动释放,无需手动清理
}
else {
std::cout << "[vulkan_context_provider] Non-NVIDIA GPU detected ("
<< get_gpu_vendor_name(static_cast<gpu_vendor>(vendor_id))
<< "), skipping NvAPI loading" << std::endl;
}
auto logical_device_result = logical_device::create(physical_device_, surface);
if (!logical_device_result.has_value()) {
std::cerr << "[vulkan_context_provider] Failed to create logical device: "
<< vk::to_string(logical_device_result.error()) << std::endl;
<< vk::to_string(logical_device_result.error()) << std::endl;
return false;
}
@@ -134,12 +168,12 @@ namespace mirage {
async_mtsdf_gen_ = std::make_unique<async_mtsdf_generator>(*thread_pool_);
atlas_config atlas_cfg{
.page_size = 2048,
.max_pages = 8,
.channels = 4,
.padding = 1,
.page_size = 2048,
.max_pages = 8,
.channels = 4,
.padding = 1,
.safety_frames = 3,
.on_eviction = nullptr
.on_eviction = nullptr
};
atlas_manager_ = std::make_unique<texture_atlas_manager>(atlas_cfg);
@@ -158,7 +192,7 @@ namespace mirage {
render_pipeline::config pipeline_config{
.frames_in_flight = config_.frames_in_flight,
.adaptive_sync = config_.adaptive_sync
.adaptive_sync = config_.adaptive_sync
};
render_pipeline_ = std::make_unique<render_pipeline>(
@@ -184,7 +218,8 @@ namespace mirage {
coordinator_config coord_config{};
start_thread_coordinator(coord_config);
std::cout << "[vulkan_context_provider] Initialized successfully (pipeline initialized in render thread)" << std::endl;
std::cout << "[vulkan_context_provider] Initialized successfully (pipeline initialized in render thread)" <<
std::endl;
return true;
}
@@ -205,7 +240,8 @@ namespace mirage {
thread_coordinator_->stop();
thread_coordinator_.reset();
std::cout << "[vulkan_context_provider] Thread coordinator stopped" << std::endl;
} else if (render_pipeline_) {
}
else if (render_pipeline_) {
// 如果线程协调器不存在但 render_pipeline 存在,
// 需要手动清理 render_pipeline 的主线程资源
render_pipeline_->cleanup_main_thread_resources();
@@ -223,7 +259,7 @@ namespace mirage {
text_shaper_.reset();
glyph_cache_.reset();
atlas_manager_.reset();
async_mtsdf_gen_.reset(); // 必须在 thread_pool 之前销毁
async_mtsdf_gen_.reset(); // 必须在 thread_pool 之前销毁
font_manager_.reset();
// ========================================================================
@@ -316,7 +352,7 @@ namespace mirage {
// 创建带初始化回调的配置
coordinator_config cfg_with_callback = config;
// 设置渲染线程初始化回调
// 这确保 render_pipeline::initialize() 在渲染线程中执行
cfg_with_callback.render_init_callback = [this]() -> bool {
@@ -326,8 +362,10 @@ namespace mirage {
render_pipeline_->initialize(vk::Format::eB8G8R8A8Srgb);
std::cout << "[vulkan_context_provider] Render pipeline initialized in render thread" << std::endl;
return true;
} catch (const std::exception& e) {
std::cerr << "[vulkan_context_provider] Failed to initialize render pipeline: " << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << "[vulkan_context_provider] Failed to initialize render pipeline: " << e.what() <<
std::endl;
return false;
}
};
@@ -351,7 +389,7 @@ namespace mirage {
auto result = logical_device_->get_handle().waitIdle();
if (result != vk::Result::eSuccess) {
std::cerr << "[vulkan_context_provider] Warning: waitIdle failed: "
<< vk::to_string(result) << std::endl;
<< vk::to_string(result) << std::endl;
}
}
}