diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bf1f66..cce3a7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ if(WIN32) # Setup some variables for Windows build set(PRIVATE_INGAMEOVERLAY_HEADERS src/VulkanHelpers.h src/Windows/DirectXVTables.h + src/Windows/DXVKDetector.h src/Windows/DX9Hook.h src/Windows/DX10Hook.h src/Windows/DX11Hook.h diff --git a/include/InGameOverlay/RendererDetector.h b/include/InGameOverlay/RendererDetector.h index 7fa1bf5..00d955c 100644 --- a/include/InGameOverlay/RendererDetector.h +++ b/include/InGameOverlay/RendererDetector.h @@ -31,6 +31,7 @@ namespace InGameOverlay { /// Starts a detector to automatically find the renderer used by the application. /// /// The time before the future will timeout if no renderer has been found. +/// Set this to any combined RendererHookType_t value to filter the renderers you want to detect. /// Prefer hooking the system libraries instead of the first one found. /// A future nullptr or the renderer. std::future DetectRenderer(std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 }, RendererHookType_t rendererToDetect = RendererHookType_t::Any, bool preferSystemLibraries = true); diff --git a/include/InGameOverlay/RendererHook.h b/include/InGameOverlay/RendererHook.h index 93f4525..5734d7c 100644 --- a/include/InGameOverlay/RendererHook.h +++ b/include/InGameOverlay/RendererHook.h @@ -43,6 +43,8 @@ enum class OverlayHookState : uint8_t /// /// Only one RendererHookType_t will always be returned by RendererHook_t::GetRendererHookType /// but you can use them as flags to limit the detection in InGameOverlay::DetectRenderer. +/// NOTE: Even if a DirectX hook has been found and DXVK is used, the renderer hook will only report the DirectX hook type. +/// There is no combination like : DirectX11 | Vulkan returned by RendererHook_t::GetRendererHookType. /// enum class RendererHookType_t : uint8_t { diff --git a/src/Linux/OpenGLXHook.cpp b/src/Linux/OpenGLXHook.cpp index f6d65e4..fd8bdfb 100644 --- a/src/Linux/OpenGLXHook.cpp +++ b/src/Linux/OpenGLXHook.cpp @@ -27,7 +27,7 @@ namespace InGameOverlay { -#define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&OpenGLXHook_t::_My##NAME))) { \ +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&OpenGLXHook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ @@ -51,7 +51,7 @@ bool OpenGLXHook_t::StartHook(std::function keyCombinationCallback, Togg _X11Hooked = true; BeginHook(); - TRY_HOOK_FUNCTION(GLXSwapBuffers); + TRY_HOOK_FUNCTION_OR_FAIL(GLXSwapBuffers); EndHook(); INGAMEOVERLAY_INFO("Hooked OpenGLX"); diff --git a/src/Linux/VulkanHook.cpp b/src/Linux/VulkanHook.cpp index 876da9d..6e4540c 100644 --- a/src/Linux/VulkanHook.cpp +++ b/src/Linux/VulkanHook.cpp @@ -29,7 +29,7 @@ namespace InGameOverlay { -#define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&VulkanHook_t::_My##NAME))) { \ +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&VulkanHook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ return false;\ } } while(0) @@ -74,13 +74,13 @@ bool VulkanHook_t::StartHook(std::function keyCombinationCallback, Toggl _X11Hooked = true; BeginHook(); - TRY_HOOK_FUNCTION(VkAcquireNextImageKHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkAcquireNextImageKHR); if (_VkAcquireNextImage2KHR != nullptr) - TRY_HOOK_FUNCTION(VkAcquireNextImage2KHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkAcquireNextImage2KHR); - TRY_HOOK_FUNCTION(VkQueuePresentKHR); - TRY_HOOK_FUNCTION(VkCreateSwapchainKHR); - TRY_HOOK_FUNCTION(VkDestroyDevice); + TRY_HOOK_FUNCTION_OR_FAIL(VkQueuePresentKHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkCreateSwapchainKHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkDestroyDevice); EndHook(); INGAMEOVERLAY_INFO("Hooked Vulkan"); @@ -269,7 +269,7 @@ bool VulkanHook_t::_CreateRenderTargets(VkSwapchainKHR swapChain) VkImageViewCreateInfo info = { }; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VK_FORMAT_B8G8R8A8_UNORM; + info.format = _VulkanTargetFormat; info.image = frame.BackBuffer; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -454,6 +454,7 @@ bool VulkanHook_t::_CreateVulkanInstance() LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceMemoryProperties); LOAD_VULKAN_FUNCTION(vkEnumerateDeviceExtensionProperties); LOAD_VULKAN_FUNCTION(vkEnumeratePhysicalDevices); + LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR); LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceProperties); LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); #undef LOAD_VULKAN_FUNCTION @@ -506,6 +507,70 @@ int32_t VulkanHook_t::_GetPhysicalDeviceFirstGraphicsQueue(VkPhysicalDevice phys return -1; } +void VulkanHook_t::_SelectFormatSurface() +{ + _VulkanTargetFormat = VK_FORMAT_R8G8B8A8_UNORM; + + // TODO: Find a way to use something like this: + //static constexpr VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + //static constexpr uint32_t requestSurfaceImageFormatCount = sizeof(requestSurfaceImageFormat)/sizeof(*requestSurfaceImageFormat); + //static constexpr VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + + //VkResult err; + //VkWin32SurfaceCreateInfoKHR sci; + //PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; + // + //vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) + // vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); + //if (!vkCreateWin32SurfaceKHR) + //{ + // _glfwInputError(GLFW_API_UNAVAILABLE, + // "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); + // return VK_ERROR_EXTENSION_NOT_PRESENT; + //} + // + //memset(&sci, 0, sizeof(sci)); + //sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + //sci.hinstance = _glfw.win32.instance; + //sci.hwnd = window->win32.handle; + // + //err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); + + //uint32_t availCount; + //_vkGetPhysicalDeviceSurfaceFormatsKHR(_VulkanPhysicalDevice, surface, &availCount, nullptr); + //std::vector availFormat; + //availFormat.resize((int)availCount); + //_vkGetPhysicalDeviceSurfaceFormatsKHR(_VulkanPhysicalDevice, surface, &availCount, availFormat.data()); + // + //// First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + //if (availCount == 1) + //{ + // if (availFormat[0].format == VK_FORMAT_UNDEFINED) + // { + // return VkSurfaceFormatKHR{ + // requestSurfaceImageFormat[0], + // requestSurfaceColorSpace + // }; + // } + // + // // No point in searching another format + // return availFormat[0]; + //} + // + //// Request several formats, the first found will be used + //for (uint32_t i = 0; i < requestSurfaceImageFormatCount; ++i) + //{ + // for (uint32_t j = 0; j < availCount; ++j) + // { + // if (availFormat[i].format == requestSurfaceImageFormat[i] && availFormat[j].colorSpace == requestSurfaceColorSpace) + // return availFormat[i]; + // } + //} + // + //// If none of the requested image formats could be found, use the first available + //return availFormat[0]; +} + bool VulkanHook_t::_GetPhysicalDevice() { _VulkanPhysicalDevice = nullptr; @@ -672,7 +737,7 @@ bool VulkanHook_t::_CreateRenderPass() return true; VkAttachmentDescription attachment = { }; - attachment.format = VK_FORMAT_B8G8R8A8_UNORM; + attachment.format = _VulkanTargetFormat; attachment.samples = VK_SAMPLE_COUNT_1_BIT; attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -762,6 +827,8 @@ void VulkanHook_t::_PrepareForOverlay(VkQueue queue, const VkPresentInfoKHR* pPr if (!X11Hook_t::Inst()->SetInitialWindowSize((Window)_Window)) return; + _SelectFormatSurface(); + if (_VulkanQueue == nullptr) _vkGetDeviceQueue(_VulkanDevice, _VulkanQueueFamily, 0, &_VulkanQueue); @@ -939,6 +1006,7 @@ VKAPI_ATTR VkResult VKAPI_CALL VulkanHook_t::_MyVkCreateSwapchainKHR(VkDevice de inst->_ResetRenderState(OverlayHookState::Reset); inst->_DestroyRenderTargets(); } + inst->_VulkanTargetFormat = pCreateInfo->imageFormat; auto res = inst->_VkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); if (inst->_VulkanDevice == device && res == VkResult::VK_SUCCESS) { @@ -974,6 +1042,7 @@ VulkanHook_t::VulkanHook_t(): _VulkanImageSampler(VK_NULL_HANDLE), _VulkanImageDescriptorSetLayout(VK_NULL_HANDLE), _VulkanRenderPass(VK_NULL_HANDLE), + _VulkanTargetFormat(VK_FORMAT_R8G8B8A8_UNORM), _VulkanDevice(VK_NULL_HANDLE), _VulkanQueue(VK_NULL_HANDLE), _ImGuiFontAtlas(nullptr), @@ -1039,6 +1108,7 @@ VulkanHook_t::VulkanHook_t(): _vkGetBufferMemoryRequirements(nullptr), _vkGetImageMemoryRequirements(nullptr), _vkEnumeratePhysicalDevices(nullptr), + _vkGetPhysicalDeviceSurfaceFormatsKHR(nullptr), _vkGetPhysicalDeviceProperties(nullptr), _vkGetPhysicalDeviceQueueFamilyProperties(nullptr), _vkGetPhysicalDeviceMemoryProperties(nullptr), diff --git a/src/Linux/VulkanHook.h b/src/Linux/VulkanHook.h index dbe78f7..66f037b 100644 --- a/src/Linux/VulkanHook.h +++ b/src/Linux/VulkanHook.h @@ -82,6 +82,7 @@ class VulkanHook_t : std::vector _Frames; VkRenderPass _VulkanRenderPass; std::vector _DescriptorsPools; + VkFormat _VulkanTargetFormat; VkDevice _VulkanDevice; VkQueue _VulkanQueue; @@ -112,6 +113,7 @@ class VulkanHook_t : void _FreeVulkanRessources(); bool _CreateVulkanInstance(); int32_t _GetPhysicalDeviceFirstGraphicsQueue(VkPhysicalDevice physicalDevice); + void _SelectFormatSurface(); bool _GetPhysicalDevice(); bool _CreateImageSampler(); @@ -203,6 +205,7 @@ class VulkanHook_t : decltype(::vkGetBufferMemoryRequirements) *_vkGetBufferMemoryRequirements; decltype(::vkGetImageMemoryRequirements) *_vkGetImageMemoryRequirements; decltype(::vkEnumeratePhysicalDevices) *_vkEnumeratePhysicalDevices; + decltype(::vkGetPhysicalDeviceSurfaceFormatsKHR) *_vkGetPhysicalDeviceSurfaceFormatsKHR; decltype(::vkGetPhysicalDeviceProperties) *_vkGetPhysicalDeviceProperties; decltype(::vkGetPhysicalDeviceQueueFamilyProperties) *_vkGetPhysicalDeviceQueueFamilyProperties; decltype(::vkGetPhysicalDeviceMemoryProperties) *_vkGetPhysicalDeviceMemoryProperties; diff --git a/src/MacOSX/OpenGLHook.mm b/src/MacOSX/OpenGLHook.mm index f7bc6dd..f092380 100644 --- a/src/MacOSX/OpenGLHook.mm +++ b/src/MacOSX/OpenGLHook.mm @@ -28,7 +28,7 @@ namespace InGameOverlay { -#define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&OpenGLHook_t::_My##NAME))) { \ +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&OpenGLHook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ @@ -63,7 +63,7 @@ static bool ImGuiOpenGL3Init() else if (_CGLFlushDrawable != nullptr) { BeginHook(); - TRY_HOOK_FUNCTION(CGLFlushDrawable); + TRY_HOOK_FUNCTION_OR_FAIL(CGLFlushDrawable); EndHook(); } diff --git a/src/Windows/DX10Hook.cpp b/src/Windows/DX10Hook.cpp index 5d92c79..aadde61 100644 --- a/src/Windows/DX10Hook.cpp +++ b/src/Windows/DX10Hook.cpp @@ -27,6 +27,10 @@ namespace InGameOverlay { #define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX10Hook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ +} } while(0) + +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX10Hook_t::_My##NAME))) { \ + INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ } } while(0) @@ -60,12 +64,12 @@ bool DX10Hook_t::StartHook(std::function keyCombinationCallback, ToggleK BeginHook(); TRY_HOOK_FUNCTION(ID3D10DeviceRelease); - TRY_HOOK_FUNCTION(IDXGISwapChainPresent); - TRY_HOOK_FUNCTION(IDXGISwapChainResizeTarget); - TRY_HOOK_FUNCTION(IDXGISwapChainResizeBuffers); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainPresent); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainResizeTarget); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainResizeBuffers); if (_IDXGISwapChain1Present1 != nullptr) - TRY_HOOK_FUNCTION(IDXGISwapChain1Present1); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChain1Present1); EndHook(); @@ -275,6 +279,7 @@ HRESULT STDMETHODCALLTYPE DX10Hook_t::_MyIDXGISwapChain1Present1(IDXGISwapChain1 DX10Hook_t::DX10Hook_t(): _Hooked(false), _WindowsHooked(false), + _UsesDXVK(false), _DeviceReleasing(0), _Device(nullptr), _HookDeviceRefCount(0), @@ -319,6 +324,15 @@ RendererHookType_t DX10Hook_t::GetRendererHookType() const return RendererHookType_t::DirectX10; } +void DX10Hook_t::SetDXVK() +{ + if (!_UsesDXVK) + { + _UsesDXVK = true; + LibraryName += " (DXVK)"; + } +} + void DX10Hook_t::LoadFunctions( decltype(_ID3D10DeviceRelease) releaseFcn, decltype(_IDXGISwapChainPresent) presentFcn, diff --git a/src/Windows/DX10Hook.h b/src/Windows/DX10Hook.h index 94d3653..6b6afe4 100644 --- a/src/Windows/DX10Hook.h +++ b/src/Windows/DX10Hook.h @@ -36,6 +36,7 @@ class DX10Hook_t : // Variables bool _Hooked; bool _WindowsHooked; + bool _UsesDXVK; uint32_t _DeviceReleasing; ID3D10Device* _Device; ULONG _HookDeviceRefCount; @@ -79,6 +80,7 @@ class DX10Hook_t : virtual const char* GetLibraryName() const; virtual RendererHookType_t GetRendererHookType() const; + void SetDXVK(); void LoadFunctions( decltype(_ID3D10DeviceRelease) releaseFcn, decltype(_IDXGISwapChainPresent) presentFcn, diff --git a/src/Windows/DX11Hook.cpp b/src/Windows/DX11Hook.cpp index 80da28d..6b86bd5 100644 --- a/src/Windows/DX11Hook.cpp +++ b/src/Windows/DX11Hook.cpp @@ -27,6 +27,10 @@ namespace InGameOverlay { #define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX11Hook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ +} } while(0) + +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX11Hook_t::_My##NAME))) { \ + INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ } } while(0) @@ -70,12 +74,12 @@ bool DX11Hook_t::StartHook(std::function keyCombinationCallback, ToggleK BeginHook(); TRY_HOOK_FUNCTION(ID3D11DeviceRelease); - TRY_HOOK_FUNCTION(IDXGISwapChainPresent); - TRY_HOOK_FUNCTION(IDXGISwapChainResizeTarget); - TRY_HOOK_FUNCTION(IDXGISwapChainResizeBuffers); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainPresent); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainResizeTarget); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainResizeBuffers); if (_IDXGISwapChain1Present1 != nullptr) - TRY_HOOK_FUNCTION(IDXGISwapChain1Present1); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChain1Present1); EndHook(); @@ -105,14 +109,15 @@ bool DX11Hook_t::IsStarted() void DX11Hook_t::_UpdateHookDeviceRefCount() { + const int BaseRefCount = 2; switch (_HookState) { // 0 ref from ImGui - case OverlayHookState::Removing: _HookDeviceRefCount = 2; break; + case OverlayHookState::Removing: _HookDeviceRefCount = BaseRefCount; break; // 1 refs from us, 10 refs from ImGui (device, vertex shader, input layout, vertex constant buffer, pixel shader, blend state, rasterizer state, depth stencil state, texture view, texture sample) //case OverlayHookState::Reset: _HookDeviceRefCount = 15 + _ImageResources.size(); break; // 1 refs from us, 12 refs from ImGui (device, vertex shader, input layout, vertex constant buffer, pixel shader, blend state, rasterizer state, depth stencil state, texture view, texture sample, vertex buffer, index buffer) - case OverlayHookState::Ready: _HookDeviceRefCount = 17 + _ImageResources.size(); + case OverlayHookState::Ready: _HookDeviceRefCount = BaseRefCount + 15 + _ImageResources.size(); } } @@ -311,6 +316,7 @@ HRESULT STDMETHODCALLTYPE DX11Hook_t::_MyIDXGISwapChain1Present1(IDXGISwapChain1 DX11Hook_t::DX11Hook_t(): _Hooked(false), _WindowsHooked(false), + _UsesDXVK(false), _DeviceReleasing(0), _Device(nullptr), _HookDeviceRefCount(0), @@ -356,6 +362,15 @@ RendererHookType_t DX11Hook_t::GetRendererHookType() const return RendererHookType_t::DirectX11; } +void DX11Hook_t::SetDXVK() +{ + if (!_UsesDXVK) + { + _UsesDXVK = true; + LibraryName += " (DXVK)"; + } +} + void DX11Hook_t::LoadFunctions( decltype(_ID3D11DeviceRelease) releaseFcn, decltype(_IDXGISwapChainPresent) presentFcn, diff --git a/src/Windows/DX11Hook.h b/src/Windows/DX11Hook.h index e4afa4e..1964d9c 100644 --- a/src/Windows/DX11Hook.h +++ b/src/Windows/DX11Hook.h @@ -36,6 +36,7 @@ class DX11Hook_t : // Variables bool _Hooked; bool _WindowsHooked; + bool _UsesDXVK; uint32_t _DeviceReleasing; ID3D11Device* _Device; ULONG _HookDeviceRefCount; @@ -80,6 +81,7 @@ class DX11Hook_t : virtual const char* GetLibraryName() const; virtual RendererHookType_t GetRendererHookType() const; + void SetDXVK(); void LoadFunctions( decltype(_ID3D11DeviceRelease) releaseFcn, decltype(_IDXGISwapChainPresent) presentFcn, diff --git a/src/Windows/DX12Hook.cpp b/src/Windows/DX12Hook.cpp index a251012..a005104 100644 --- a/src/Windows/DX12Hook.cpp +++ b/src/Windows/DX12Hook.cpp @@ -27,6 +27,10 @@ namespace InGameOverlay { #define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX12Hook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ +} } while(0) + +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX12Hook_t::_My##NAME))) { \ + INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ } } while(0) @@ -75,16 +79,16 @@ bool DX12Hook_t::StartHook(std::function keyCombinationCallback, ToggleK BeginHook(); TRY_HOOK_FUNCTION(ID3D12DeviceRelease); - TRY_HOOK_FUNCTION(IDXGISwapChainPresent); - TRY_HOOK_FUNCTION(IDXGISwapChainResizeTarget); - TRY_HOOK_FUNCTION(IDXGISwapChainResizeBuffers); - TRY_HOOK_FUNCTION(ID3D12CommandQueueExecuteCommandLists); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainPresent); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainResizeTarget); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChainResizeBuffers); + TRY_HOOK_FUNCTION_OR_FAIL(ID3D12CommandQueueExecuteCommandLists); if (_IDXGISwapChain1Present1 != nullptr) - TRY_HOOK_FUNCTION(IDXGISwapChain1Present1); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChain1Present1); if (_IDXGISwapChain3ResizeBuffers1 != nullptr) - TRY_HOOK_FUNCTION(IDXGISwapChain3ResizeBuffers1); + TRY_HOOK_FUNCTION_OR_FAIL(IDXGISwapChain3ResizeBuffers1); EndHook(); @@ -171,13 +175,18 @@ void DX12Hook_t::_ReleaseShaderRessourceView(uint32_t id) ID3D12CommandQueue* DX12Hook_t::_FindCommandQueueFromSwapChain(IDXGISwapChain* pSwapChain) { + constexpr int MaxRetries = 10; ID3D12CommandQueue* pCommandQueue = nullptr; + ID3D12CommandQueue* pHookedCommandQueue = _CommandQueue; + + if (pHookedCommandQueue == nullptr) + return nullptr; - if (_CommandQueueOffset == 0 && _CommandQueue != nullptr) + if (_CommandQueueOffset == 0) { for (size_t i = 0; i < 1024; ++i) { - if (*reinterpret_cast(reinterpret_cast(pSwapChain) + i) == _CommandQueue) + if (*reinterpret_cast(reinterpret_cast(pSwapChain) + i) == pHookedCommandQueue) { INGAMEOVERLAY_INFO("Found IDXGISwapChain::ppCommandQueue at offset {}.", i); _CommandQueueOffset = i; @@ -187,18 +196,33 @@ ID3D12CommandQueue* DX12Hook_t::_FindCommandQueueFromSwapChain(IDXGISwapChain* p } if (_CommandQueueOffset != 0) + { pCommandQueue = *reinterpret_cast(reinterpret_cast(pSwapChain) + _CommandQueueOffset); + } + else if (_CommandQueueOffsetRetries <= MaxRetries) + { + if (++_CommandQueueOffsetRetries >= MaxRetries) + { + INGAMEOVERLAY_INFO("Failed to find IDXGISwapChain::ppCommandQueue, fallback to ID3D12CommandQueue::ExecuteCommandLists"); + } + } + else + { + pCommandQueue = pHookedCommandQueue; + } return pCommandQueue; } void DX12Hook_t::_UpdateHookDeviceRefCount() { + constexpr int BaseRefCount = 2; + switch (_HookState) { // 2 Base reference count value // 0 ref from ImGui - case OverlayHookState::Removing: _HookDeviceRefCount = 3; break; + case OverlayHookState::Removing: _HookDeviceRefCount = BaseRefCount + 1; break; // 0 ref from ImGui //case OverlayHookState::Reset: _HookDeviceRefCount = 15 + _ImageResources.size(); break; // Us: 2 + FrameCount * 5 + ImagesResourcesCount @@ -211,7 +235,7 @@ void DX12Hook_t::_UpdateHookDeviceRefCount() // PipelineState // FontTexture // (VertexBuffer + IndexBuffer) * FrameCount - case OverlayHookState::Ready: _HookDeviceRefCount = 2 + (_OverlayFrames.size() * 9) + _ShaderResourceViewHeapDescriptors.size() + _ImageResources.size(); + case OverlayHookState::Ready: _HookDeviceRefCount = BaseRefCount + (_OverlayFrames.size() * 9) + _ShaderResourceViewHeapDescriptors.size() + _ImageResources.size(); } } @@ -316,6 +340,7 @@ void DX12Hook_t::_ResetRenderState(OverlayHookState state) _ShaderResourceViewHeapDescriptors.clear(); SafeRelease(_Device); _CommandQueueOffset = 0; + _CommandQueueOffsetRetries = 0; _CommandQueue = nullptr; } @@ -518,6 +543,7 @@ DX12Hook_t::DX12Hook_t(): _Hooked(false), _WindowsHooked(false), _DeviceReleasing(0), + _CommandQueueOffsetRetries(0), _CommandQueueOffset(0), _CommandQueue(nullptr), _Device(nullptr), diff --git a/src/Windows/DX12Hook.h b/src/Windows/DX12Hook.h index f32175c..0ffe831 100644 --- a/src/Windows/DX12Hook.h +++ b/src/Windows/DX12Hook.h @@ -127,6 +127,7 @@ class DX12Hook_t : bool _WindowsHooked; uint32_t _DeviceReleasing; + int _CommandQueueOffsetRetries; size_t _CommandQueueOffset; ID3D12CommandQueue* _CommandQueue; ID3D12Device* _Device; diff --git a/src/Windows/DX9Hook.cpp b/src/Windows/DX9Hook.cpp index 626c179..c8ea397 100644 --- a/src/Windows/DX9Hook.cpp +++ b/src/Windows/DX9Hook.cpp @@ -27,6 +27,10 @@ namespace InGameOverlay { #define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX9Hook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ +} } while(0) + +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&DX9Hook_t::_My##NAME))) { \ + INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ } } while(0) @@ -60,13 +64,13 @@ bool DX9Hook_t::StartHook(std::function keyCombinationCallback, ToggleKe BeginHook(); TRY_HOOK_FUNCTION(IDirect3DDevice9Release); - TRY_HOOK_FUNCTION(IDirect3DDevice9Reset); - TRY_HOOK_FUNCTION(IDirect3DDevice9Present); + TRY_HOOK_FUNCTION_OR_FAIL(IDirect3DDevice9Reset); + TRY_HOOK_FUNCTION_OR_FAIL(IDirect3DDevice9Present); if (_IDirect3DDevice9ExPresentEx != nullptr) - TRY_HOOK_FUNCTION(IDirect3DDevice9ExPresentEx); + TRY_HOOK_FUNCTION_OR_FAIL(IDirect3DDevice9ExPresentEx); if (_IDirect3DSwapChain9SwapChainPresent != nullptr) - TRY_HOOK_FUNCTION(IDirect3DSwapChain9SwapChainPresent); + TRY_HOOK_FUNCTION_OR_FAIL(IDirect3DSwapChain9SwapChainPresent); EndHook(); @@ -96,14 +100,16 @@ bool DX9Hook_t::IsStarted() void DX9Hook_t::_UpdateHookDeviceRefCount() { + constexpr int BaseRefCount = 3; + switch (_HookState) { // 0 ref from ImGui - case OverlayHookState::Removing: _HookDeviceRefCount = 3; break; + case OverlayHookState::Removing: _HookDeviceRefCount = BaseRefCount; break; // 1 ref from us, 1 ref from ImGui (device) - case OverlayHookState::Reset: _HookDeviceRefCount = 4; break; + case OverlayHookState::Reset: _HookDeviceRefCount = BaseRefCount + 1; break; // 1 ref from us, 4 refs from ImGui (device, vertex buffer, index buffer, font texture) - case OverlayHookState::Ready: _HookDeviceRefCount = 7 + _ImageResources.size(); + case OverlayHookState::Ready: _HookDeviceRefCount = BaseRefCount + 4 + _ImageResources.size(); } } @@ -288,6 +294,7 @@ HRESULT STDMETHODCALLTYPE DX9Hook_t::_MyIDirect3DSwapChain9SwapChainPresent(IDir DX9Hook_t::DX9Hook_t(): _Hooked(false), _WindowsHooked(false), + _UsesDXVK(false), _LastWindow(nullptr), _DeviceReleasing(0), _Device(nullptr), @@ -333,6 +340,15 @@ RendererHookType_t DX9Hook_t::GetRendererHookType() const return RendererHookType_t::DirectX9; } +void DX9Hook_t::SetDXVK() +{ + if (!_UsesDXVK) + { + _UsesDXVK = true; + LibraryName += " (DXVK)"; + } +} + void DX9Hook_t::LoadFunctions( decltype(_IDirect3DDevice9Release) ReleaseFcn, decltype(_IDirect3DDevice9Present) PresentFcn, diff --git a/src/Windows/DX9Hook.h b/src/Windows/DX9Hook.h index 3b6c0a6..1b8d55d 100644 --- a/src/Windows/DX9Hook.h +++ b/src/Windows/DX9Hook.h @@ -35,6 +35,7 @@ class DX9Hook_t : // Variables bool _Hooked; bool _WindowsHooked; + bool _UsesDXVK; HWND _LastWindow; uint32_t _DeviceReleasing; IDirect3DDevice9* _Device; @@ -78,6 +79,7 @@ class DX9Hook_t : virtual const char* GetLibraryName() const; virtual RendererHookType_t GetRendererHookType() const; + void SetDXVK(); void LoadFunctions( decltype(_IDirect3DDevice9Release) ReleaseFcn, decltype(_IDirect3DDevice9Present) PresentFcn, diff --git a/src/Windows/DXVKDetector.h b/src/Windows/DXVKDetector.h new file mode 100644 index 0000000..52ddc06 --- /dev/null +++ b/src/Windows/DXVKDetector.h @@ -0,0 +1,452 @@ +/* + * Copyright (C) Nemirtingas + * This file is part of the ingame overlay project + * + * The ingame overlay project is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * The ingame overlay project is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the ingame overlay project; if not, see + * . + */ + +namespace InGameOverlay { + +// DX9 +constexpr static GUID ID3D9VkInteropInterfaceGUID{ 0x3461a81b, 0xce41, 0x485b, { 0xb6, 0xb5, 0xfc, 0xf0, 0x8b, 0xa6, 0xa6, 0xbd } }; +constexpr static GUID ID3D9VkInteropTextureGUID { 0xd56344f5, 0x8d35, 0x46fd, { 0x80, 0x6d, 0x94, 0xc3, 0x51, 0xb4, 0x72, 0xc1 } }; +constexpr static GUID ID3D9VkInteropDeviceGUID { 0x2eaa4b89, 0x0107, 0x4bdb, { 0x87, 0xf7, 0x0f, 0x54, 0x1c, 0x49, 0x3c, 0xe0 } }; +constexpr static GUID ID3D9VkExtSwapchainGUID { 0x13776e93, 0x4aa9, 0x430a, { 0xa4, 0xec, 0xfe, 0x9e, 0x28, 0x11, 0x81, 0xd5 } }; + +/** + * \brief D3D9 interface for Vulkan interop + * + * Provides access to the instance and physical device + * handles for the given D3D9 interface and adapter ordinals. + */ +struct ID3D9VkInteropInterface : public IUnknown{ + /** + * \brief Queries Vulkan handles used by DXVK + * + * \param [out] pInstance The Vulkan instance + */ + virtual void STDMETHODCALLTYPE GetInstanceHandle( + VkInstance * pInstance) = 0; + + /** + * \brief Queries Vulkan handles used by DXVK + * + * \param [in] Adapter Adapter ordinal + * \param [out] pInstance The Vulkan instance + */ + virtual void STDMETHODCALLTYPE GetPhysicalDeviceHandle( + UINT Adapter, + VkPhysicalDevice* pPhysicalDevice) = 0; +}; + +/** + * \brief D3D9 texture interface for Vulkan interop + * + * Provides access to the backing resource of a + * D3D9 texture. + */ +struct ID3D9VkInteropTexture : public IUnknown{ + /** + * \brief Retrieves Vulkan image info + * + * Retrieves both the image handle as well as the image's + * properties. Any of the given pointers may be \c nullptr. + * + * If \c pInfo is not \c nullptr, the following rules apply: + * - \c pInfo->sType \e must be \c VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO + * - \c pInfo->pNext \e must be \c nullptr or point to a supported + * extension-specific structure (currently none) + * - \c pInfo->queueFamilyIndexCount must be the length of the + * \c pInfo->pQueueFamilyIndices array, in \c uint32_t units. + * - \c pInfo->pQueueFamilyIndices must point to a pre-allocated + * array of \c uint32_t of size \c pInfo->pQueueFamilyIndices. + * + * \note As of now, the sharing mode will always be + * \c VK_SHARING_MODE_EXCLUSIVE and no queue + * family indices will be written to the array. + * + * After the call, the structure pointed to by \c pInfo can + * be used to create an image with identical properties. + * + * If \c pLayout is not \c nullptr, it will receive the + * layout that the image will be in after flushing any + * outstanding commands on the device. + * \param [out] pHandle The image handle + * \param [out] pLayout Image layout + * \param [out] pInfo Image properties + * \returns \c S_OK on success, or \c D3DERR_INVALIDCALL + */ + virtual HRESULT STDMETHODCALLTYPE GetVulkanImageInfo( + VkImage * pHandle, + VkImageLayout * pLayout, + VkImageCreateInfo * pInfo) = 0; +}; + +/** + * \brief D3D9 device interface for Vulkan interop + * + * Provides access to the device and instance handles + * as well as the queue that is used for rendering. + */ +struct ID3D9VkInteropDevice : public IUnknown{ + /** + * \brief Queries Vulkan handles used by DXVK + * + * \param [out] pInstance The Vulkan instance + * \param [out] pPhysDev The physical device + * \param [out] pDevide The device handle + */ + virtual void STDMETHODCALLTYPE GetVulkanHandles( + VkInstance * pInstance, + VkPhysicalDevice * pPhysDev, + VkDevice * pDevice) = 0; + + /** + * \brief Queries the rendering queue used by DXVK + * + * \param [out] pQueue The Vulkan queue handle + * \param [out] pQueueIndex Queue index + * \param [out] pQueueFamilyIndex Queue family index + */ + virtual void STDMETHODCALLTYPE GetSubmissionQueue( + VkQueue* pQueue, + uint32_t* pQueueIndex, + uint32_t* pQueueFamilyIndex) = 0; + + /** + * \brief Transitions a Texture to a given layout + * + * Executes an explicit image layout transition on the + * D3D device. Note that the image subresources \e must + * be transitioned back to its original layout before + * using it again from D3D9. + * Synchronization is left up to the caller. + * This function merely emits a call to transition the + * texture on the DXVK internal command stream. + * \param [in] pTexture The image to transform + * \param [in] pSubresources Subresources to transform + * \param [in] OldLayout Current image layout + * \param [in] NewLayout Desired image layout + */ + virtual void STDMETHODCALLTYPE TransitionTextureLayout( + ID3D9VkInteropTexture* pTexture, + const VkImageSubresourceRange* pSubresources, + VkImageLayout OldLayout, + VkImageLayout NewLayout) = 0; + + /** + * \brief Flushes outstanding D3D rendering commands + * + * Must be called before submitting Vulkan commands + * to the rendering queue if those commands use the + * backing resource of a D3D9 object. + */ + virtual void STDMETHODCALLTYPE FlushRenderingCommands() = 0; + + /** + * \brief Locks submission queue + * + * Should be called immediately before submitting + * Vulkan commands to the rendering queue in order + * to prevent DXVK from using the queue. + * + * While the submission queue is locked, no D3D9 + * methods must be called from the locking thread, + * or otherwise a deadlock might occur. + */ + virtual void STDMETHODCALLTYPE LockSubmissionQueue() = 0; + + /** + * \brief Releases submission queue + * + * Should be called immediately after submitting + * Vulkan commands to the rendering queue in order + * to allow DXVK to submit new commands. + */ + virtual void STDMETHODCALLTYPE ReleaseSubmissionQueue() = 0; + + /** + * \brief Locks the device + * + * Can be called to ensure no D3D9 device methods + * can be executed until UnlockDevice has been called. + * + * This will do nothing if the D3DCREATE_MULTITHREADED + * is not set. + */ + virtual void STDMETHODCALLTYPE LockDevice() = 0; + + /** + * \brief Unlocks the device + * + * Must only be called after a call to LockDevice. + */ + virtual void STDMETHODCALLTYPE UnlockDevice() = 0; + + /** + * \brief Wait for a resource to finish being used + * + * Waits for the GPU resource associated with the + * resource to finish being used by the GPU. + * + * Valid D3DLOCK flags: + * - D3DLOCK_READONLY: Only waits for writes + * - D3DLOCK_DONOTWAIT: Does not wait for the resource (may flush) + * + * \param [in] pResource Resource to be waited upon + * \param [in] MapFlags D3DLOCK flags + * \returns true if the resource is ready to use, + * false if the resource is till in use + */ + virtual bool STDMETHODCALLTYPE WaitForResource( + IDirect3DResource9* pResource, + DWORD MapFlags) = 0; +}; + +/** + * \brief D3D9 current output metadata + */ +struct D3D9VkExtOutputMetadata { + float RedPrimary[2]; + float GreenPrimary[2]; + float BluePrimary[2]; + float WhitePoint[2]; + float MinLuminance; + float MaxLuminance; + float MaxFullFrameLuminance; +}; + +/** + * \brief D3D9 extended swapchain + */ +struct ID3D9VkExtSwapchain : public IUnknown{ + virtual BOOL STDMETHODCALLTYPE CheckColorSpaceSupport( + VkColorSpaceKHR ColorSpace) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetColorSpace( + VkColorSpaceKHR ColorSpace) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetHDRMetaData( + const VkHdrMetadataEXT* pHDRMetadata) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentOutputDesc( + D3D9VkExtOutputMetadata* pOutputDesc) = 0; + + virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0; +}; + +// DXGI +constexpr static GUID IDXGIDXVKAdapterGUID { 0x907bf281, 0xea3c, 0x43b4, { 0xa8, 0xe4, 0x9f, 0x23, 0x11, 0x07, 0xb4, 0xff } }; +constexpr static GUID IDXGIDXVKDeviceGUID { 0x92a5d77b, 0xb6e1, 0x420a, { 0xb2, 0x60, 0xfd, 0xf7, 0x01, 0x27, 0x28, 0x27 } }; +constexpr static GUID IDXGIVkMonitorInfoGUID { 0xc06a236f, 0x5be3, 0x448a, { 0x89, 0x43, 0x89, 0xc6, 0x11, 0xc0, 0xc2, 0xc1 } }; +constexpr static GUID IDXGIVkInteropFactoryGUID { 0x4c5e1b0d, 0xb0c8, 0x4131, { 0xbf, 0xd8, 0x9b, 0x24, 0x76, 0xf7, 0xf4, 0x08 } }; +constexpr static GUID IDXGIVkInteropFactory1GUID { 0x2a289dbd, 0x2d0a, 0x4a51, { 0x89, 0xf7, 0xf2, 0xad, 0xce, 0x46, 0x5c, 0xd6 } }; +constexpr static GUID IDXGIVkInteropAdapterGUID { 0x3a6d8f2c, 0xb0e8, 0x4ab4, { 0xb4, 0xdc, 0x4f, 0xd2, 0x48, 0x91, 0xbf, 0xa5 } }; +constexpr static GUID IDXGIVkInteropDeviceGUID { 0xe2ef5fa5, 0xdc21, 0x4af7, { 0x90, 0xc4, 0xf6, 0x7e, 0xf6, 0xa0, 0x93, 0x23 } }; +constexpr static GUID IDXGIVkInteropDevice1GUID { 0xe2ef5fa5, 0xdc21, 0x4af7, { 0x90, 0xc4, 0xf6, 0x7e, 0xf6, 0xa0, 0x93, 0x24 } }; +constexpr static GUID IDXGIVkInteropSurfaceGUID { 0x5546cf8c, 0x77e7, 0x4341, { 0xb0, 0x5d, 0x8d, 0x4d, 0x50, 0x00, 0xe7, 0x7d } }; +constexpr static GUID IDXGIVkSurfaceFactoryGUID { 0x1e7895a1, 0x1bc3, 0x4f9c, { 0xa6, 0x70, 0x29, 0x0a, 0x4b, 0xc9, 0x58, 0x1a } }; +constexpr static GUID IDXGIVkSwapChainGUID { 0xe4a9059e, 0xb569, 0x46ab, { 0x8d, 0xe7, 0x50, 0x1b, 0xd2, 0xbc, 0x7f, 0x7a } }; +constexpr static GUID IDXGIVkSwapChain1GUID { 0x785326d4, 0xb77b, 0x4826, { 0xae, 0x70, 0x8d, 0x08, 0x30, 0x8e, 0xe6, 0xd1 } }; +constexpr static GUID IDXGIVkSwapChain2GUID { 0xaed91093, 0xe02e, 0x458c, { 0xbd, 0xef, 0xa9, 0x7d, 0xa1, 0xa7, 0xe6, 0xd2 } }; +constexpr static GUID IDXGIVkSwapChainFactoryGUID{ 0xe7d6c3ca, 0x23a0, 0x4e08, { 0x9f, 0x2f, 0xea, 0x52, 0x31, 0xdf, 0x66, 0x33 } }; + +/** + * \brief DXGI factory interface for Vulkan interop + */ +struct IDXGIVkInteropFactory : public IUnknown{ + /** + * \brief Queries Vulkan instance used by DXVK + * + * \param [out] pInstance The Vulkan instance + * \param [out] ppfnVkGetInstanceProcAddr Vulkan entry point + */ + virtual void STDMETHODCALLTYPE GetVulkanInstance( + VkInstance * pInstance, + PFN_vkGetInstanceProcAddr * ppfnVkGetInstanceProcAddr) = 0; +}; + +/** + * \brief Private DXGI device interface + */ +struct IDXGIDXVKDevice : public IUnknown{ + virtual void STDMETHODCALLTYPE SetAPIVersion( + UINT Version) = 0; + + virtual UINT STDMETHODCALLTYPE GetAPIVersion() = 0; + +}; + +struct IDXGIVkInteropDevice; + +/** + * \brief DXGI surface interface for Vulkan interop + * + * Provides access to the backing resource of a + * DXGI surface, which is typically a D3D texture. + */ +struct IDXGIVkInteropSurface : public IUnknown{ + /** + * \brief Retrieves device interop interfaceSlots + * + * Queries the device that owns the surface for + * the \ref IDXGIVkInteropDevice interface. + * \param [out] ppDevice The device interface + * \returns \c S_OK on success + */ + virtual HRESULT STDMETHODCALLTYPE GetDevice( + IDXGIVkInteropDevice **ppDevice) = 0; + + /** + * \brief Retrieves Vulkan image info + * + * Retrieves both the image handle as well as the image's + * properties. Any of the given pointers may be \c nullptr. + * + * If \c pInfo is not \c nullptr, the following rules apply: + * - \c pInfo->sType \e must be \c VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO + * - \c pInfo->pNext \e must be \c nullptr or point to a supported + * extension-specific structure (currently none) + * - \c pInfo->queueFamilyIndexCount must be the length of the + * \c pInfo->pQueueFamilyIndices array, in \c uint32_t units. + * - \c pInfo->pQueueFamilyIndices must point to a pre-allocated + * array of \c uint32_t of size \c pInfo->pQueueFamilyIndices. + * + * \note As of now, the sharing mode will always be + * \c VK_SHARING_MODE_EXCLUSIVE and no queue + * family indices will be written to the array. + * + * After the call, the structure pointed to by \c pInfo can + * be used to create an image with identical properties. + * + * If \c pLayout is not \c nullptr, it will receive the + * layout that the image will be in after flushing any + * outstanding commands on the device. + * \param [out] pHandle The image handle + * \param [out] pLayout Image layout + * \param [out] pInfo Image properties + * \returns \c S_OK on success, or \c E_INVALIDARG + */ + virtual HRESULT STDMETHODCALLTYPE GetVulkanImageInfo( + VkImage* pHandle, + VkImageLayout* pLayout, + VkImageCreateInfo* pInfo) = 0; +}; + +/** + * \brief DXGI device interface for Vulkan interop + * + * Provides access to the device and instance handles + * as well as the queue that is used for rendering. + */ +struct IDXGIVkInteropDevice : public IUnknown{ + /** + * \brief Queries Vulkan handles used by DXVK + * + * \param [out] pInstance The Vulkan instance + * \param [out] pPhysDev The physical device + * \param [out] pDevide The device handle + */ + virtual void STDMETHODCALLTYPE GetVulkanHandles( + VkInstance * pInstance, + VkPhysicalDevice * pPhysDev, + VkDevice * pDevice) = 0; + + /** + * \brief Queries the rendering queue used by DXVK + * + * \param [out] pQueue The Vulkan queue handle + * \param [out] pQueueFamilyIndex Queue family index + */ + virtual void STDMETHODCALLTYPE GetSubmissionQueue( + VkQueue* pQueue, + uint32_t* pQueueFamilyIndex) = 0; + + /** + * \brief Transitions a surface to a given layout + * + * Executes an explicit image layout transition on the + * D3D device. Note that the image subresources \e must + * be transitioned back to its original layout before + * using it again from D3D11. + * \param [in] pSurface The image to transform + * \param [in] pSubresources Subresources to transform + * \param [in] OldLayout Current image layout + * \param [in] NewLayout Desired image layout + */ + virtual void STDMETHODCALLTYPE TransitionSurfaceLayout( + IDXGIVkInteropSurface* pSurface, + const VkImageSubresourceRange* pSubresources, + VkImageLayout OldLayout, + VkImageLayout NewLayout) = 0; + + /** + * \brief Flushes outstanding D3D rendering commands + * + * Must be called before submitting Vulkan commands + * to the rendering queue if those commands use the + * backing resource of a D3D11 object. + */ + virtual void STDMETHODCALLTYPE FlushRenderingCommands() = 0; + + /** + * \brief Locks submission queue + * + * Should be called immediately before submitting + * Vulkan commands to the rendering queue in order + * to prevent DXVK from using the queue. + * + * While the submission queue is locked, no D3D11 + * methods must be called from the locking thread, + * or otherwise a deadlock might occur. + */ + virtual void STDMETHODCALLTYPE LockSubmissionQueue() = 0; + + /** + * \brief Releases submission queue + * + * Should be called immediately after submitting + * Vulkan commands to the rendering queue in order + * to allow DXVK to submit new commands. + */ + virtual void STDMETHODCALLTYPE ReleaseSubmissionQueue() = 0; +}; + +struct D3D11_TEXTURE2D_DESC1; +struct ID3D11Texture2D; + +/** + * \brief See IDXGIVkInteropDevice. + */ +struct IDXGIVkInteropDevice1 : public IDXGIVkInteropDevice{ + /** + * \brief Queries the rendering queue used by DXVK + * + * \param [out] pQueue The Vulkan queue handle + * \param [out] pQueueIndex Queue index + * \param [out] pQueueFamilyIndex Queue family index + */ + virtual void STDMETHODCALLTYPE GetSubmissionQueue1( + VkQueue * pQueue, + uint32_t * pQueueIndex, + uint32_t * pQueueFamilyIndex) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateTexture2DFromVkImage( + const D3D11_TEXTURE2D_DESC1* pDesc, + VkImage vkImage, + ID3D11Texture2D** ppTexture2D) = 0; +}; + +} \ No newline at end of file diff --git a/src/Windows/OpenGLHook.cpp b/src/Windows/OpenGLHook.cpp index 2dec8ea..9b9920d 100644 --- a/src/Windows/OpenGLHook.cpp +++ b/src/Windows/OpenGLHook.cpp @@ -27,7 +27,7 @@ namespace InGameOverlay { -#define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&OpenGLHook_t::_My##NAME))) { \ +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&OpenGLHook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ UnhookAll();\ return false;\ @@ -51,7 +51,7 @@ bool OpenGLHook_t::StartHook(std::function keyCombinationCallback, Toggl _WindowsHooked = true; BeginHook(); - TRY_HOOK_FUNCTION(WGLSwapBuffers); + TRY_HOOK_FUNCTION_OR_FAIL(WGLSwapBuffers); EndHook(); INGAMEOVERLAY_INFO("Hooked OpenGL"); diff --git a/src/Windows/RendererDetector.cpp b/src/Windows/RendererDetector.cpp index 7273c39..322461d 100644 --- a/src/Windows/RendererDetector.cpp +++ b/src/Windows/RendererDetector.cpp @@ -38,6 +38,7 @@ #include "DX9Hook.h" #include "OpenGLHook.h" #include "VulkanHook.h" +#include "DXVKDetector.h" #include "DirectXVTables.h" @@ -248,6 +249,53 @@ static void GetDXGIFunctions(IDXGISwapChain* pSwapChain, IDXGISwapChain1* pSwapC } } +static bool DX9DeviceIsDXVK(IUnknown* pDevice) +{ + ID3D9VkInteropDevice* dxvkDevice9 = nullptr; + pDevice->QueryInterface(ID3D9VkInteropDeviceGUID, (void**)&dxvkDevice9); + // Will only work on newer DXVK + if (dxvkDevice9 == nullptr) + return false; + + dxvkDevice9->Release(); + return true; +} + +static bool DXGIDeviceIsDXVK(IUnknown* pDevice) +{ + IDXGIVkInteropDevice* pDxvkInterface = nullptr; + pDevice->QueryInterface(IDXGIVkInteropDeviceGUID, (void**)&pDxvkInterface); + if (pDxvkInterface == nullptr) + return false; + +#ifdef INGAMEOVERLAY_USE_SPDLOG + VkInstance vkInstance; + VkPhysicalDevice vkPhysicalDevice; + VkDevice vkDevice; + + pDxvkInterface->GetVulkanHandles(&vkInstance, &vkPhysicalDevice, &vkDevice); + + void* hVulkan = System::Library::GetLibraryHandle(VULKAN_DLL_NAME); + if (hVulkan != nullptr) + { + auto _vkGetInstanceProcAddr = (decltype(::vkGetInstanceProcAddr)*)System::Library::GetSymbol(hVulkan, "vkGetInstanceProcAddr"); + if (_vkGetInstanceProcAddr != nullptr) + { + auto _vkGetPhysicalDeviceProperties = (decltype(::vkGetPhysicalDeviceProperties)*)_vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties"); + if (_vkGetPhysicalDeviceProperties != nullptr) + { + VkPhysicalDeviceProperties vkPhysicalDeviceProperties; + _vkGetPhysicalDeviceProperties(vkPhysicalDevice, &vkPhysicalDeviceProperties); + INGAMEOVERLAY_INFO("DXVK Device: {}", vkPhysicalDeviceProperties.deviceName); + } + } + } +#endif + + pDxvkInterface->Release(); + return true; +} + static DirectX9Driver_t GetDX9Driver(std::string const& directX9LibraryPath, HWND windowHandle) { DirectX9Driver_t driver{}; @@ -559,10 +607,14 @@ static DirectX12Driver_t GetDX12Driver(std::string const& directX12LibraryPath, auto D3D12CreateDevice = (decltype(::D3D12CreateDevice)*)System::Library::GetSymbol(hD3D12, "D3D12CreateDevice"); if (D3D12CreateDevice != nullptr) { + INGAMEOVERLAY_DEBUG("Creating D3D12 device..."); D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&pDevice)); if (pDevice != nullptr) { + INGAMEOVERLAY_DEBUG("Created D3D12 device!"); + INGAMEOVERLAY_DEBUG("Creating CommandQueue..."); + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; @@ -570,9 +622,11 @@ static DirectX12Driver_t GetDX12Driver(std::string const& directX12LibraryPath, if (pCommandQueue != nullptr) { + INGAMEOVERLAY_DEBUG("Created CommandQueue!"); decltype(CreateDXGIFactory1)* CreateDXGIFactory1 = (decltype(CreateDXGIFactory1))System::Library::GetSymbol(dxgi, "CreateDXGIFactory1"); if (CreateDXGIFactory1 != nullptr) { + INGAMEOVERLAY_DEBUG("Creating DXGI Factory..."); CreateDXGIFactory1(IID_PPV_ARGS(&pDXGIFactory)); if (pDXGIFactory != nullptr) { @@ -588,6 +642,8 @@ static DirectX12Driver_t GetDX12Driver(std::string const& directX12LibraryPath, SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + INGAMEOVERLAY_DEBUG("Created DXGI Factory!"); + INGAMEOVERLAY_DEBUG("Creating SwapChain..."); pDXGIFactory->CreateSwapChainForHwnd(pCommandQueue, windowHandle, &SwapChainDesc, NULL, NULL, &pSwapChain); } } @@ -597,6 +653,7 @@ static DirectX12Driver_t GetDX12Driver(std::string const& directX12LibraryPath, if (pCommandQueue != nullptr && pSwapChain != nullptr) { + INGAMEOVERLAY_DEBUG("Created SwapChain!"); IDXGISwapChain3* pSwapChain3; pSwapChain->QueryInterface(IID_PPV_ARGS(&pSwapChain3)); @@ -899,12 +956,15 @@ class RendererDetector_t void _DeduceDXVersionFromSwapChain(IDXGISwapChain* pSwapChain) { IUnknown* pDevice = nullptr; - if (Inst()->_DX12Hooked) + + if (_DX12Hooked) { pSwapChain->GetDevice(IID_PPV_ARGS(reinterpret_cast(&pDevice))); } if (pDevice != nullptr) { + // DXVK doesn't support (yet) DX12, vkd3d is used instead. + INGAMEOVERLAY_DEBUG("Detected DX12"); _HookDetected(_DX12Hook); } else @@ -912,7 +972,7 @@ class RendererDetector_t if (_DX11Hooked) { pSwapChain->GetDevice(IID_PPV_ARGS(reinterpret_cast(&pDevice))); - if (pDevice != nullptr) + if (pDevice != nullptr && _DX10Hooked) { // It seems that when you are using a DX10 device, sometimes, the swapchain has a DX11 device. ID3D10Device* pD10Device = nullptr; @@ -927,6 +987,15 @@ class RendererDetector_t } if (pDevice != nullptr) { + if (DXGIDeviceIsDXVK(pDevice)) + { + _DX11Hook->SetDXVK(); + INGAMEOVERLAY_DEBUG("Detected DX11 (DXVK)"); + } + else + { + INGAMEOVERLAY_DEBUG("Detected DX11"); + } _HookDetected(_DX11Hook); } else @@ -937,6 +1006,15 @@ class RendererDetector_t } if (pDevice != nullptr) { + if (DXGIDeviceIsDXVK(pDevice)) + { + _DX10Hook->SetDXVK(); + INGAMEOVERLAY_DEBUG("Detected DX10 (DXVK)"); + } + else + { + INGAMEOVERLAY_DEBUG("Detected DX10"); + } _HookDetected(_DX10Hook); } } @@ -993,6 +1071,16 @@ class RendererDetector_t if (!inst->_DetectionStarted || inst->_DetectionDone) return res; + if (DX9DeviceIsDXVK(_this)) + { + inst->_DX9Hook->SetDXVK(); + INGAMEOVERLAY_DEBUG("Detected DX9 (DXVK)"); + } + else + { + INGAMEOVERLAY_DEBUG("Detected DX9"); + } + inst->_HookDetected(inst->_DX9Hook); return res; @@ -1008,6 +1096,15 @@ class RendererDetector_t if (!inst->_DetectionStarted || inst->_DetectionDone) return res; + if (DX9DeviceIsDXVK(_this)) + { + inst->_DX9Hook->SetDXVK(); + INGAMEOVERLAY_DEBUG("Detected DX9 (DXVK)"); + } + else + { + INGAMEOVERLAY_DEBUG("Detected DX9"); + } inst->_HookDetected(inst->_DX9Hook); return res; @@ -1016,13 +1113,34 @@ class RendererDetector_t 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->_RendererMutex); + // Some implementations redirect to DX9 swapchain. + std::unique_lock lk(inst->_RendererMutex, std::try_to_lock); INGAMEOVERLAY_INFO("IDirect3DSwapChain9::Present"); auto res = (_this->*inst->_IDirect3DSwapChain9Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); if (!inst->_DetectionStarted || inst->_DetectionDone) return res; + IDirect3DDevice9 *pDevice; + if (SUCCEEDED(_this->GetDevice(&pDevice))) + { + if (DX9DeviceIsDXVK(pDevice)) + { + inst->_DX9Hook->SetDXVK(); + INGAMEOVERLAY_DEBUG("Detected DX9 (DXVK)"); + } + else + { + INGAMEOVERLAY_DEBUG("Detected DX9"); + } + + pDevice->Release(); + } + else + { + INGAMEOVERLAY_DEBUG("Detected DX9"); + } + inst->_HookDetected(inst->_DX9Hook); return res; @@ -1056,6 +1174,7 @@ class RendererDetector_t if (!inst->_DetectionStarted || !inst->_VulkanHooked || inst->_DetectionDone) return res; + INGAMEOVERLAY_DEBUG("Detected Vulkan"); inst->_HookDetected(inst->_VulkanHook); return res; @@ -1397,16 +1516,21 @@ class RendererDetector_t void* libraryHandle = System::Library::GetLibraryHandle(libraryPath.c_str()); if (libraryHandle != nullptr) { + INGAMEOVERLAY_DEBUG("Waiting for renderer mutex for {}...", libraryPath); std::lock_guard lk(_RendererMutex); + INGAMEOVERLAY_DEBUG("Got renderer mutex for {}...", libraryPath); (this->*library.DetectionProcedure)(System::Library::GetLibraryPath(libraryHandle), preferSystemLibraries); } } } + INGAMEOVERLAY_DEBUG("Detection started"); _StopDetectionConditionVariable.wait_for(lck, std::chrono::milliseconds{ 100 }); if (!_DetectionStarted) { + INGAMEOVERLAY_DEBUG("Detection started 1"); std::lock_guard lck(_RendererMutex); + INGAMEOVERLAY_DEBUG("Detection started 2"); _DetectionStarted = true; } diff --git a/src/Windows/VulkanHook.cpp b/src/Windows/VulkanHook.cpp index f997cef..2603692 100644 --- a/src/Windows/VulkanHook.cpp +++ b/src/Windows/VulkanHook.cpp @@ -27,7 +27,7 @@ namespace InGameOverlay { -#define TRY_HOOK_FUNCTION(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&VulkanHook_t::_My##NAME))) { \ +#define TRY_HOOK_FUNCTION_OR_FAIL(NAME) do { if (!HookFunc(std::make_pair(&(void*&)_##NAME, (void*)&VulkanHook_t::_My##NAME))) { \ INGAMEOVERLAY_ERROR("Failed to hook {}", #NAME);\ return false;\ } } while(0) @@ -72,13 +72,13 @@ bool VulkanHook_t::StartHook(std::function keyCombinationCallback, Toggl _WindowsHooked = true; BeginHook(); - TRY_HOOK_FUNCTION(VkAcquireNextImageKHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkAcquireNextImageKHR); if (_VkAcquireNextImage2KHR != nullptr) - TRY_HOOK_FUNCTION(VkAcquireNextImage2KHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkAcquireNextImage2KHR); - TRY_HOOK_FUNCTION(VkQueuePresentKHR); - TRY_HOOK_FUNCTION(VkCreateSwapchainKHR); - TRY_HOOK_FUNCTION(VkDestroyDevice); + TRY_HOOK_FUNCTION_OR_FAIL(VkQueuePresentKHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkCreateSwapchainKHR); + TRY_HOOK_FUNCTION_OR_FAIL(VkDestroyDevice); EndHook(); INGAMEOVERLAY_INFO("Hooked Vulkan"); @@ -267,7 +267,7 @@ bool VulkanHook_t::_CreateRenderTargets(VkSwapchainKHR swapChain) VkImageViewCreateInfo info = { }; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VK_FORMAT_B8G8R8A8_UNORM; + info.format = _VulkanTargetFormat; info.image = frame.BackBuffer; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -452,6 +452,7 @@ bool VulkanHook_t::_CreateVulkanInstance() LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceMemoryProperties); LOAD_VULKAN_FUNCTION(vkEnumerateDeviceExtensionProperties); LOAD_VULKAN_FUNCTION(vkEnumeratePhysicalDevices); + LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR); LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceProperties); LOAD_VULKAN_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); #undef LOAD_VULKAN_FUNCTION @@ -504,6 +505,70 @@ int32_t VulkanHook_t::_GetPhysicalDeviceFirstGraphicsQueue(VkPhysicalDevice phys return -1; } +void VulkanHook_t::_SelectFormatSurface() +{ + _VulkanTargetFormat = VK_FORMAT_R8G8B8A8_UNORM; + + // TODO: Find a way to use something like this: + //static constexpr VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + //static constexpr uint32_t requestSurfaceImageFormatCount = sizeof(requestSurfaceImageFormat)/sizeof(*requestSurfaceImageFormat); + //static constexpr VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + + //VkResult err; + //VkWin32SurfaceCreateInfoKHR sci; + //PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; + // + //vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) + // vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); + //if (!vkCreateWin32SurfaceKHR) + //{ + // _glfwInputError(GLFW_API_UNAVAILABLE, + // "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); + // return VK_ERROR_EXTENSION_NOT_PRESENT; + //} + // + //memset(&sci, 0, sizeof(sci)); + //sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + //sci.hinstance = _glfw.win32.instance; + //sci.hwnd = window->win32.handle; + // + //err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); + + //uint32_t availCount; + //_vkGetPhysicalDeviceSurfaceFormatsKHR(_VulkanPhysicalDevice, surface, &availCount, nullptr); + //std::vector availFormat; + //availFormat.resize((int)availCount); + //_vkGetPhysicalDeviceSurfaceFormatsKHR(_VulkanPhysicalDevice, surface, &availCount, availFormat.data()); + // + //// First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + //if (availCount == 1) + //{ + // if (availFormat[0].format == VK_FORMAT_UNDEFINED) + // { + // return VkSurfaceFormatKHR{ + // requestSurfaceImageFormat[0], + // requestSurfaceColorSpace + // }; + // } + // + // // No point in searching another format + // return availFormat[0]; + //} + // + //// Request several formats, the first found will be used + //for (uint32_t i = 0; i < requestSurfaceImageFormatCount; ++i) + //{ + // for (uint32_t j = 0; j < availCount; ++j) + // { + // if (availFormat[i].format == requestSurfaceImageFormat[i] && availFormat[j].colorSpace == requestSurfaceColorSpace) + // return availFormat[i]; + // } + //} + // + //// If none of the requested image formats could be found, use the first available + //return availFormat[0]; +} + bool VulkanHook_t::_GetPhysicalDevice() { _VulkanPhysicalDevice = nullptr; @@ -670,7 +735,7 @@ bool VulkanHook_t::_CreateRenderPass() return true; VkAttachmentDescription attachment = { }; - attachment.format = VK_FORMAT_B8G8R8A8_UNORM; + attachment.format = _VulkanTargetFormat; attachment.samples = VK_SAMPLE_COUNT_1_BIT; attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -759,6 +824,8 @@ void VulkanHook_t::_PrepareForOverlay(VkQueue queue, const VkPresentInfoKHR* pPr WindowsHook_t::Inst()->SetInitialWindowSize(_MainWindow); + _SelectFormatSurface(); + if (_VulkanQueue == nullptr) _vkGetDeviceQueue(_VulkanDevice, _VulkanQueueFamily, 0, &_VulkanQueue); @@ -936,6 +1003,7 @@ VKAPI_ATTR VkResult VKAPI_CALL VulkanHook_t::_MyVkCreateSwapchainKHR(VkDevice de inst->_ResetRenderState(OverlayHookState::Reset); inst->_DestroyRenderTargets(); } + inst->_VulkanTargetFormat = pCreateInfo->imageFormat; auto res = inst->_VkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); if (inst->_VulkanDevice == device && res == VkResult::VK_SUCCESS) { @@ -971,6 +1039,7 @@ VulkanHook_t::VulkanHook_t(): _VulkanImageSampler(VK_NULL_HANDLE), _VulkanImageDescriptorSetLayout(VK_NULL_HANDLE), _VulkanRenderPass(VK_NULL_HANDLE), + _VulkanTargetFormat(VK_FORMAT_R8G8B8A8_UNORM), _VulkanDevice(VK_NULL_HANDLE), _VulkanQueue(VK_NULL_HANDLE), _ImGuiFontAtlas(nullptr), @@ -1036,6 +1105,7 @@ VulkanHook_t::VulkanHook_t(): _vkGetBufferMemoryRequirements(nullptr), _vkGetImageMemoryRequirements(nullptr), _vkEnumeratePhysicalDevices(nullptr), + _vkGetPhysicalDeviceSurfaceFormatsKHR(nullptr), _vkGetPhysicalDeviceProperties(nullptr), _vkGetPhysicalDeviceQueueFamilyProperties(nullptr), _vkGetPhysicalDeviceMemoryProperties(nullptr), diff --git a/src/Windows/VulkanHook.h b/src/Windows/VulkanHook.h index 50a2859..8e6a5be 100644 --- a/src/Windows/VulkanHook.h +++ b/src/Windows/VulkanHook.h @@ -82,6 +82,7 @@ class VulkanHook_t : std::vector _Frames; VkRenderPass _VulkanRenderPass; std::vector _DescriptorsPools; + VkFormat _VulkanTargetFormat; VkDevice _VulkanDevice; VkQueue _VulkanQueue; @@ -112,6 +113,7 @@ class VulkanHook_t : void _FreeVulkanRessources(); bool _CreateVulkanInstance(); int32_t _GetPhysicalDeviceFirstGraphicsQueue(VkPhysicalDevice physicalDevice); + void _SelectFormatSurface(); bool _GetPhysicalDevice(); bool _CreateImageSampler(); @@ -203,6 +205,7 @@ class VulkanHook_t : decltype(::vkGetBufferMemoryRequirements) *_vkGetBufferMemoryRequirements; decltype(::vkGetImageMemoryRequirements) *_vkGetImageMemoryRequirements; decltype(::vkEnumeratePhysicalDevices) *_vkEnumeratePhysicalDevices; + decltype(::vkGetPhysicalDeviceSurfaceFormatsKHR) *_vkGetPhysicalDeviceSurfaceFormatsKHR; decltype(::vkGetPhysicalDeviceProperties) *_vkGetPhysicalDeviceProperties; decltype(::vkGetPhysicalDeviceQueueFamilyProperties) *_vkGetPhysicalDeviceQueueFamilyProperties; decltype(::vkGetPhysicalDeviceMemoryProperties) *_vkGetPhysicalDeviceMemoryProperties;