Commit e96d1744 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Optimize glMemoryBarrier

Previous to this change, glMemoryBarrier was processed as it is issued. This made it impossible to know whether a draw call would follow or a dispatch call, and what resources it would use. The render pass was conservatively broken due to this limitation. To address this limitation, handling of glMemoryBarrier is deferred until the next draw or dispatch call. Note that glMemoryBarrier acts as two barriers: - An execution+memory barrier: shader writes are made visible to subsequent accesses - Another execution barrier: shader accesses are finished before subsequent writes An important observation is that for most resources, ANGLE actually necessarily has to issue memory barriers automatically to conform with Vulkan. In terms of memory barrier thus, ANGLE already does the right thing except for when there's no binding change. This means WaW hazards (i.e. storage buffer and image writes) with no binding change require a memory barrier as a result of glMemoryBarrier. In all other cases, it's enough for glMemoryBarrier to break the render pass if necessary and ensure that corresponding bindings are marked dirty (for the execution or memory barriers to happen automatically later). Bug: angleproject:5070 Change-Id: Ide359c43362f8a78805ecf797a91de7aa79221f9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2693473Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 22aea430
...@@ -430,6 +430,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk ...@@ -430,6 +430,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
DirtyBits{DIRTY_BIT_PIPELINE_BINDING, DIRTY_BIT_TEXTURES, DIRTY_BIT_SHADER_RESOURCES, DirtyBits{DIRTY_BIT_PIPELINE_BINDING, DIRTY_BIT_TEXTURES, DIRTY_BIT_SHADER_RESOURCES,
DIRTY_BIT_DESCRIPTOR_SETS, DIRTY_BIT_DRIVER_UNIFORMS_BINDING}; DIRTY_BIT_DESCRIPTOR_SETS, DIRTY_BIT_DRIVER_UNIFORMS_BINDING};
mGraphicsDirtyBitHandlers[DIRTY_BIT_MEMORY_BARRIER] =
&ContextVk::handleDirtyGraphicsMemorybarrier;
mGraphicsDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyGraphicsEventLog; mGraphicsDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyGraphicsEventLog;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DEFAULT_ATTRIBS] = mGraphicsDirtyBitHandlers[DIRTY_BIT_DEFAULT_ATTRIBS] =
&ContextVk::handleDirtyGraphicsDefaultAttribs; &ContextVk::handleDirtyGraphicsDefaultAttribs;
...@@ -466,6 +468,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk ...@@ -466,6 +468,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mGraphicsDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] = mGraphicsDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] =
&ContextVk::handleDirtyGraphicsDescriptorSets; &ContextVk::handleDirtyGraphicsDescriptorSets;
mComputeDirtyBitHandlers[DIRTY_BIT_MEMORY_BARRIER] =
&ContextVk::handleDirtyComputeMemoryBarrier;
mComputeDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyComputeEventLog; mComputeDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyComputeEventLog;
mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE_DESC] = &ContextVk::handleDirtyComputePipelineDesc; mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE_DESC] = &ContextVk::handleDirtyComputePipelineDesc;
mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE_BINDING] = mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE_BINDING] =
...@@ -1062,6 +1066,150 @@ angle::Result ContextVk::setupDispatch(const gl::Context *context) ...@@ -1062,6 +1066,150 @@ angle::Result ContextVk::setupDispatch(const gl::Context *context)
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ContextVk::handleDirtyGraphicsMemorybarrier(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyMemorybarrierImpl(dirtyBitsIterator, dirtyBitMask);
}
angle::Result ContextVk::handleDirtyComputeMemoryBarrier()
{
return handleDirtyMemorybarrierImpl(nullptr, {});
}
bool ContextVk::renderPassUsesStorageResources() const
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
// Storage images:
for (size_t imageUnitIndex : executable->getActiveImagesMask())
{
const gl::Texture *texture = mState.getImageUnit(imageUnitIndex).texture.get();
if (texture == nullptr)
{
continue;
}
TextureVk *textureVk = vk::GetImpl(texture);
if (texture->getType() == gl::TextureType::Buffer)
{
vk::BufferHelper &buffer = vk::GetImpl(textureVk->getBuffer().get())->getBuffer();
if (mRenderPassCommands->usesBuffer(buffer))
{
return true;
}
}
else
{
vk::ImageHelper &image = textureVk->getImage();
// Images only need to close the render pass if they need a layout transition. Outside
// render pass command buffer doesn't need closing as the layout transition barriers are
// recorded in sequence with the rest of the commands.
if (IsRenderPassStartedAndUsesImage(*mRenderPassCommands, image))
{
return true;
}
}
}
gl::ShaderMap<const gl::ProgramState *> programStates;
mExecutable->fillProgramStateMap(this, &programStates);
for (const gl::ShaderType shaderType : executable->getLinkedShaderStages())
{
const gl::ProgramState *programState = programStates[shaderType];
ASSERT(programState);
// Storage buffers:
const std::vector<gl::InterfaceBlock> &blocks = programState->getShaderStorageBlocks();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
mState.getIndexedShaderStorageBuffer(block.binding);
if (!block.isActive(shaderType) || bufferBinding.get() == nullptr)
{
continue;
}
vk::BufferHelper &buffer = vk::GetImpl(bufferBinding.get())->getBuffer();
if (mRenderPassCommands->usesBuffer(buffer))
{
return true;
}
}
// Atomic counters:
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
programState->getAtomicCounterBuffers();
for (uint32_t bufferIndex = 0; bufferIndex < atomicCounterBuffers.size(); ++bufferIndex)
{
uint32_t binding = atomicCounterBuffers[bufferIndex].binding;
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
mState.getIndexedAtomicCounterBuffer(binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
vk::BufferHelper &buffer = vk::GetImpl(bufferBinding.get())->getBuffer();
if (mRenderPassCommands->usesBuffer(buffer))
{
return true;
}
}
}
return false;
}
angle::Result ContextVk::handleDirtyMemorybarrierImpl(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
const bool hasImages = executable->hasImages();
const bool hasStorageBuffers = executable->hasStorageBuffers();
const bool hasAtomicCounters = executable->hasAtomicCounterBuffers();
if (!hasImages && !hasStorageBuffers && !hasAtomicCounters)
{
return angle::Result::Continue;
}
// Break the render pass if necessary. This is only needed for write-after-read situations, and
// is done by checking whether current storage buffers and images are used in the render pass.
if (renderPassUsesStorageResources())
{
// Either set later bits (if called during handling of graphics dirty bits), or set the
// dirty bits directly (if called during handling of compute dirty bits).
if (dirtyBitsIterator)
{
return flushDirtyGraphicsRenderPass(dirtyBitsIterator, dirtyBitMask);
}
else
{
return flushCommandsAndEndRenderPass();
}
}
// Flushing outside render pass commands is cheap. If a memory barrier has been issued in its
// life time, just flush it instead of wasting time trying to figure out if it's necessary.
if (mOutsideRenderPassCommands->hasGLMemoryBarrierIssued())
{
ANGLE_TRY(flushOutsideRenderPassCommands());
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator, angle::Result ContextVk::handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask) DirtyBits dirtyBitMask)
{ {
...@@ -1453,18 +1601,29 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyShaderResourcesImpl( ...@@ -1453,18 +1601,29 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyShaderResourcesImpl(
const gl::ProgramExecutable *executable = mState.getProgramExecutable(); const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable); ASSERT(executable);
if (executable->hasImages()) const bool hasImages = executable->hasImages();
const bool hasStorageBuffers =
executable->hasStorageBuffers() || executable->hasAtomicCounterBuffers();
const bool hasUniformBuffers = executable->hasUniformBuffers();
if (hasImages)
{ {
ANGLE_TRY(updateActiveImages(commandBufferHelper)); ANGLE_TRY(updateActiveImages(commandBufferHelper));
} }
if (executable->hasUniformBuffers() || executable->hasStorageBuffers() || if (hasUniformBuffers || hasStorageBuffers || hasImages || executable->usesFramebufferFetch())
executable->hasAtomicCounterBuffers() || executable->hasImages() ||
executable->usesFramebufferFetch())
{ {
ANGLE_TRY(mExecutable->updateShaderResourcesDescriptorSet(this, mDrawFramebuffer, ANGLE_TRY(mExecutable->updateShaderResourcesDescriptorSet(this, mDrawFramebuffer,
commandBufferHelper)); commandBufferHelper));
} }
// Record usage of storage buffers and images in the command buffer to aid handling of
// glMemoryBarrier.
if (hasImages || hasStorageBuffers)
{
commandBufferHelper->setHasShaderStorageOutput();
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -3541,13 +3700,28 @@ void ContextVk::invalidateCurrentShaderResources() ...@@ -3541,13 +3700,28 @@ void ContextVk::invalidateCurrentShaderResources()
const gl::ProgramExecutable *executable = mState.getProgramExecutable(); const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable); ASSERT(executable);
if (executable->hasUniformBuffers() || executable->hasStorageBuffers() || const bool hasImages = executable->hasImages();
executable->hasAtomicCounterBuffers() || executable->hasImages() || const bool hasStorageBuffers =
executable->usesFramebufferFetch()) executable->hasStorageBuffers() || executable->hasAtomicCounterBuffers();
const bool hasUniformBuffers = executable->hasUniformBuffers();
if (hasUniformBuffers || hasStorageBuffers || hasImages || executable->usesFramebufferFetch())
{ {
mGraphicsDirtyBits |= kResourcesAndDescSetDirtyBits; mGraphicsDirtyBits |= kResourcesAndDescSetDirtyBits;
mComputeDirtyBits |= kResourcesAndDescSetDirtyBits; mComputeDirtyBits |= kResourcesAndDescSetDirtyBits;
} }
// If memory barrier has been issued but the command buffers haven't been flushed, make sure
// they get a chance to do so if necessary on program and storage buffer/image binding change.
const bool hasGLMemoryBarrierIssuedInCommandBuffers =
mOutsideRenderPassCommands->hasGLMemoryBarrierIssued() ||
mRenderPassCommands->hasGLMemoryBarrierIssued();
if ((hasStorageBuffers || hasImages) && hasGLMemoryBarrierIssuedInCommandBuffers)
{
mGraphicsDirtyBits.set(DIRTY_BIT_MEMORY_BARRIER);
mComputeDirtyBits.set(DIRTY_BIT_MEMORY_BARRIER);
}
} }
void ContextVk::invalidateGraphicsDriverUniforms() void ContextVk::invalidateGraphicsDriverUniforms()
...@@ -3786,19 +3960,6 @@ angle::Result ContextVk::dispatchComputeIndirect(const gl::Context *context, GLi ...@@ -3786,19 +3960,6 @@ angle::Result ContextVk::dispatchComputeIndirect(const gl::Context *context, GLi
angle::Result ContextVk::memoryBarrier(const gl::Context *context, GLbitfield barriers) angle::Result ContextVk::memoryBarrier(const gl::Context *context, GLbitfield barriers)
{ {
return memoryBarrierImpl(barriers, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
}
angle::Result ContextVk::memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers)
{
// Note: memoryBarrierByRegion is expected to affect only the fragment pipeline, but is
// otherwise similar to memoryBarrier.
return memoryBarrierImpl(barriers, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
angle::Result ContextVk::memoryBarrierImpl(GLbitfield barriers, VkPipelineStageFlags stageMask)
{
// First, turn GL_ALL_BARRIER_BITS into a mask that has only the valid barriers set. // First, turn GL_ALL_BARRIER_BITS into a mask that has only the valid barriers set.
constexpr GLbitfield kCoreBarrierBits = constexpr GLbitfield kCoreBarrierBits =
GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT | GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT |
...@@ -3823,41 +3984,84 @@ angle::Result ContextVk::memoryBarrierImpl(GLbitfield barriers, VkPipelineStageF ...@@ -3823,41 +3984,84 @@ angle::Result ContextVk::memoryBarrierImpl(GLbitfield barriers, VkPipelineStageF
return angle::Result::Continue; return angle::Result::Continue;
} }
// Note: many of the barriers specified here are already covered automatically. // glMemoryBarrier acts as two barriers:
// //
// The barriers that are necessary all have SHADER_WRITE as src access and the dst access is // - An execution+memory barrier: shader writes are made visible to subsequent accesses
// determined by the given bitfield. Currently, all image-related barriers that require the // - Another execution barrier: shader accesses are finished before subsequent writes
// image to change usage are handled through image layout transitions. Most buffer-related //
// barriers where the buffer usage changes are also handled automatically through dirty bits. // For the first barrier, we can simplify the implementation by assuming that prior writes are
// The only barriers that are necessary are thus barriers in situations where the resource can // expected to be used right after this barrier, so we can close the render pass or flush the
// be written to and read from without changing the bindings. // outside render pass commands right away if they have had any writes.
//
VkAccessFlags srcAccess = 0; // It's noteworthy that some barrier bits affect draw/dispatch calls only, while others affect
VkAccessFlags dstAccess = 0; // other commands. For the latter, since storage buffer and images are not tracked in command
// buffers, we can't rely on the command buffers being flushed in the usual way when recording
// these commands (i.e. through |getOutsideRenderPassCommandBuffer()| and
// |vk::CommandBufferAccess|). Conservatively flushing command buffers with any storage output
// simplifies this use case. If this needs to be avoided in the future,
// |getOutsideRenderPassCommandBuffer()| can be modified to flush the command buffers if they
// have had any storage output.
//
// For the second barrier, we need to defer closing the render pass until there's a draw or
// dispatch call that uses storage buffers or images that were previously used in the render
// pass. This allows the render pass to remain open in scenarios such as this:
//
// - Draw using resource X
// - glMemoryBarrier
// - Draw/dispatch with storage buffer/image Y
//
// To achieve this, a dirty bit is added that breaks the render pass if any storage
// buffer/images are used in it. Until the render pass breaks, changing the program or storage
// buffer/image bindings should set this dirty bit again.
constexpr GLbitfield kBarrierBitsAffectinDrawDispatch =
GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT |
GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_COMMAND_BARRIER_BIT |
GL_TRANSFORM_FEEDBACK_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT |
GL_SHADER_STORAGE_BARRIER_BIT;
// Both IMAGE_ACCESS and STORAGE barrier flags translate to the same Vulkan dst access mask. const bool hasAnyBitsAffectingDrawDispatch =
constexpr GLbitfield kShaderWriteBarriers = (barriers & ~kBarrierBitsAffectinDrawDispatch) != 0;
GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT;
if ((barriers & kShaderWriteBarriers) != 0) if (hasAnyBitsAffectingDrawDispatch)
{ {
srcAccess |= VK_ACCESS_SHADER_WRITE_BIT; mGraphicsDirtyBits.set(DIRTY_BIT_MEMORY_BARRIER);
dstAccess |= VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; mComputeDirtyBits.set(DIRTY_BIT_MEMORY_BARRIER);
} }
ANGLE_TRY(flushCommandsAndEndRenderPass()); // Make sure memory barrier is issued for future usages of storage buffers and images even if
// there's no binding change.
mGraphicsDirtyBits.set(DIRTY_BIT_SHADER_RESOURCES);
mComputeDirtyBits.set(DIRTY_BIT_SHADER_RESOURCES);
VkMemoryBarrier memoryBarrier = {}; // Break the render pass if necessary as future non-draw commands can't know if they should.
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; if (mRenderPassCommands->hasShaderStorageOutput())
memoryBarrier.srcAccessMask = srcAccess; {
memoryBarrier.dstAccessMask = dstAccess; ANGLE_TRY(flushCommandsAndEndRenderPass());
}
else if (mOutsideRenderPassCommands->hasShaderStorageOutput())
{
// Otherwise flush the outside render pass commands if necessary.
ANGLE_TRY(flushOutsideRenderPassCommands());
}
mOutsideRenderPassCommands->getCommandBuffer().memoryBarrier(stageMask, stageMask, // Mark the command buffers as affected by glMemoryBarrier, so future program and storage
&memoryBarrier); // buffer/image binding changes can set DIRTY_BIT_MEMORY_BARRIER again.
mOutsideRenderPassCommands->setGLMemoryBarrierIssued();
mRenderPassCommands->setGLMemoryBarrierIssued();
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ContextVk::memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers)
{
// Note: memoryBarrierByRegion is expected to affect only the fragment pipeline, but is
// otherwise similar to memoryBarrier in function.
//
// TODO: Optimize memoryBarrierByRegion by issuing an in-subpass pipeline barrier instead of
// breaking the render pass. http://anglebug.com/5132
return memoryBarrier(context, barriers);
}
void ContextVk::framebufferFetchBarrier() void ContextVk::framebufferFetchBarrier()
{ {
mGraphicsDirtyBits.set(DIRTY_BIT_FRAMEBUFFER_FETCH_BARRIER); mGraphicsDirtyBits.set(DIRTY_BIT_FRAMEBUFFER_FETCH_BARRIER);
......
...@@ -594,6 +594,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -594,6 +594,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
// Dirty bits. // Dirty bits.
enum DirtyBitType : size_t enum DirtyBitType : size_t
{ {
// A glMemoryBarrier has been called and command buffers may need flushing.
DIRTY_BIT_MEMORY_BARRIER,
// Dirty bits that must be processed before the render pass is started. The handlers for // Dirty bits that must be processed before the render pass is started. The handlers for
// these dirty bits don't record any commands. // these dirty bits don't record any commands.
DIRTY_BIT_DEFAULT_ATTRIBS, DIRTY_BIT_DEFAULT_ATTRIBS,
...@@ -781,6 +783,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -781,6 +783,8 @@ 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 handleDirtyGraphicsMemorybarrier(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator, angle::Result handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask); DirtyBits dirtyBitMask);
angle::Result handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator, angle::Result handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator,
...@@ -817,6 +821,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -817,6 +821,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
DirtyBits dirtyBitMask); DirtyBits dirtyBitMask);
// Handlers for compute pipeline dirty bits. // Handlers for compute pipeline dirty bits.
angle::Result handleDirtyComputeMemoryBarrier();
angle::Result handleDirtyComputeEventLog(); angle::Result handleDirtyComputeEventLog();
angle::Result handleDirtyComputePipelineDesc(); angle::Result handleDirtyComputePipelineDesc();
angle::Result handleDirtyComputePipelineBinding(); angle::Result handleDirtyComputePipelineBinding();
...@@ -827,6 +832,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -827,6 +832,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
angle::Result handleDirtyComputeDescriptorSets(); angle::Result handleDirtyComputeDescriptorSets();
// Common parts of the common dirty bit handlers. // Common parts of the common dirty bit handlers.
angle::Result handleDirtyMemorybarrierImpl(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask);
angle::Result handleDirtyEventLogImpl(vk::CommandBuffer *commandBuffer); angle::Result handleDirtyEventLogImpl(vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyTexturesImpl(vk::CommandBufferHelper *commandBufferHelper); angle::Result handleDirtyTexturesImpl(vk::CommandBufferHelper *commandBufferHelper);
angle::Result handleDirtyShaderResourcesImpl(vk::CommandBufferHelper *commandBufferHelper); angle::Result handleDirtyShaderResourcesImpl(vk::CommandBufferHelper *commandBufferHelper);
...@@ -890,6 +897,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -890,6 +897,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
angle::Result onResourceAccess(const vk::CommandBufferAccess &access); angle::Result onResourceAccess(const vk::CommandBufferAccess &access);
angle::Result flushCommandBuffersIfNecessary(const vk::CommandBufferAccess &access); angle::Result flushCommandBuffersIfNecessary(const vk::CommandBufferAccess &access);
bool renderPassUsesStorageResources() const;
void outputCumulativePerfCounters(); void outputCumulativePerfCounters();
......
...@@ -793,6 +793,8 @@ CommandBufferHelper::CommandBufferHelper() ...@@ -793,6 +793,8 @@ CommandBufferHelper::CommandBufferHelper()
mIsTransformFeedbackActiveUnpaused(false), mIsTransformFeedbackActiveUnpaused(false),
mIsRenderPassCommandBuffer(false), mIsRenderPassCommandBuffer(false),
mReadOnlyDepthStencilMode(false), mReadOnlyDepthStencilMode(false),
mHasShaderStorageOutput(false),
mHasGLMemoryBarrierIssued(false),
mDepthAccess(ResourceAccess::Unused), mDepthAccess(ResourceAccess::Unused),
mStencilAccess(ResourceAccess::Unused), mStencilAccess(ResourceAccess::Unused),
mDepthCmdSizeInvalidated(kInfiniteCmdSize), mDepthCmdSizeInvalidated(kInfiniteCmdSize),
...@@ -1553,6 +1555,8 @@ void CommandBufferHelper::reset() ...@@ -1553,6 +1555,8 @@ void CommandBufferHelper::reset()
mRenderPassStarted = false; mRenderPassStarted = false;
mValidTransformFeedbackBufferCount = 0; mValidTransformFeedbackBufferCount = 0;
mRebindTransformFeedbackBuffers = false; mRebindTransformFeedbackBuffers = false;
mHasShaderStorageOutput = false;
mHasGLMemoryBarrierIssued = false;
mDepthAccess = ResourceAccess::Unused; mDepthAccess = ResourceAccess::Unused;
mStencilAccess = ResourceAccess::Unused; mStencilAccess = ResourceAccess::Unused;
mDepthCmdSizeInvalidated = kInfiniteCmdSize; mDepthCmdSizeInvalidated = kInfiniteCmdSize;
......
...@@ -1170,6 +1170,18 @@ class CommandBufferHelper : angle::NonCopyable ...@@ -1170,6 +1170,18 @@ class CommandBufferHelper : angle::NonCopyable
bool hasRenderPass() const { return mIsRenderPassCommandBuffer; } bool hasRenderPass() const { return mIsRenderPassCommandBuffer; }
void setHasShaderStorageOutput() { mHasShaderStorageOutput = true; }
bool hasShaderStorageOutput() const { return mHasShaderStorageOutput; }
void setGLMemoryBarrierIssued()
{
if (!empty())
{
mHasGLMemoryBarrierIssued = true;
}
}
bool hasGLMemoryBarrierIssued() const { return mHasGLMemoryBarrierIssued; }
private: private:
bool onDepthStencilAccess(ResourceAccess access, bool onDepthStencilAccess(ResourceAccess access,
uint32_t *cmdCountInvalidated, uint32_t *cmdCountInvalidated,
...@@ -1207,6 +1219,15 @@ class CommandBufferHelper : angle::NonCopyable ...@@ -1207,6 +1219,15 @@ class CommandBufferHelper : angle::NonCopyable
bool mIsRenderPassCommandBuffer; bool mIsRenderPassCommandBuffer;
bool mReadOnlyDepthStencilMode; bool mReadOnlyDepthStencilMode;
// Whether the command buffers contains any draw/dispatch calls that possibly output data
// through storage buffers and images. This is used to determine whether glMemoryBarrier*
// should flush the command buffer.
bool mHasShaderStorageOutput;
// Whether glMemoryBarrier has been called while commands are recorded in this command buffer.
// This is used to know when to check and potentially flush the command buffer if storage
// buffers and images are used in it.
bool mHasGLMemoryBarrierIssued;
// State tracking for the maximum (Write been the highest) depth access during the entire // State tracking for the maximum (Write been the highest) depth access during the entire
// renderpass. Note that this does not include VK_ATTACHMENT_LOAD_OP_CLEAR which is tracked // renderpass. Note that this does not include VK_ATTACHMENT_LOAD_OP_CLEAR which is tracked
// separately. This is done this way to allow clear op to being optimized out when we find out // separately. This is done this way to allow clear op to being optimized out when we find out
......
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