1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-19 05:52:11 +01:00

[d3d8] Respect D3DCREATE_MULTITHREADED and make d3d8 thread safe

This commit is contained in:
WinterSnowfall 2024-09-17 01:05:35 +03:00 committed by Philip Rebohle
parent c8791a6ba5
commit c7cf0a7368
7 changed files with 180 additions and 5 deletions

View File

@ -44,7 +44,8 @@ namespace dxvk {
, m_presentParams(*pParams)
, m_deviceType(DeviceType)
, m_window(hFocusWindow)
, m_behaviorFlags(BehaviorFlags) {
, m_behaviorFlags(BehaviorFlags)
, m_multithread(BehaviorFlags & D3DCREATE_MULTITHREADED) {
// Get the bridge interface to D3D9.
if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), (void**)&m_bridge))) {
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
@ -226,6 +227,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) {
D3D8DeviceLock lock = LockDevice();
StateChange();
if (unlikely(pPresentationParameters == nullptr))
@ -248,6 +251,8 @@ namespace dxvk {
const RECT* pDestRect,
HWND hDestWindowOverride,
const RGNDATA* pDirtyRegion) {
D3D8DeviceLock lock = LockDevice();
m_batcher->EndFrame();
StateChange();
return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
@ -257,6 +262,8 @@ namespace dxvk {
UINT iBackBuffer,
D3DBACKBUFFER_TYPE Type,
IDirect3DSurface8** ppBackBuffer) {
D3D8DeviceLock lock = LockDevice();
InitReturnPtr(ppBackBuffer);
if (unlikely(ppBackBuffer == nullptr))
@ -620,6 +627,8 @@ namespace dxvk {
UINT cRects,
IDirect3DSurface8* pDestinationSurface,
const POINT* pDestPointsArray) {
D3D8DeviceLock lock = LockDevice();
// The source and destination surfaces can not be identical.
if (unlikely(pSourceSurface == nullptr ||
pDestinationSurface == nullptr ||
@ -879,6 +888,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil) {
D3D8DeviceLock lock = LockDevice();
HRESULT res;
if (pRenderTarget != nullptr) {
@ -910,6 +921,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderTarget(IDirect3DSurface8** ppRenderTarget) {
D3D8DeviceLock lock = LockDevice();
InitReturnPtr(ppRenderTarget);
if (unlikely(ppRenderTarget == nullptr))
@ -932,6 +945,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) {
D3D8DeviceLock lock = LockDevice();
InitReturnPtr(ppZStencilSurface);
if (unlikely(ppZStencilSurface == nullptr))
@ -983,6 +998,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) {
D3D8DeviceLock lock = LockDevice();
if (likely(pViewport != nullptr)) {
// we need a valid render target to validate the viewport
if (unlikely(m_renderTarget == nullptr))
@ -1004,6 +1021,7 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetViewport(D3DVIEWPORT8* pViewport) {
D3D8DeviceLock lock = LockDevice();
return GetD3D9()->GetViewport(reinterpret_cast<d3d9::D3DVIEWPORT9*>(pViewport));
}
@ -1046,6 +1064,8 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D8Device::CreateStateBlock(
D3DSTATEBLOCKTYPE Type,
DWORD* pToken) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pToken == nullptr))
return D3DERR_INVALIDCALL;
@ -1064,6 +1084,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) {
D3D8DeviceLock lock = LockDevice();
auto stateBlockIter = m_stateBlocks.find(Token);
if (unlikely(stateBlockIter == m_stateBlocks.end())) {
@ -1075,6 +1097,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) {
D3D8DeviceLock lock = LockDevice();
StateChange();
auto stateBlockIter = m_stateBlocks.find(Token);
@ -1088,6 +1112,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) {
D3D8DeviceLock lock = LockDevice();
// "Applications cannot delete a device-state block while another is being recorded"
if (unlikely(ShouldRecord()))
return D3DERR_INVALIDCALL;
@ -1111,6 +1137,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::BeginStateBlock() {
D3D8DeviceLock lock = LockDevice();
if (unlikely(m_recorder != nullptr))
return D3DERR_INVALIDCALL;
@ -1129,6 +1157,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::EndStateBlock(DWORD* pToken) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pToken == nullptr || m_recorder == nullptr))
return D3DERR_INVALIDCALL;
@ -1157,6 +1187,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) {
D3D8DeviceLock lock = LockDevice();
InitReturnPtr(ppTexture);
if (unlikely(ppTexture == nullptr))
@ -1168,6 +1200,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(Stage >= d8caps::MAX_TEXTURE_STAGES))
return D3DERR_INVALIDCALL;
@ -1244,6 +1278,8 @@ namespace dxvk {
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount) {
D3D8DeviceLock lock = LockDevice();
if (ShouldBatch())
return m_batcher->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount);
return GetD3D9()->DrawPrimitive(d3d9::D3DPRIMITIVETYPE(PrimitiveType), StartVertex, PrimitiveCount);
@ -1255,6 +1291,8 @@ namespace dxvk {
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount) {
D3D8DeviceLock lock = LockDevice();
return GetD3D9()->DrawIndexedPrimitive(
d3d9::D3DPRIMITIVETYPE(PrimitiveType),
static_cast<INT>(std::min(m_baseVertexIndex, static_cast<UINT>(INT_MAX))), // set by SetIndices
@ -1269,6 +1307,8 @@ namespace dxvk {
UINT PrimitiveCount,
const void* pVertexStreamZeroData,
UINT VertexStreamZeroStride) {
D3D8DeviceLock lock = LockDevice();
StateChange();
// Stream 0 is set to null by this call
@ -1286,6 +1326,8 @@ namespace dxvk {
D3DFORMAT IndexDataFormat,
const void* pVertexStreamZeroData,
UINT VertexStreamZeroStride) {
D3D8DeviceLock lock = LockDevice();
StateChange();
// Stream 0 and the index buffer are set to null by this call
@ -1338,6 +1380,8 @@ namespace dxvk {
UINT StreamNumber,
IDirect3DVertexBuffer8* pStreamData,
UINT Stride) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(StreamNumber >= d8caps::MAX_STREAMS))
return D3DERR_INVALIDCALL;
@ -1358,6 +1402,8 @@ namespace dxvk {
UINT StreamNumber,
IDirect3DVertexBuffer8** ppStreamData,
UINT* pStride) {
D3D8DeviceLock lock = LockDevice();
InitReturnPtr(ppStreamData);
if (likely(pStride != nullptr))
@ -1378,6 +1424,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(ShouldRecord()))
return m_recorder->SetIndices(pIndexData, BaseVertexIndex);
@ -1403,6 +1451,8 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D8Device::GetIndices(
IDirect3DIndexBuffer8** ppIndexData,
UINT* pBaseVertexIndex) {
D3D8DeviceLock lock = LockDevice();
InitReturnPtr(ppIndexData);
if (unlikely(ppIndexData == nullptr || pBaseVertexIndex == nullptr))
@ -1452,6 +1502,8 @@ namespace dxvk {
static constexpr float ZBIAS_SCALE_INV = 1 / ZBIAS_SCALE;
HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {
D3D8DeviceLock lock = LockDevice();
d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State;
bool stateChange = true;
@ -1501,8 +1553,10 @@ namespace dxvk {
if (stateChange) {
DWORD value;
GetRenderState(State, &value);
if (value != Value)
// Value at this point is converted for use with D3D9,
// so we need to compare it against D3D9 directly
HRESULT res = GetD3D9()->GetRenderState(State9, &value);
if (likely(SUCCEEDED(res)) && value != Value)
StateChange();
}
@ -1511,6 +1565,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pValue == nullptr))
return D3DERR_INVALIDCALL;
@ -1559,6 +1615,8 @@ namespace dxvk {
const DWORD* pFunction,
DWORD* pHandle,
DWORD Usage ) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pDeclaration == nullptr || pHandle == nullptr))
return D3DERR_INVALIDCALL;
@ -1596,7 +1654,7 @@ namespace dxvk {
}
inline D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle) {
Handle = getShaderIndex(Handle);
if (unlikely(Handle >= device->m_vertexShaders.size())) {
@ -1615,6 +1673,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShader(DWORD Handle) {
D3D8DeviceLock lock = LockDevice();
HRESULT res;
if (unlikely(ShouldRecord())) {
@ -1660,6 +1720,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShader(DWORD* pHandle) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pHandle == nullptr))
return D3DERR_INVALIDCALL;
@ -1692,6 +1754,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::DeleteVertexShader(DWORD Handle) {
D3D8DeviceLock lock = LockDevice();
if (!isFVF(Handle)) {
D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle);
@ -1711,6 +1775,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData) {
D3D8DeviceLock lock = LockDevice();
D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle);
if (unlikely(!pInfo))
@ -1737,6 +1803,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) {
D3D8DeviceLock lock = LockDevice();
D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle);
if (unlikely(!pInfo))
@ -1768,6 +1836,8 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D8Device::CreatePixelShader(
const DWORD* pFunction,
DWORD* pHandle) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pFunction == nullptr || pHandle == nullptr))
return D3DERR_INVALIDCALL;
@ -1785,6 +1855,7 @@ namespace dxvk {
}
inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle) {
Handle = getShaderIndex(Handle);
if (unlikely(Handle >= device->m_pixelShaders.size())) {
@ -1803,6 +1874,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShader(DWORD Handle) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(ShouldRecord())) {
return m_recorder->SetPixelShader(Handle);
}
@ -1831,6 +1904,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShader(DWORD* pHandle) {
D3D8DeviceLock lock = LockDevice();
if (unlikely(pHandle == nullptr))
return D3DERR_INVALIDCALL;
@ -1841,6 +1916,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::DeletePixelShader(DWORD Handle) {
D3D8DeviceLock lock = LockDevice();
d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);
if (unlikely(!pPixelShader)) {
@ -1853,6 +1930,8 @@ namespace dxvk {
}
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) {
D3D8DeviceLock lock = LockDevice();
d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);
if (unlikely(!pPixelShader))

View File

@ -1,6 +1,7 @@
#pragma once
#include "d3d8_include.h"
#include "d3d8_multithread.h"
#include "d3d8_texture.h"
#include "d3d8_buffer.h"
#include "d3d8_swapchain.h"
@ -360,6 +361,10 @@ namespace dxvk {
inline bool ShouldRecord() { return m_recorder != nullptr; }
inline bool ShouldBatch() { return m_batcher != nullptr; }
D3D8DeviceLock LockDevice() {
return m_multithread.AcquireLock();
}
/**
* Marks any state change in the device, so we can signal
* the batcher to emit draw calls. StateChange should be
@ -451,6 +456,8 @@ namespace dxvk {
DWORD m_behaviorFlags;
D3D8Multithread m_multithread;
};
}

View File

@ -117,6 +117,8 @@ namespace d3d9 {
#include "../util/log/log.h"
#include "../util/log/log_debug.h"
#include "../util/sync/sync_recursive.h"
#include "../util/util_error.h"
#include "../util/util_likely.h"
#include "../util/util_string.h"

View File

@ -0,0 +1,9 @@
#include "d3d8_device.h"
namespace dxvk {
D3D8Multithread::D3D8Multithread(
BOOL Protected)
: m_protected( Protected ) { }
}

View File

@ -0,0 +1,77 @@
#pragma once
#include "d3d8_include.h"
namespace dxvk {
/**
* \brief Device lock
*
* Lightweight RAII wrapper that implements
* a subset of the functionality provided by
* \c std::unique_lock, with the goal of being
* cheaper to construct and destroy.
*/
class D3D8DeviceLock {
public:
D3D8DeviceLock()
: m_mutex(nullptr) { }
D3D8DeviceLock(sync::RecursiveSpinlock& mutex)
: m_mutex(&mutex) {
mutex.lock();
}
D3D8DeviceLock(D3D8DeviceLock&& other)
: m_mutex(other.m_mutex) {
other.m_mutex = nullptr;
}
D3D8DeviceLock& operator = (D3D8DeviceLock&& other) {
if (m_mutex)
m_mutex->unlock();
m_mutex = other.m_mutex;
other.m_mutex = nullptr;
return *this;
}
~D3D8DeviceLock() {
if (m_mutex != nullptr)
m_mutex->unlock();
}
private:
sync::RecursiveSpinlock* m_mutex;
};
/**
* \brief D3D8 context lock
*/
class D3D8Multithread {
public:
D3D8Multithread(
BOOL Protected);
D3D8DeviceLock AcquireLock() {
return m_protected
? D3D8DeviceLock(m_mutex)
: D3D8DeviceLock();
}
private:
BOOL m_protected;
sync::RecursiveSpinlock m_mutex;
};
}

View File

@ -4,6 +4,7 @@ d3d8_src = [
'd3d8_main.cpp',
'd3d8_interface.cpp',
'd3d8_device.cpp',
'd3d8_multithread.cpp',
'd3d8_options.cpp',
'd3d8_surface.cpp',
'd3d8_state_block.cpp',

View File

@ -31,7 +31,7 @@ IDxvkD3D8Bridge : public IUnknown {
/**
* \brief Enables or disables D3D9-specific device features and validations
*
* \param [in] compatibility state
* \param [in] compatMode Compatibility state
*/
virtual void SetD3D8CompatibilityMode(const bool compatMode) = 0;