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 ...@@ -397,6 +397,14 @@ class CommandGraphResource : angle::NonCopyable
void onGraphAccess(CommandGraph *commandGraph); void onGraphAccess(CommandGraph *commandGraph);
void updateCurrentAccessNodes(); 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. // Allocates a write node via getNewWriteNode and returns a started command buffer.
// The started command buffer will render outside of a RenderPass. // The started command buffer will render outside of a RenderPass.
// Will append to an existing command buffer/graph node if possible. // Will append to an existing command buffer/graph node if possible.
...@@ -612,6 +620,12 @@ ANGLE_INLINE void CommandGraphResource::updateCurrentAccessNodes() ...@@ -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) ANGLE_INLINE void CommandGraphResource::onGraphAccess(CommandGraph *commandGraph)
{ {
updateCurrentAccessNodes(); updateCurrentAccessNodes();
......
...@@ -1163,24 +1163,22 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context) ...@@ -1163,24 +1163,22 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
// Redefine the images with mipmaps. // Redefine the images with mipmaps.
// Copy image to the staging buffer and stage an update to the new one. // Copy image to the staging buffer and stage an update to the new one.
vk::BufferHelper *stagingBuffer = nullptr; ANGLE_TRY(copyAndStageImageSubresource(contextVk, baseLevelDesc, false,
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, baseLevelDesc, false, getNativeImageLayer(0), 0, mImage->getBaseLevel()));
getNativeImageLayer(0), 0, mImage->getBaseLevel(),
&stagingBuffer)); // 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(); onStagingBufferChange();
// Release the origin image and recreate it with new mipmap counts. // Release the origin image and recreate it with new mipmap counts.
releaseImage(contextVk); releaseImage(contextVk);
mImage->onResourceRecreated(contextVk->getCommandGraph());
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::FullMipChain)); 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 // Check if the image supports blit. If it does, we can do the mipmap generation on the gpu
// only. // only.
...@@ -1197,13 +1195,12 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context) ...@@ -1197,13 +1195,12 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk, angle::Result TextureVk::copyAndStageImageSubresource(ContextVk *contextVk,
const gl::ImageDesc &desc, const gl::ImageDesc &desc,
bool ignoreLayerCount, bool ignoreLayerCount,
uint32_t currentLayer, uint32_t currentLayer,
uint32_t sourceMipLevel, uint32_t sourceMipLevel,
uint32_t stagingDstMipLevel, uint32_t stagingDstMipLevel)
vk::BufferHelper **stagingBuffer)
{ {
const gl::Extents &baseLevelExtents = desc.size; const gl::Extents &baseLevelExtents = desc.size;
...@@ -1221,21 +1218,18 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk, ...@@ -1221,21 +1218,18 @@ angle::Result TextureVk::copyImageDataToStagingBuffer(ContextVk *contextVk,
} }
// Copy from the base level image to the staging buffer // Copy from the base level image to the staging buffer
vk::BufferHelper *stagingBuffer = nullptr;
vk::StagingBufferOffsetArray stagingBufferOffsets = {0, 0}; vk::StagingBufferOffsetArray stagingBufferOffsets = {0, 0};
size_t bufferSize = 0; size_t bufferSize = 0;
ANGLE_TRY(mImage->copyImageDataToBuffer(contextVk, sourceMipLevel, layerCount, currentLayer, ANGLE_TRY(mImage->copyImageDataToBuffer(contextVk, sourceMipLevel, layerCount, currentLayer,
area, stagingBuffer, &bufferSize, &stagingBufferOffsets, area, &stagingBuffer, &bufferSize,
nullptr)); &stagingBufferOffsets, nullptr));
// Stage an update to the new image // Stage an update to the new image
ASSERT(stagingBuffer);
ASSERT(*stagingBuffer);
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer( ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(
contextVk, bufferSize, stagingDstMipLevel, currentLayer, layerCount, updatedExtents, offset, contextVk, bufferSize, stagingDstMipLevel, currentLayer, layerCount, updatedExtents, offset,
*stagingBuffer, stagingBufferOffsets)); 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);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1247,8 +1241,7 @@ angle::Result TextureVk::setBaseLevel(const gl::Context *context, GLuint baseLev ...@@ -1247,8 +1241,7 @@ angle::Result TextureVk::setBaseLevel(const gl::Context *context, GLuint baseLev
angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk, angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
GLuint baseLevel, GLuint baseLevel,
GLuint maxLevel, GLuint maxLevel)
vk::BufferHelper **stagingBuffer)
{ {
if (!mImage) if (!mImage)
{ {
...@@ -1277,14 +1270,13 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk, ...@@ -1277,14 +1270,13 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
return changeLevels(contextVk, previousBaseLevel, baseLevel, maxLevel, stagingBuffer); return changeLevels(contextVk, previousBaseLevel, baseLevel, maxLevel);
} }
angle::Result TextureVk::changeLevels(ContextVk *contextVk, angle::Result TextureVk::changeLevels(ContextVk *contextVk,
GLuint previousBaseLevel, GLuint previousBaseLevel,
GLuint baseLevel, GLuint baseLevel,
GLuint maxLevel, GLuint maxLevel)
vk::BufferHelper **stagingBuffer)
{ {
// Recreate the image to reflect new base or max levels. // Recreate the image to reflect new base or max levels.
// First, flush any pending updates so we have good data in the existing vkImage // First, flush any pending updates so we have good data in the existing vkImage
...@@ -1336,17 +1328,25 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk, ...@@ -1336,17 +1328,25 @@ angle::Result TextureVk::changeLevels(ContextVk *contextVk,
uint32_t srcLevelVK = baseLevelChanged ? level - previousBaseLevel : level; uint32_t srcLevelVK = baseLevelChanged ? level - previousBaseLevel : level;
ASSERT(srcLevelVK <= mImage->getLevelCount()); ASSERT(srcLevelVK <= mImage->getLevelCount());
ANGLE_TRY(copyImageDataToStagingBuffer(contextVk, desc, true, layer, srcLevelVK, level, ANGLE_TRY(
stagingBuffer)); 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 // Inform the front end that we've updated the staging buffer
onStagingBufferChange(); onStagingBufferChange();
// Now that we've staged all the updates, release the current image so that it will be // 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. // recreated with the correct number of mip levels, base level, and max level.
releaseImage(contextVk); releaseImage(contextVk);
mImage->onResourceRecreated(contextVk->getCommandGraph());
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1463,7 +1463,6 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1463,7 +1463,6 @@ angle::Result TextureVk::syncState(const gl::Context *context,
{ {
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
vk::BufferHelper *stagingBuffer = nullptr;
// Create a new image if the storage state is enabled for the first time. // Create a new image if the storage state is enabled for the first time.
if (dirtyBits.test(gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE)) if (dirtyBits.test(gl::Texture::DIRTY_BIT_BOUND_AS_IMAGE))
{ {
...@@ -1472,8 +1471,7 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1472,8 +1471,7 @@ angle::Result TextureVk::syncState(const gl::Context *context,
{ {
mImageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT; mImageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
ANGLE_TRY(changeLevels(contextVk, mImage->getBaseLevel(), ANGLE_TRY(changeLevels(contextVk, mImage->getBaseLevel(),
mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel(), mState.getEffectiveBaseLevel(), mState.getEffectiveMaxLevel()));
&stagingBuffer));
} }
} }
...@@ -1482,20 +1480,12 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1482,20 +1480,12 @@ angle::Result TextureVk::syncState(const gl::Context *context,
dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL)) dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL))
{ {
ANGLE_TRY(updateBaseMaxLevels(contextVk, mState.getEffectiveBaseLevel(), ANGLE_TRY(updateBaseMaxLevels(contextVk, mState.getEffectiveBaseLevel(),
mState.getEffectiveMaxLevel(), &stagingBuffer)); mState.getEffectiveMaxLevel()));
} }
// Initialize the image storage and flush the pixel buffer. // Initialize the image storage and flush the pixel buffer.
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels)); 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()) if (dirtyBits.none() && mSampler.valid())
{ {
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -345,13 +345,15 @@ class TextureVk : public TextureImpl ...@@ -345,13 +345,15 @@ class TextureVk : public TextureImpl
void releaseStagingBuffer(ContextVk *contextVk); void releaseStagingBuffer(ContextVk *contextVk);
uint32_t getMipLevelCount(ImageMipLevels mipLevels) const; uint32_t getMipLevelCount(ImageMipLevels mipLevels) const;
uint32_t getMaxLevelCount() 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, const gl::ImageDesc &desc,
bool ignoreLayerCount, bool ignoreLayerCount,
uint32_t currentLayer, uint32_t currentLayer,
uint32_t sourceLevel, uint32_t sourceLevel,
uint32_t stagingDstMipLevel, uint32_t stagingDstMipLevel);
vk::BufferHelper **stagingBuffer);
angle::Result initImageViews(ContextVk *contextVk, angle::Result initImageViews(ContextVk *contextVk,
const vk::Format &format, const vk::Format &format,
const bool sized, const bool sized,
...@@ -376,14 +378,10 @@ class TextureVk : public TextureImpl ...@@ -376,14 +378,10 @@ class TextureVk : public TextureImpl
angle::Result changeLevels(ContextVk *contextVk, angle::Result changeLevels(ContextVk *contextVk,
GLuint previousBaseLevel, GLuint previousBaseLevel,
GLuint baseLevel, GLuint baseLevel,
GLuint maxLevel, GLuint maxLevel);
vk::BufferHelper **stagingBuffer);
// Update base and max levels, and re-create image if needed. // Update base and max levels, and re-create image if needed.
angle::Result updateBaseMaxLevels(ContextVk *contextVk, angle::Result updateBaseMaxLevels(ContextVk *contextVk, GLuint baseLevel, GLuint maxLevel);
GLuint baseLevel,
GLuint maxLevel,
vk::BufferHelper **stagingBuffer);
bool mOwnsImage; bool mOwnsImage;
......
...@@ -506,7 +506,7 @@ class BufferHelper final : public CommandGraphResource ...@@ -506,7 +506,7 @@ class BufferHelper final : public CommandGraphResource
reader->onWriteAccess(contextVk, 0, writeAccessType); reader->onWriteAccess(contextVk, 0, writeAccessType);
} }
// Helper for setting a barrier when different parts of the same buffer is being read from and // 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, void onSelfReadWrite(ContextVk *contextVk,
VkAccessFlags readAccessType, VkAccessFlags readAccessType,
VkAccessFlags writeAccessType) VkAccessFlags writeAccessType)
......
...@@ -3592,14 +3592,14 @@ void main(void) { ...@@ -3592,14 +3592,14 @@ void main(void) {
glEnableVertexAttribArray(posTex); glEnableVertexAttribArray(posTex);
glVertexAttribPointer(posTex, 2, GL_FLOAT, GL_FALSE, 0, texCoords); 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()); glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red); EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 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); glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
...@@ -3622,7 +3622,7 @@ void main(void) { ...@@ -3622,7 +3622,7 @@ void main(void) {
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red); EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 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); glViewport(0, 0, getWindowWidth() / 2, getWindowHeight() / 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); 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