Commit e8c0aa81 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Clean up transform feedback extension pause/resume

1. The xfb counter buffer barrier issued was wrong, following a typo in the spec. This barrier is now correctly issued using the usual barrier APIs. 2. A mechanism was added to automatically pause/resume transform feedback when a program pipeline needs to be rebound. This is incorrect as it misses the xfb counter buffer barrier. The render pass is broken instead if transform feedback is active/unpaused and the program pipeline is changed. 3. The transform feedback counter buffers are now disposed of when transform feedback is ended. This avoids an unnecessary barrier that this change would have otherwise incurred (and hence render pass break) in Manhattan which repurposes the same transform feedback object. Bug: angleproject:5528 Change-Id: I1ffe8b4b8975645ba43afd70e9cdbb0765529da5 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2651647Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 60b03e62
......@@ -78,6 +78,12 @@ class BitSetT final
mBitsCopy.set(index);
}
void setLaterBits(const BitSetT &bits)
{
ASSERT((BitSetT(bits) &= Mask(mCurrentBit + 1)).none());
mBitsCopy |= bits;
}
private:
std::size_t getNextBit();
......@@ -532,7 +538,7 @@ class BitSetArray final
ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator);
prepareCopy();
mParentCopy.reset(pos);
updateIterator(pos, false);
updateIteratorBit(pos, false);
}
void setLaterBit(std::size_t pos)
......@@ -540,7 +546,14 @@ class BitSetArray final
ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator);
prepareCopy();
mParentCopy.set(pos);
updateIterator(pos, true);
updateIteratorBit(pos, true);
}
void setLaterBits(const BitSetArray &bits)
{
prepareCopy();
mParentCopy |= bits;
updateIteratorBits(bits);
}
private:
......@@ -555,7 +568,7 @@ class BitSetArray final
}
}
ANGLE_INLINE void updateIterator(std::size_t pos, bool setBit)
ANGLE_INLINE void updateIteratorBit(std::size_t pos, bool setBit)
{
// Get the index and offset, update current interator if within range
size_t index = pos >> kShiftForDivision;
......@@ -573,6 +586,11 @@ class BitSetArray final
}
}
ANGLE_INLINE void updateIteratorBits(const BitSetArray &bits)
{
mCurrentIterator.setLaterBits(bits.mBaseBitSetArray[mIndex]);
}
// Problem -
// We want to provide the fastest path possible for usecases that iterate though the bitset.
//
......
......@@ -299,7 +299,7 @@ TYPED_TEST(BitSetIteratorTest, BitAssignment)
TYPED_TEST(BitSetIteratorTest, SetLaterBit)
{
TypeParam mStateBits = this->mStateBits;
std::set<size_t> expectedValues = {0, 1, 3, 5, 7, 9, 35};
std::set<size_t> expectedValues = {0, 1, 3, 5, 6, 7, 9, 35};
mStateBits.set(0);
mStateBits.set(1);
......@@ -311,6 +311,10 @@ TYPED_TEST(BitSetIteratorTest, SetLaterBit)
{
iter.setLaterBit(3);
iter.setLaterBit(5);
}
if (*iter == 5)
{
iter.setLaterBit(6);
iter.setLaterBit(7);
iter.setLaterBit(9);
iter.setLaterBit(35);
......@@ -349,6 +353,37 @@ TYPED_TEST(BitSetIteratorTest, ResetLaterBit)
EXPECT_EQ(expectedValues, actualValues);
}
// Tests adding bitsets to the iterator during iteration.
TYPED_TEST(BitSetIteratorTest, SetLaterBits)
{
TypeParam mStateBits = this->mStateBits;
std::set<size_t> expectedValues = {1, 2, 3, 4, 5, 7, 9};
mStateBits.set(1);
mStateBits.set(2);
mStateBits.set(3);
TypeParam laterBits;
laterBits.set(4);
laterBits.set(5);
laterBits.set(7);
laterBits.set(9);
std::set<size_t> actualValues;
for (auto iter = mStateBits.begin(), end = mStateBits.end(); iter != end; ++iter)
{
if (*iter == 3)
{
iter.setLaterBits(laterBits);
}
EXPECT_EQ(actualValues.count(*iter), 0u);
actualValues.insert(*iter);
}
EXPECT_EQ(expectedValues, actualValues);
}
template <typename T>
class BitSetArrayTest : public testing::Test
{
......
......@@ -451,8 +451,6 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
{
mGraphicsDirtyBitHandlers[DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS] =
&ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension;
mGraphicsDirtyBitHandlers[DIRTY_BIT_TRANSFORM_FEEDBACK_STATE] =
&ContextVk::handleDirtyGraphicsTransformFeedbackState;
mGraphicsDirtyBitHandlers[DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME] =
&ContextVk::handleDirtyGraphicsTransformFeedbackResume;
}
......@@ -844,7 +842,7 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
++dirtyBitIter)
{
ASSERT(mGraphicsDirtyBitHandlers[*dirtyBitIter]);
ANGLE_TRY((this->*mGraphicsDirtyBitHandlers[*dirtyBitIter])(&dirtyBitIter));
ANGLE_TRY((this->*mGraphicsDirtyBitHandlers[*dirtyBitIter])(&dirtyBitIter, dirtyBitMask));
}
mGraphicsDirtyBits &= ~dirtyBitMask;
......@@ -1069,7 +1067,8 @@ angle::Result ContextVk::setupDispatch(const gl::Context *context)
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyEventLogImpl(mRenderPassCommandBuffer);
}
......@@ -1129,7 +1128,8 @@ angle::Result ContextVk::handleDirtyEventLogImpl(vk::CommandBuffer *commandBuffe
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mDirtyDefaultAttribsMask.any());
......@@ -1142,7 +1142,8 @@ angle::Result ContextVk::handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const VkPipeline previousPipeline = mCurrentGraphicsPipeline
? mCurrentGraphicsPipeline->getPipeline().getHandle()
......@@ -1200,7 +1201,17 @@ angle::Result ContextVk::handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *di
return angle::Result::Continue;
}
pauseTransformFeedbackIfStarted({});
// VK_EXT_transform_feedback disallows binding pipelines while transform feedback is active.
// If a new pipeline needs to be bound, the render pass should necessarily be broken (which
// implicitly pauses transform feedback), as resuming requires a barrier on the transform
// feedback counter buffer.
if (mRenderPassCommands->started() && mRenderPassCommands->isTransformFeedbackActiveUnpaused())
{
ANGLE_TRY(
flushCommandsAndEndRenderPassMidDirtyBitHandling(dirtyBitsIterator, dirtyBitMask));
dirtyBitsIterator->setLaterBit(DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME);
}
// The pipeline needs to rebind because it's changed.
dirtyBitsIterator->setLaterBit(DIRTY_BIT_PIPELINE_BINDING);
......@@ -1208,7 +1219,8 @@ angle::Result ContextVk::handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *di
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mRenderPassCommandBuffer == nullptr);
......@@ -1226,13 +1238,14 @@ angle::Result ContextVk::handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirt
// pipeline.
if (renderPassDescChanged)
{
ANGLE_TRY(handleDirtyGraphicsPipelineDesc(dirtyBitsIterator));
ANGLE_TRY(handleDirtyGraphicsPipelineDesc(dirtyBitsIterator, dirtyBitMask));
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mCurrentGraphicsPipeline);
......@@ -1365,7 +1378,8 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyTexturesImpl(mRenderPassCommands);
}
......@@ -1375,7 +1389,8 @@ angle::Result ContextVk::handleDirtyComputeTextures()
return handleDirtyTexturesImpl(mOutsideRenderPassCommands);
}
angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
uint32_t maxAttrib = mState.getProgramExecutable()->getMaxActiveAttribLocation();
const gl::AttribArray<VkBuffer> &bufferHandles = mVertexArray->getCurrentArrayBufferHandles();
......@@ -1404,7 +1419,8 @@ angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *d
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsIndexBuffer(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsIndexBuffer(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
vk::BufferHelper *elementArrayBuffer = mVertexArray->getCurrentElementArrayBuffer();
ASSERT(elementArrayBuffer != nullptr);
......@@ -1438,7 +1454,8 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyShaderResourcesImpl(
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyShaderResourcesImpl(mRenderPassCommands);
}
......@@ -1449,7 +1466,8 @@ angle::Result ContextVk::handleDirtyComputeShaderResources()
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation(
DirtyBits::Iterator *dirtyBitsIterator)
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
......@@ -1489,7 +1507,8 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation(
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension(
DirtyBits::Iterator *dirtyBitsIterator)
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
......@@ -1502,18 +1521,31 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension(
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(mState.getCurrentTransformFeedback());
size_t bufferCount = executable->getTransformFeedbackBufferCount();
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &bufferHelpers =
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers =
transformFeedbackVk->getBufferHelpers();
gl::TransformFeedbackBuffersArray<vk::BufferHelper> &counterBuffers =
transformFeedbackVk->getCounterBufferHelpers();
// Issue necessary barriers for the transform feedback buffers.
for (size_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
{
vk::BufferHelper *bufferHelper = bufferHelpers[bufferIndex];
vk::BufferHelper *bufferHelper = buffers[bufferIndex];
ASSERT(bufferHelper);
mRenderPassCommands->bufferWrite(this, VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT,
vk::PipelineStage::TransformFeedback,
vk::AliasingMode::Disallowed, bufferHelper);
}
// Issue necessary barriers for the transform feedback counter buffer. Note that the barrier is
// issued only on the first buffer (which uses a global memory barrier), as all the counter
// buffers of the transform feedback object are used together.
ASSERT(counterBuffers[0].valid());
mRenderPassCommands->bufferWrite(this,
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT |
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
vk::PipelineStage::TransformFeedback,
vk::AliasingMode::Disallowed, &counterBuffers[0]);
const gl::TransformFeedbackBuffersArray<VkBuffer> &bufferHandles =
transformFeedbackVk->getBufferHandles();
const gl::TransformFeedbackBuffersArray<VkDeviceSize> &bufferOffsets =
......@@ -1525,24 +1557,12 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension(
static_cast<uint32_t>(bufferCount), bufferHandles.data(), bufferOffsets.data(),
bufferSizes.data());
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackState(
DirtyBits::Iterator *dirtyBitsIterator)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
if (!executable->hasTransformFeedbackOutput() || !mState.isTransformFeedbackActiveUnpaused())
if (!mState.isTransformFeedbackActiveUnpaused())
{
return angle::Result::Continue;
}
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(mState.getCurrentTransformFeedback());
// We should have same number of counter buffers as xfb buffers have
size_t bufferCount = executable->getTransformFeedbackBufferCount();
const gl::TransformFeedbackBuffersArray<VkBuffer> &counterBufferHandles =
transformFeedbackVk->getCounterBufferHandles();
......@@ -1555,7 +1575,8 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackState(
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackResume(
DirtyBits::Iterator *dirtyBitsIterator)
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
if (mRenderPassCommands->isTransformFeedbackStarted())
{
......@@ -1564,7 +1585,8 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackResume(
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyDescriptorSetsImpl(mRenderPassCommandBuffer);
}
......@@ -3577,8 +3599,7 @@ void ContextVk::onTransformFeedbackStateChanged()
{
if (getFeatures().supportsTransformFeedbackExtension.enabled)
{
mGraphicsDirtyBits |=
DirtyBits{DIRTY_BIT_TRANSFORM_FEEDBACK_STATE, DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS};
mGraphicsDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS);
}
else if (getFeatures().emulateTransformFeedback.enabled)
{
......@@ -3589,28 +3610,47 @@ void ContextVk::onTransformFeedbackStateChanged()
angle::Result ContextVk::onBeginTransformFeedback(
size_t bufferCount,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers)
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper> &counterBuffers)
{
onTransformFeedbackStateChanged();
bool shouldEndRenderPass = false;
// If any of the buffers were previously used in the render pass, break the render pass as a
// barrier is needed.
for (size_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
{
const vk::BufferHelper *buffer = buffers[bufferIndex];
if (mCurrentTransformFeedbackBuffers.contains(buffer) ||
mRenderPassCommands->usesBuffer(*buffer))
if (mRenderPassCommands->usesBuffer(*buffer))
{
ANGLE_TRY(flushCommandsAndEndRenderPass());
shouldEndRenderPass = true;
break;
}
}
populateTransformFeedbackBufferSet(bufferCount, buffers);
if (getFeatures().supportsTransformFeedbackExtension.enabled)
{
// Break the render pass if the counter buffers are used too. Note that Vulkan requires a
// barrier on the counter buffer between pause and resume, so it cannot be resumed in the
// same render pass. Note additionally that we don't need to test all counters being used
// in the render pass, as outside of the transform feedback object these buffers are
// inaccessible and are therefore always used together.
if (!shouldEndRenderPass && mRenderPassCommands->usesBuffer(counterBuffers[0]))
{
shouldEndRenderPass = true;
}
mGraphicsDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME);
}
if (shouldEndRenderPass)
{
ANGLE_TRY(flushCommandsAndEndRenderPass());
}
populateTransformFeedbackBufferSet(bufferCount, buffers);
return angle::Result::Continue;
}
......@@ -3647,12 +3687,17 @@ angle::Result ContextVk::onPauseTransformFeedback()
{
if (getFeatures().supportsTransformFeedbackExtension.enabled)
{
return flushCommandsAndEndRenderPass();
// If transform feedback was already active on this render pass, break it. This
// is for simplicity to avoid tracking multiple simultaneously active transform feedback
// settings in the render pass.
if (mRenderPassCommands->isTransformFeedbackActiveUnpaused())
{
return flushCommandsAndEndRenderPass();
}
}
else if (getFeatures().emulateTransformFeedback.enabled)
{
invalidateCurrentTransformFeedbackBuffers();
return flushCommandsAndEndRenderPass();
}
return angle::Result::Continue;
}
......@@ -3861,27 +3906,23 @@ void ContextVk::writeAtomicCounterBufferDriverUniformOffsets(uint32_t *offsetsOu
}
}
void ContextVk::pauseTransformFeedbackIfStartedAndRebindBuffersOnResume()
{
DirtyBits rebindTransformFeedbackOnResume{DIRTY_BIT_TRANSFORM_FEEDBACK_STATE};
pauseTransformFeedbackIfStarted(rebindTransformFeedbackOnResume);
}
ANGLE_INLINE void ContextVk::pauseTransformFeedbackIfStarted(DirtyBits onResumeOps)
void ContextVk::pauseTransformFeedbackIfActiveUnpaused()
{
// Note that UtilsVk may have already paused transform feedback, so don't pause it again if it's
// already paused.
if (mRenderPassCommands->isTransformFeedbackStarted() &&
!mGraphicsDirtyBits.test(DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME))
if (mRenderPassCommands->isTransformFeedbackActiveUnpaused())
{
ASSERT(getFeatures().supportsTransformFeedbackExtension.enabled);
mGraphicsDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME);
mGraphicsDirtyBits |= onResumeOps;
mRenderPassCommands->pauseTransformFeedback();
// Note that this function is called when render pass break is imminent
// (flushCommandsAndEndRenderPass(), or UtilsVk::clearFramebuffer which will close the
// render pass after the clear). This dirty bit allows transform feedback to resume
// automatically on next render pass.
mGraphicsDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME);
}
}
angle::Result ContextVk::handleDirtyGraphicsDriverUniforms(DirtyBits::Iterator *dirtyBitsIterator)
angle::Result ContextVk::handleDirtyGraphicsDriverUniforms(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
// Allocate a new region in the dynamic buffer.
bool useGraphicsDriverUniformsExtended = getFeatures().forceDriverUniformOverSpecConst.enabled;
......@@ -4046,7 +4087,8 @@ void ContextVk::handleDirtyDriverUniformsBindingImpl(vk::CommandBuffer *commandB
}
angle::Result ContextVk::handleDirtyGraphicsDriverUniformsBinding(
DirtyBits::Iterator *dirtyBitsIterator)
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
// Bind the driver descriptor set.
handleDirtyDriverUniformsBindingImpl(mRenderPassCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
......@@ -4793,7 +4835,7 @@ uint32_t ContextVk::getCurrentSubpassIndex() const
return mGraphicsPipelineDesc->getSubpass();
}
angle::Result ContextVk::flushCommandsAndEndRenderPass()
angle::Result ContextVk::flushCommandsAndEndRenderPassImpl()
{
// Ensure we flush the RenderPass *after* the prior commands.
ANGLE_TRY(flushOutsideRenderPassCommands());
......@@ -4833,7 +4875,7 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass()
addOverlayUsedBuffersCount(mRenderPassCommands);
pauseTransformFeedbackIfStartedAndRebindBuffersOnResume();
pauseTransformFeedbackIfActiveUnpaused();
mRenderPassCommands->endRenderPass(this);
......@@ -4862,7 +4904,36 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass()
ANGLE_TRY(flushImpl(nullptr));
}
mGraphicsDirtyBits |= mNewGraphicsCommandBufferDirtyBits;
return angle::Result::Continue;
}
angle::Result ContextVk::flushCommandsAndEndRenderPass()
{
bool isRenderPassStarted = mRenderPassCommands->started();
ANGLE_TRY(flushCommandsAndEndRenderPassImpl());
// Set dirty bits if render pass was open (and thus has been closed).
if (isRenderPassStarted)
{
mGraphicsDirtyBits |= mNewGraphicsCommandBufferDirtyBits;
// Restart at subpass 0.
mGraphicsPipelineDesc->resetSubpass(&mGraphicsPipelineTransition);
}
return angle::Result::Continue;
}
angle::Result ContextVk::flushCommandsAndEndRenderPassMidDirtyBitHandling(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mRenderPassCommands->started());
ANGLE_TRY(flushCommandsAndEndRenderPassImpl());
dirtyBitsIterator->setLaterBits(mNewGraphicsCommandBufferDirtyBits & dirtyBitMask);
// Restart at subpass 0.
mGraphicsPipelineDesc->resetSubpass(&mGraphicsPipelineTransition);
......
......@@ -325,10 +325,11 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void onTransformFeedbackStateChanged();
angle::Result onBeginTransformFeedback(
size_t bufferCount,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers);
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper> &counterBuffers);
void onEndTransformFeedback();
angle::Result onPauseTransformFeedback();
void pauseTransformFeedbackIfStartedAndRebindBuffersOnResume();
void pauseTransformFeedbackIfActiveUnpaused();
// When UtilsVk issues draw or dispatch calls, it binds a new pipeline and descriptor sets that
// the context is not aware of. These functions are called to make sure the pipeline and
......@@ -614,7 +615,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
// Shader resources excluding textures, which are handled separately.
DIRTY_BIT_SHADER_RESOURCES,
DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS,
DIRTY_BIT_TRANSFORM_FEEDBACK_STATE,
DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME,
DIRTY_BIT_DESCRIPTOR_SETS,
DIRTY_BIT_MAX,
......@@ -622,8 +622,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
using GraphicsDirtyBitHandler =
angle::Result (ContextVk::*)(DirtyBits::Iterator *dirtyBitsIterator);
using GraphicsDirtyBitHandler = angle::Result (
ContextVk::*)(DirtyBits::Iterator *dirtyBitsIterator, DirtyBits dirtyBitMask);
using ComputeDirtyBitHandler = angle::Result (ContextVk::*)();
struct DriverUniformsDescriptorSet
......@@ -777,25 +777,38 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void invalidateDriverUniforms();
// Handlers for graphics pipeline dirty bits.
angle::Result handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsIndexBuffer(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsDriverUniforms(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsDriverUniformsBinding(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsIndexBuffer(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDriverUniforms(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDriverUniformsBinding(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsTransformFeedbackBuffersEmulation(
DirtyBits::Iterator *dirtyBitsIterator);
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsTransformFeedbackBuffersExtension(
DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsTransformFeedbackState(DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsTransformFeedbackResume(
DirtyBits::Iterator *dirtyBitsIterator);
angle::Result handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator);
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsTransformFeedbackResume(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
// Handlers for compute pipeline dirty bits.
angle::Result handleDirtyComputeEventLog();
......@@ -840,6 +853,14 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
bool hasRecordedCommands();
void dumpCommandStreamDiagnostics();
angle::Result flushOutsideRenderPassCommands();
// Flush commands and end render pass without setting any dirty bits.
// flushCommandsAndEndRenderPass() and flushCommandsAndEndRenderPassMidDirtyBitHandling() will
// set the dirty bits directly or through the iterator respectively. Outside those two
// functions, this shouldn't be called directly.
angle::Result flushCommandsAndEndRenderPassImpl();
angle::Result flushCommandsAndEndRenderPassMidDirtyBitHandling(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
void flushDescriptorSetUpdates();
void onRenderPassFinished();
......@@ -851,7 +872,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void populateTransformFeedbackBufferSet(
size_t bufferCount,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers);
void pauseTransformFeedbackIfStarted(DirtyBits onResumeOps);
// DescriptorSet writes
template <typename T, const T *VkWriteDescriptorSet::*pInfo>
......
......@@ -37,11 +37,19 @@ TransformFeedbackVk::~TransformFeedbackVk() {}
void TransformFeedbackVk::onDestroy(const gl::Context *context)
{
RendererVk *rendererVk = vk::GetImpl(context)->getRenderer();
ASSERT(std::all_of(mCounterBufferHelpers.begin(), mCounterBufferHelpers.end(),
[](vk::BufferHelper &counterBuffer) { return !counterBuffer.valid(); }));
}
void TransformFeedbackVk::releaseCounterBuffers(RendererVk *renderer)
{
for (vk::BufferHelper &bufferHelper : mCounterBufferHelpers)
{
bufferHelper.release(rendererVk);
bufferHelper.release(renderer);
}
for (VkBuffer &buffer : mCounterBufferHandles)
{
buffer = VK_NULL_HANDLE;
}
}
......@@ -129,7 +137,8 @@ angle::Result TransformFeedbackVk::begin(const gl::Context *context,
mRebindTransformFeedbackBuffer = true;
}
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers);
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
mCounterBufferHelpers);
}
angle::Result TransformFeedbackVk::end(const gl::Context *context)
......@@ -147,6 +156,9 @@ angle::Result TransformFeedbackVk::end(const gl::Context *context)
}
contextVk->onEndTransformFeedback();
releaseCounterBuffers(contextVk->getRenderer());
return angle::Result::Continue;
}
......@@ -184,7 +196,8 @@ angle::Result TransformFeedbackVk::resume(const gl::Context *context)
initializeXFBBuffersDesc(contextVk, xfbBufferCount);
}
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers);
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
mCounterBufferHelpers);
}
angle::Result TransformFeedbackVk::bindIndexedBuffer(
......
......@@ -87,6 +87,11 @@ class TransformFeedbackVk : public TransformFeedbackImpl
return mBufferSizes;
}
gl::TransformFeedbackBuffersArray<vk::BufferHelper> &getCounterBufferHelpers()
{
return mCounterBufferHelpers;
}
const gl::TransformFeedbackBuffersArray<VkBuffer> &getCounterBufferHandles() const
{
return mCounterBufferHandles;
......@@ -103,6 +108,8 @@ class TransformFeedbackVk : public TransformFeedbackImpl
void initializeXFBBuffersDesc(ContextVk *contextVk, size_t xfbBufferCount);
void releaseCounterBuffers(RendererVk *renderer);
// This member variable is set when glBindTransformFeedbackBuffers/glBeginTransformFeedback
// is called and unset in dirty bit handler for transform feedback state change. If this
// value is true, vertex shader will record transform feedback varyings from the beginning
......
......@@ -1604,7 +1604,9 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
sizeof(shaderParams), commandBuffer));
// Make sure transform feedback is paused
contextVk->pauseTransformFeedbackIfStartedAndRebindBuffersOnResume();
bool isTransformFeedbackActiveUnpaused =
contextVk->getStartedRenderPassCommands().isTransformFeedbackActiveUnpaused();
contextVk->pauseTransformFeedbackIfActiveUnpaused();
// Make sure this draw call doesn't count towards occlusion query results.
contextVk->pauseRenderPassQueriesIfActive();
......@@ -1615,7 +1617,7 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
// If transform feedback was active, we can't pause and resume it in the same render pass
// because we can't insert a memory barrier for the counter buffers. In that case, break the
// render pass.
if (contextVk->getStartedRenderPassCommands().isTransformFeedbackStarted())
if (isTransformFeedbackActiveUnpaused)
{
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
}
......
......@@ -823,6 +823,7 @@ CommandBufferHelper::CommandBufferHelper()
mTransformFeedbackCounterBuffers{},
mValidTransformFeedbackBufferCount(0),
mRebindTransformFeedbackBuffers(false),
mIsTransformFeedbackActiveUnpaused(false),
mIsRenderPassCommandBuffer(false),
mReadOnlyDepthStencilMode(false),
mDepthAccess(ResourceAccess::Unused),
......@@ -1463,26 +1464,6 @@ angle::Result CommandBufferHelper::flushToPrimary(const angle::FeaturesVk &featu
primary->beginRenderPass(beginInfo, VK_SUBPASS_CONTENTS_INLINE);
mCommandBuffer.executeCommands(primary->getHandle());
primary->endRenderPass();
if (mValidTransformFeedbackBufferCount != 0)
{
// Would be better to accumulate this barrier using the command APIs.
// TODO: Clean thus up before we close http://anglebug.com/3206
VkBufferMemoryBarrier bufferBarrier = {};
bufferBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
bufferBarrier.pNext = nullptr;
bufferBarrier.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
bufferBarrier.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufferBarrier.buffer = mTransformFeedbackCounterBuffers[0];
bufferBarrier.offset = 0;
bufferBarrier.size = VK_WHOLE_SIZE;
primary->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0u, 0u, nullptr, 1u,
&bufferBarrier, 0u, nullptr);
}
}
else
{
......@@ -1622,7 +1603,8 @@ void CommandBufferHelper::reset()
// This state should never change for non-renderPass command buffer
ASSERT(mRenderPassStarted == false);
ASSERT(mValidTransformFeedbackBufferCount == 0);
ASSERT(mRebindTransformFeedbackBuffers == false);
ASSERT(!mRebindTransformFeedbackBuffers);
ASSERT(!mIsTransformFeedbackActiveUnpaused);
ASSERT(mRenderPassUsedImages.empty());
}
......@@ -1634,7 +1616,8 @@ void CommandBufferHelper::resumeTransformFeedback()
uint32_t numCounterBuffers =
mRebindTransformFeedbackBuffers ? 0 : mValidTransformFeedbackBufferCount;
mRebindTransformFeedbackBuffers = false;
mRebindTransformFeedbackBuffers = false;
mIsTransformFeedbackActiveUnpaused = true;
mCommandBuffer.beginTransformFeedback(numCounterBuffers,
mTransformFeedbackCounterBuffers.data());
......@@ -1643,7 +1626,8 @@ void CommandBufferHelper::resumeTransformFeedback()
void CommandBufferHelper::pauseTransformFeedback()
{
ASSERT(mIsRenderPassCommandBuffer);
ASSERT(isTransformFeedbackStarted());
ASSERT(isTransformFeedbackStarted() && isTransformFeedbackActiveUnpaused());
mIsTransformFeedbackActiveUnpaused = false;
mCommandBuffer.endTransformFeedback(mValidTransformFeedbackBufferCount,
mTransformFeedbackCounterBuffers.data());
}
......
......@@ -1123,6 +1123,7 @@ class CommandBufferHelper : angle::NonCopyable
void resumeTransformFeedback();
void pauseTransformFeedback();
bool isTransformFeedbackStarted() const { return mValidTransformFeedbackBufferCount > 0; }
bool isTransformFeedbackActiveUnpaused() const { return mIsTransformFeedbackActiveUnpaused; }
uint32_t getAndResetCounter()
{
......@@ -1201,6 +1202,7 @@ class CommandBufferHelper : angle::NonCopyable
gl::TransformFeedbackBuffersArray<VkBuffer> mTransformFeedbackCounterBuffers;
uint32_t mValidTransformFeedbackBufferCount;
bool mRebindTransformFeedbackBuffers;
bool mIsTransformFeedbackActiveUnpaused;
bool mIsRenderPassCommandBuffer;
bool mReadOnlyDepthStencilMode;
......
......@@ -656,6 +656,77 @@ TEST_P(MultisampledRenderToTextureTest, ScissoredDrawTest)
EXPECT_PIXEL_COLOR_EQ(kScissorEndX, kScissorEndY + 1, GLColor::green);
}
// Test transform feedback with state change. In the Vulkan backend, this results in an implicit
// break of the render pass, and must work correctly with respect to the subpass index that's used.
TEST_P(MultisampledRenderToTextureES3Test, TransformFeedbackTest)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
GLFramebuffer FBO;
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
// Set up color attachment and bind to FBO
constexpr GLsizei kSize = 1024;
GLTexture texture;
GLRenderbuffer renderbuffer;
createAndAttachColorAttachment(false, kSize, GL_COLOR_ATTACHMENT0, nullptr, &texture,
&renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set up transform feedback.
GLTransformFeedback xfb;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb);
constexpr size_t kXfbBufferSize = 1024; // arbitrary number
GLBuffer xfbBuffer;
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, kXfbBufferSize, nullptr, GL_STATIC_DRAW);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer);
// Set up program with transform feedback
std::vector<std::string> tfVaryings = {"gl_Position"};
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(drawColor, essl1_shaders::vs::Simple(),
essl1_shaders::fs::UniformColor(), tfVaryings,
GL_INTERLEAVED_ATTRIBS);
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
// Start transform feedback
glBeginTransformFeedback(GL_TRIANGLES);
// Set viewport and clear to black
glViewport(0, 0, kSize, kSize);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Draw green. There's no unresolve operation as the framebuffer has just been cleared.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
ASSERT_GL_NO_ERROR();
// Incur a state change while transform feedback is active. This will result in a pipeline
// rebind in the Vulkan backend, which should necessarily break the render pass when
// VK_EXT_transform_feedback is used.
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
// Draw red. The implicit render pass break means that there's an unresolve operation.
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
// End transform feedback
glEndTransformFeedback();
// Expect yellow.
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::yellow);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::yellow);
}
// Draw test using both color and depth attachments.
TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
{
......
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