Commit c81da1c8 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Avoid double-copy when generating mipmap

If the image needs to be redefined with mips, level 0 was copied to a buffer which was then copied to the new image. This change instead stages the old image directly for copy to the new image. Bug: angleproject:4551 Change-Id: I7625f140ddadde0a2b439c5e91c519ad49ae2fd7 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2257264Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 6ee18c2d
......@@ -1362,11 +1362,8 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context)
baseLevelExtents.depth, sourceRowPitch, sourceDepthPitch, imageData + bufferOffset));
}
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
return mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), mImage->getLevelCount(),
getNativeImageLayer(0), mImage->getLayerCount(), {},
commandBuffer);
ASSERT(!mRedefinedLevels.any());
return flushImageStagedUpdates(contextVk);
}
angle::Result TextureVk::generateMipmap(const gl::Context *context)
......@@ -1502,11 +1499,7 @@ angle::Result TextureVk::respecifyImageAttributesAndLevels(ContextVk *contextVk,
// First, flush any pending updates so we have good data in the existing vkImage
if (mImage->valid() && mImage->hasStagedUpdates())
{
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
ANGLE_TRY(mImage->flushStagedUpdates(
contextVk, getNativeImageLevel(0), mImage->getLevelCount(), getNativeImageLayer(0),
mImage->getLayerCount(), mRedefinedLevels, commandBuffer));
ANGLE_TRY(flushImageStagedUpdates(contextVk));
}
// After flushing, track the new levels (they are used in the flush, hence the wait)
......@@ -1646,6 +1639,13 @@ angle::Result TextureVk::ensureImageInitializedImpl(ContextVk *contextVk,
levelCount));
}
return flushImageStagedUpdates(contextVk);
}
angle::Result TextureVk::flushImageStagedUpdates(ContextVk *contextVk)
{
ASSERT(mImage->valid());
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
return mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), mImage->getLevelCount(),
......@@ -1744,18 +1744,16 @@ angle::Result TextureVk::syncState(const gl::Context *context,
if (isGenerateMipmap && mImage->valid() &&
mImage->getLevelCount() != getMipLevelCount(ImageMipLevels::FullMipChain))
{
// Redefine the image with mipmaps.
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
ASSERT(mOwnsImage);
// Copy image to the staging buffer and stage an update to the new one.
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
ANGLE_TRY(copyAndStageImageSubresource(contextVk, baseLevelDesc, false,
getNativeImageLayer(0), 0, mImage->getBaseLevel()));
// 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.
ANGLE_TRY(flushImageStagedUpdates(contextVk));
// Release the original image so it can be recreated with the new mipmap counts.
releaseImage(contextVk);
mImage->stageSelfForBaseLevel();
mImage->retain(&contextVk->getResourceUseList());
// Release views and render targets created for the old image.
releaseImage(contextVk);
}
// Initialize the image storage and flush the pixel buffer.
......
......@@ -366,6 +366,9 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
uint32_t levelCount,
const vk::Format &format);
// Flush image's staged updates for all levels and layers.
angle::Result flushImageStagedUpdates(ContextVk *contextVk);
const gl::InternalFormat &getImplementationSizedFormat(const gl::Context *context) const;
const vk::Format &getBaseLevelFormat(RendererVk *renderer) const;
// Queues a flush of any modified image attributes. The image will be reallocated with its new
......
......@@ -3944,6 +3944,41 @@ void ImageHelper::stageClearIfEmulatedFormat(Context *context)
}
}
void ImageHelper::stageSelfForBaseLevel()
{
std::unique_ptr<vk::ImageHelper> prevImage = std::make_unique<vk::ImageHelper>();
// Move the necessary information for staged update to work, and keep the rest as part of this
// object.
// Vulkan objects
prevImage->mImage = std::move(mImage);
prevImage->mDeviceMemory = std::move(mDeviceMemory);
// Barrier information. Note: mLevelCount is set to 1 so that only the base level is
// transitioned when flushing the update.
prevImage->mFormat = mFormat;
prevImage->mSerial = mSerial;
prevImage->mCurrentLayout = mCurrentLayout;
prevImage->mCurrentQueueFamilyIndex = mCurrentQueueFamilyIndex;
prevImage->mLastNonShaderReadOnlyLayout = mLastNonShaderReadOnlyLayout;
prevImage->mCurrentShaderReadStageMask = mCurrentShaderReadStageMask;
prevImage->mLevelCount = 1;
prevImage->mLayerCount = mLayerCount;
// Reset barrier information for current (invalid) image.
mCurrentLayout = ImageLayout::Undefined;
mCurrentQueueFamilyIndex = std::numeric_limits<uint32_t>::max();
mLastNonShaderReadOnlyLayout = ImageLayout::Undefined;
mCurrentShaderReadStageMask = 0;
// Stage an update from the previous image.
const gl::ImageIndex baseLevelIndex =
gl::ImageIndex::Make2DArrayRange(mBaseLevel, 0, mLayerCount);
stageSubresourceUpdateFromImage(prevImage.release(), baseLevelIndex, gl::kOffsetZero,
getLevelExtents(0), mImageType);
}
angle::Result ImageHelper::allocateStagingMemory(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
......
......@@ -1243,6 +1243,10 @@ class ImageHelper final : public Resource, public angle::Subject
const vk::Format &format);
void stageRobustResourceClear(const gl::ImageIndex &index);
// Stage the currently allocated image as an update to base level, making this !valid(). This
// is used for mipmap generation.
void stageSelfForBaseLevel();
// This will use the underlying dynamic buffer to allocate some memory to be used as a src or
// dst.
angle::Result allocateStagingMemory(ContextVk *contextVk,
......
......@@ -856,6 +856,9 @@ TEST_P(MipmapTest, TextureCubeGeneralLevelZero)
// http://anglebug.com/2822
ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan());
// http://issuetracker.google.com/159666631
ANGLE_SKIP_TEST_IF(isSwiftshader());
glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
// Draw. Since the negative-Y face's is blue, this should be blue.
......@@ -1155,6 +1158,9 @@ TEST_P(MipmapTestES3, GenerateMipmapPreservesOutOfRangeMips)
// http://anglebug.com/4782
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsWindows() && (IsAMD() || IsIntel()));
// http://anglebug.com/4784
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsLinux() && IsIntel());
// http://anglebug.com/4786
ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsNVIDIAShield());
......@@ -1176,13 +1182,17 @@ TEST_P(MipmapTestES3, GenerateMipmapPreservesOutOfRangeMips)
// Set base level to 1, and generate mipmaps. Levels 1 through 5 will be green.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, kLevel1Data[0]);
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GL_NO_ERROR();
// Verify that the mips are all green.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
for (int mip = 0; mip < 5; ++mip)
{
int scale = 1 << mip;
......
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