diff --git a/src/d3d9/d3d9_interfaces.h b/src/d3d9/d3d9_interfaces.h index c4956e597b3..5f8c6565856 100644 --- a/src/d3d9/d3d9_interfaces.h +++ b/src/d3d9/d3d9_interfaces.h @@ -3,6 +3,42 @@ #include "d3d9_include.h" #include "../vulkan/vulkan_loader.h" +#include "../dxvk/dxvk_device_info.h" + +using D3D9VkQueueLockCallback = void(bool); + +/** + * \brief Device create info + */ +struct D3D9VkDeviceCreateInfo { + VkDeviceCreateInfo info; + dxvk::DxvkDeviceFeatures features; + uint32_t graphicsQueueFamily; + uint32_t transferQueueFamily; + uint32_t sparseQueueFamily; + VkDeviceQueueCreateInfo* pQueueCreateInfos; + const char** ppEnabledExtensionNames; +}; + +/** + * \brief Device import info + */ +struct D3D9VkDeviceImportInfo { + VkDevice device; + D3DDEVTYPE deviceType; + VkQueue graphicsQueue; + uint32_t graphicsQueueFamily; + VkQueue transferQueue; + uint32_t transferQueueFamily; + VkQueue sparseQueue; + uint32_t sparseQueueFamily; + uint32_t extensionCount; + const char** extensionNames; + const VkPhysicalDeviceFeatures2* features; + D3D9VkQueueLockCallback* queueLockCallback; +}; + + /** * \brief D3D9 interface for Vulkan interop * @@ -48,6 +84,49 @@ ID3D9VkInteropInterface1 : public ID3D9VkInteropInterface { virtual HRESULT STDMETHODCALLTYPE GetInstanceExtensions( UINT* pExtensionCount, const char** ppExtensions) = 0; + + /** + * \brief Gets the device creation info for a D3D9 adapter + * + * When done using the info, call FreeDeviceCreateInfo to release it. + * + * \param [in] Adapter Adapter ordinal + * \param [out] ppCreateInfo Device create info + */ + virtual HRESULT STDMETHODCALLTYPE GetDeviceCreateInfo( + UINT Adapter, + D3D9VkDeviceCreateInfo** ppCreateInfo) = 0; + + /** + * \brief Releases device creation info returned by GetDeviceCreateInfo + * + * \param [out] pCreateInfo Device create info + */ + virtual void STDMETHODCALLTYPE FreeDeviceCreateInfo( + D3D9VkDeviceCreateInfo* pCreateInfo) = 0; + + /** + * \brief Create a D3D9 device for an existing Vulkan device + * + * It is suggested to create the device with the + * VkDeviceCreateInfo returned by GetDeviceCreateInfo, + * which will specify everything the device needs for + * DXVK to function. + * + * \param [in] Adapter Adapter ordinal + * \param [in] ... Arguments to IDirect3D9Ex::CreateDeviceEx + * \param [in] pInfo Info about the created device + * \param [out] ppReturnedDevice The D3D9 device + */ + virtual HRESULT STDMETHODCALLTYPE ImportDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + D3DDISPLAYMODEEX* pFullscreenDisplayMode, + D3D9VkDeviceImportInfo* pInfo, + IDirect3DDevice9Ex** ppReturnedDevice) = 0; }; /** diff --git a/src/d3d9/d3d9_interop.cpp b/src/d3d9/d3d9_interop.cpp index 079f2919a65..d09a1c78321 100644 --- a/src/d3d9/d3d9_interop.cpp +++ b/src/d3d9/d3d9_interop.cpp @@ -76,6 +76,160 @@ namespace dxvk { return (count < maxCount) ? D3DERR_MOREDATA : D3D_OK; } + HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::GetDeviceCreateInfo( + UINT Adapter, + D3D9VkDeviceCreateInfo** ppCreateInfo) { + if (unlikely(ppCreateInfo == nullptr)) + return D3DERR_INVALIDCALL; + InitReturnPtr(ppCreateInfo); + + D3D9VkDeviceCreateInfo* pCreateInfo = new D3D9VkDeviceCreateInfo; + + auto* adapter = m_interface->GetAdapter(Adapter); + if (unlikely(adapter == nullptr)) + return D3DERR_INVALIDCALL; + + auto dxvkAdapter = adapter->GetDXVKAdapter(); + + pCreateInfo->features = D3D9DeviceEx::GetDeviceFeatures(dxvkAdapter); + + DxvkDeviceCreateExtInfo extInfo; + DxvkDeviceCreateQueueInfo queueInfo; + bool success = dxvkAdapter->getDeviceCreateInfo( + m_interface->GetInstance(), + pCreateInfo->info, + pCreateInfo->features, + extInfo, + queueInfo); + + if (!success) { + delete pCreateInfo; + return D3DERR_INVALIDCALL; + } + + // Queue family indices + pCreateInfo->graphicsQueueFamily = queueInfo.queueFamilies.graphics; + pCreateInfo->transferQueueFamily = queueInfo.queueFamilies.transfer; + pCreateInfo->sparseQueueFamily = queueInfo.queueFamilies.sparse; + + // Queue create infos + const size_t queueCount = queueInfo.queueInfos.size(); + pCreateInfo->pQueueCreateInfos = queueCount ? new VkDeviceQueueCreateInfo[queueCount] : nullptr; + for (size_t i = 0; i < queueCount; i++) { + pCreateInfo->pQueueCreateInfos[i] = queueInfo.queueInfos[i]; + } + pCreateInfo->info.pQueueCreateInfos = pCreateInfo->pQueueCreateInfos; + pCreateInfo->info.queueCreateInfoCount = queueCount; + + // Extension names + const uint32_t extCount = extInfo.extensionNameList.count(); + pCreateInfo->ppEnabledExtensionNames = extCount > 0 ? new const char*[extCount] : nullptr; + for (uint32_t i = 0; i < extCount; i++) { + const char* nameStr = extInfo.extensionNameList.name(i); + size_t nameLen = std::strlen(nameStr); + char* name = new char[nameLen + 1]; + std::strncpy(name, nameStr, nameLen); + name[nameLen] = '\0'; + pCreateInfo->ppEnabledExtensionNames[i] = name; + } + pCreateInfo->info.ppEnabledExtensionNames = pCreateInfo->ppEnabledExtensionNames; + pCreateInfo->info.enabledExtensionCount = extCount; + + *ppCreateInfo = pCreateInfo; + return D3D_OK; + } + + void STDMETHODCALLTYPE D3D9VkInteropInterface::FreeDeviceCreateInfo( + D3D9VkDeviceCreateInfo* pCreateInfo) { + if (!pCreateInfo) + return; + + if (pCreateInfo->ppEnabledExtensionNames != nullptr) { + for (uint32_t i = 0; i < pCreateInfo->info.enabledExtensionCount; i++) { + delete pCreateInfo->ppEnabledExtensionNames[i]; + } + + delete[] pCreateInfo->ppEnabledExtensionNames; + } + + if (pCreateInfo->pQueueCreateInfos != nullptr) + delete[] pCreateInfo->pQueueCreateInfos; + delete pCreateInfo; + } + + HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::ImportDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + D3DDISPLAYMODEEX* pFullscreenDisplayMode, + D3D9VkDeviceImportInfo* pInfo, + IDirect3DDevice9Ex** ppReturnedDevice) { + InitReturnPtr(ppReturnedDevice); + + if (unlikely(ppReturnedDevice == nullptr + || pPresentationParameters == nullptr)) + return D3DERR_INVALIDCALL; + + // Creating a device with D3DCREATE_PUREDEVICE only works in conjunction + // with D3DCREATE_HARDWARE_VERTEXPROCESSING on native drivers. + if (unlikely(BehaviorFlags & D3DCREATE_PUREDEVICE && + !(BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING))) + return D3DERR_INVALIDCALL; + + HRESULT hr; + // Black Desert creates a D3DDEVTYPE_NULLREF device and + // expects it be created despite passing invalid parameters. + if (likely(DeviceType != D3DDEVTYPE_NULLREF)) { + hr = m_interface->ValidatePresentationParameters(pPresentationParameters); + + if (unlikely(FAILED(hr))) + return hr; + } + + auto* adapter = m_interface->GetAdapter(Adapter); + + if (adapter == nullptr) + return D3DERR_INVALIDCALL; + + auto dxvkAdapter = adapter->GetDXVKAdapter(); + + DxvkDeviceImportInfo info; + info.device = pInfo->device; + info.queue = pInfo->graphicsQueue; + info.queueFamily = pInfo->graphicsQueueFamily; + info.extensionCount = pInfo->extensionCount; + info.extensionNames = pInfo->extensionNames; + info.features = pInfo->features; + info.queueCallback = pInfo->queueLockCallback; + + try { + auto dxvkDevice = dxvkAdapter->importDevice(m_interface->GetInstance(), info); + + auto* device = new D3D9DeviceEx( + m_interface, + adapter, + DeviceType, + hFocusWindow, + BehaviorFlags, + dxvkDevice); + + hr = device->InitialReset(pPresentationParameters, pFullscreenDisplayMode); + + if (unlikely(FAILED(hr))) + return hr; + + *ppReturnedDevice = ref(device); + } + catch (const DxvkError& e) { + Logger::err(e.message()); + return D3DERR_NOTAVAILABLE; + } + + return D3D_OK; + } + //////////////////////////////// // Texture Interop /////////////////////////////// diff --git a/src/d3d9/d3d9_interop.h b/src/d3d9/d3d9_interop.h index b5ef1e2661d..db602dfde82 100644 --- a/src/d3d9/d3d9_interop.h +++ b/src/d3d9/d3d9_interop.h @@ -38,6 +38,23 @@ namespace dxvk { UINT* pExtensionCount, const char** ppExtensions); + HRESULT STDMETHODCALLTYPE GetDeviceCreateInfo( + UINT Adapter, + D3D9VkDeviceCreateInfo** ppCreateInfo); + + void STDMETHODCALLTYPE FreeDeviceCreateInfo( + D3D9VkDeviceCreateInfo* pCreateInfo); + + HRESULT STDMETHODCALLTYPE ImportDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + D3DDISPLAYMODEEX* pFullscreenDisplayMode, + D3D9VkDeviceImportInfo* pInfo, + IDirect3DDevice9Ex** ppReturnedDevice); + private: D3D9InterfaceEx* m_interface; diff --git a/src/dxvk/dxvk_device_info.h b/src/dxvk/dxvk_device_info.h index 87f444a9d66..1ace4c5bbe8 100644 --- a/src/dxvk/dxvk_device_info.h +++ b/src/dxvk/dxvk_device_info.h @@ -38,6 +38,9 @@ namespace dxvk { * Stores core features and extension-specific features. * If the respective extensions are not available, the * extended features will be marked as unsupported. + * + * NOTE: This struct is exposed by interop interfaces, please add + * new fields at the end of the struct to maintain compatibility. */ struct DxvkDeviceFeatures { VkPhysicalDeviceFeatures2 core;