diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 67b4d9bdf..6a9bc2ef2 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -71,7 +71,7 @@ namespace dxvk { if (m_dxvkDevice->instance()->extensions().extDebugUtils) m_annotation = new D3D9UserDefinedAnnotation(this); - m_initializer = new D3D9Initializer(m_dxvkDevice); + m_initializer = new D3D9Initializer(this); m_converter = new D3D9FormatHelper(m_dxvkDevice); EmitCs([ @@ -5512,6 +5512,10 @@ namespace dxvk { void D3D9DeviceEx::EmitCsChunk(DxvkCsChunkRef&& chunk) { + // Flush init commands so that the CS thread + // can processe them before the first use. + m_initializer->FlushCsChunk(); + m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk)); } @@ -5881,7 +5885,6 @@ namespace dxvk { if constexpr (Synchronize9On12) m_submitStatus.result = VK_NOT_READY; - m_initializer->Flush(); m_converter->Flush(); EmitStagingBufferMarker(); @@ -5908,6 +5911,10 @@ namespace dxvk { // Vulkan queue submission is performed. if constexpr (Synchronize9On12) m_dxvkDevice->waitForSubmission(&m_submitStatus); + + // Notify the device that the context has been flushed, + // this resets some resource initialization heuristics. + m_initializer->NotifyContextFlush(); } diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 5b1119291..392b058f0 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -1052,13 +1052,13 @@ namespace dxvk { InjectCsChunk(std::move(chunk), false); } - private: - DxvkCsChunkRef AllocCsChunk() { DxvkCsChunk* chunk = m_csChunkPool.allocChunk(DxvkCsChunkFlag::SingleUse); return DxvkCsChunkRef(chunk, &m_csChunkPool); } + private: + template void EmitCs(Cmd&& command) { if (unlikely(!m_csChunk->push(command))) { diff --git a/src/d3d9/d3d9_initializer.cpp b/src/d3d9/d3d9_initializer.cpp index ece83bd74..18ad01839 100644 --- a/src/d3d9/d3d9_initializer.cpp +++ b/src/d3d9/d3d9_initializer.cpp @@ -1,27 +1,27 @@ #include #include "d3d9_initializer.h" +#include "d3d9_device.h" namespace dxvk { D3D9Initializer::D3D9Initializer( - const Rc& Device) - : m_device(Device), m_context(m_device->createContext(DxvkContextType::Supplementary)) { - m_context->beginRecording( - m_device->createCommandList()); + D3D9DeviceEx* pParent) + : m_parent(pParent), + m_device(pParent->GetDXVKDevice()), + m_csChunk(m_parent->AllocCsChunk()) { + } - + D3D9Initializer::~D3D9Initializer() { } - void D3D9Initializer::Flush() { + void D3D9Initializer::NotifyContextFlush() { std::lock_guard lock(m_mutex); - - if (m_transferCommands != 0) - FlushInternal(); + m_transferCommands = 0; } @@ -36,7 +36,7 @@ namespace dxvk { if (pBuffer->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) InitHostVisibleBuffer(pBuffer->GetBufferSlice()); } - + void D3D9Initializer::InitTexture( D3D9CommonTexture* pTexture, @@ -59,6 +59,8 @@ namespace dxvk { InitHostVisibleTexture(pTexture, pInitialData, mapPtr); pTexture->UnmapData(); } + + SyncSharedTexture(pTexture); } @@ -68,10 +70,14 @@ namespace dxvk { m_transferCommands += 1; - m_context->initBuffer( - Slice.buffer()); + EmitCs([ + cBuffer = Slice.buffer() + ] (DxvkContext* ctx) { + ctx->initBuffer( + cBuffer); + }); - FlushImplicit(); + ThrottleAllocationLocked(); } @@ -90,30 +96,17 @@ namespace dxvk { D3D9CommonTexture* pTexture) { std::lock_guard lock(m_mutex); - auto InitImage = [&](Rc image) { - if (image == nullptr) - return; + Rc image = pTexture->GetImage(); - auto formatInfo = lookupFormatInfo(image->info().format); + EmitCs([ + cImage = std::move(image) + ] (DxvkContext* ctx) { + ctx->initImage(cImage, + cImage->getAvailableSubresources(), + VK_IMAGE_LAYOUT_UNDEFINED); + }); - m_transferCommands += 1; - - // While the Microsoft docs state that resource contents are - // undefined if no initial data is provided, some applications - // expect a resource to be pre-cleared. - VkImageSubresourceRange subresources; - subresources.aspectMask = formatInfo->aspectMask; - subresources.baseMipLevel = 0; - subresources.levelCount = image->info().mipLevels; - subresources.baseArrayLayer = 0; - subresources.layerCount = image->info().numLayers; - - m_context->initImage(image, subresources, VK_IMAGE_LAYOUT_UNDEFINED); - }; - - InitImage(pTexture->GetImage()); - - FlushImplicit(); + ThrottleAllocationLocked(); } @@ -154,18 +147,46 @@ namespace dxvk { } - void D3D9Initializer::FlushImplicit() { - if (m_transferCommands > MaxTransferCommands - || m_transferMemory > MaxTransferMemory) - FlushInternal(); + void D3D9Initializer::ThrottleAllocationLocked() { + if (m_transferCommands > MaxTransferCommands) + ExecuteFlushLocked(); } - void D3D9Initializer::FlushInternal() { - m_context->flushCommandList(nullptr); - + void D3D9Initializer::ExecuteFlush() { + std::lock_guard lock(m_mutex); + + ExecuteFlushLocked(); + } + + + void D3D9Initializer::ExecuteFlushLocked() { + EmitCs([] (DxvkContext* ctx) { + ctx->flushCommandList(nullptr); + }); + + FlushCsChunk(); + m_transferCommands = 0; - m_transferMemory = 0; + } + + + void D3D9Initializer::SyncSharedTexture(D3D9CommonTexture* pResource) { + if (pResource->GetImage() == nullptr || pResource->GetImage()->info().sharing.mode == DxvkSharedHandleMode::None) + return; + + // Ensure that initialization commands are submitted and waited on before + // returning control to the application in order to avoid race conditions + // in case the texture is used immediately on a secondary device. + ExecuteFlush(); + + m_device->waitForResource(pResource->GetImage(), DxvkAccess::Write); + } + + + void D3D9Initializer::FlushCsChunkLocked() { + m_parent->InjectCsChunk(std::move(m_csChunk), false); + m_csChunk = m_parent->AllocCsChunk(); } } diff --git a/src/d3d9/d3d9_initializer.h b/src/d3d9/d3d9_initializer.h index 69ce5d33d..f0339dc5d 100644 --- a/src/d3d9/d3d9_initializer.h +++ b/src/d3d9/d3d9_initializer.h @@ -5,6 +5,8 @@ namespace dxvk { + class D3D9DeviceEx; + /** * \brief Resource initialization context * @@ -18,28 +20,37 @@ namespace dxvk { public: D3D9Initializer( - const Rc& Device); - + D3D9DeviceEx* pParent); + ~D3D9Initializer(); - void Flush(); + void FlushCsChunk() { + std::lock_guard lock(m_csMutex); + + if (!m_csChunk->empty()) + FlushCsChunkLocked(); + } + + void NotifyContextFlush(); void InitBuffer( D3D9CommonBuffer* pBuffer); - + void InitTexture( D3D9CommonTexture* pTexture, void* pInitialData = nullptr); - + private: dxvk::mutex m_mutex; + D3D9DeviceEx* m_parent; Rc m_device; - Rc m_context; size_t m_transferCommands = 0; - size_t m_transferMemory = 0; + + dxvk::mutex m_csMutex; + DxvkCsChunkRef m_csChunk; void InitDeviceLocalBuffer( DxvkBufferSlice Slice); @@ -54,9 +65,30 @@ namespace dxvk { D3D9CommonTexture* pTexture, void* pInitialData, void* mapPtr); - - void FlushImplicit(); - void FlushInternal(); + + void ThrottleAllocationLocked(); + + void ExecuteFlush(); + + void ExecuteFlushLocked(); + + void SyncSharedTexture( + D3D9CommonTexture* pResource); + + void FlushCsChunkLocked(); + + void NotifyContextFlushLocked(); + + template + void EmitCs(Cmd&& command) { + std::lock_guard lock(m_csMutex); + + if (unlikely(!m_csChunk->push(command))) { + FlushCsChunkLocked(); + + m_csChunk->push(command); + } + } };