Commit 88752889 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Fix barriers w.r.t sampled->storage image update

df415528 implemented a feature where images are not created with the storage flag until needed. This is a necessary optimization. There were a few misuses of the BufferHelper::onRead/Write helpers that set up the appropriate barriers that this change fixes. Bug: angleproject:3816 Change-Id: I7e62d98b7325f938152a1972f4ebee083ed319c2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1924989 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarXinyi He <xinyi.he@arm.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarSunny Sun <sunny.sun@arm.com>
parent 5b2740c5
......@@ -397,6 +397,14 @@ class CommandGraphResource : angle::NonCopyable
void onGraphAccess(CommandGraph *commandGraph);
void updateCurrentAccessNodes();
// If a resource is recreated, as in released and reinitialized, the next access to the
// resource will not create an edge from its last node and will create a new independent node.
// This is because mUse is reset and the graph believes it's an entirely new resource. In very
// particular cases, such as recreating an image with full mipchain or adding STORAGE_IMAGE flag
// to its uses, this function is used to preserve the link between the previous and new
// nodes allocated for this resource.
void onResourceRecreated(CommandGraph *commandGraph);
// Allocates a write node via getNewWriteNode and returns a started command buffer.
// The started command buffer will render outside of a RenderPass.
// Will append to an existing command buffer/graph node if possible.
......@@ -612,6 +620,12 @@ ANGLE_INLINE void CommandGraphResource::updateCurrentAccessNodes()
}
}
ANGLE_INLINE void CommandGraphResource::onResourceRecreated(CommandGraph *commandGraph)
{
// Store reference to usage in graph.
commandGraph->onResourceUse(mUse);
}
ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph)
{
updateCurrentAccessNodes();
......
......@@ -1163,24 +1163,22 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
// Redefine the images with mipmaps.
// Copy image to the staging buffer and stage an update to the new one.
vk::BufferHelper *stagingBuffer = nullptr;
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, baseLevelDesc, false,
getNativeImageLayer(0), 0, mImage->getBaseLevel(),
&stagingBuffer));
ANGLE_TRY(copyAndStageImageSubresource(contextVk, baseLevelDesc, false,
getNativeImageLayer(0), 0, mImage->getBaseLevel()));
// Create a new node for the image and add a global memory barrier for the staging buffer.
// It's written to and staged to be read from when ensureImageInitialized() is called.
mImage->finishCurrentCommands(contextVk);
mImage->addGlobalMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
onStagingBufferChange();
// Release the origin image and recreate it with new mipmap counts.
releaseImage(contextVk);
mImage->onResourceRecreated(contextVk->getCommandGraph());
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::FullMipChain));
// Set up read dependency, we are now reading from this buffer to the new image.
if (stagingBuffer)
{
stagingBuffer->onRead(contextVk, mImage, VK_ACCESS_TRANSFER_READ_BIT);
// Different parts of the buffer might be read from or write to.
stagingBuffer->onSelfReadWrite(contextVk, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
}
// Check if the image supports blit. If it does, we can do the mipmap generation on the gpu
// only.
......@@ -1197,13 +1195,12 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
return angle::Result::Continue;
}
angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
angle::Result TextureVk::copyAndStageImageSubresource(ContextVk *contextVk,
const gl::ImageDesc &desc,
bool ignoreLayerCount,
uint32_t currentLayer,
uint32_t sourceMipLevel,
uint32_t stagingDstMipLevel,
vk::BufferHelper **stagingBuffer)
uint32_t stagingDstMipLevel)
{
const gl::Extents &baseLevelExtents = desc.size;
......@@ -1221,21 +1218,18 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
}
// Copy from the base level image to the staging buffer
vk::BufferHelper *stagingBuffer = nullptr;
vk::StagingBufferOffsetArray stagingBufferOffsets = {0, 0};
size_t bufferSize = 0;
ANGLE_TRY(mImage->copyImageDataToBuffer(contextVk, sourceMipLevel, layerCount, currentLayer,
area, stagingBuffer, &bufferSize, &stagingBufferOffsets,
nullptr));
area, &stagingBuffer, &bufferSize,
&stagingBufferOffsets, nullptr));
// Stage an update to the new image
ASSERT(*stagingBuffer);
ASSERT(stagingBuffer);
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(
contextVk, bufferSize, stagingDstMipLevel, currentLayer, layerCount, updatedExtents, offset,
*stagingBuffer, stagingBufferOffsets));
// Set up write dependency, we are writing to this buffer from the old image.
(*stagingBuffer)->onWrite(contextVk, mImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
stagingBuffer, stagingBufferOffsets));
return angle::Result::Continue;
}
......@@ -1247,8 +1241,7 @@ angle::Result TextureVk::setBaseLevel(const gl::Context *context, GLuint baseLev
angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer)
GLuint maxLevel)
{
if (!mImage)
{
......@@ -1277,14 +1270,13 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
return angle::Result::Continue;
}
return changeLevels(contextVk, previousBaseLevel, baseLevel, maxLevel, stagingBuffer);
return changeLevels(contextVk, previousBaseLevel, baseLevel, maxLevel);
}
angle::Result TextureVk::changeLevels(ContextVk *contextVk,
GLuint previousBaseLevel,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer)
GLuint maxLevel)
{
// Recreate the image to reflect new base or max levels.
// First, flush any pending updates so we have good data in the existing vkImage
......@@ -1336,17 +1328,25 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk,
uint32_t srcLevelVK = baseLevelChanged ? level - previousBaseLevel : level;
ASSERT(srcLevelVK <= mImage->getLevelCount());
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, desc, true, layer, srcLevelVK, level,
stagingBuffer));
ANGLE_TRY(
copyAndStageImageSubresource(contextVk, desc, true, layer, srcLevelVK, level));
}
}
// Create a new node for the image and add a global memory barrier for the staging buffers.
// They are written to and staged to be read from when ensureImageInitialized() is called.
mImage->finishCurrentCommands(contextVk);
mImage->addGlobalMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
// Inform the front end that we've updated the staging buffer
onStagingBufferChange();
// Now that we've staged all the updates, release the current image so that it will be
// recreated with the correct number of mip levels, base level, and max level.
releaseImage(contextVk);
mImage->onResourceRecreated(contextVk->getCommandGraph());
return angle::Result::Continue;
}
......@@ -1463,7 +1463,6 @@ angle::Result TextureVk::syncState(const gl::Context *context,
{
ContextVk *contextVk = vk::GetImpl(context);
vk::BufferHelper *stagingBuffer = nullptr;
// Create a new image if the storage state is enabled for the first time.
if (dirtyBits.test(gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE))
{
......@@ -1472,8 +1471,7 @@ angle::Result TextureVk::syncState(const gl::Context *context,
{
mImageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
ANGLE_TRY(changeLevels(contextVk, mImage->getBaseLevel(),
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel(),
&stagingBuffer));
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel()));
}
}
......@@ -1482,20 +1480,12 @@ angle::Result TextureVk::syncState(const gl::Context *context,
dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL))
{
ANGLE_TRY(updateBaseMaxLevels(contextVk, mState.getEffectiveBaseLevel(),
mState.getEffectiveMaxLevel(), &stagingBuffer));
mState.getEffectiveMaxLevel()));
}
// Initialize the image storage and flush the pixel buffer.
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
if (stagingBuffer)
{
stagingBuffer->onRead(contextVk, mImage, VK_ACCESS_TRANSFER_READ_BIT);
// Different parts of the buffer might be read from or write to.
stagingBuffer->onSelfReadWrite(contextVk, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
if (dirtyBits.none() && mSampler.valid())
{
return angle::Result::Continue;
......
......@@ -345,13 +345,15 @@ class TextureVk : public TextureImpl
void releaseStagingBuffer(ContextVk *contextVk);
uint32_t getMipLevelCount(ImageMipLevels mipLevels) const;
uint32_t getMaxLevelCount() const;
angle::Result copyImageDataToStagingBuffer(ContextVk *contextVk,
// Used when the image is being redefined (for example to add mips or change base level) to copy
// each subresource of the image and stage it for another subresource. When all subresources
// are taken care of, the image is recreated.
angle::Result copyAndStageImageSubresource(ContextVk *contextVk,
const gl::ImageDesc &desc,
bool ignoreLayerCount,
uint32_t currentLayer,
uint32_t sourceLevel,
uint32_t stagingDstMipLevel,
vk::BufferHelper **stagingBuffer);
uint32_t stagingDstMipLevel);
angle::Result initImageViews(ContextVk *contextVk,
const vk::Format &format,
const bool sized,
......@@ -376,14 +378,10 @@ class TextureVk : public TextureImpl
angle::Result changeLevels(ContextVk *contextVk,
GLuint previousBaseLevel,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer);
GLuint maxLevel);
// Update base and max levels, and re-create image if needed.
angle::Result updateBaseMaxLevels(ContextVk *contextVk,
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer);
angle::Result updateBaseMaxLevels(ContextVk *contextVk, GLuint baseLevel, GLuint maxLevel);
bool mOwnsImage;
......
......@@ -506,7 +506,7 @@ class BufferHelper final : public CommandGraphResource
reader->onWriteAccess(contextVk, 0, writeAccessType);
}
// Helper for setting a barrier when different parts of the same buffer is being read from and
// written to.
// written to in the same command.
void onSelfReadWrite(ContextVk *contextVk,
VkAccessFlags readAccessType,
VkAccessFlags writeAccessType)
......
......@@ -3592,14 +3592,14 @@ void main(void) {
glEnableVertexAttribArray(posTex);
glVertexAttribPointer(posTex, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
// Draw with level 0, the whole frame buffer should be Red.
// Draw with level 0, the whole framebuffer should be Red.
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
// Draw with level 1, the whole frame buffer should be Green.
// Draw with level 1, a quarter of the framebuffer should be Green.
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
......@@ -3622,7 +3622,7 @@ void main(void) {
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
// Draw with level 1, the whole frame buffer should be Green.
// Draw with level 1, a quarter of the framebuffer should be Green.
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
......
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