Skip to content

Commit

Permalink
[d3d9] Add ID3D9VkInteropDevice
Browse files Browse the repository at this point in the history
Provides access to the device and instance handles
as well as the queue that is used for rendering.
  • Loading branch information
misyltoad committed Sep 22, 2022
1 parent e976218 commit d221bb7
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/d3d9/d3d9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/d3d9/d3d9_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "d3d9_swvp_emu.h"

#include "d3d9_spec_constants.h"
#include "d3d9_interop.h"

#include <unordered_set>
#include <vector>
Expand Down Expand Up @@ -118,6 +119,7 @@ namespace dxvk {
friend class D3D9SwapChainEx;
friend class D3D9ConstantBuffer;
friend class D3D9UserDefinedAnnotation;
friend D3D9VkInteropDevice;
public:

D3D9DeviceEx(
Expand Down Expand Up @@ -1317,6 +1319,8 @@ namespace dxvk {
#ifdef D3D9_ALLOW_UNMAPPING
lru_list<D3D9CommonTexture*> m_mappedTextures;
#endif

D3D9VkInteropDevice m_d3d9Interop;
};

}
124 changes: 124 additions & 0 deletions src/d3d9/d3d9_interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
126 changes: 126 additions & 0 deletions src/d3d9/d3d9_interop.cpp
Original file line number Diff line number Diff line change
@@ -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 {

Expand Down Expand Up @@ -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<D3D9VkInteropTexture *>(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<DxvkResource> GetDxvkResource(IDirect3DResource9 *pResource) {
switch (pResource->GetType()) {
case D3DRTYPE_SURFACE: return static_cast<D3D9Surface*> (pResource)->GetCommonTexture()->GetImage();
// Does not inherit from IDirect3DResource9... lol.
//case D3DRTYPE_VOLUME: return static_cast<D3D9Volume*> (pResource)->GetCommonTexture()->GetImage();
case D3DRTYPE_TEXTURE: return static_cast<D3D9Texture2D*> (pResource)->GetCommonTexture()->GetImage();
case D3DRTYPE_VOLUMETEXTURE: return static_cast<D3D9Texture3D*> (pResource)->GetCommonTexture()->GetImage();
case D3DRTYPE_CUBETEXTURE: return static_cast<D3D9TextureCube*> (pResource)->GetCommonTexture()->GetImage();
case D3DRTYPE_VERTEXBUFFER: return static_cast<D3D9VertexBuffer*>(pResource)->GetCommonBuffer()->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>();
case D3DRTYPE_INDEXBUFFER: return static_cast<D3D9IndexBuffer*> (pResource)->GetCommonBuffer()->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>();
default: return nullptr;
}
}

bool STDMETHODCALLTYPE D3D9VkInteropDevice::WaitForResource(
IDirect3DResource9* pResource,
DWORD MapFlags) {
return m_device->WaitForResource(GetDxvkResource(pResource), DxvkCsThread::SynchronizeAll, MapFlags);
}

}
56 changes: 56 additions & 0 deletions src/d3d9/d3d9_interop.h
Original file line number Diff line number Diff line change
@@ -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 {

Expand Down Expand Up @@ -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;

};

}

0 comments on commit d221bb7

Please sign in to comment.