diff --git a/src/d3d10/d3d10_device.cpp b/src/d3d10/d3d10_device.cpp index 3f6045477..83f41f57c 100644 --- a/src/d3d10/d3d10_device.cpp +++ b/src/d3d10/d3d10_device.cpp @@ -8,10 +8,9 @@ namespace dxvk { D3D10Device::D3D10Device( D3D11Device* pDevice, D3D11ImmediateContext* pContext) - : m_device(pDevice), m_context(pContext) { - // Respecting the single-threaded flag may improve performance - UINT flags = pDevice->GetCreationFlags(); - m_threadSafe = !(flags & D3D10_CREATE_DEVICE_SINGLETHREADED); + : m_device(pDevice), m_context(pContext), + m_multithread(this, !(pDevice->GetCreationFlags() & D3D10_CREATE_DEVICE_SINGLETHREADED)) { + } diff --git a/src/d3d10/d3d10_device.h b/src/d3d10/d3d10_device.h index df9d50fd7..e8d917e2e 100644 --- a/src/d3d10/d3d10_device.h +++ b/src/d3d10/d3d10_device.h @@ -1,12 +1,9 @@ #pragma once -#include "d3d10_include.h" +#include "d3d10_multithread.h" namespace dxvk { - using D3D10DeviceMutex = sync::Spinlock; - using D3D10DeviceLock = std::unique_lock; - class D3D11Device; class D3D11ImmediateContext; @@ -474,18 +471,18 @@ namespace dxvk { UINT* pHeight); D3D10DeviceLock LockDevice() { - return m_threadSafe - ? D3D10DeviceLock(m_mutex) - : D3D10DeviceLock(); + return m_multithread.AcquireLock(); + } + + D3D10Multithread* GetMultithread() { + return &m_multithread; } private: - D3D10DeviceMutex m_mutex; D3D11Device* m_device; D3D11ImmediateContext* m_context; - - bool m_threadSafe = true; + D3D10Multithread m_multithread; }; diff --git a/src/d3d10/d3d10_multithread.cpp b/src/d3d10/d3d10_multithread.cpp new file mode 100644 index 000000000..bc93e92e7 --- /dev/null +++ b/src/d3d10/d3d10_multithread.cpp @@ -0,0 +1,90 @@ +#include "d3d10_device.h" + +namespace dxvk { + + void D3D10DeviceMutex::lock() { + while (!try_lock()) + dxvk::this_thread::yield(); + } + + + void D3D10DeviceMutex::unlock() { + if (likely(m_counter == 0)) + m_owner.store(0, std::memory_order_release); + else + m_counter -= 1; + } + + + bool D3D10DeviceMutex::try_lock() { + uint32_t threadId = GetCurrentThreadId(); + uint32_t expected = 0; + + bool status = m_owner.compare_exchange_weak( + expected, threadId, std::memory_order_acquire); + + if (status) + return true; + + if (expected != threadId) + return false; + + m_counter += 1; + return true; + } + + + D3D10Multithread::D3D10Multithread( + IUnknown* pParent, + BOOL Protected) + : m_parent (pParent), + m_protected (Protected) { + + } + + + D3D10Multithread::~D3D10Multithread() { + + } + + + ULONG STDMETHODCALLTYPE D3D10Multithread::AddRef() { + return m_parent->AddRef(); + } + + + ULONG STDMETHODCALLTYPE D3D10Multithread::Release() { + return m_parent->Release(); + } + + + HRESULT STDMETHODCALLTYPE D3D10Multithread::QueryInterface( + REFIID riid, + void** ppvObject) { + return m_parent->QueryInterface(riid, ppvObject); + } + + + void STDMETHODCALLTYPE D3D10Multithread::Enter() { + if (m_protected) + m_mutex.lock(); + } + + + void STDMETHODCALLTYPE D3D10Multithread::Leave() { + if (m_protected) + m_mutex.unlock(); + } + + + BOOL STDMETHODCALLTYPE D3D10Multithread::SetMultithreadProtected( + BOOL bMTProtect) { + return std::exchange(m_protected, bMTProtect); + } + + + BOOL STDMETHODCALLTYPE D3D10Multithread::GetMultithreadProtected() { + return m_protected; + } + +} \ No newline at end of file diff --git a/src/d3d10/d3d10_multithread.h b/src/d3d10/d3d10_multithread.h new file mode 100644 index 000000000..f29ee8c77 --- /dev/null +++ b/src/d3d10/d3d10_multithread.h @@ -0,0 +1,127 @@ +#pragma once + +#include "d3d10_include.h" + +namespace dxvk { + + /** + * \brief Device mutex + * + * Effectively implements a recursive spinlock + * which is used to lock the D3D10 device. + */ + class D3D10DeviceMutex { + + public: + + void lock(); + + void unlock(); + + bool try_lock(); + + private: + + std::atomic m_owner = { 0u }; + uint32_t m_counter = { 0u }; + + }; + + + /** + * \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 D3D10DeviceLock { + + public: + + D3D10DeviceLock() + : m_mutex(nullptr) { } + + D3D10DeviceLock(D3D10DeviceMutex& mutex) + : m_mutex(&mutex) { + mutex.lock(); + } + + D3D10DeviceLock(D3D10DeviceLock&& other) + : m_mutex(other.m_mutex) { + other.m_mutex = nullptr; + } + + D3D10DeviceLock& operator = (D3D10DeviceLock&& other) { + if (m_mutex) + m_mutex->unlock(); + + m_mutex = other.m_mutex; + other.m_mutex = nullptr; + return *this; + } + + ~D3D10DeviceLock() { + if (unlikely(m_mutex != nullptr)) + m_mutex->unlock(); + } + + private: + + D3D10DeviceMutex* m_mutex; + + }; + + + /** + * \brief D3D10 device and D3D11 context lock + * + * Can be queried from the D3D10 device or from + * any D3D11 context in order to make individual + * calls thread-safe. Provides methods to lock + * the device or context explicitly. + */ + class D3D10Multithread : public ID3D10Multithread { + + public: + + D3D10Multithread( + IUnknown* pParent, + BOOL Protected); + + ~D3D10Multithread(); + + ULONG STDMETHODCALLTYPE AddRef() final; + + ULONG STDMETHODCALLTYPE Release() final; + + HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void** ppvObject) final; + + void STDMETHODCALLTYPE Enter() final; + + void STDMETHODCALLTYPE Leave() final; + + BOOL STDMETHODCALLTYPE SetMultithreadProtected( + BOOL bMTProtect) final; + + BOOL STDMETHODCALLTYPE GetMultithreadProtected() final; + + D3D10DeviceLock AcquireLock() { + return unlikely(m_protected) + ? D3D10DeviceLock(m_mutex) + : D3D10DeviceLock(); + } + + private: + + IUnknown* m_parent; + BOOL m_protected; + + D3D10DeviceMutex m_mutex; + + }; + +} \ No newline at end of file diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index e62b53f05..684593a1e 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -72,6 +72,11 @@ namespace dxvk { *ppvObject = ref(m_d3d11Presenter); return S_OK; } + + if (riid == __uuidof(ID3D10Multithread)) { + *ppvObject = ref(m_d3d11Device->GetD3D10Multithread()); + return S_OK; + } if (riid == __uuidof(ID3D11Debug)) return E_NOINTERFACE; diff --git a/src/d3d11/d3d11_device.h b/src/d3d11/d3d11_device.h index 718d33cef..4049e6b22 100644 --- a/src/d3d11/d3d11_device.h +++ b/src/d3d11/d3d11_device.h @@ -345,6 +345,10 @@ namespace dxvk { D3D10Device* GetD3D10Interface() const { return m_d3d10Device; } + + D3D10Multithread* GetD3D10Multithread() const { + return m_d3d10Device->GetMultithread(); + } DxvkBufferSlice AllocUavCounterSlice() { return m_uavCounters->AllocSlice(); } DxvkBufferSlice AllocXfbCounterSlice() { return m_xfbCounters->AllocSlice(); } diff --git a/src/d3d11/meson.build b/src/d3d11/meson.build index a604e687b..3e6870387 100644 --- a/src/d3d11/meson.build +++ b/src/d3d11/meson.build @@ -8,6 +8,7 @@ d3d10_src = [ '../d3d10/d3d10_depth_stencil.cpp', '../d3d10/d3d10_device.cpp', '../d3d10/d3d10_input_layout.cpp', + '../d3d10/d3d10_multithread.cpp', '../d3d10/d3d10_query.cpp', '../d3d10/d3d10_rasterizer.cpp', '../d3d10/d3d10_sampler.cpp',