diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp index 12d382e556..a7ad6b7cee 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp @@ -822,7 +822,6 @@ void D3D12GSRender::Flip() storage.m_frameFinishedFence->SetEventOnCompletion(storage.m_fenceValue, storage.m_frameFinishedHandle); storage.m_fenceValue++; - storage.m_dirtyTextures = m_textureCache.getCurrentDisposableTexture(); storage.m_inUse = true; // Get the put pos - 1. This way after cleaning we can set the get ptr to diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h index a515813b9b..76d2f0ffd3 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h @@ -190,6 +190,26 @@ struct DataHeap } }; +struct TextureEntry +{ + int m_format; + size_t m_width; + size_t m_height; + size_t m_mipmap; + bool m_isDirty; + + TextureEntry() : m_format(0), m_width(0), m_height(0), m_isDirty(true) + {} + + TextureEntry(int f, size_t w, size_t h, size_t m) : m_format(f), m_width(w), m_height(h), m_isDirty(false) + {} + + bool operator==(const TextureEntry &other) + { + return (m_format == other.m_format && m_width == other.m_width && m_height == other.m_height); + } +}; + /** * Manages cache of data (texture/vertex/index) */ @@ -203,14 +223,22 @@ private: */ std::mutex mut; - std::unordered_map > m_dataCache; // Storage + std::unordered_map> > m_dataCache; // Storage std::list > m_protectedRange; // address, start of protected range, size of protected range - std::list > m_dataToDispose; public: - void storeAndProtectData(u64 key, u32 start, size_t size, ComPtr data) + void storeAndProtectData(u64 key, u32 start, size_t size, int format, size_t w, size_t h, size_t m, ComPtr data) { std::lock_guard lock(mut); - m_dataCache[key] = data; + m_dataCache[key] = std::make_pair(TextureEntry(format, w, h, m), data); + protectData(key, start, size); + } + + /** + * Make memory from start to start + size write protected. + * Associate key to this range so that when a write is detected, data at key is marked dirty. + */ + void protectData(u64 key, u32 start, size_t size) + { /// align start to 4096 byte u32 protected_range_start = align(start, 4096); u32 protected_range_size = (u32)align(size, 4096); @@ -233,8 +261,7 @@ public: { std::lock_guard lock(mut); u64 texadrr = std::get<0>(protectedTexture); - m_dataToDispose.push_back(m_dataCache[texadrr]); - m_dataCache.erase(texadrr); + m_dataCache[texadrr].first.m_isDirty = true; vm::page_protect(protectedRangeStart, protectedRangeSize, 0, vm::page_writable, 0); m_protectedRange.erase(currentIt); @@ -244,13 +271,13 @@ public: return handled; } - ID3D12Resource *findDataIfAvailable(u64 key) + std::pair > *findDataIfAvailable(u64 key) { std::lock_guard lock(mut); - std::unordered_map >::const_iterator It = m_dataCache.find(key); + auto It = m_dataCache.find(key); if (It == m_dataCache.end()) return nullptr; - return It->second.Get(); + return &It->second; } void unprotedAll() @@ -263,10 +290,14 @@ public: } } - std::list > getCurrentDisposableTexture() + /** + * Remove data stored at key, and returns a ComPtr owning it. + * The caller is responsible for releasing the ComPtr. + */ + ComPtr removeFromCache(u64 key) { - std::list > result; - result.swap(m_dataToDispose); + auto result = m_dataCache[key].second; + m_dataCache.erase(key); return result; } }; diff --git a/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp b/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp index fc37b25557..6f71a2a41f 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp @@ -172,6 +172,46 @@ ComPtr uploadSingleTexture( return vramTexture; } + + +/** +* +*/ +static +void updateExistingTexture( + const RSXTexture &texture, + ID3D12GraphicsCommandList *commandList, + DataHeap &textureBuffersHeap, + ID3D12Resource *existingTexture) +{ + size_t w = texture.GetWidth(), h = texture.GetHeight(); + + int format = texture.GetFormat() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); + DXGI_FORMAT dxgiFormat = getTextureDXGIFormat(format); + + size_t textureSize = getPlacedTextureStorageSpace(texture, 256); + assert(textureBuffersHeap.canAlloc(textureSize)); + size_t heapOffset = textureBuffersHeap.alloc(textureSize); + + void *buffer; + ThrowIfFailed(textureBuffersHeap.m_heap->Map(0, &CD3DX12_RANGE(heapOffset, heapOffset + textureSize), &buffer)); + void *textureData = (char*)buffer + heapOffset; + std::vector mipInfos = uploadPlacedTexture(texture, 256, textureData); + textureBuffersHeap.m_heap->Unmap(0, &CD3DX12_RANGE(heapOffset, heapOffset + textureSize)); + + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(existingTexture, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST)); + size_t miplevel = 0; + for (const MipmapLevelInfo mli : mipInfos) + { + commandList->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(existingTexture, (UINT)miplevel), 0, 0, 0, + &CD3DX12_TEXTURE_COPY_LOCATION(textureBuffersHeap.m_heap, { heapOffset + mli.offset,{ dxgiFormat, (UINT)mli.width, (UINT)mli.height, 1, (UINT)mli.rowPitch } }), nullptr); + miplevel++; + } + + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(existingTexture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ)); +} + + /** * Get number of bytes occupied by texture in RSX mem */ @@ -262,22 +302,29 @@ size_t D3D12GSRender::UploadTextures(ID3D12GraphicsCommandList *cmdlist) ID3D12Resource *vramTexture; std::unordered_map::const_iterator ItRTT = m_rtts.m_renderTargets.find(texaddr); - ID3D12Resource *cachedTex = m_textureCache.findDataIfAvailable(texaddr); + std::pair > *cachedTex = m_textureCache.findDataIfAvailable(texaddr); bool isRenderTarget = false; if (ItRTT != m_rtts.m_renderTargets.end()) { vramTexture = ItRTT->second; isRenderTarget = true; } - else if (cachedTex != nullptr) + else if (cachedTex != nullptr && (cachedTex->first == TextureEntry(format, w, h, m_textures[i].GetMipmap()))) { - vramTexture = cachedTex; + if (cachedTex->first.m_isDirty) + { + updateExistingTexture(m_textures[i], cmdlist, m_textureUploadData, cachedTex->second.Get()); + m_textureCache.protectData(texaddr, texaddr, getTextureSize(m_textures[i])); + } + vramTexture = cachedTex->second.Get(); } else { + if (cachedTex != nullptr) + getCurrentResourceStorage().m_dirtyTextures.push_back(m_textureCache.removeFromCache(texaddr)); ComPtr tex = uploadSingleTexture(m_textures[i], m_device.Get(), cmdlist, m_textureUploadData); vramTexture = tex.Get(); - m_textureCache.storeAndProtectData(texaddr, texaddr, getTextureSize(m_textures[i]), tex); + m_textureCache.storeAndProtectData(texaddr, texaddr, getTextureSize(m_textures[i]), format, w, h, m_textures[i].GetMipmap(), tex); } D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};