实现自动根据显卡类型设置NvidiaNativePresent,但需要重启应用程序
This commit is contained in:
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal 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
|
||||
@@ -63,6 +63,7 @@ else ()
|
||||
add_definitions(-DDEBUG=0)
|
||||
endif ()
|
||||
|
||||
add_subdirectory(nvapi)
|
||||
add_subdirectory(src)
|
||||
|
||||
# 构建示例
|
||||
|
||||
@@ -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
4
nvapi/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
if (WIN32)
|
||||
add_subdirectory(api)
|
||||
add_subdirectory(sdk)
|
||||
endif()
|
||||
8
nvapi/api/CMakeLists.txt
Normal file
8
nvapi/api/CMakeLists.txt
Normal 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
49
nvapi/api/export.cpp
Normal 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
25
nvapi/api/export.h
Normal 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
10
nvapi/sdk/CMakeLists.txt
Normal 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
1
nvapi/sdk/nvapi
Submodule
Submodule nvapi/sdk/nvapi added at 832a3673d6
101
src/core/gpu_vendor_detector.cpp
Normal file
101
src/core/gpu_vendor_detector.cpp
Normal 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
|
||||
72
src/core/gpu_vendor_detector.h
Normal file
72
src/core/gpu_vendor_detector.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user