diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp index 16187c3b..1192b4bb 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp @@ -142,11 +142,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { } SizeT count = 0; - for (const auto& [raw, weak]: m_aliveObjects) { - if (weak.expired()) { - count++; - m_pendingClears.erase(raw); - m_aliveObjects.erase(raw); + for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) { + auto current = it++; + if (current->second.expired()) { + m_pendingClears.erase(current->first); + m_aliveObjects.erase(current); + ++count; } } return count; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index af2526e3..ea22dcf1 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -443,6 +443,21 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkTextureManager::TextureResource* VkTextureManager::SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture) { MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE"); + auto aliveIt = m_aliveObjects.find(&texture); + if (aliveIt != m_aliveObjects.end() && aliveIt->second.expired()) { + auto resourceIt = m_textureResources.find(&texture); + if (resourceIt != m_textureResources.end()) { + DeferResourceRelease(Move(resourceIt->second)); + m_textureResources.erase(resourceIt); + } + m_aliveObjects.erase(aliveIt); + } + + const auto& liveTexture = MG_State::pGLContext->GetTextureObject(texture.GetExternalIndex()); + if (liveTexture && liveTexture.get() == &texture) { + m_aliveObjects[&texture] = WeakPtr(liveTexture); + } + auto it = m_textureResources.find(&texture); if (it == m_textureResources.end()) { TextureResource initial{}; @@ -668,11 +683,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { return 0; } SizeT count = 0; - for (const auto& [raw, weak]: m_aliveObjects) { - if (weak.expired()) { - count++; - m_textureResources.erase(raw); - m_aliveObjects.erase(raw); + for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) { + auto current = it++; + if (current->second.expired()) { + auto resourceIt = m_textureResources.find(current->first); + if (resourceIt != m_textureResources.end()) { + DeferResourceRelease(Move(resourceIt->second)); + m_textureResources.erase(resourceIt); + } + m_aliveObjects.erase(current); + ++count; } } return count; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index f93ba4d7..c0ccd560 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -1323,19 +1323,25 @@ void main() { "UploadAndBindVertexStreams failed to resolve shared source buffer"); BufferSlice slice{}; const Bool isDirty = (sourceBufferShared->GetChangeBits() & BufferChangeBits::DirtyBit); + const Uint64 changeSerial = sourceBufferShared->GetChangeSerial(); + const SizeT sourceSize = sourceBufferShared->GetSize(); auto cachedTransient = m_transientVertexIndexBufferSlicesThisFrame.find(sourceBufferShared.get()); - if (cachedTransient != m_transientVertexIndexBufferSlicesThisFrame.end() && !isDirty) { - slice = cachedTransient->second; + if (cachedTransient != m_transientVertexIndexBufferSlicesThisFrame.end() && !isDirty && + cachedTransient->second.changeSerial == changeSerial && cachedTransient->second.size == sourceSize) { + slice = cachedTransient->second.slice; } else if (ShouldUseTransientVertexIndexBuffer(*sourceBufferShared) || isDirty) { const auto sourceData = sourceBufferShared->GetDataReadOnly(); - const SizeT sourceSize = sourceBufferShared->GetSize(); if (!m_bufferManager.UploadTransient(BufferKind::Vertex, m_frameContext.GetCurrentFrameIndex(), sourceData->data(), static_cast(sourceSize), 16, slice)) { MOBILEGL_ASSERT(false, "UploadAndBindVertexStreams skipped: failed to upload transient binding %zu", binding); return false; } - m_transientVertexIndexBufferSlicesThisFrame[sourceBufferShared.get()] = slice; + m_transientVertexIndexBufferSlicesThisFrame[sourceBufferShared.get()] = { + .slice = slice, + .changeSerial = changeSerial, + .size = sourceSize, + }; m_bufferManager.DowngradeResidentBufferToTransient(sourceBufferShared); sourceBufferShared->ClearDirty(); } else { @@ -1415,9 +1421,12 @@ void main() { auto indexBufferShared = MG_State::pGLContext->GetBufferObject(indexBuffer->GetExternalIndex()); MOBILEGL_ASSERT(indexBufferShared != nullptr, "UploadAndBindIndexBuffer failed to resolve shared EBO"); const Bool isDirty = (indexBufferShared->GetChangeBits() & BufferChangeBits::DirtyBit); + const Uint64 changeSerial = indexBufferShared->GetChangeSerial(); + const SizeT indexBufferSize = indexBufferShared->GetSize(); auto cachedTransient = m_transientVertexIndexBufferSlicesThisFrame.find(indexBufferShared.get()); - if (cachedTransient != m_transientVertexIndexBufferSlicesThisFrame.end() && !isDirty) { - slice = cachedTransient->second; + if (cachedTransient != m_transientVertexIndexBufferSlicesThisFrame.end() && !isDirty && + cachedTransient->second.changeSerial == changeSerial && cachedTransient->second.size == indexBufferSize) { + slice = cachedTransient->second.slice; vkCmdBindIndexBuffer(frame.commandBuffer, slice.buffer, slice.offset + static_cast(pIndexBufferView->indexByteOffset), @@ -1429,12 +1438,16 @@ void main() { MOBILEGL_ASSERT(indexData != nullptr && !indexData->empty(), "DrawElements requires non-empty EBO data"); if (!m_bufferManager.UploadTransient(BufferKind::Index, m_frameContext.GetCurrentFrameIndex(), indexData->data(), - static_cast(indexBufferShared->GetSize()), indexSize, + static_cast(indexBufferSize), indexSize, slice)) { MOBILEGL_ASSERT(false, "DrawElements skipped: failed to prepare transient index buffer"); return false; } - m_transientVertexIndexBufferSlicesThisFrame[indexBufferShared.get()] = slice; + m_transientVertexIndexBufferSlicesThisFrame[indexBufferShared.get()] = { + .slice = slice, + .changeSerial = changeSerial, + .size = indexBufferSize, + }; m_bufferManager.DowngradeResidentBufferToTransient(indexBufferShared); indexBufferShared->ClearDirty(); vkCmdBindIndexBuffer(frame.commandBuffer, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 57ac30e8..32dfd5e3 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -197,8 +197,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkCommandPool m_commandPool = VK_NULL_HANDLE; + struct TransientBufferSliceCacheEntry { + BufferSlice slice; + Uint64 changeSerial = 0; + SizeT size = 0; + }; + VkBufferManager m_bufferManager; - UnorderedMap m_transientVertexIndexBufferSlicesThisFrame; + UnorderedMap + m_transientVertexIndexBufferSlicesThisFrame; Uint m_imageIndexAcquired = 0; FrameContext m_frameContext; diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp index 963ea427..e625803b 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp @@ -23,6 +23,7 @@ namespace MobileGL::MG_State::GLState { m_dataPtr->resize(size); m_change.Bits |= BufferChangeBits::DirtyBit; m_change.Bits |= BufferChangeBits::PreferReallocationBit; + ++m_changeSerial; } void BufferObject::UploadData(DataPtr data, SizeT atOffset) { @@ -35,6 +36,7 @@ namespace MobileGL::MG_State::GLState { m_change.Bits |= BufferChangeBits::DirtyBit; m_change.Bits |= BufferChangeBits::ForbidInvalidationBit; m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit; + ++m_changeSerial; // This function may be called by `glBufferData`, but we still set the forbid bits above, // because when `PreferReallocationBit` is set, those bits are ignored anyway. // The bits can fit the `glBufferSubData` semantics @@ -54,6 +56,7 @@ namespace MobileGL::MG_State::GLState { m_mappedRange.end - m_mappedRange.start); m_change.DirtyRanges.Add({m_mappedRange.start, m_mappedRange.end}); m_change.Bits |= BufferChangeBits::DirtyBit; + ++m_changeSerial; } m_stagingData.clear(); @@ -80,6 +83,7 @@ namespace MobileGL::MG_State::GLState { Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length); m_change.DirtyRanges.Add({start, end}); m_change.Bits |= BufferChangeBits::DirtyBit; + ++m_changeSerial; } void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) { @@ -93,6 +97,7 @@ namespace MobileGL::MG_State::GLState { m_change.Bits |= BufferChangeBits::DirtyBit; m_change.Bits |= BufferChangeBits::ForbidInvalidationBit; m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit; + ++m_changeSerial; } void BufferObject::CopyDataFrom(const SharedPtr& src, SizeT srcOffset, SizeT dstOffset, SizeT size) { @@ -109,6 +114,7 @@ namespace MobileGL::MG_State::GLState { Memcpy(m_dataPtr->data() + dstOffset, srcData, size); m_change.DirtyRanges.Add({dstOffset, dstOffset + size}); m_change.Bits |= BufferChangeBits::DirtyBit; + ++m_changeSerial; } void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) { @@ -191,6 +197,10 @@ namespace MobileGL::MG_State::GLState { return m_change.Bits; } + Uint64 BufferObject::GetChangeSerial() const { + return m_changeSerial; + } + Bool BufferObject::IsMapped() const { return m_isMapped; } diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.h b/MobileGL/MG_State/GLState/BufferState/BufferObject.h index 18899e88..e6a01383 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.h @@ -99,6 +99,7 @@ namespace MobileGL { Uint GetExternalIndex() const; const VecRange1D& GetDirtyRanges() const; Flags GetChangeBits() const; + Uint64 GetChangeSerial() const; private: const Uint m_externalIndex = 0; @@ -108,6 +109,7 @@ namespace MobileGL { Bool m_isMapped; Flags m_mappingAccess; BufferChange m_change; + Uint64 m_changeSerial = 0; Range1D m_mappedRange; Vector m_stagingData; Bool m_ownsStagingData;