[Fix] (MG_Backend/DirectVulkan): fix 26.2 texture bug.

- Fix texture object / buffer lifecycle issues
This commit is contained in:
2026-05-10 10:03:03 +08:00
parent d7e768096e
commit 4a36d63c1b
6 changed files with 72 additions and 19 deletions
@@ -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;
@@ -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<MG_State::GLState::ITextureObject>(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;
@@ -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<VkDeviceSize>(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<VkDeviceSize>(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<VkDeviceSize>(indexBufferShared->GetSize()), indexSize,
static_cast<VkDeviceSize>(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,
@@ -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<const MG_State::GLState::BufferObject*, BufferSlice> m_transientVertexIndexBufferSlicesThisFrame;
UnorderedMap<const MG_State::GLState::BufferObject*, TransientBufferSliceCacheEntry>
m_transientVertexIndexBufferSlicesThisFrame;
Uint m_imageIndexAcquired = 0;
FrameContext m_frameContext;
@@ -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<BufferObject>& 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;
}
@@ -99,6 +99,7 @@ namespace MobileGL {
Uint GetExternalIndex() const;
const VecRange1D& GetDirtyRanges() const;
Flags<BufferChangeBits> GetChangeBits() const;
Uint64 GetChangeSerial() const;
private:
const Uint m_externalIndex = 0;
@@ -108,6 +109,7 @@ namespace MobileGL {
Bool m_isMapped;
Flags<BufferMappingAccessBit> m_mappingAccess;
BufferChange m_change;
Uint64 m_changeSerial = 0;
Range1D m_mappedRange;
Vector<Uint8> m_stagingData;
Bool m_ownsStagingData;