Commit d657e1d7 by Jamie Madill Committed by Commit Bot

Vulkan: Defer framebuffer clears.

This works by storing the deferred clears in the ImageHelper's staging buffers. We apply the deferred clears onto the RenderPass right before we begin to draw. Storing the clears in the ImageHelper solves problems where we clear GL Textures in a Framebuffer and then unbind the Textures and sample from them. Or do other commands like CopyTexImage. Note that because the staging buffer clears only handle full-image clears we need to immediately apply some scissored clears where before we would use the RP. This should be a pretty rare occurrence and it is possible to optimize that in the future. Reduces the RenderPass count in the Manhattan "frame 10" trace from max 22 to max 20. May improve perf slightly on Android or may have effects too small to measure. Should not regress performance. Bug: angleproject:4517 Change-Id: I02150d531022afb903f1058f070937ec6337bd88 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2142711Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarCharlie Lao <cclao@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 3b0c01f4
......@@ -711,6 +711,8 @@ using ShaderVector = angle::FixedVector<T, static_cast<size_t>(ShaderType::EnumC
template <typename T>
using AttachmentArray = std::array<T, IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS>;
using AttachmentsMask = angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS>;
template <typename T>
using DrawBuffersArray = std::array<T, IMPLEMENTATION_MAX_DRAW_BUFFERS>;
......
......@@ -47,10 +47,6 @@ namespace rx
namespace
{
// The value to assign an alpha channel that's emulated. The type is unsigned int, though it will
// automatically convert to the actual data type.
constexpr unsigned int kEmulatedAlphaValue = 1;
// For shader uniforms such as gl_DepthRange and the viewport size.
struct GraphicsDriverUniforms
{
......@@ -972,7 +968,7 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
if (!mRenderPassCommandBuffer)
{
gl::Rectangle scissoredRenderArea = mDrawFramebuffer->getScissoredRenderArea(this);
ANGLE_TRY(startRenderPass(scissoredRenderArea));
ANGLE_TRY(startRenderPass(scissoredRenderArea, nullptr));
}
// We keep a local copy of the command buffer. It's possible that some state changes could
......@@ -2298,112 +2294,6 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result ContextVk::clearWithRenderPassOp(
const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
bool clearStencil,
const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue)
{
// Validate cache variable is in sync.
ASSERT(mDrawFramebuffer == vk::GetImpl(mState.getDrawFramebuffer()));
// Start a new render pass if:
//
// - no render pass has started,
// - there is a render pass started but it contains commands; we cannot modify its ops, so new
// render pass is needed,
// - the current render area doesn't match the clear area. We need the render area to be
// exactly as specified by the scissor for the loadOp to clear only that area. See
// ContextVk::updateScissor for more information.
if (!mRenderPassCommands.started() ||
(mRenderPassCommands.started() && !mRenderPassCommands.empty()) ||
mRenderPassCommands.getRenderArea() != clearArea)
{
ANGLE_TRY(startRenderPass(clearArea));
}
else
{
flushOutsideRenderPassCommands();
}
size_t attachmentIndexVk = 0;
// Go through clearColorBuffers and set the appropriate loadOp and clear values.
for (size_t colorIndexGL : mDrawFramebuffer->getState().getEnabledDrawBuffers())
{
if (clearColorBuffers.test(colorIndexGL))
{
RenderTargetVk *renderTarget = mDrawFramebuffer->getColorDrawRenderTarget(colorIndexGL);
// If the render target doesn't have alpha, but its emulated format has it, clear the
// alpha to 1.
VkClearColorValue value = clearColorValue;
if (mDrawFramebuffer->getEmulatedAlphaAttachmentMask()[colorIndexGL])
{
const vk::Format &format = renderTarget->getImageFormat();
if (format.vkFormatIsInt)
{
if (format.vkFormatIsUnsigned)
{
value.uint32[3] = kEmulatedAlphaValue;
}
else
{
value.int32[3] = kEmulatedAlphaValue;
}
}
else
{
value.float32[3] = kEmulatedAlphaValue;
}
}
mRenderPassCommands.clearRenderPassColorAttachment(attachmentIndexVk, value);
}
++attachmentIndexVk;
}
// Set the appropriate loadOp and clear values for depth and stencil.
RenderTargetVk *depthStencilRenderTarget = mDrawFramebuffer->getDepthStencilRenderTarget();
if (depthStencilRenderTarget)
{
const vk::Format &format = depthStencilRenderTarget->getImageFormat();
if (format.hasEmulatedImageChannels())
{
if (format.intendedFormat().stencilBits == 0)
{
// If the format we picked has stencil but user did not ask for
// it due to hardware limitation, force clear the stencil so
// that no load will happen. Also don't try to store stencil
// value as well. Same logic for depth bits bellow.
mRenderPassCommands.invalidateRenderPassStencilAttachment(attachmentIndexVk);
clearStencil = true;
}
if (format.intendedFormat().depthBits == 0)
{
mRenderPassCommands.invalidateRenderPassDepthAttachment(attachmentIndexVk);
clearDepth = true;
}
}
if (clearDepth)
{
mRenderPassCommands.clearRenderPassDepthAttachment(attachmentIndexVk,
clearDepthStencilValue.depth);
}
if (clearStencil)
{
mRenderPassCommands.clearRenderPassStencilAttachment(attachmentIndexVk,
clearDepthStencilValue.stencil);
}
}
return angle::Result::Continue;
}
void ContextVk::optimizeRenderPassForPresent(VkFramebuffer framebufferHandle)
{
if (!mRenderPassCommands.started())
......@@ -4244,7 +4134,7 @@ angle::Result ContextVk::flushAndBeginRenderPass(
const gl::Rectangle &renderArea,
const vk::RenderPassDesc &renderPassDesc,
const vk::AttachmentOpsArray &renderPassAttachmentOps,
const std::vector<VkClearValue> &clearValues,
const vk::ClearValuesArray &clearValues,
vk::CommandBuffer **commandBufferOut)
{
// Flush any outside renderPass commands first
......@@ -4267,7 +4157,8 @@ angle::Result ContextVk::flushAndBeginRenderPass(
return angle::Result::Continue;
}
ANGLE_INLINE angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea)
angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea,
vk::CommandBuffer **commandBufferOut)
{
mGraphicsDirtyBits |= mNewGraphicsCommandBufferDirtyBits;
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, renderArea, &mRenderPassCommandBuffer));
......@@ -4281,6 +4172,12 @@ ANGLE_INLINE angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea)
mActiveQueryAnySamplesConservative->getQueryHelper()->beginOcclusionQuery(
this, mRenderPassCommandBuffer);
}
if (commandBufferOut)
{
*commandBufferOut = mRenderPassCommandBuffer;
}
return angle::Result::Continue;
}
......@@ -4639,7 +4536,7 @@ void RenderPassCommandBuffer::beginRenderPass(const vk::Framebuffer &framebuffer
const gl::Rectangle &renderArea,
const vk::RenderPassDesc &renderPassDesc,
const vk::AttachmentOpsArray &renderPassAttachmentOps,
const std::vector<VkClearValue> &clearValues,
const vk::ClearValuesArray &clearValues,
vk::CommandBuffer **commandBufferOut)
{
ASSERT(empty());
......@@ -4647,8 +4544,8 @@ void RenderPassCommandBuffer::beginRenderPass(const vk::Framebuffer &framebuffer
mRenderPassDesc = renderPassDesc;
mAttachmentOps = renderPassAttachmentOps;
mFramebuffer.setHandle(framebuffer.getHandle());
mRenderArea = renderArea;
std::copy(clearValues.begin(), clearValues.end(), mClearValues.begin());
mRenderArea = renderArea;
mClearValues = clearValues;
*commandBufferOut = &mCommandBuffer;
......
......@@ -180,31 +180,13 @@ class RenderPassCommandBuffer final : public CommandBufferHelper
const gl::Rectangle &renderArea,
const vk::RenderPassDesc &renderPassDesc,
const vk::AttachmentOpsArray &renderPassAttachmentOps,
const std::vector<VkClearValue> &clearValues,
const vk::ClearValuesArray &clearValues,
vk::CommandBuffer **commandBufferOut);
void beginTransformFeedback(size_t validBufferCount,
const VkBuffer *counterBuffers,
bool rebindBuffer);
void clearRenderPassColorAttachment(size_t attachmentIndex, const VkClearColorValue &clearValue)
{
SetBitField(mAttachmentOps[attachmentIndex].loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR);
mClearValues[attachmentIndex].color = clearValue;
}
void clearRenderPassDepthAttachment(size_t attachmentIndex, float depth)
{
SetBitField(mAttachmentOps[attachmentIndex].loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR);
SetBitField(mClearValues[attachmentIndex].depthStencil.depth, depth);
}
void clearRenderPassStencilAttachment(size_t attachmentIndex, uint32_t stencil)
{
SetBitField(mAttachmentOps[attachmentIndex].stencilLoadOp, VK_ATTACHMENT_LOAD_OP_CLEAR);
SetBitField(mClearValues[attachmentIndex].depthStencil.stencil, stencil);
}
void invalidateRenderPassColorAttachment(size_t attachmentIndex)
{
SetBitField(mAttachmentOps[attachmentIndex].storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE);
......@@ -254,7 +236,7 @@ class RenderPassCommandBuffer final : public CommandBufferHelper
vk::AttachmentOpsArray mAttachmentOps;
vk::Framebuffer mFramebuffer;
gl::Rectangle mRenderArea;
gl::AttachmentArray<VkClearValue> mClearValues;
vk::ClearValuesArray mClearValues;
bool mRenderPassStarted;
// Transform feedback state
......@@ -351,12 +333,6 @@ class ContextVk : public ContextImpl, public vk::Context
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect) override;
angle::Result clearWithRenderPassOp(const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
bool clearStencil,
const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue);
// Device loss
gl::GraphicsResetStatus getResetStatus() override;
......@@ -663,7 +639,7 @@ class ContextVk : public ContextImpl, public vk::Context
const gl::Rectangle &renderArea,
const vk::RenderPassDesc &renderPassDesc,
const vk::AttachmentOpsArray &renderPassAttachmentOps,
const std::vector<VkClearValue> &clearValues,
const vk::ClearValuesArray &clearValues,
vk::CommandBuffer **commandBufferOut);
bool hasStartedRenderPass() const { return !mRenderPassCommands.empty(); }
......@@ -675,7 +651,7 @@ class ContextVk : public ContextImpl, public vk::Context
}
egl::ContextPriority getContextPriority() const override { return mContextPriority; }
angle::Result startRenderPass(gl::Rectangle renderArea);
angle::Result startRenderPass(gl::Rectangle renderArea, vk::CommandBuffer **commandBufferOut);
angle::Result endRenderPass();
angle::Result syncExternalMemory();
......
......@@ -110,7 +110,7 @@ class FramebufferVk : public FramebufferImpl
RenderTargetVk *getColorDrawRenderTarget(size_t colorIndex) const;
RenderTargetVk *getColorReadRenderTarget() const;
angle::Result startNewRenderPass(ContextVk *context,
angle::Result startNewRenderPass(ContextVk *contextVk,
const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut);
......@@ -160,22 +160,33 @@ class FramebufferVk : public FramebufferImpl
angle::Result clearWithDraw(ContextVk *contextVk,
const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
bool clearStencil,
VkColorComponentFlags colorMaskFlags,
uint8_t stencilMask,
const VkClearColorValue &clearColorValue,
uint8_t clearStencilValue);
const VkClearDepthStencilValue &clearDepthStencilValue);
void clearWithRenderPassOp(gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
bool clearStencil,
const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue);
void updateActiveColorMasks(size_t colorIndex, bool r, bool g, bool b, bool a);
void updateRenderPassDesc();
angle::Result updateColorAttachment(const gl::Context *context, size_t colorIndex);
angle::Result updateColorAttachment(const gl::Context *context,
bool deferClears,
uint32_t colorIndex);
angle::Result invalidateImpl(ContextVk *contextVk, size_t count, const GLenum *attachments);
// Release all FramebufferVk objects in the cache and clear cache
void clearCache(ContextVk *contextVk);
angle::Result updateDepthStencilAttachment(const gl::Context *context, bool deferClears);
void updateDepthStencilAttachmentSerial(ContextVk *contextVk);
RenderTargetVk *getReadPixelsRenderTarget(GLenum format) const;
VkImageAspectFlagBits getReadPixelsAspectFlags(GLenum format) const;
angle::Result flushDeferredClears(ContextVk *contextVk, const gl::Rectangle &renderArea);
WindowSurfaceVk *mBackbuffer;
vk::RenderPassDesc mRenderPassDesc;
......@@ -197,6 +208,8 @@ class FramebufferVk : public FramebufferImpl
vk::FramebufferDesc mCurrentFramebufferDesc;
std::unordered_map<vk::FramebufferDesc, vk::FramebufferHelper> mFramebufferCache;
bool mSupportDepthStencilFeedbackLoops;
vk::ClearValuesArray mDeferredClears;
};
} // namespace rx
......
......@@ -144,20 +144,47 @@ vk::ImageHelper *RenderTargetVk::getImageForWrite(ContextVk *contextVk) const
return mImage;
}
angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk)
angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk,
vk::ClearValuesArray *deferredClears,
uint32_t deferredClearIndex) const
{
// Note that the layer index for 3D textures is always zero according to Vulkan.
uint32_t layerIndex = mLayerIndex;
if (mImage->getType() == VK_IMAGE_TYPE_3D)
{
layerIndex = 0;
}
ASSERT(mImage->valid());
if (!mImage->hasStagedUpdates())
if (!mImage->isUpdateStaged(mLevelIndex, layerIndex))
return angle::Result::Continue;
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
return mImage->flushStagedUpdates(contextVk, mLevelIndex, mLevelIndex + 1, mLayerIndex,
mLayerIndex + 1, commandBuffer);
return mImage->flushSingleSubresourceStagedUpdates(
contextVk, mLevelIndex, layerIndex, commandBuffer, deferredClears, deferredClearIndex);
}
void RenderTargetVk::retainImageViews(ContextVk *contextVk) const
{
mImageViews->retain(&contextVk->getResourceUseList());
}
gl::ImageIndex RenderTargetVk::getImageIndex() const
{
// Determine the GL type from the Vk Image properties.
if (mImage->getType() == VK_IMAGE_TYPE_3D)
{
return gl::ImageIndex::Make3D(mLevelIndex, mLayerIndex);
}
// We don't need to distinguish 2D array and cube.
if (mImage->getLayerCount() > 1)
{
return gl::ImageIndex::Make2DArray(mLevelIndex, mLayerIndex);
}
ASSERT(mLayerIndex == 0);
return gl::ImageIndex::Make2D(mLevelIndex);
}
} // namespace rx
......@@ -68,11 +68,15 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
uint32_t getLevelIndex() const { return mLevelIndex; }
uint32_t getLayerIndex() const { return mLayerIndex; }
gl::ImageIndex getImageIndex() const;
// Special mutator for Surface RenderTargets. Allows the Framebuffer to keep a single
// RenderTargetVk pointer.
void updateSwapchainImage(vk::ImageHelper *image, vk::ImageViewHelper *imageViews);
angle::Result flushStagedUpdates(ContextVk *contextVk);
angle::Result flushStagedUpdates(ContextVk *contextVk,
vk::ClearValuesArray *deferredClears,
uint32_t deferredClearIndex) const;
void retainImageViews(ContextVk *contextVk) const;
......@@ -92,7 +96,6 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// A vector of rendertargets
using RenderTargetVector = std::vector<RenderTargetVk>;
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_RENDERTARGETVK_H_
......@@ -158,7 +158,6 @@ angle::Result RenderbufferVk::getAttachmentRenderTarget(const gl::Context *conte
FramebufferAttachmentRenderTarget **rtOut)
{
ASSERT(mImage && mImage->valid());
ANGLE_TRY(mRenderTarget.flushStagedUpdates(vk::GetImpl(context)));
*rtOut = &mRenderTarget;
return angle::Result::Continue;
}
......@@ -167,7 +166,7 @@ angle::Result RenderbufferVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex)
{
// Note: stageSubresourceRobustClear only uses the intended format to count channels.
mImage->stageSubresourceClear(imageIndex);
mImage->stageRobustResourceClear(imageIndex);
return mImage->flushAllStagedUpdates(vk::GetImpl(context));
}
......
......@@ -122,17 +122,13 @@ angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut)
{
ContextVk *contextVk = vk::GetImpl(context);
if (binding == GL_BACK)
{
ANGLE_TRY(mColorRenderTarget.flushStagedUpdates(contextVk));
*rtOut = &mColorRenderTarget;
}
else
{
ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
ANGLE_TRY(mDepthStencilRenderTarget.flushStagedUpdates(contextVk));
*rtOut = &mDepthStencilRenderTarget;
}
......@@ -365,13 +361,13 @@ angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
if (mColorAttachment.image.valid())
{
mColorAttachment.image.stageSubresourceClear(imageIndex);
mColorAttachment.image.stageRobustResourceClear(imageIndex);
ANGLE_TRY(mColorAttachment.image.flushAllStagedUpdates(contextVk));
}
if (mDepthStencilAttachment.image.valid())
{
mDepthStencilAttachment.image.stageSubresourceClear(imageIndex);
mDepthStencilAttachment.image.stageRobustResourceClear(imageIndex);
ANGLE_TRY(mDepthStencilAttachment.image.flushAllStagedUpdates(contextVk));
}
return angle::Result::Continue;
......@@ -1353,6 +1349,12 @@ VkResult WindowSurfaceVk::nextSwapchainImage(vk::Context *context)
mColorRenderTarget.updateSwapchainImage(&image.image, &image.imageViews);
}
// Notify the owning framebuffer there may be staged updates.
if (image.image.hasStagedUpdates())
{
onStateChange(angle::SubjectMessage::SubjectChanged);
}
return VK_SUCCESS;
}
......@@ -1574,12 +1576,12 @@ angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
vk::ImageHelper *image =
isMultiSampled() ? &mColorImageMS : &mSwapchainImages[mCurrentSwapchainImageIndex].image;
image->stageSubresourceClear(imageIndex);
image->stageRobustResourceClear(imageIndex);
ANGLE_TRY(image->flushAllStagedUpdates(contextVk));
if (mDepthStencilImage.valid())
{
mDepthStencilImage.stageSubresourceClear(gl::ImageIndex::Make2D(0));
mDepthStencilImage.stageRobustResourceClear(gl::ImageIndex::Make2D(0));
ANGLE_TRY(mDepthStencilImage.flushAllStagedUpdates(contextVk));
}
......
......@@ -967,7 +967,9 @@ angle::Result TextureVk::redefineImage(const gl::Context *context,
{
// If there is any staged changes for this index, we can remove them since we're going to
// override them with this call.
mImage->removeStagedUpdates(contextVk, index);
uint32_t levelIndex = index.getLevelIndex();
uint32_t layerIndex = index.hasLayer() ? index.getLayerIndex() : 0;
mImage->removeStagedUpdates(contextVk, levelIndex, layerIndex);
if (mImage->valid())
{
......@@ -1370,7 +1372,19 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
ASSERT(imageIndex.getLevelIndex() >= 0);
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
if (!mImage->valid())
{
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
const gl::Extents &baseLevelExtents = baseLevelDesc.size;
const uint32_t levelCount = getMipLevelCount(ImageMipLevels::EnabledLevels);
const vk::Format &format = getBaseLevelFormat(contextVk->getRenderer());
ANGLE_TRY(initImage(contextVk, format, baseLevelDesc.format.info->sized, baseLevelExtents,
levelCount));
}
// Don't flush staged updates here. We'll handle that in FramebufferVk so it can defer clears.
GLuint layerIndex = 0, layerCount = 0;
GetRenderTargetLayerCountAndIndex(mImage, imageIndex, &layerCount, &layerIndex);
......@@ -1522,10 +1536,9 @@ angle::Result TextureVk::initializeContents(const gl::Context *context,
contextVk->getRenderer()->getFormat(desc.format.info->sizedInternalFormat);
ASSERT(mImage);
// Note that we cannot ensure the image is initialized because we might be calling subImage
// on a non-complete cube map.
return mImage->stageRobustResourceClear(contextVk, desc.size, imageIndex, format);
return mImage->stageRobustResourceClearWithFormat(contextVk, imageIndex, desc.size, format);
}
void TextureVk::releaseOwnershipOfImage(const gl::Context *context)
......
......@@ -1186,8 +1186,8 @@ angle::Result UtilsVk::startRenderPass(ContextVk *contextVk,
ANGLE_VK_TRY(contextVk, framebuffer.init(contextVk->getDevice(), framebufferInfo));
vk::AttachmentOpsArray renderPassAttachmentOps;
std::vector<VkClearValue> clearValues = {{}};
ASSERT(clearValues.size() == 1);
vk::ClearValuesArray clearValues;
clearValues.store(0, VK_IMAGE_ASPECT_COLOR_BIT, {});
renderPassAttachmentOps.initWithLoadStore(0, vk::ImageLayout::ColorAttachment,
vk::ImageLayout::ColorAttachment);
......@@ -1210,7 +1210,7 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
const gl::Rectangle &scissoredRenderArea = params.clearArea;
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(framebuffer->startNewRenderPass(contextVk, scissoredRenderArea, &commandBuffer));
ANGLE_TRY(contextVk->startRenderPass(scissoredRenderArea, &commandBuffer));
ImageClearShaderParams shaderParams;
shaderParams.clearValue = params.colorClearValue;
......
......@@ -432,7 +432,7 @@ const angle::Format &GetDepthStencilImageToBufferFormat(const angle::Format &ima
}
}
VkClearValue GetClearValue(const vk::Format &format)
VkClearValue GetRobustResourceClearValue(const vk::Format &format)
{
VkClearValue clearValue;
if (format.intendedFormat().hasDepthOrStencilBits())
......@@ -2646,12 +2646,11 @@ void ImageHelper::resolve(ImageHelper *dest,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
}
void ImageHelper::removeStagedUpdates(ContextVk *contextVk, const gl::ImageIndex &index)
void ImageHelper::removeStagedUpdates(ContextVk *contextVk,
uint32_t levelIndex,
uint32_t layerIndex)
{
// Find any staged updates for this index and removes them from the pending list.
uint32_t levelIndex = index.getLevelIndex();
uint32_t layerIndex = index.hasLayer() ? index.getLayerIndex() : 0;
for (size_t index = 0; index < mSubresourceUpdates.size();)
{
auto update = mSubresourceUpdates.begin() + index;
......@@ -3139,26 +3138,34 @@ void ImageHelper::stageSubresourceUpdateFromImage(ImageHelper *image,
appendSubresourceUpdate(SubresourceUpdate(image, copyToImage));
}
void ImageHelper::stageSubresourceClear(const gl::ImageIndex &index)
void ImageHelper::stageClear(const gl::ImageIndex &index,
VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue)
{
appendSubresourceUpdate(SubresourceUpdate(aspectFlags, clearValue, index));
}
void ImageHelper::stageRobustResourceClear(const gl::ImageIndex &index)
{
const VkImageAspectFlags aspectFlags = getAspectFlags();
ASSERT(mFormat);
VkClearValue clearValue = GetClearValue(*mFormat);
VkClearValue clearValue = GetRobustResourceClearValue(*mFormat);
appendSubresourceUpdate(SubresourceUpdate(aspectFlags, clearValue, index));
}
angle::Result ImageHelper::stageRobustResourceClear(ContextVk *contextVk,
const gl::Extents &glExtents,
const gl::ImageIndex &index,
const vk::Format &format)
angle::Result ImageHelper::stageRobustResourceClearWithFormat(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
const vk::Format &format)
{
const angle::Format &imageFormat = format.actualImageFormat();
const VkImageAspectFlags aspectFlags = GetFormatAspectFlags(imageFormat);
// Robust clears must only be staged if we do not have any prior data for this subresource.
ASSERT(!isUpdateStaged(index.getLevelIndex(), index.getLayerIndex()));
VkClearValue clearValue = GetClearValue(format);
VkClearValue clearValue = GetRobustResourceClearValue(format);
if (imageFormat.isBlock)
{
......@@ -3240,6 +3247,56 @@ angle::Result ImageHelper::allocateStagingMemory(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contextVk,
uint32_t level,
uint32_t layer,
CommandBuffer *commandBuffer,
ClearValuesArray *deferredClears,
uint32_t deferredClearIndex)
{
// Handle deferred clears. Search the updates list for a matching clear index.
if (deferredClears)
{
Optional<size_t> foundClear;
for (size_t updateIndex = 0; updateIndex < mSubresourceUpdates.size(); ++updateIndex)
{
SubresourceUpdate &update = mSubresourceUpdates[updateIndex];
if (update.isUpdateToLayerLevel(layer, level))
{
// On any data update, exit out. We'll need to do a full upload.
if (update.updateSource != UpdateSource::Clear || update.clear.layerCount != 1)
{
foundClear.reset();
break;
}
// Otherwise track the latest clear update index.
foundClear = updateIndex;
}
}
// If we have a valid index we defer the clear using the clear reference.
if (foundClear.valid())
{
size_t foundIndex = foundClear.value();
const ClearUpdate &update = mSubresourceUpdates[foundIndex].clear;
// Note that this set command handles combined or separate depth/stencil clears.
deferredClears->store(deferredClearIndex, update.aspectFlags, update.value);
// We process the updates again to erase any clears for this level.
removeStagedUpdates(contextVk, level, layer);
return angle::Result::Continue;
}
// Otherwise we proceed with a normal update.
}
return flushStagedUpdates(contextVk, level, level + 1, layer, layer + 1, commandBuffer);
}
angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
uint32_t levelStart,
uint32_t levelEnd,
......@@ -3661,6 +3718,8 @@ angle::Result ImageHelper::readPixels(ContextVk *contextVk,
ImageHelper *src = this;
ASSERT(!isUpdateStaged(level, layer));
if (isMultisampled)
{
ANGLE_TRY(resolvedImage.get().init2DStaging(
......
......@@ -873,7 +873,7 @@ class ImageHelper final : public Resource, public angle::Subject
void resolve(ImageHelper *dest, const VkImageResolve &region, CommandBuffer *commandBuffer);
// Data staging
void removeStagedUpdates(ContextVk *contextVk, const gl::ImageIndex &index);
void removeStagedUpdates(ContextVk *contextVk, uint32_t levelIndex, uint32_t layerIndex);
angle::Result stageSubresourceUpdateImpl(ContextVk *contextVk,
const gl::ImageIndex &index,
......@@ -931,12 +931,17 @@ class ImageHelper final : public Resource, public angle::Subject
const gl::Extents &glExtents,
const VkImageType imageType);
// Stage a clear to an arbitrary value.
void stageClear(const gl::ImageIndex &index,
VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue);
// Stage a clear based on robust resource init.
angle::Result stageRobustResourceClear(ContextVk *contextVk,
const gl::Extents &glExtents,
const gl::ImageIndex &index,
const vk::Format &format);
void stageSubresourceClear(const gl::ImageIndex &index);
angle::Result stageRobustResourceClearWithFormat(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Extents &glExtents,
const vk::Format &format);
void stageRobustResourceClear(const gl::ImageIndex &index);
// This will use the underlying dynamic buffer to allocate some memory to be used as a src or
// dst.
......@@ -947,6 +952,15 @@ class ImageHelper final : public Resource, public angle::Subject
StagingBufferOffsetArray *offsetOut,
bool *newBufferAllocatedOut);
// Flush staged updates for a single subresource. Can optionally take a parameter to defer
// clears to a subsequent RenderPass load op.
angle::Result flushSingleSubresourceStagedUpdates(ContextVk *contextVk,
uint32_t level,
uint32_t layer,
CommandBuffer *commandBuffer,
ClearValuesArray *deferredClears,
uint32_t deferredClearIndex);
// Flushes staged updates to a range of levels and layers from start to (but not including) end.
// Due to the nature of updates (done wholly to a VkImageSubresourceLayers), some unsolicited
// layers may also be updated.
......@@ -956,6 +970,7 @@ class ImageHelper final : public Resource, public angle::Subject
uint32_t layerStart,
uint32_t layerEnd,
CommandBuffer *commandBuffer);
// Creates a command buffer and flushes all staged updates. This is used for one-time
// initialization of resources that we don't expect to accumulate further staged updates, such
// as with renderbuffers or surface images.
......
......@@ -682,6 +682,37 @@ void MakeDebugUtilsLabel(GLenum source, const char *marker, VkDebugUtilsLabelEXT
label->pLabelName = marker;
kLabelColors[colorIndex].writeData(label->color);
}
// ClearValuesArray implementation.
ClearValuesArray::ClearValuesArray() : mValues{}, mEnabled{} {}
ClearValuesArray::~ClearValuesArray() = default;
ClearValuesArray::ClearValuesArray(const ClearValuesArray &other) = default;
ClearValuesArray &ClearValuesArray::operator=(const ClearValuesArray &rhs) = default;
void ClearValuesArray::store(uint32_t index,
VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue)
{
ASSERT(aspectFlags != 0);
// We do this double if to handle the packed depth-stencil case.
if ((aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT) != 0)
{
// Special case for stencil.
ASSERT(index == kClearValueDepthIndex);
mValues[kClearValueStencilIndex] = clearValue;
mEnabled.set(kClearValueStencilIndex);
}
if (aspectFlags != VK_IMAGE_ASPECT_STENCIL_BIT)
{
mValues[index] = clearValue;
mEnabled.set(index);
}
}
} // namespace vk
#if !defined(ANGLE_SHARED_LIBVULKAN)
......
......@@ -624,6 +624,51 @@ template <typename T>
using SpecializationConstantMap = angle::PackedEnumMap<sh::vk::SpecializationConstantId, T>;
void MakeDebugUtilsLabel(GLenum source, const char *marker, VkDebugUtilsLabelEXT *label);
constexpr size_t kClearValueDepthIndex = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
constexpr size_t kClearValueStencilIndex = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS + 1;
class ClearValuesArray final
{
public:
ClearValuesArray();
~ClearValuesArray();
ClearValuesArray(const ClearValuesArray &other);
ClearValuesArray &operator=(const ClearValuesArray &rhs);
void store(uint32_t index, VkImageAspectFlags aspectFlags, const VkClearValue &clearValue);
void reset(size_t index)
{
mValues[index] = {};
mEnabled.reset(index);
}
bool test(size_t index) const { return mEnabled.test(index); }
bool testDepth() const { return mEnabled.test(kClearValueDepthIndex); }
bool testStencil() const { return mEnabled.test(kClearValueStencilIndex); }
const VkClearValue &operator[](size_t index) const { return mValues[index]; }
float getDepthValue() const { return mValues[kClearValueDepthIndex].depthStencil.depth; }
uint32_t getStencilValue() const
{
return mValues[kClearValueStencilIndex].depthStencil.stencil;
}
const VkClearValue *data() const { return mValues.data(); }
bool empty() const { return mEnabled.none(); }
gl::DrawBufferMask getEnabledColorAttachmentsMask() const
{
return gl::DrawBufferMask(mEnabled.to_ulong());
}
private:
gl::AttachmentArray<VkClearValue> mValues;
gl::AttachmentsMask mEnabled;
};
} // namespace vk
#if !defined(ANGLE_SHARED_LIBVULKAN)
......
......@@ -1500,6 +1500,53 @@ TEST_P(ClearTestES3, ClearBuffer1OnDefaultFramebufferNoAssert)
EXPECT_GL_NO_ERROR();
}
// Clears many small concentric rectangles using scissor regions.
TEST_P(ClearTest, InceptionScissorClears)
{
angle::RNG rng;
constexpr GLuint kSize = 16;
// Create a square user FBO so we have more control over the dimensions.
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLRenderbuffer rbo;
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kSize, kSize);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glViewport(0, 0, kSize, kSize);
// Draw small concentric squares using scissor.
std::vector<GLColor> expectedColors;
for (GLuint index = 0; index < (kSize - 1) / 2; index++)
{
// Do the first clear without the scissor.
if (index > 0)
{
glEnable(GL_SCISSOR_TEST);
glScissor(index, index, kSize - (index * 2), kSize - (index * 2));
}
GLColor color = RandomColor(&rng);
expectedColors.push_back(color);
Vector4 floatColor = color.toNormalizedVector();
glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
glClear(GL_COLOR_BUFFER_BIT);
}
ASSERT_GL_NO_ERROR();
std::vector<GLColor> actualColors(expectedColors.size());
glReadPixels(0, kSize / 2, actualColors.size(), 1, GL_RGBA, GL_UNSIGNED_BYTE,
actualColors.data());
EXPECT_EQ(expectedColors, actualColors);
}
#ifdef Bool
// X11 craziness.
# undef Bool
......
......@@ -13,6 +13,7 @@
#include "gpu_info_util/SystemInfo.h"
#include "util/EGLWindow.h"
#include "util/OSWindow.h"
#include "util/random_utils.h"
#include "util/test_utils.h"
#if defined(ANGLE_PLATFORM_WINDOWS)
......@@ -221,6 +222,12 @@ Vector4 GLColor::toNormalizedVector() const
return Vector4(ColorNorm(R), ColorNorm(G), ColorNorm(B), ColorNorm(A));
}
GLColor RandomColor(angle::RNG *rng)
{
return GLColor(rng->randomIntBetween(0, 255), rng->randomIntBetween(0, 255),
rng->randomIntBetween(0, 255), rng->randomIntBetween(0, 255));
}
GLColor ReadColor(GLint x, GLint y)
{
GLColor actual;
......
......@@ -27,6 +27,7 @@
namespace angle
{
struct SystemInfo;
class RNG;
} // namespace angle
#define ASSERT_GL_TRUE(a) ASSERT_EQ(static_cast<GLboolean>(GL_TRUE), (a))
......@@ -161,6 +162,8 @@ GLColor MakeGLColor(TR r, TG g, TB b, TA a)
static_cast<GLubyte>(a));
}
GLColor RandomColor(angle::RNG *rng);
bool operator==(const GLColor &a, const GLColor &b);
bool operator!=(const GLColor &a, const GLColor &b);
std::ostream &operator<<(std::ostream &ostream, const GLColor &color);
......
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