diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9c47737 --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 53a9b67..73d9068 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ else () add_definitions(-DDEBUG=0) endif () +add_subdirectory(nvapi) add_subdirectory(src) # 构建示例 diff --git a/example/test_thread/main.cpp b/example/test_thread/main.cpp index e078470..6ecd7a4 100644 --- a/example/test_thread/main.cpp +++ b/example/test_thread/main.cpp @@ -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(); diff --git a/nvapi/CMakeLists.txt b/nvapi/CMakeLists.txt new file mode 100644 index 0000000..1665506 --- /dev/null +++ b/nvapi/CMakeLists.txt @@ -0,0 +1,4 @@ +if (WIN32) + add_subdirectory(api) + add_subdirectory(sdk) +endif() \ No newline at end of file diff --git a/nvapi/api/CMakeLists.txt b/nvapi/api/CMakeLists.txt new file mode 100644 index 0000000..18b610e --- /dev/null +++ b/nvapi/api/CMakeLists.txt @@ -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) diff --git a/nvapi/api/export.cpp b/nvapi/api/export.cpp new file mode 100644 index 0000000..cc1e673 --- /dev/null +++ b/nvapi/api/export.cpp @@ -0,0 +1,49 @@ +#include "export.h" +#include +#include "nvapi.h" +#include "NvApiDriverSettings.h" +#include + +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; +} diff --git a/nvapi/api/export.h b/nvapi/api/export.h new file mode 100644 index 0000000..2368910 --- /dev/null +++ b/nvapi/api/export.h @@ -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 diff --git a/nvapi/sdk/CMakeLists.txt b/nvapi/sdk/CMakeLists.txt new file mode 100644 index 0000000..f6836ea --- /dev/null +++ b/nvapi/sdk/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/nvapi/sdk/nvapi b/nvapi/sdk/nvapi new file mode 160000 index 0000000..832a367 --- /dev/null +++ b/nvapi/sdk/nvapi @@ -0,0 +1 @@ +Subproject commit 832a3673d66a0fdf6d6e522468821d5cbd925f23 diff --git a/src/core/gpu_vendor_detector.cpp b/src/core/gpu_vendor_detector.cpp new file mode 100644 index 0000000..d9ddb57 --- /dev/null +++ b/src/core/gpu_vendor_detector.cpp @@ -0,0 +1,101 @@ +#include "gpu_vendor_detector.h" +#include "render/vulkan/vulkan_common.h" + +#include +#include + +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(); + + // 加载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("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 \ No newline at end of file diff --git a/src/core/gpu_vendor_detector.h b/src/core/gpu_vendor_detector.h new file mode 100644 index 0000000..855f017 --- /dev/null +++ b/src/core/gpu_vendor_detector.h @@ -0,0 +1,72 @@ +#pragma once + +/// @file gpu_vendor_detector.h +/// @brief GPU厂商检测和NvAPI动态库加载 + +#include +#include +#include +#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 \ No newline at end of file diff --git a/src/core/vulkan_context_provider.cpp b/src/core/vulkan_context_provider.cpp index 88c4e2f..15557ae 100644 --- a/src/core/vulkan_context_provider.cpp +++ b/src/core/vulkan_context_provider.cpp @@ -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 @@ -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(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(gpu_vendor::nvidia)) { + std::cout << "[vulkan_context_provider] NVIDIA GPU detected, applying NvAPI settings..." << std::endl; + + // 临时创建GPU厂商检测器 + auto temp_gpu_detector = std::make_unique(); + + // 应用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(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(*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(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( @@ -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; } } }