diff --git a/overlay_experimental/Renderer_Detector.cpp b/overlay_experimental/Renderer_Detector.cpp index 6aacff5..abaacc2 100644 --- a/overlay_experimental/Renderer_Detector.cpp +++ b/overlay_experimental/Renderer_Detector.cpp @@ -48,6 +48,12 @@ #undef GetModuleHandle #endif + // Minimal timeout for detecting the real rendering API when multiple are used. + #define MIN_RENDER_DETECT_TIMEOUT 1 + // Variance is in milliseconds. 1000 (one second) / 24 (FPS) = Aprox. 41.66 Milliseconds between frames.) + #define MAX_RENDER_DETECT_FRAME_TIMING_VARIANCE 42 + #define GET_RENDERER_DETECT_TIMEOUT ((MAX_RENDERER_API_DETECT_TIMEOUT > MIN_RENDER_DETECT_TIMEOUT) ? MAX_RENDERER_API_DETECT_TIMEOUT : MIN_RENDER_DETECT_TIMEOUT) * 1000 + #elif defined(__linux__) || defined(linux) #define RENDERERDETECTOR_OS_LINUX @@ -105,6 +111,15 @@ private: decltype(::SwapBuffers)* wglSwapBuffers; decltype(::vkQueuePresentKHR)* vkQueuePresentKHR; + std::mutex timing_detect_mutex; + std::chrono::time_point initial_hook_detect_time; + std::chrono::time_point d3d_hook_detect_time; + std::chrono::time_point ogl_hook_detect_time; + std::chrono::time_point vkn_hook_detect_time; + std::chrono::steady_clock::duration d3d_frame_duration; + std::chrono::steady_clock::duration ogl_frame_duration; + std::chrono::steady_clock::duration vkn_frame_duration; + bool dxgi_hooked; bool dxgi1_2_hooked; bool dx12_hooked; @@ -113,6 +128,7 @@ private: bool dx9_hooked; bool opengl_hooked; bool vulkan_hooked; + bool wine_found; DX12_Hook* dx12_hook; DX11_Hook* dx11_hook; @@ -135,6 +151,7 @@ private: dx9_hooked(false), opengl_hooked(false), vulkan_hooked(false), + wine_found(false), renderer_hook(nullptr), dx9_hook(nullptr), dx10_hook(nullptr), @@ -257,6 +274,8 @@ private: void DeduceDXVersionFromSwapChain(IDXGISwapChain* pSwapChain) { + PRINT_DEBUG("%s.\n", + "Renderer_Detector DeduceDXVersionFromSwapChain"); IUnknown* pDevice = nullptr; if (Inst()->dx12_hooked) { @@ -294,21 +313,154 @@ private: } } + static bool CheckD3DHookDetectTimings() + { + auto inst = Inst(); + bool ret = false; + + // It appears that (NVidia at least) calls IDXGISwapChain when calling OpenGL or Vulkan SwapBuffers. + // + // ---BUT--- + // + // Wine and it's derivatives will -always- pass the OpenGL or Vulkan renderer + // detection, depending on what backend they are set to use for D3D. + // (wined3d == OpenGL. vkd3d || vkd3d-proton || DXVK || etc. == Vulkan.) + // + // So pass this check if, under Windows, D3D's frame interval timing isn't close to + // OpenGL's or Vulkan's. (I.e. Multiple frames have passed and only D3D has kept pace.) + // --OR-- + // Invert the result under Windows to cover the NVIDIA case. + // --OR-- + // Fail this check under Wine if OpenGL's / Vulkan's timing is the only regularly + // incrementing value. (I.e. Multiple frames have passed and only OpenGL OR Vulkan is + // keeping pace.) + // --OR-- + // Pass this check under Wine if D3D's frame interval timing is keeping up with + // OpenGL's / Vulkan's. (I.e. Multiple frames have passed, D3D and either OpenGL or + // Vulkan has kept pace.) + { + if (inst->d3d_hook_detect_time != std::chrono::steady_clock::time_point() && + inst->d3d_frame_duration != std::chrono::steady_clock::duration()) + { + if (inst->ogl_hook_detect_time != std::chrono::steady_clock::time_point() && + inst->ogl_frame_duration != std::chrono::steady_clock::duration()) + { + std::chrono::duration duration_diff(0); + if (inst->ogl_frame_duration > inst->d3d_frame_duration) + { + duration_diff = std::chrono::duration_cast(inst->ogl_frame_duration - inst->d3d_frame_duration); + } + else + { + duration_diff = std::chrono::duration_cast(inst->d3d_frame_duration - inst->ogl_frame_duration); + } + + if (duration_diff < std::chrono::duration(MAX_RENDER_DETECT_FRAME_TIMING_VARIANCE)) + { + ret = true; + } + + if (!inst->wine_found) + ret = !ret; + } + else + { + if (inst->vkn_hook_detect_time != std::chrono::steady_clock::time_point() && + inst->vkn_frame_duration != std::chrono::steady_clock::duration()) + { + std::chrono::duration duration_diff(0); + if (inst->vkn_frame_duration > inst->d3d_frame_duration) + { + duration_diff = std::chrono::duration_cast(inst->vkn_frame_duration - inst->d3d_frame_duration); + } + else + { + duration_diff = std::chrono::duration_cast(inst->d3d_frame_duration - inst->vkn_frame_duration); + } + + if (duration_diff < std::chrono::duration(MAX_RENDER_DETECT_FRAME_TIMING_VARIANCE)) + { + ret = true; + } + + if (!inst->wine_found) + ret = !ret; + } + else + { + ret = true; + } + } + } + } + + PRINT_DEBUG("%s %s.\n", + "Renderer_Detector CheckD3DHookDetectTimings Correct API:", + ((ret == true) ? "D3D" : "OpenGL / Vulkan")); + + return ret; + } + static HRESULT STDMETHODCALLTYPE MyIDXGISwapChain_Present(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags) { auto inst = Inst(); HRESULT res; bool locked; - std::unique_lock lk(inst->renderer_mutex, std::defer_lock); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyIDXGISwapChain_Present current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } // It appears that (NVidia at least) calls IDXGISwapChain when calling OpenGL or Vulkan SwapBuffers. // So only lock when OpenGL or Vulkan hasn't already locked the mutex. - locked = lk.try_lock(); - res = (_this->*inst->IDXGISwapChainPresent)(SyncInterval, Flags); - if (!locked || inst->detection_done) - return res; - inst->DeduceDXVersionFromSwapChain(_this); + res = (_this->*inst->IDXGISwapChainPresent)(SyncInterval, Flags); + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; + + if (detect_pass) + inst->DeduceDXVersionFromSwapChain(_this); + } return res; } @@ -318,16 +470,60 @@ private: auto inst = Inst(); HRESULT res; bool locked; - std::unique_lock lk(inst->renderer_mutex, std::defer_lock); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present1"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present1 Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present1 Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyIDXGISwapChain_Present1 current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } // It appears that (NVidia at least) calls IDXGISwapChain when calling OpenGL or Vulkan SwapBuffers. // So only lock when OpenGL or Vulkan hasn't already locked the mutex. - locked = lk.try_lock(); - res = (_this->*inst->IDXGISwapChainPresent1)(SyncInterval, Flags, pPresentParameters); - if (!locked || inst->detection_done) - return res; - inst->DeduceDXVersionFromSwapChain(_this); + res = (_this->*inst->IDXGISwapChainPresent1)(SyncInterval, Flags, pPresentParameters); + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; + + if (detect_pass) + inst->DeduceDXVersionFromSwapChain(_this); + } return res; } @@ -335,13 +531,58 @@ private: static HRESULT STDMETHODCALLTYPE MyDX9Present(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9Present"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9Present Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9Present Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyDX9Present current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } auto res = (_this->*inst->IDirect3DDevice9Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->dx9_hook); + if (detect_pass) + inst->HookDetected(inst->dx9_hook); + } return res; } @@ -349,13 +590,58 @@ private: static HRESULT STDMETHODCALLTYPE MyDX9PresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9PresentEx"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9PresentEx Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9PresentEx Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyDX9PresentEx current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } auto res = (_this->*inst->IDirect3DDevice9ExPresentEx)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->dx9_hook); + if (detect_pass) + inst->HookDetected(inst->dx9_hook); + } return res; } @@ -363,13 +649,58 @@ private: static HRESULT STDMETHODCALLTYPE MyDX9SwapChainPresent(IDirect3DSwapChain9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9SwapChainPresent"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9SwapChainPresent Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9SwapChainPresent Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyDX9SwapChainPresent current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } auto res = (_this->*inst->IDirect3DSwapChain9Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->dx9_hook); + if (detect_pass) + inst->HookDetected(inst->dx9_hook); + } return res; } @@ -377,15 +708,57 @@ private: static BOOL WINAPI MywglSwapBuffers(HDC hDC) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + + PRINT_DEBUG("%s.\n", + "Renderer_Detector MywglSwapBuffers"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MywglSwapBuffers Initial time"); + } + if (inst->ogl_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->ogl_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MywglSwapBuffers Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->ogl_frame_duration = std::chrono::duration_cast(current - inst->ogl_hook_detect_time); + inst->ogl_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MywglSwapBuffers current frame duration:", + (std::chrono::duration_cast(inst->ogl_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = !(CheckD3DHookDetectTimings()); + } + } + } + } auto res = inst->wglSwapBuffers(hDC); - if (inst->detection_done) - return res; - - if (gladLoaderLoadGL() >= GLAD_MAKE_VERSION(3, 1)) { - inst->HookDetected(inst->opengl_hook); + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; + + if (detect_pass == true && gladLoaderLoadGL() >= GLAD_MAKE_VERSION(3, 1)) + inst->HookDetected(inst->opengl_hook); } return res; @@ -394,13 +767,58 @@ private: static VkResult VKAPI_CALL MyvkQueuePresentKHR(VkQueue Queue, const VkPresentInfoKHR* pPresentInfo) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyvkQueuePresentKHR"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyvkQueuePresentKHR Initial time"); + } + if (inst->vkn_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->vkn_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyvkQueuePresentKHR Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->vkn_frame_duration = std::chrono::duration_cast(current - inst->vkn_hook_detect_time); + inst->vkn_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyvkQueuePresentKHR current frame duration:", + (std::chrono::duration_cast(inst->vkn_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = !(CheckD3DHookDetectTimings()); + } + } + } + } auto res = inst->vkQueuePresentKHR(Queue, pPresentInfo); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->vulkan_hook); + if (detect_pass) + inst->HookDetected(inst->vulkan_hook); + } return res; } @@ -644,6 +1062,24 @@ private: pDXGIFactory->CreateSwapChainForHwnd(pDevice, dummyWindow, &SwapChainDesc, NULL, NULL, reinterpret_cast(&pSwapChain)); } + else + { + SPDLOG_WARN("Failed to instanciate IDXGIFactory2, fallback to pure DX10 detection"); + } + } + else + { + SPDLOG_WARN("Failed to instanciate ID3D10Device, fallback to pure DX10 detection"); + } + } + else + { + if (D3D10CreateDevice == nullptr) + { + SPDLOG_WARN("Failed to get address of D3D10CreateDevice, fallback to pure DX10 detection"); + } else + { + SPDLOG_WARN("Failed to get address of CreateDXGIFactory1, fallback to pure DX10 detection"); } } @@ -679,6 +1115,10 @@ private: D3D10CreateDeviceAndSwapChain(NULL, (found_wine) ? D3D10_DRIVER_TYPE_WARP : D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice); } + else + { + SPDLOG_WARN("Failed to get address of D3D10CreateDeviceAndSwapChain, DX10 API failed."); + } } if (pSwapChain != nullptr) @@ -775,7 +1215,17 @@ private: SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; pDXGIFactory->CreateSwapChainForHwnd(pDevice, dummyWindow, &SwapChainDesc, NULL, NULL, reinterpret_cast(&pSwapChain)); + } else { + SPDLOG_WARN("Failed to instanciate IDXGIFactory2, fallback to pure DX11 detection"); } + } else { + SPDLOG_WARN("Failed to instanciate ID3D11Device, fallback to pure DX11 detection"); + } + } else { + if (D3D11CreateDevice == nullptr) { + SPDLOG_WARN("Failed to get address of D3D11CreateDevice, fallback to pure DX11 detection"); + } else { + SPDLOG_WARN("Failed to get address of CreateDXGIFactory1, fallback to pure DX11 detection"); } } @@ -810,6 +1260,8 @@ private: SwapChainDesc.Windowed = TRUE; D3D11CreateDeviceAndSwapChain(NULL, (found_wine) ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, NULL, D3D11_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice, NULL, NULL); + } else { + SPDLOG_WARN("Failed to get address of D3D11CreateDeviceAndSwapChain, DX11 API failed."); } } @@ -1079,6 +1531,20 @@ private: bool EnterDetection() { + std::string ntdll_path = FindPreferedModulePath("ntdll.dll"); + if (!ntdll_path.empty()) + { + HMODULE hNTdll = GetModuleHandleA(ntdll_path.c_str()); + if (hNTdll != nullptr) + { + auto wine_get_version = (const char*(*)())GetProcAddress(hNTdll, "wine_get_version"); + if (wine_get_version != nullptr) + { + wine_found = true; + PRINT_DEBUG("Found WINE version: %s.\n", wine_get_version()); + } + } + } return CreateHWND() != nullptr; } @@ -1097,6 +1563,7 @@ private: dx9_hooked = false; opengl_hooked = false; vulkan_hooked = false; + wine_found = false; delete dx9_hook ; dx9_hook = nullptr; delete dx10_hook ; dx10_hook = nullptr; @@ -1104,6 +1571,15 @@ private: delete dx12_hook ; dx12_hook = nullptr; delete opengl_hook; opengl_hook = nullptr; delete vulkan_hook; vulkan_hook = nullptr; + + initial_hook_detect_time = std::chrono::time_point(); + d3d_hook_detect_time = std::chrono::time_point(); + ogl_hook_detect_time = std::chrono::time_point(); + vkn_hook_detect_time = std::chrono::time_point(); + + d3d_frame_duration = std::chrono::steady_clock::duration(); + ogl_frame_duration = std::chrono::steady_clock::duration(); + vkn_frame_duration = std::chrono::steady_clock::duration(); } #elif defined(RENDERERDETECTOR_OS_LINUX) @@ -1346,6 +1822,22 @@ public: { std::lock_guard lk(stop_detection_mutex); + if (timeout != std::chrono::milliseconds{ -1 }) + { + if (timeout < std::chrono::milliseconds{ GET_RENDERER_DETECT_TIMEOUT * 2 }) + {// Need enough time to determine API if multiple are used. + timeout = std::chrono::milliseconds{ GET_RENDERER_DETECT_TIMEOUT * 2 }; + } + PRINT_DEBUG("%s %" PRIu64 ".\n", + "Renderer_Detector Detection timeout set to:", + timeout.count()); + } + else + { + PRINT_DEBUG("%s.\n", + "Renderer_Detector Detection timeout set to: infinite"); + } + if (detection_count == 0) {// If we have no detections in progress, restart detection. detection_cancelled = false; diff --git a/overlay_experimental/Renderer_Detector.h b/overlay_experimental/Renderer_Detector.h index bec8368..34a534b 100644 --- a/overlay_experimental/Renderer_Detector.h +++ b/overlay_experimental/Renderer_Detector.h @@ -27,6 +27,21 @@ #include "Renderer_Hook.h" +/* Timeout is in seconds. + Used for determining which rendering API is the real one used by + the app when multiple APIs are used. (NVIDIA, Wine, etc.) + + This is an internally used value, separate from the timeout given + to DetectRenderer(). + + If you define it below the minimal level (one second), the minimal level + will be used. + + Any timeout given to DetectRenderer() less than this value will be upgraded + to it. +*/ +#define MAX_RENDERER_API_DETECT_TIMEOUT 1 + namespace ingame_overlay { std::future DetectRenderer(std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 }); diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index b18c719..85cf877 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -718,7 +718,7 @@ void Steam_Overlay::UnSetupOverlay() { ingame_overlay::StopRendererDetection(); if (!Ready() && future_renderer.valid()) { - if (future_renderer.wait_for(std::chrono::milliseconds{500}) == std::future_status::ready) { + if (future_renderer.wait_for(std::chrono::milliseconds{500} + std::chrono::seconds{MAX_RENDERER_API_DETECT_TIMEOUT}) == std::future_status::ready) { future_renderer.get(); ingame_overlay::FreeDetector(); }