Commit a7158eb5 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Fix image leak in staged updates in ImageHelper

When removing superseding updates, the superseded update was not released, causing a memory leak. This change also makes SubresourceUpdate non-copyable and correctly implements the move assignment operator such that swap between different update types are correct. As a result, the destructor can now ASSERT that the image is not leaked. Bug: chromium:1146516 Bug: chromium:1163354 Change-Id: I7531c91d8559c23b2e09159118fe645d12fc601f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2613201Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 157ddfdc
...@@ -5551,7 +5551,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -5551,7 +5551,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
removeSupersededUpdates(skipLevelsMask); removeSupersededUpdates(contextVk, skipLevelsMask);
// If a clear is requested and we know it was previously cleared with the same value, we drop // If a clear is requested and we know it was previously cleared with the same value, we drop
// the clear. // the clear.
...@@ -5639,7 +5639,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -5639,7 +5639,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
if (isUpdateLevelOutsideRange || areUpdateLayersOutsideRange || if (isUpdateLevelOutsideRange || areUpdateLayersOutsideRange ||
skipLevelsMask.test(updateMipLevelVk.get())) skipLevelsMask.test(updateMipLevelVk.get()))
{ {
updatesToKeep.emplace_back(update); updatesToKeep.emplace_back(std::move(update));
continue; continue;
} }
...@@ -5813,7 +5813,7 @@ bool ImageHelper::hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelI ...@@ -5813,7 +5813,7 @@ bool ImageHelper::hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelI
return false; return false;
} }
void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask) void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask skipLevelsMask)
{ {
if (mLayerCount > 64) if (mLayerCount > 64)
{ {
...@@ -5822,6 +5822,8 @@ void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask) ...@@ -5822,6 +5822,8 @@ void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask)
return; return;
} }
RendererVk *renderer = contextVk->getRenderer();
// Go over updates in reverse order, and mark the layers they completely overwrite. If an // Go over updates in reverse order, and mark the layers they completely overwrite. If an
// update is encountered whose layers are all already marked, that update is superseded by // update is encountered whose layers are all already marked, that update is superseded by
// future updates, so it can be dropped. This tracking is done per level. If the aspect being // future updates, so it can be dropped. This tracking is done per level. If the aspect being
...@@ -5837,7 +5839,7 @@ void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask) ...@@ -5837,7 +5839,7 @@ void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask)
// Note: this lambda only needs |this|, but = is specified because clang warns about kIndex* not // Note: this lambda only needs |this|, but = is specified because clang warns about kIndex* not
// needing capture, while MSVC fails to compile without capturing them. // needing capture, while MSVC fails to compile without capturing them.
auto markLayersAndDropSuperseded = [=, &supersededLayers, auto markLayersAndDropSuperseded = [=, &supersededLayers,
&levelExtents](const SubresourceUpdate &update) { &levelExtents](SubresourceUpdate &update) {
uint32_t updateBaseLayer, updateLayerCount; uint32_t updateBaseLayer, updateLayerCount;
update.getDestSubresource(mLayerCount, &updateBaseLayer, &updateLayerCount); update.getDestSubresource(mLayerCount, &updateBaseLayer, &updateLayerCount);
...@@ -5861,6 +5863,7 @@ void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask) ...@@ -5861,6 +5863,7 @@ void ImageHelper::removeSupersededUpdates(gl::TexLevelMask skipLevelsMask)
if (isColorOrDepthSuperseded && isStencilSuperseded) if (isColorOrDepthSuperseded && isStencilSuperseded)
{ {
update.release(renderer);
return true; return true;
} }
...@@ -6272,6 +6275,11 @@ angle::Result ImageHelper::readPixels(ContextVk *contextVk, ...@@ -6272,6 +6275,11 @@ angle::Result ImageHelper::readPixels(ContextVk *contextVk,
ImageHelper::SubresourceUpdate::SubresourceUpdate() : updateSource(UpdateSource::Buffer), buffer{} ImageHelper::SubresourceUpdate::SubresourceUpdate() : updateSource(UpdateSource::Buffer), buffer{}
{} {}
ImageHelper::SubresourceUpdate::~SubresourceUpdate()
{
ASSERT(updateSource != UpdateSource::Image || image.image == nullptr);
}
ImageHelper::SubresourceUpdate::SubresourceUpdate(BufferHelper *bufferHelperIn, ImageHelper::SubresourceUpdate::SubresourceUpdate(BufferHelper *bufferHelperIn,
const VkBufferImageCopy &copyRegionIn) const VkBufferImageCopy &copyRegionIn)
: updateSource(UpdateSource::Buffer), buffer{bufferHelperIn, copyRegionIn} : updateSource(UpdateSource::Buffer), buffer{bufferHelperIn, copyRegionIn}
...@@ -6295,39 +6303,45 @@ ImageHelper::SubresourceUpdate::SubresourceUpdate(VkImageAspectFlags aspectFlags ...@@ -6295,39 +6303,45 @@ ImageHelper::SubresourceUpdate::SubresourceUpdate(VkImageAspectFlags aspectFlags
imageIndex.hasLayer() ? imageIndex.getLayerCount() : VK_REMAINING_ARRAY_LAYERS; imageIndex.hasLayer() ? imageIndex.getLayerCount() : VK_REMAINING_ARRAY_LAYERS;
} }
ImageHelper::SubresourceUpdate::SubresourceUpdate(const SubresourceUpdate &other) ImageHelper::SubresourceUpdate::SubresourceUpdate(SubresourceUpdate &&other)
: updateSource(other.updateSource) : updateSource(other.updateSource)
{ {
if (updateSource == UpdateSource::Clear) switch (updateSource)
{
clear = other.clear;
}
else if (updateSource == UpdateSource::Buffer)
{
buffer = other.buffer;
}
else
{ {
image = other.image; case UpdateSource::Clear:
clear = other.clear;
break;
case UpdateSource::Buffer:
buffer = other.buffer;
break;
case UpdateSource::Image:
image = other.image;
other.image.image = nullptr;
break;
default:
UNREACHABLE();
} }
} }
ImageHelper::SubresourceUpdate &ImageHelper::SubresourceUpdate::operator=( ImageHelper::SubresourceUpdate &ImageHelper::SubresourceUpdate::operator=(SubresourceUpdate &&other)
const SubresourceUpdate &other)
{ {
updateSource = other.updateSource; // Given that the update is a union of three structs, we can't use std::swap on the fields. For
if (updateSource == UpdateSource::Clear) // example, |this| may be an Image update and |other| may be a Buffer update.
{ // The following could work:
clear = other.clear; //
} // SubresourceUpdate oldThis;
else if (updateSource == UpdateSource::Buffer) // Set oldThis to this->field based on updateSource
{ // Set this->otherField to other.otherField based on other.updateSource
buffer = other.buffer; // Set other.field to oldThis->field based on updateSource
} // std::Swap(updateSource, other.updateSource);
else //
{ // It's much simpler to just swap the memory instead.
image = other.image;
} SubresourceUpdate oldThis;
memcpy(&oldThis, this, sizeof(*this));
memcpy(this, &other, sizeof(*this));
memcpy(&other, &oldThis, sizeof(*this));
return *this; return *this;
} }
......
...@@ -1792,17 +1792,18 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1792,17 +1792,18 @@ class ImageHelper final : public Resource, public angle::Subject
VkImageCopy copyRegion; VkImageCopy copyRegion;
}; };
struct SubresourceUpdate struct SubresourceUpdate : angle::NonCopyable
{ {
SubresourceUpdate(); SubresourceUpdate();
~SubresourceUpdate();
SubresourceUpdate(BufferHelper *bufferHelperIn, const VkBufferImageCopy &copyRegion); SubresourceUpdate(BufferHelper *bufferHelperIn, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(ImageHelper *image, const VkImageCopy &copyRegion); SubresourceUpdate(ImageHelper *image, const VkImageCopy &copyRegion);
SubresourceUpdate(VkImageAspectFlags aspectFlags, SubresourceUpdate(VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue, const VkClearValue &clearValue,
const gl::ImageIndex &imageIndex); const gl::ImageIndex &imageIndex);
SubresourceUpdate(const SubresourceUpdate &other); SubresourceUpdate(SubresourceUpdate &&other);
SubresourceUpdate &operator=(const SubresourceUpdate &other); SubresourceUpdate &operator=(SubresourceUpdate &&other);
void release(RendererVk *renderer); void release(RendererVk *renderer);
...@@ -1824,7 +1825,7 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1824,7 +1825,7 @@ class ImageHelper final : public Resource, public angle::Subject
// Called from flushStagedUpdates, removes updates that are later superseded by another. This // Called from flushStagedUpdates, removes updates that are later superseded by another. This
// cannot be done at the time the updates were staged, as the image is not created (and thus the // cannot be done at the time the updates were staged, as the image is not created (and thus the
// extents are not known). // extents are not known).
void removeSupersededUpdates(gl::TexLevelMask skipLevelsMask); void removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask skipLevelsMask);
void initImageMemoryBarrierStruct(VkImageAspectFlags aspectMask, void initImageMemoryBarrierStruct(VkImageAspectFlags aspectMask,
ImageLayout newLayout, ImageLayout newLayout,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment