diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp index b0ee9013e..5ddd83415 100644 --- a/src/d3d9/d3d9_common_texture.cpp +++ b/src/d3d9/d3d9_common_texture.cpp @@ -17,6 +17,10 @@ namespace dxvk { ? D3D9Format::D32 : D3D9Format::X8R8G8B8; + for (uint32_t i = 0; i < m_updateDirtyBoxes.size(); i++) { + AddUpdateDirtyBox(nullptr, i); + } + m_mapping = pDevice->LookupFormat(m_desc.Format); m_mapMode = DetermineMapMode(); diff --git a/src/d3d9/d3d9_common_texture.h b/src/d3d9/d3d9_common_texture.h index 9ec487dc8..05f791379 100644 --- a/src/d3d9/d3d9_common_texture.h +++ b/src/d3d9/d3d9_common_texture.h @@ -367,6 +367,40 @@ namespace dxvk { void PreLoadAll(); void PreLoadSubresource(UINT Subresource); + void AddUpdateDirtyBox(CONST D3DBOX* pDirtyBox, uint32_t layer) { + if (pDirtyBox) { + D3DBOX box = *pDirtyBox; + if (box.Right <= box.Left + || box.Bottom <= box.Top + || box.Back <= box.Front) + return; + + D3DBOX& updateBox = m_updateDirtyBoxes[layer]; + if (updateBox.Left == updateBox.Right) { + updateBox = box; + } else { + updateBox.Left = std::min(updateBox.Left, box.Left); + updateBox.Right = std::max(updateBox.Right, box.Right); + updateBox.Top = std::min(updateBox.Top, box.Top); + updateBox.Bottom = std::max(updateBox.Bottom, box.Bottom); + updateBox.Front = std::min(updateBox.Front, box.Front); + updateBox.Back = std::max(updateBox.Back, box.Back); + } + } else { + m_updateDirtyBoxes[layer] = { 0, 0, m_desc.Width, m_desc.Height, 0, m_desc.Depth }; + } + } + + void ClearUpdateDirtyBoxes() { + for (uint32_t i = 0; i < m_updateDirtyBoxes.size(); i++) { + m_updateDirtyBoxes[i] = { 0, 0, 0, 0, 0, 0 }; + } + } + + const D3DBOX& GetUpdateDirtyBox(uint32_t layer) const { + return m_updateDirtyBoxes[layer]; + } + private: D3D9DeviceEx* m_device; @@ -407,6 +441,8 @@ namespace dxvk { D3DTEXTUREFILTERTYPE m_mipFilter = D3DTEXF_LINEAR; + std::array m_updateDirtyBoxes; + /** * \brief Mip level * \returns Size of packed mip level in bytes diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index c344672d6..36f45cbb3 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -649,6 +649,19 @@ namespace dxvk { if (unlikely(srcTextureInfo->Desc()->Format != dstTextureInfo->Desc()->Format)) return D3DERR_INVALIDCALL; + if (dst->GetMipLevel() == 0) { + if (pSourceRect) { + D3DBOX updateDirtyRect = { UINT(pSourceRect->left), UINT(pSourceRect->top), UINT(pSourceRect->right), UINT(pSourceRect->bottom), 0, 1 }; + if (pDestinationSurface) { + updateDirtyRect.Left = pDestPoint->x; + updateDirtyRect.Top = pDestPoint->y; + } + dstTextureInfo->AddUpdateDirtyBox(&updateDirtyRect, dst->GetFace()); + } else { + dstTextureInfo->AddUpdateDirtyBox(nullptr, dst->GetFace()); + } + } + const DxvkFormatInfo* formatInfo = imageFormatInfo(dstTextureInfo->GetFormatMapping().FormatColor); VkOffset3D srcBlockOffset = { 0u, 0u, 0u }; @@ -726,27 +739,54 @@ namespace dxvk { return D3DERR_INVALIDCALL; const Rc dstImage = dstTexInfo->GetImage(); - + const DxvkFormatInfo* formatInfo = imageFormatInfo(dstTexInfo->GetFormatMapping().FormatColor); uint32_t mipLevels = std::min(srcTexInfo->Desc()->MipLevels, dstTexInfo->Desc()->MipLevels); uint32_t arraySlices = std::min(srcTexInfo->Desc()->ArraySize, dstTexInfo->Desc()->ArraySize); + for (uint32_t a = 0; a < arraySlices; a++) { + const D3DBOX& box = srcTexInfo->GetUpdateDirtyBox(a); for (uint32_t m = 0; m < mipLevels; m++) { Rc srcBuffer = srcTexInfo->GetBuffer(srcTexInfo->CalcSubresource(a, m)); - VkImageSubresourceLayers dstLayers = { VK_IMAGE_ASPECT_COLOR_BIT, m, a, 1 }; - - VkExtent3D extent = dstImage->mipLevelExtent(m); - + + VkOffset3D offset = { + alignDown(int32_t(box.Left) >> m, int32_t(formatInfo->blockSize.width)), + alignDown(int32_t(box.Top) >> m, int32_t(formatInfo->blockSize.height)), + alignDown(int32_t(box.Front) >> m, int32_t(formatInfo->blockSize.depth)) + }; + VkExtent3D extent = { + alignDown(uint32_t((box.Right - box.Left) >> m), formatInfo->blockSize.width), + alignDown(uint32_t((box.Bottom - box.Top) >> m), formatInfo->blockSize.height), + alignDown(uint32_t((box.Back - box.Front) >> m), formatInfo->blockSize.depth), + }; + + VkExtent3D levelExtent = dstImage->mipLevelExtent(m); + VkExtent3D blockCount = util::computeBlockCount(levelExtent, formatInfo->blockSize); + VkOffset3D srcByteOffset = { + offset.x / int32_t(formatInfo->blockSize.width), + offset.y / int32_t(formatInfo->blockSize.height), + offset.z / int32_t(formatInfo->blockSize.depth) + }; + VkDeviceSize srcOffset = srcByteOffset.z * formatInfo->elementSize * blockCount.depth + + srcByteOffset.y * formatInfo->elementSize * blockCount.width + + srcByteOffset.x * formatInfo->elementSize; + VkExtent2D srcExtent = VkExtent2D{ blockCount.width * formatInfo->blockSize.width, + blockCount.height * formatInfo->blockSize.height }; + EmitCs([ cDstImage = dstImage, cSrcBuffer = srcBuffer, cDstLayers = dstLayers, - cExtent = extent + cExtent = extent, + cOffset = offset, + cSrcOffset = srcOffset, + cSrcExtent = srcExtent ] (DxvkContext* ctx) { ctx->copyBufferToImage( cDstImage, cDstLayers, - VkOffset3D{ 0, 0, 0 }, cExtent, - cSrcBuffer, 0, { 0u, 0u }); + cOffset, cExtent, + cSrcBuffer, cSrcOffset, + cSrcExtent); }); } } @@ -760,6 +800,8 @@ namespace dxvk { else dstTexInfo->MarkAllDirty(); + srcTexInfo->ClearUpdateDirtyBoxes(); + FlushImplicit(false); return D3D_OK; @@ -3929,6 +3971,10 @@ namespace dxvk { if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) == (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE))) Flags &= ~D3DLOCK_DISCARD; + if (!(Flags & D3DLOCK_NO_DIRTY_UPDATE) && !(Flags & D3DLOCK_READONLY)) { + pResource->AddUpdateDirtyBox(pBox, Face); + } + auto& desc = *(pResource->Desc()); bool alloced = pResource->CreateBufferSubresource(Subresource); diff --git a/src/d3d9/d3d9_texture.cpp b/src/d3d9/d3d9_texture.cpp index 1cf50b49a..eec1333d0 100644 --- a/src/d3d9/d3d9_texture.cpp +++ b/src/d3d9/d3d9_texture.cpp @@ -76,6 +76,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9Texture2D::AddDirtyRect(CONST RECT* pDirtyRect) { + if (pDirtyRect) { + D3DBOX box = { UINT(pDirtyRect->left), UINT(pDirtyRect->top), UINT(pDirtyRect->right), UINT(pDirtyRect->bottom), 0, 1 }; + m_texture.AddUpdateDirtyBox(&box, 0); + } else { + m_texture.AddUpdateDirtyBox(nullptr, 0); + } return D3D_OK; } @@ -151,8 +157,8 @@ namespace dxvk { return GetSubresource(Level)->UnlockBox(); } - HRESULT STDMETHODCALLTYPE D3D9Texture3D::AddDirtyBox(CONST D3DBOX* pDirtyBox) { + m_texture.AddUpdateDirtyBox(pDirtyBox, 0); return D3D_OK; } @@ -230,6 +236,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9TextureCube::AddDirtyRect(D3DCUBEMAP_FACES Face, CONST RECT* pDirtyRect) { + if (pDirtyRect) { + D3DBOX box = { UINT(pDirtyRect->left), UINT(pDirtyRect->top), UINT(pDirtyRect->right), UINT(pDirtyRect->bottom), 0, 1 }; + m_texture.AddUpdateDirtyBox(&box, Face); + } else { + m_texture.AddUpdateDirtyBox(nullptr, Face); + } return D3D_OK; }