Commit cf3af29a by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Optimize respecifying an image

When recreating a texture image to change a usage flag or the base/max levels, this change directly places the levels of the previous image as updates to the new image. This is instead of copying the image to a temp one and staging that. Bug: angleproject:4835 Change-Id: Ibc210b9ff0e8d11cba10b1cd9ab262c8f706cea5 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2898417 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCharlie Lao <cclao@google.com>
parent 8bd3d7d5
...@@ -1937,6 +1937,10 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, ...@@ -1937,6 +1937,10 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
// levels outside the range of the Vulkan image will remain intact. // levels outside the range of the Vulkan image will remain intact.
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
// This path is only called when switching from !owned to owned, in which case if any level was
// redefined it's already released and deleted by TextureVk::redefineLevel().
ASSERT(!mRedefinedLevels.any());
// Create a temp copy of srcImage for staging. // Create a temp copy of srcImage for staging.
std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage; std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage;
stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>(); stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>();
...@@ -1967,15 +1971,6 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, ...@@ -1967,15 +1971,6 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
for (vk::LevelIndex levelVk(0); levelVk < vk::LevelIndex(levelCount); ++levelVk) for (vk::LevelIndex levelVk(0); levelVk < vk::LevelIndex(levelCount); ++levelVk)
{ {
if (mRedefinedLevels.test(levelVk.get()))
{
// Note: if this level is incompatibly redefined, there will necessarily be a
// staged update, and the contents of the image are to be thrown away.
ASSERT(srcImage->hasStagedUpdatesForSubresource(
vk_gl::GetLevelIndex(levelVk, previousFirstAllocateLevel), 0, layerCount));
continue;
}
gl::Extents levelExtents = srcImage->getLevelExtents(levelVk); gl::Extents levelExtents = srcImage->getLevelExtents(levelVk);
copyRegion.srcSubresource.mipLevel = levelVk.get(); copyRegion.srcSubresource.mipLevel = levelVk.get();
...@@ -1988,8 +1983,8 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, ...@@ -1988,8 +1983,8 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
} }
// Stage the staging image in the destination // Stage the staging image in the destination
dstImage->stageSubresourceUpdatesFromAllImageLevels( dstImage->stageSubresourceUpdatesFromAllImageLevels(stagingImage.release(),
renderer, stagingImage.release(), previousFirstAllocateLevel, mRedefinedLevels); previousFirstAllocateLevel);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -2065,14 +2060,11 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk, ...@@ -2065,14 +2060,11 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk,
} }
else else
{ {
// TODO: Make the image stage itself as an update to its levels. http://anglebug.com/4835 // Make the image stage itself as updates to its levels.
mImage->stageSelfAsSubresourceUpdates(contextVk, mImage->getLevelCount(), mRedefinedLevels);
// Make a copy of the current image and stage that as an update to the new image.
ANGLE_TRY(copyAndStageImageData(contextVk, previousFirstAllocateLevel, mImage, mImage));
// Now that we've staged all the updates, release the current image so that it will be // Release the current image so that it will be recreated with the correct number of mip
// recreated with the correct number of mip levels, base level, and max level. // levels, base level, and max level.
// Do this iff we owned the image and didn't create a new one.
releaseImage(contextVk); releaseImage(contextVk);
if (!mState.getImmutableFormat()) if (!mState.getImmutableFormat())
...@@ -2423,8 +2415,8 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -2423,8 +2415,8 @@ angle::Result TextureVk::syncState(const gl::Context *context,
// For immutable texture, base level does not affect allocation. Only usage flags are. If usage // For immutable texture, base level does not affect allocation. Only usage flags are. If usage
// flag changed, we respecify image storage early on. This makes the code more reliable and also // flag changed, we respecify image storage early on. This makes the code more reliable and also
// better performance wise. Otherwise, we will try to preserve base level by calling // better performance wise. Otherwise, we will try to preserve base level by calling
// stageSelfForBaseLevel and then later on find out the mImageUsageFlags changed and the whole // stageSelfAsSubresourceUpdates and then later on find out the mImageUsageFlags changed and the
// thing has to be respecified. // whole thing has to be respecified.
if (mState.getImmutableFormat() && if (mState.getImmutableFormat() &&
(oldUsageFlags != mImageUsageFlags || oldCreateFlags != mImageCreateFlags)) (oldUsageFlags != mImageUsageFlags || oldCreateFlags != mImageCreateFlags))
{ {
...@@ -2463,14 +2455,14 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -2463,14 +2455,14 @@ angle::Result TextureVk::syncState(const gl::Context *context,
{ {
ASSERT(mOwnsImage); ASSERT(mOwnsImage);
// Immutable texture is not expected to reach here. The usage flag change should have // Immutable texture is not expected to reach here. The usage flag change should have
// handled earlier and level count change should not need to reallocate // been handled earlier and level count change should not need to reallocate
ASSERT(!mState.getImmutableFormat()); ASSERT(!mState.getImmutableFormat());
// Flush staged updates to the base level of the image. Note that updates to the rest of // Flush staged updates to the base level of the image. Note that updates to the rest of
// the levels have already been discarded through the |removeStagedUpdates| call above. // the levels have already been discarded through the |removeStagedUpdates| call above.
ANGLE_TRY(flushImageStagedUpdates(contextVk)); ANGLE_TRY(flushImageStagedUpdates(contextVk));
mImage->stageSelfForBaseLevel(contextVk); mImage->stageSelfAsSubresourceUpdates(contextVk, 1, {});
// Release views and render targets created for the old image. // Release views and render targets created for the old image.
releaseImage(contextVk); releaseImage(contextVk);
......
...@@ -5764,21 +5764,11 @@ void ImageHelper::stageSubresourceUpdateFromImage(RefCounted<ImageHelper> *image ...@@ -5764,21 +5764,11 @@ void ImageHelper::stageSubresourceUpdateFromImage(RefCounted<ImageHelper> *image
appendSubresourceUpdate(updateLevelGL, SubresourceUpdate(image, copyToImage)); appendSubresourceUpdate(updateLevelGL, SubresourceUpdate(image, copyToImage));
} }
void ImageHelper::stageSubresourceUpdatesFromAllImageLevels(RendererVk *renderer, void ImageHelper::stageSubresourceUpdatesFromAllImageLevels(RefCounted<ImageHelper> *image,
RefCounted<ImageHelper> *image, gl::LevelIndex baseLevel)
gl::LevelIndex baseLevel,
gl::TexLevelMask skipLevelsMask)
{ {
uint32_t levelsStaged = 0;
for (LevelIndex levelVk(0); levelVk < LevelIndex(image->get().getLevelCount()); ++levelVk) for (LevelIndex levelVk(0); levelVk < LevelIndex(image->get().getLevelCount()); ++levelVk)
{ {
if (skipLevelsMask.test(levelVk.get()))
{
continue;
}
++levelsStaged;
const gl::LevelIndex levelGL = vk_gl::GetLevelIndex(levelVk, baseLevel); const gl::LevelIndex levelGL = vk_gl::GetLevelIndex(levelVk, baseLevel);
const gl::ImageIndex index = const gl::ImageIndex index =
gl::ImageIndex::Make2DArrayRange(levelGL.get(), 0, image->get().getLayerCount()); gl::ImageIndex::Make2DArrayRange(levelGL.get(), 0, image->get().getLayerCount());
...@@ -5787,15 +5777,6 @@ void ImageHelper::stageSubresourceUpdatesFromAllImageLevels(RendererVk *renderer ...@@ -5787,15 +5777,6 @@ void ImageHelper::stageSubresourceUpdatesFromAllImageLevels(RendererVk *renderer
image->get().getLevelExtents(levelVk), image->get().getLevelExtents(levelVk),
image->get().getType()); image->get().getType());
} }
// TODO: remove skipLevelsMask and this code after optimizing image-respecify to stage itself
// (instead of a copy). http://anglebug.com/4835
if (levelsStaged == 0)
{
image->get().releaseImage(renderer);
image->get().releaseStagingBuffer(renderer);
SafeDelete(image);
}
} }
void ImageHelper::stageClear(const gl::ImageIndex &index, void ImageHelper::stageClear(const gl::ImageIndex &index,
...@@ -5905,8 +5886,17 @@ void ImageHelper::stageClearIfEmulatedFormat(bool isRobustResourceInitEnabled) ...@@ -5905,8 +5886,17 @@ void ImageHelper::stageClearIfEmulatedFormat(bool isRobustResourceInitEnabled)
} }
} }
void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk) void ImageHelper::stageSelfAsSubresourceUpdates(ContextVk *contextVk,
uint32_t levelCount,
gl::TexLevelMask skipLevelsMask)
{ {
// Nothing to do if every level must be skipped
if ((~skipLevelsMask & gl::TexLevelMask(angle::Bit<uint32_t>(levelCount) - 1)).none())
{
return;
}
// Because we are cloning this object to another object, we must finalize the layout if it is // Because we are cloning this object to another object, we must finalize the layout if it is
// being used by current renderpass as attachment. Otherwise we are copying the incorrect layout // being used by current renderpass as attachment. Otherwise we are copying the incorrect layout
// since it is determined at endRenderPass time. // since it is determined at endRenderPass time.
...@@ -5922,14 +5912,14 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk) ...@@ -5922,14 +5912,14 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk)
prevImage->get().mImage = std::move(mImage); prevImage->get().mImage = std::move(mImage);
prevImage->get().mDeviceMemory = std::move(mDeviceMemory); prevImage->get().mDeviceMemory = std::move(mDeviceMemory);
// Barrier information. Note: mLevelCount is set to 1 so that only the base level is // Barrier information. Note: mLevelCount is set to levelCount so that only the necessary
// transitioned when flushing the update. // levels are transitioned when flushing the update.
prevImage->get().mFormat = mFormat; prevImage->get().mFormat = mFormat;
prevImage->get().mCurrentLayout = mCurrentLayout; prevImage->get().mCurrentLayout = mCurrentLayout;
prevImage->get().mCurrentQueueFamilyIndex = mCurrentQueueFamilyIndex; prevImage->get().mCurrentQueueFamilyIndex = mCurrentQueueFamilyIndex;
prevImage->get().mLastNonShaderReadOnlyLayout = mLastNonShaderReadOnlyLayout; prevImage->get().mLastNonShaderReadOnlyLayout = mLastNonShaderReadOnlyLayout;
prevImage->get().mCurrentShaderReadStageMask = mCurrentShaderReadStageMask; prevImage->get().mCurrentShaderReadStageMask = mCurrentShaderReadStageMask;
prevImage->get().mLevelCount = 1; prevImage->get().mLevelCount = levelCount;
prevImage->get().mLayerCount = mLayerCount; prevImage->get().mLayerCount = mLayerCount;
prevImage->get().mImageSerial = mImageSerial; prevImage->get().mImageSerial = mImageSerial;
...@@ -5942,11 +5932,23 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk) ...@@ -5942,11 +5932,23 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk)
setEntireContentUndefined(); setEntireContentUndefined();
// Stage an update from the previous image. // Stage updates from the previous image.
const gl::ImageIndex firstAllocateLevelIndex = for (LevelIndex levelVk(0); levelVk < LevelIndex(levelCount); ++levelVk)
gl::ImageIndex::Make2DArrayRange(mFirstAllocatedLevel.get(), 0, mLayerCount); {
stageSubresourceUpdateFromImage(prevImage.release(), firstAllocateLevelIndex, LevelIndex(0), if (skipLevelsMask.test(levelVk.get()))
gl::kOffsetZero, getLevelExtents(LevelIndex(0)), mImageType); {
continue;
}
const gl::ImageIndex index =
gl::ImageIndex::Make2DArrayRange(toGLLevel(levelVk).get(), 0, mLayerCount);
stageSubresourceUpdateFromImage(prevImage.get(), index, levelVk, gl::kOffsetZero,
getLevelExtents(levelVk), mImageType);
}
ASSERT(levelCount > 0);
prevImage.release();
} }
angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contextVk, angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contextVk,
......
...@@ -1696,10 +1696,8 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1696,10 +1696,8 @@ class ImageHelper final : public Resource, public angle::Subject
// Takes an image and stages a subresource update for each level of it, including its full // Takes an image and stages a subresource update for each level of it, including its full
// extent and all its layers, at the specified GL level. // extent and all its layers, at the specified GL level.
void stageSubresourceUpdatesFromAllImageLevels(RendererVk *renderer, void stageSubresourceUpdatesFromAllImageLevels(RefCounted<ImageHelper> *image,
RefCounted<ImageHelper> *image, gl::LevelIndex baseLevel);
gl::LevelIndex baseLevel,
gl::TexLevelMask skipLevelsMask);
// Stage a clear to an arbitrary value. // Stage a clear to an arbitrary value.
void stageClear(const gl::ImageIndex &index, void stageClear(const gl::ImageIndex &index,
...@@ -1713,9 +1711,14 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1713,9 +1711,14 @@ class ImageHelper final : public Resource, public angle::Subject
const Format &format); const Format &format);
void stageRobustResourceClear(const gl::ImageIndex &index); void stageRobustResourceClear(const gl::ImageIndex &index);
// Stage the currently allocated image as an update to base level, making this !valid(). This // Stage the currently allocated image as updates to base level and on, making this !valid().
// is used for mipmap generation. // This is used for:
void stageSelfForBaseLevel(ContextVk *contextVk); //
// - Mipmap generation, where levelCount is 1 so only the base level is retained
// - Image respecification, where every level (other than those explicitly skipped) is staged
void stageSelfAsSubresourceUpdates(ContextVk *contextVk,
uint32_t levelCount,
gl::TexLevelMask skipLevelsMask);
// Flush staged updates for a single subresource. Can optionally take a parameter to defer // Flush staged updates for a single subresource. Can optionally take a parameter to defer
// clears to a subsequent RenderPass load op. // clears to a subsequent RenderPass load op.
......
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