diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index cbe5404f2..facd03d18 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -54,7 +54,8 @@ namespace dxvk { , m_multithread ( BehaviorFlags & D3DCREATE_MULTITHREADED ) , m_isSWVP ( (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) ? true : false ) , m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) ) - , m_csChunk ( AllocCsChunk() ) { + , m_csChunk ( AllocCsChunk() ) + , m_d3d9Interop ( this ) { // If we can SWVP, then we use an extended constant set // as SWVP has many more slots available than HWVP. bool canSWVP = CanSWVP(); @@ -191,6 +192,11 @@ namespace dxvk { return S_OK; } + if (riid == __uuidof(ID3D9VkInteropDevice)) { + *ppvObject = ref(&m_d3d9Interop); + return S_OK; + } + // We want to ignore this if the extended device is queried and we weren't made extended. if (riid == __uuidof(IDirect3DDevice9Ex)) return E_NOINTERFACE; diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 7b4a7c52e..bc04b35eb 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -27,6 +27,7 @@ #include "d3d9_swvp_emu.h" #include "d3d9_spec_constants.h" +#include "d3d9_interop.h" #include #include @@ -118,6 +119,7 @@ namespace dxvk { friend class D3D9SwapChainEx; friend class D3D9ConstantBuffer; friend class D3D9UserDefinedAnnotation; + friend D3D9VkInteropDevice; public: D3D9DeviceEx( @@ -1317,6 +1319,8 @@ namespace dxvk { #ifdef D3D9_ALLOW_UNMAPPING lru_list m_mappedTextures; #endif + + D3D9VkInteropDevice m_d3d9Interop; }; } diff --git a/src/d3d9/d3d9_interfaces.h b/src/d3d9/d3d9_interfaces.h index 065e95cbd..48fbed9c6 100644 --- a/src/d3d9/d3d9_interfaces.h +++ b/src/d3d9/d3d9_interfaces.h @@ -74,10 +74,134 @@ ID3D9VkInteropTexture : public IUnknown { 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. + */ +MIDL_INTERFACE("2eaa4b89-0107-4bdb-87f7-0f541c493ce0") +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; +}; + #ifdef _MSC_VER struct __declspec(uuid("3461a81b-ce41-485b-b6b5-fcf08ba6a6bd")) ID3D9VkInteropInterface; struct __declspec(uuid("d56344f5-8d35-46fd-806d-94c351b472c1")) ID3D9VkInteropTexture; +struct __declspec(uuid("2eaa4b89-0107-4bdb-87f7-0f541c493ce0")) ID3D9VkInteropDevice; #else __CRT_UUID_DECL(ID3D9VkInteropInterface, 0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd); __CRT_UUID_DECL(ID3D9VkInteropTexture, 0xd56344f5,0x8d35,0x46fd,0x80,0x6d,0x94,0xc3,0x51,0xb4,0x72,0xc1); +__CRT_UUID_DECL(ID3D9VkInteropDevice, 0x2eaa4b89,0x0107,0x4bdb,0x87,0xf7,0x0f,0x54,0x1c,0x49,0x3c,0xe0); #endif diff --git a/src/d3d9/d3d9_interop.cpp b/src/d3d9/d3d9_interop.cpp index 64ac0b367..6f072f620 100644 --- a/src/d3d9/d3d9_interop.cpp +++ b/src/d3d9/d3d9_interop.cpp @@ -1,6 +1,9 @@ #include "d3d9_interop.h" #include "d3d9_interface.h" #include "d3d9_common_texture.h" +#include "d3d9_device.h" +#include "d3d9_texture.h" +#include "d3d9_buffer.h" namespace dxvk { @@ -113,4 +116,127 @@ namespace dxvk { return S_OK; } + //////////////////////////////// + // Device Interop + /////////////////////////////// + + D3D9VkInteropDevice::D3D9VkInteropDevice( + D3D9DeviceEx* pInterface) + : m_device(pInterface) { + + } + + D3D9VkInteropDevice::~D3D9VkInteropDevice() { + + } + + ULONG STDMETHODCALLTYPE D3D9VkInteropDevice::AddRef() { + return m_device->AddRef(); + } + + ULONG STDMETHODCALLTYPE D3D9VkInteropDevice::Release() { + return m_device->Release(); + } + + HRESULT STDMETHODCALLTYPE D3D9VkInteropDevice::QueryInterface( + REFIID riid, + void** ppvObject) { + return m_device->QueryInterface(riid, ppvObject); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::GetVulkanHandles( + VkInstance* pInstance, + VkPhysicalDevice* pPhysDev, + VkDevice* pDevice) { + auto device = m_device->GetDXVKDevice(); + auto adapter = device->adapter(); + auto instance = device->instance(); + + if (pDevice != nullptr) + *pDevice = device->handle(); + + if (pPhysDev != nullptr) + *pPhysDev = adapter->handle(); + + if (pInstance != nullptr) + *pInstance = instance->handle(); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::GetSubmissionQueue( + VkQueue* pQueue, + uint32_t* pQueueIndex, + uint32_t* pQueueFamilyIndex) { + auto device = m_device->GetDXVKDevice(); + DxvkDeviceQueue queue = device->queues().graphics; + + if (pQueue != nullptr) + *pQueue = queue.queueHandle; + + if (pQueueIndex != nullptr) + *pQueueIndex = queue.queueIndex; + + if (pQueueFamilyIndex != nullptr) + *pQueueFamilyIndex = queue.queueFamily; + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::TransitionTextureLayout( + ID3D9VkInteropTexture* pTexture, + const VkImageSubresourceRange* pSubresources, + VkImageLayout OldLayout, + VkImageLayout NewLayout) { + auto texture = static_cast(pTexture)->GetCommonTexture(); + + m_device->EmitCs([ + cImage = texture->GetImage(), + cSubresources = *pSubresources, + cOldLayout = OldLayout, + cNewLayout = NewLayout + ] (DxvkContext* ctx) { + ctx->transformImage( + cImage, cSubresources, + cOldLayout, cNewLayout); + }); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::FlushRenderingCommands() { + m_device->Flush(); + m_device->SynchronizeCsThread(DxvkCsThread::SynchronizeAll); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::LockSubmissionQueue() { + m_device->GetDXVKDevice()->lockSubmission(); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::ReleaseSubmissionQueue() { + m_device->GetDXVKDevice()->unlockSubmission(); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::LockDevice() { + m_lock = m_device->LockDevice(); + } + + void STDMETHODCALLTYPE D3D9VkInteropDevice::UnlockDevice() { + m_lock = D3D9DeviceLock(); + } + + static Rc GetDxvkResource(IDirect3DResource9 *pResource) { + switch (pResource->GetType()) { + case D3DRTYPE_SURFACE: return static_cast (pResource)->GetCommonTexture()->GetImage(); + // Does not inherit from IDirect3DResource9... lol. + //case D3DRTYPE_VOLUME: return static_cast (pResource)->GetCommonTexture()->GetImage(); + case D3DRTYPE_TEXTURE: return static_cast (pResource)->GetCommonTexture()->GetImage(); + case D3DRTYPE_VOLUMETEXTURE: return static_cast (pResource)->GetCommonTexture()->GetImage(); + case D3DRTYPE_CUBETEXTURE: return static_cast (pResource)->GetCommonTexture()->GetImage(); + case D3DRTYPE_VERTEXBUFFER: return static_cast(pResource)->GetCommonBuffer()->GetBuffer(); + case D3DRTYPE_INDEXBUFFER: return static_cast (pResource)->GetCommonBuffer()->GetBuffer(); + default: return nullptr; + } + } + + bool STDMETHODCALLTYPE D3D9VkInteropDevice::WaitForResource( + IDirect3DResource9* pResource, + DWORD MapFlags) { + return m_device->WaitForResource(GetDxvkResource(pResource), DxvkCsThread::SynchronizeAll, MapFlags); + } + } diff --git a/src/d3d9/d3d9_interop.h b/src/d3d9/d3d9_interop.h index 30596696f..742f69a9b 100644 --- a/src/d3d9/d3d9_interop.h +++ b/src/d3d9/d3d9_interop.h @@ -1,11 +1,13 @@ #pragma once #include "d3d9_interfaces.h" +#include "d3d9_multithread.h" namespace dxvk { class D3D9InterfaceEx; class D3D9CommonTexture; + class D3D9DeviceEx; class D3D9VkInteropInterface final : public ID3D9VkInteropInterface { @@ -69,4 +71,58 @@ namespace dxvk { }; + class D3D9VkInteropDevice final : public ID3D9VkInteropDevice { + + public: + + D3D9VkInteropDevice( + D3D9DeviceEx* pInterface); + + ~D3D9VkInteropDevice(); + + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void** ppvObject); + + void STDMETHODCALLTYPE GetVulkanHandles( + VkInstance* pInstance, + VkPhysicalDevice* pPhysDev, + VkDevice* pDevice); + + void STDMETHODCALLTYPE GetSubmissionQueue( + VkQueue* pQueue, + uint32_t* pQueueIndex, + uint32_t* pQueueFamilyIndex); + + void STDMETHODCALLTYPE TransitionTextureLayout( + ID3D9VkInteropTexture* pTexture, + const VkImageSubresourceRange* pSubresources, + VkImageLayout OldLayout, + VkImageLayout NewLayout); + + void STDMETHODCALLTYPE FlushRenderingCommands(); + + void STDMETHODCALLTYPE LockSubmissionQueue(); + + void STDMETHODCALLTYPE ReleaseSubmissionQueue(); + + void STDMETHODCALLTYPE LockDevice(); + + void STDMETHODCALLTYPE UnlockDevice(); + + bool STDMETHODCALLTYPE WaitForResource( + IDirect3DResource9* pResource, + DWORD MapFlags); + + private: + + D3D9DeviceEx* m_device; + D3D9DeviceLock m_lock; + + }; + }