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 ...@@ -78,6 +78,12 @@ class BitSetT final
mBitsCopy.set(index); mBitsCopy.set(index);
} }
void setLaterBits(const BitSetT &bits)
{
ASSERT((BitSetT(bits) &= Mask(mCurrentBit + 1)).none());
mBitsCopy |= bits;
}
private: private:
std::size_t getNextBit(); std::size_t getNextBit();
...@@ -532,7 +538,7 @@ class BitSetArray final ...@@ -532,7 +538,7 @@ class BitSetArray final
ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator); ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator);
prepareCopy(); prepareCopy();
mParentCopy.reset(pos); mParentCopy.reset(pos);
updateIterator(pos, false); updateIteratorBit(pos, false);
} }
void setLaterBit(std::size_t pos) void setLaterBit(std::size_t pos)
...@@ -540,7 +546,14 @@ class BitSetArray final ...@@ -540,7 +546,14 @@ class BitSetArray final
ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator); ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator);
prepareCopy(); prepareCopy();
mParentCopy.set(pos); mParentCopy.set(pos);
updateIterator(pos, true); updateIteratorBit(pos, true);
}
void setLaterBits(const BitSetArray &bits)
{
prepareCopy();
mParentCopy |= bits;
updateIteratorBits(bits);
} }
private: private:
...@@ -555,7 +568,7 @@ class BitSetArray final ...@@ -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 // Get the index and offset, update current interator if within range
size_t index = pos >> kShiftForDivision; size_t index = pos >> kShiftForDivision;
...@@ -573,6 +586,11 @@ class BitSetArray final ...@@ -573,6 +586,11 @@ class BitSetArray final
} }
} }
ANGLE_INLINE void updateIteratorBits(const BitSetArray &bits)
{
mCurrentIterator.setLaterBits(bits.mBaseBitSetArray[mIndex]);
}
// Problem - // Problem -
// We want to provide the fastest path possible for usecases that iterate though the bitset. // We want to provide the fastest path possible for usecases that iterate though the bitset.
// //
......
...@@ -299,7 +299,7 @@ TYPED_TEST(BitSetIteratorTest, BitAssignment) ...@@ -299,7 +299,7 @@ TYPED_TEST(BitSetIteratorTest, BitAssignment)
TYPED_TEST(BitSetIteratorTest, SetLaterBit) TYPED_TEST(BitSetIteratorTest, SetLaterBit)
{ {
TypeParam mStateBits = this->mStateBits; 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(0);
mStateBits.set(1); mStateBits.set(1);
...@@ -311,6 +311,10 @@ TYPED_TEST(BitSetIteratorTest, SetLaterBit) ...@@ -311,6 +311,10 @@ TYPED_TEST(BitSetIteratorTest, SetLaterBit)
{ {
iter.setLaterBit(3); iter.setLaterBit(3);
iter.setLaterBit(5); iter.setLaterBit(5);
}
if (*iter == 5)
{
iter.setLaterBit(6);
iter.setLaterBit(7); iter.setLaterBit(7);
iter.setLaterBit(9); iter.setLaterBit(9);
iter.setLaterBit(35); iter.setLaterBit(35);
...@@ -349,6 +353,37 @@ TYPED_TEST(BitSetIteratorTest, ResetLaterBit) ...@@ -349,6 +353,37 @@ TYPED_TEST(BitSetIteratorTest, ResetLaterBit)
EXPECT_EQ(expectedValues, actualValues); 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> template <typename T>
class BitSetArrayTest : public testing::Test class BitSetArrayTest : public testing::Test
{ {
......
...@@ -325,10 +325,11 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -325,10 +325,11 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void onTransformFeedbackStateChanged(); void onTransformFeedbackStateChanged();
angle::Result onBeginTransformFeedback( angle::Result onBeginTransformFeedback(
size_t bufferCount, size_t bufferCount,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers); const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper> &counterBuffers);
void onEndTransformFeedback(); void onEndTransformFeedback();
angle::Result onPauseTransformFeedback(); angle::Result onPauseTransformFeedback();
void pauseTransformFeedbackIfStartedAndRebindBuffersOnResume(); void pauseTransformFeedbackIfActiveUnpaused();
// When UtilsVk issues draw or dispatch calls, it binds a new pipeline and descriptor sets that // 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 // 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 ...@@ -614,7 +615,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
// Shader resources excluding textures, which are handled separately. // Shader resources excluding textures, which are handled separately.
DIRTY_BIT_SHADER_RESOURCES, DIRTY_BIT_SHADER_RESOURCES,
DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS, DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS,
DIRTY_BIT_TRANSFORM_FEEDBACK_STATE,
DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME, DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME,
DIRTY_BIT_DESCRIPTOR_SETS, DIRTY_BIT_DESCRIPTOR_SETS,
DIRTY_BIT_MAX, DIRTY_BIT_MAX,
...@@ -622,8 +622,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -622,8 +622,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>; using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
using GraphicsDirtyBitHandler = using GraphicsDirtyBitHandler = angle::Result (
angle::Result (ContextVk::*)(DirtyBits::Iterator *dirtyBitsIterator); ContextVk::*)(DirtyBits::Iterator *dirtyBitsIterator, DirtyBits dirtyBitMask);
using ComputeDirtyBitHandler = angle::Result (ContextVk::*)(); using ComputeDirtyBitHandler = angle::Result (ContextVk::*)();
struct DriverUniformsDescriptorSet struct DriverUniformsDescriptorSet
...@@ -777,25 +777,38 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -777,25 +777,38 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void invalidateDriverUniforms(); void invalidateDriverUniforms();
// Handlers for graphics pipeline dirty bits. // Handlers for graphics pipeline dirty bits.
angle::Result handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator); angle::Result handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator,
angle::Result handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator); angle::Result handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator,
angle::Result handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator); angle::Result handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator,
angle::Result handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator); angle::Result handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator,
angle::Result handleDirtyGraphicsIndexBuffer(DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDriverUniforms(DirtyBits::Iterator *dirtyBitsIterator); angle::Result handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator,
angle::Result handleDirtyGraphicsDriverUniformsBinding(DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator); 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( angle::Result handleDirtyGraphicsTransformFeedbackBuffersEmulation(
DirtyBits::Iterator *dirtyBitsIterator); DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsTransformFeedbackBuffersExtension( angle::Result handleDirtyGraphicsTransformFeedbackBuffersExtension(
DirtyBits::Iterator *dirtyBitsIterator); DirtyBits::Iterator *dirtyBitsIterator,
angle::Result handleDirtyGraphicsTransformFeedbackState(DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsTransformFeedbackResume( angle::Result handleDirtyGraphicsTransformFeedbackResume(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits::Iterator *dirtyBitsIterator); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator); angle::Result handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
// Handlers for compute pipeline dirty bits. // Handlers for compute pipeline dirty bits.
angle::Result handleDirtyComputeEventLog(); angle::Result handleDirtyComputeEventLog();
...@@ -840,6 +853,14 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -840,6 +853,14 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
bool hasRecordedCommands(); bool hasRecordedCommands();
void dumpCommandStreamDiagnostics(); void dumpCommandStreamDiagnostics();
angle::Result flushOutsideRenderPassCommands(); 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 flushDescriptorSetUpdates();
void onRenderPassFinished(); void onRenderPassFinished();
...@@ -851,7 +872,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -851,7 +872,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void populateTransformFeedbackBufferSet( void populateTransformFeedbackBufferSet(
size_t bufferCount, size_t bufferCount,
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers); const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers);
void pauseTransformFeedbackIfStarted(DirtyBits onResumeOps);
// DescriptorSet writes // DescriptorSet writes
template <typename T, const T *VkWriteDescriptorSet::*pInfo> template <typename T, const T *VkWriteDescriptorSet::*pInfo>
......
...@@ -37,11 +37,19 @@ TransformFeedbackVk::~TransformFeedbackVk() {} ...@@ -37,11 +37,19 @@ TransformFeedbackVk::~TransformFeedbackVk() {}
void TransformFeedbackVk::onDestroy(const gl::Context *context) 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) 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, ...@@ -129,7 +137,8 @@ angle::Result TransformFeedbackVk::begin(const gl::Context *context,
mRebindTransformFeedbackBuffer = true; mRebindTransformFeedbackBuffer = true;
} }
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers); return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
mCounterBufferHelpers);
} }
angle::Result TransformFeedbackVk::end(const gl::Context *context) angle::Result TransformFeedbackVk::end(const gl::Context *context)
...@@ -147,6 +156,9 @@ angle::Result TransformFeedbackVk::end(const gl::Context *context) ...@@ -147,6 +156,9 @@ angle::Result TransformFeedbackVk::end(const gl::Context *context)
} }
contextVk->onEndTransformFeedback(); contextVk->onEndTransformFeedback();
releaseCounterBuffers(contextVk->getRenderer());
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -184,7 +196,8 @@ angle::Result TransformFeedbackVk::resume(const gl::Context *context) ...@@ -184,7 +196,8 @@ angle::Result TransformFeedbackVk::resume(const gl::Context *context)
initializeXFBBuffersDesc(contextVk, xfbBufferCount); initializeXFBBuffersDesc(contextVk, xfbBufferCount);
} }
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers); return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
mCounterBufferHelpers);
} }
angle::Result TransformFeedbackVk::bindIndexedBuffer( angle::Result TransformFeedbackVk::bindIndexedBuffer(
......
...@@ -87,6 +87,11 @@ class TransformFeedbackVk : public TransformFeedbackImpl ...@@ -87,6 +87,11 @@ class TransformFeedbackVk : public TransformFeedbackImpl
return mBufferSizes; return mBufferSizes;
} }
gl::TransformFeedbackBuffersArray<vk::BufferHelper> &getCounterBufferHelpers()
{
return mCounterBufferHelpers;
}
const gl::TransformFeedbackBuffersArray<VkBuffer> &getCounterBufferHandles() const const gl::TransformFeedbackBuffersArray<VkBuffer> &getCounterBufferHandles() const
{ {
return mCounterBufferHandles; return mCounterBufferHandles;
...@@ -103,6 +108,8 @@ class TransformFeedbackVk : public TransformFeedbackImpl ...@@ -103,6 +108,8 @@ class TransformFeedbackVk : public TransformFeedbackImpl
void initializeXFBBuffersDesc(ContextVk *contextVk, size_t xfbBufferCount); void initializeXFBBuffersDesc(ContextVk *contextVk, size_t xfbBufferCount);
void releaseCounterBuffers(RendererVk *renderer);
// This member variable is set when glBindTransformFeedbackBuffers/glBeginTransformFeedback // This member variable is set when glBindTransformFeedbackBuffers/glBeginTransformFeedback
// is called and unset in dirty bit handler for transform feedback state change. If this // 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 // value is true, vertex shader will record transform feedback varyings from the beginning
......
...@@ -1604,7 +1604,9 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk, ...@@ -1604,7 +1604,9 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
sizeof(shaderParams), commandBuffer)); sizeof(shaderParams), commandBuffer));
// Make sure transform feedback is paused // 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. // Make sure this draw call doesn't count towards occlusion query results.
contextVk->pauseRenderPassQueriesIfActive(); contextVk->pauseRenderPassQueriesIfActive();
...@@ -1615,7 +1617,7 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk, ...@@ -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 // 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 // because we can't insert a memory barrier for the counter buffers. In that case, break the
// render pass. // render pass.
if (contextVk->getStartedRenderPassCommands().isTransformFeedbackStarted()) if (isTransformFeedbackActiveUnpaused)
{ {
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass()); ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
} }
......
...@@ -823,6 +823,7 @@ CommandBufferHelper::CommandBufferHelper() ...@@ -823,6 +823,7 @@ CommandBufferHelper::CommandBufferHelper()
mTransformFeedbackCounterBuffers{}, mTransformFeedbackCounterBuffers{},
mValidTransformFeedbackBufferCount(0), mValidTransformFeedbackBufferCount(0),
mRebindTransformFeedbackBuffers(false), mRebindTransformFeedbackBuffers(false),
mIsTransformFeedbackActiveUnpaused(false),
mIsRenderPassCommandBuffer(false), mIsRenderPassCommandBuffer(false),
mReadOnlyDepthStencilMode(false), mReadOnlyDepthStencilMode(false),
mDepthAccess(ResourceAccess::Unused), mDepthAccess(ResourceAccess::Unused),
...@@ -1463,26 +1464,6 @@ angle::Result CommandBufferHelper::flushToPrimary(const angle::FeaturesVk &featu ...@@ -1463,26 +1464,6 @@ angle::Result CommandBufferHelper::flushToPrimary(const angle::FeaturesVk &featu
primary->beginRenderPass(beginInfo, VK_SUBPASS_CONTENTS_INLINE); primary->beginRenderPass(beginInfo, VK_SUBPASS_CONTENTS_INLINE);
mCommandBuffer.executeCommands(primary->getHandle()); mCommandBuffer.executeCommands(primary->getHandle());
primary->endRenderPass(); 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 else
{ {
...@@ -1622,7 +1603,8 @@ void CommandBufferHelper::reset() ...@@ -1622,7 +1603,8 @@ void CommandBufferHelper::reset()
// This state should never change for non-renderPass command buffer // This state should never change for non-renderPass command buffer
ASSERT(mRenderPassStarted == false); ASSERT(mRenderPassStarted == false);
ASSERT(mValidTransformFeedbackBufferCount == 0); ASSERT(mValidTransformFeedbackBufferCount == 0);
ASSERT(mRebindTransformFeedbackBuffers == false); ASSERT(!mRebindTransformFeedbackBuffers);
ASSERT(!mIsTransformFeedbackActiveUnpaused);
ASSERT(mRenderPassUsedImages.empty()); ASSERT(mRenderPassUsedImages.empty());
} }
...@@ -1634,7 +1616,8 @@ void CommandBufferHelper::resumeTransformFeedback() ...@@ -1634,7 +1616,8 @@ void CommandBufferHelper::resumeTransformFeedback()
uint32_t numCounterBuffers = uint32_t numCounterBuffers =
mRebindTransformFeedbackBuffers ? 0 : mValidTransformFeedbackBufferCount; mRebindTransformFeedbackBuffers ? 0 : mValidTransformFeedbackBufferCount;
mRebindTransformFeedbackBuffers = false; mRebindTransformFeedbackBuffers = false;
mIsTransformFeedbackActiveUnpaused = true;
mCommandBuffer.beginTransformFeedback(numCounterBuffers, mCommandBuffer.beginTransformFeedback(numCounterBuffers,
mTransformFeedbackCounterBuffers.data()); mTransformFeedbackCounterBuffers.data());
...@@ -1643,7 +1626,8 @@ void CommandBufferHelper::resumeTransformFeedback() ...@@ -1643,7 +1626,8 @@ void CommandBufferHelper::resumeTransformFeedback()
void CommandBufferHelper::pauseTransformFeedback() void CommandBufferHelper::pauseTransformFeedback()
{ {
ASSERT(mIsRenderPassCommandBuffer); ASSERT(mIsRenderPassCommandBuffer);
ASSERT(isTransformFeedbackStarted()); ASSERT(isTransformFeedbackStarted() && isTransformFeedbackActiveUnpaused());
mIsTransformFeedbackActiveUnpaused = false;
mCommandBuffer.endTransformFeedback(mValidTransformFeedbackBufferCount, mCommandBuffer.endTransformFeedback(mValidTransformFeedbackBufferCount,
mTransformFeedbackCounterBuffers.data()); mTransformFeedbackCounterBuffers.data());
} }
......
...@@ -1123,6 +1123,7 @@ class CommandBufferHelper : angle::NonCopyable ...@@ -1123,6 +1123,7 @@ class CommandBufferHelper : angle::NonCopyable
void resumeTransformFeedback(); void resumeTransformFeedback();
void pauseTransformFeedback(); void pauseTransformFeedback();
bool isTransformFeedbackStarted() const { return mValidTransformFeedbackBufferCount > 0; } bool isTransformFeedbackStarted() const { return mValidTransformFeedbackBufferCount > 0; }
bool isTransformFeedbackActiveUnpaused() const { return mIsTransformFeedbackActiveUnpaused; }
uint32_t getAndResetCounter() uint32_t getAndResetCounter()
{ {
...@@ -1201,6 +1202,7 @@ class CommandBufferHelper : angle::NonCopyable ...@@ -1201,6 +1202,7 @@ class CommandBufferHelper : angle::NonCopyable
gl::TransformFeedbackBuffersArray<VkBuffer> mTransformFeedbackCounterBuffers; gl::TransformFeedbackBuffersArray<VkBuffer> mTransformFeedbackCounterBuffers;
uint32_t mValidTransformFeedbackBufferCount; uint32_t mValidTransformFeedbackBufferCount;
bool mRebindTransformFeedbackBuffers; bool mRebindTransformFeedbackBuffers;
bool mIsTransformFeedbackActiveUnpaused;
bool mIsRenderPassCommandBuffer; bool mIsRenderPassCommandBuffer;
bool mReadOnlyDepthStencilMode; bool mReadOnlyDepthStencilMode;
......
...@@ -656,6 +656,77 @@ TEST_P(MultisampledRenderToTextureTest, ScissoredDrawTest) ...@@ -656,6 +656,77 @@ TEST_P(MultisampledRenderToTextureTest, ScissoredDrawTest)
EXPECT_PIXEL_COLOR_EQ(kScissorEndX, kScissorEndY + 1, GLColor::green); 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. // Draw test using both color and depth attachments.
TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest) 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