diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp index 2d012de8d6..cc07aa5a96 100644 --- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp +++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp @@ -6137,12 +6137,19 @@ void ImageHelper::finalizeImageLayoutInShareContexts(Renderer *renderer, { vk::GetImpl(context.second)->finalizeImageLayout(this, imageSiblingSerial); } - if (mUseTileMemory) + // Drop the `if (mUseTileMemory)` gate. fallbackFromTileMemory clears + // mUseTileMemory (via copyStateAndMoveStorageFrom line 9821) without + // broadcasting removal first. Two of four callers of fallbackFromTileMemory + // (readPixelsImpl at vk_helpers.cpp:11485 and finalizeImagesWithTileMemory at + // ContextVk.cpp:9043) skip the broadcast that FramebufferVk.cpp:1298/1305 + // correctly perform. After fallback, mImageSerial is regenerated at line 6499 + // (passes the outer guard above) but mUseTileMemory is false, so the removal + // loop was skipped here, leaving a dangling raw ImageHelper* in + // ContextVk::mImagesWithTileMemory after SafeDelete. removeImageWithTileMemory + // is a no-op if the pointer is not in the list, so unconditional broadcast is safe. + for (auto context : contextVk->getShareGroup()->getContexts()) { - for (auto context : contextVk->getShareGroup()->getContexts()) - { - vk::GetImpl(context.second)->removeImageWithTileMemory(this); - } + vk::GetImpl(context.second)->removeImageWithTileMemory(this); } } }