Commit 1c88cf27 by Jamie Madill Committed by Commit Bot

Add optimized rendering feedback loop tracking.

This can be used both for WebGL and the Vulkan back-end workaround for Manhattan. Uses the recently added tracking for Textures being bound as samplers. Then caches this information in the Framebuffer using the Subject/Observer pattern. Bug: angleproject:4490 Change-Id: I08bef0a1b95c4333da19c2dae1f02a993e5835e1 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2109335 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent a4337121
...@@ -8493,7 +8493,8 @@ void Context::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMess ...@@ -8493,7 +8493,8 @@ void Context::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMess
default: default:
if (index < kTextureMaxSubjectIndex) if (index < kTextureMaxSubjectIndex)
{ {
if (message != angle::SubjectMessage::ContentsChanged) if (message != angle::SubjectMessage::ContentsChanged &&
message != angle::SubjectMessage::BindingChanged)
{ {
mState.onActiveTextureStateChange(this, index); mState.onActiveTextureStateChange(this, index);
mStateCache.onActiveTextureChange(this); mStateCache.onActiveTextureChange(this);
......
...@@ -316,6 +316,8 @@ FramebufferState::FramebufferState() ...@@ -316,6 +316,8 @@ FramebufferState::FramebufferState()
mDefaultFixedSampleLocations(GL_FALSE), mDefaultFixedSampleLocations(GL_FALSE),
mDefaultLayers(0), mDefaultLayers(0),
mWebGLDepthStencilConsistent(true), mWebGLDepthStencilConsistent(true),
mDepthBufferFeedbackLoop(false),
mStencilBufferFeedbackLoop(false),
mDefaultFramebufferReadAttachmentInitialized(false) mDefaultFramebufferReadAttachmentInitialized(false)
{ {
ASSERT(mDrawBufferStates.size() > 0); ASSERT(mDrawBufferStates.size() > 0);
...@@ -335,6 +337,8 @@ FramebufferState::FramebufferState(const Caps &caps, FramebufferID id) ...@@ -335,6 +337,8 @@ FramebufferState::FramebufferState(const Caps &caps, FramebufferID id)
mDefaultFixedSampleLocations(GL_FALSE), mDefaultFixedSampleLocations(GL_FALSE),
mDefaultLayers(0), mDefaultLayers(0),
mWebGLDepthStencilConsistent(true), mWebGLDepthStencilConsistent(true),
mDepthBufferFeedbackLoop(false),
mStencilBufferFeedbackLoop(false),
mDefaultFramebufferReadAttachmentInitialized(false) mDefaultFramebufferReadAttachmentInitialized(false)
{ {
ASSERT(mId != Framebuffer::kDefaultDrawFramebufferHandle); ASSERT(mId != Framebuffer::kDefaultDrawFramebufferHandle);
...@@ -673,6 +677,26 @@ bool FramebufferState::isDefault() const ...@@ -673,6 +677,26 @@ bool FramebufferState::isDefault() const
return mId == Framebuffer::kDefaultDrawFramebufferHandle; return mId == Framebuffer::kDefaultDrawFramebufferHandle;
} }
bool FramebufferState::updateAttachmentFeedbackLoop(size_t dirtyBit)
{
switch (dirtyBit)
{
case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
mDepthBufferFeedbackLoop = mDepthAttachment.isBoundAsSamplerOrImage();
return mDepthBufferFeedbackLoop;
case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
mStencilBufferFeedbackLoop = mStencilAttachment.isBoundAsSamplerOrImage();
return mStencilBufferFeedbackLoop;
default:
ASSERT(dirtyBit <= Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
mDrawBufferFeedbackLoops[dirtyBit] =
mColorAttachments[dirtyBit].isBoundAsSamplerOrImage();
return mDrawBufferFeedbackLoops.test(dirtyBit);
}
}
const FramebufferID Framebuffer::kDefaultDrawFramebufferHandle = {0}; const FramebufferID Framebuffer::kDefaultDrawFramebufferHandle = {0};
Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, FramebufferID id) Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, FramebufferID id)
...@@ -1859,18 +1883,14 @@ void Framebuffer::setAttachmentImpl(const Context *context, ...@@ -1859,18 +1883,14 @@ void Framebuffer::setAttachmentImpl(const Context *context,
if (!resource) if (!resource)
{ {
mColorAttachmentBits.reset(colorIndex);
mFloat32ColorAttachmentBits.reset(colorIndex); mFloat32ColorAttachmentBits.reset(colorIndex);
} }
else else
{ {
mColorAttachmentBits.set(colorIndex);
updateFloat32ColorAttachmentBits( updateFloat32ColorAttachmentBits(
colorIndex, resource->getAttachmentFormat(binding, textureIndex).info); colorIndex, resource->getAttachmentFormat(binding, textureIndex).info);
} }
// TODO(jmadill): ASSERT instead of checking the attachment exists in
// formsRenderingFeedbackLoopWith
bool enabled = (type != GL_NONE && getDrawBufferState(colorIndex) != GL_NONE); bool enabled = (type != GL_NONE && getDrawBufferState(colorIndex) != GL_NONE);
mState.mEnabledDrawBuffers.set(colorIndex, enabled); mState.mEnabledDrawBuffers.set(colorIndex, enabled);
SetComponentTypeMask(getDrawbufferWriteType(colorIndex), colorIndex, SetComponentTypeMask(getDrawbufferWriteType(colorIndex), colorIndex,
...@@ -1899,6 +1919,7 @@ void Framebuffer::updateAttachment(const Context *context, ...@@ -1899,6 +1919,7 @@ void Framebuffer::updateAttachment(const Context *context,
mState.mResourceNeedsInit.set(dirtyBit, attachment->initState() == InitState::MayNeedInit); mState.mResourceNeedsInit.set(dirtyBit, attachment->initState() == InitState::MayNeedInit);
onDirtyBinding->bind(resource); onDirtyBinding->bind(resource);
mState.updateAttachmentFeedbackLoop(dirtyBit);
invalidateCompletenessCache(); invalidateCompletenessCache();
} }
...@@ -1931,6 +1952,17 @@ void Framebuffer::onSubjectStateChange(angle::SubjectIndex index, angle::Subject ...@@ -1931,6 +1952,17 @@ void Framebuffer::onSubjectStateChange(angle::SubjectIndex index, angle::Subject
return; return;
} }
// Triggered by changes to Texture feedback loops.
if (message == angle::SubjectMessage::BindingChanged)
{
if (mState.updateAttachmentFeedbackLoop(index))
{
mDirtyBits.set(index);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
}
return;
}
// This can be triggered by the GL back-end TextureGL class. // This can be triggered by the GL back-end TextureGL class.
ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged); ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged);
return; return;
...@@ -1973,68 +2005,16 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject ...@@ -1973,68 +2005,16 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject
bool Framebuffer::formsRenderingFeedbackLoopWith(const Context *context) const bool Framebuffer::formsRenderingFeedbackLoopWith(const Context *context) const
{ {
const State &state = context->getState(); // We don't handle tricky cases where the default FBO is bound as a sampler.
const Program *program = state.getProgram(); // We also don't handle tricky cases with EGLImages and mipmap selection.
// TODO(http://anglebug.com/4500): Tricky rendering feedback loop cases.
// TODO(jmadill): Default framebuffer feedback loops.
if (mState.isDefault()) if (mState.isDefault())
{ {
return false; return false;
} }
const FramebufferAttachment *depth = getDepthAttachment(); return mState.mDrawBufferFeedbackLoops.any() || mState.mDepthBufferFeedbackLoop ||
const FramebufferAttachment *stencil = getStencilAttachment(); mState.mStencilBufferFeedbackLoop;
const bool checkDepth = depth && depth->type() == GL_TEXTURE;
// Skip the feedback loop check for stencil if depth/stencil point to the same resource.
const bool checkStencil =
(stencil && stencil->type() == GL_TEXTURE) && (!depth || *stencil != *depth);
const gl::ActiveTextureMask &activeTextures = program->getActiveSamplersMask();
const gl::ActiveTexturePointerArray &textures = state.getActiveTexturesCache();
for (size_t textureUnit : activeTextures)
{
Texture *texture = textures[textureUnit];
if (texture == nullptr)
{
continue;
}
// Depth and stencil attachment form feedback loops
// Regardless of if enabled or masked.
if (checkDepth)
{
if (texture->getId() == depth->id())
{
return true;
}
}
if (checkStencil)
{
if (texture->getId() == stencil->id())
{
return true;
}
}
// Check if any color attachment forms a feedback loop.
for (size_t drawIndex : mColorAttachmentBits)
{
const FramebufferAttachment &attachment = mState.mColorAttachments[drawIndex];
ASSERT(attachment.isAttached());
if (attachment.isTextureWithId(texture->id()))
{
// TODO(jmadill): Check for appropriate overlap.
return true;
}
}
}
return false;
} }
bool Framebuffer::formsCopyingFeedbackLoopWith(TextureID copyTextureID, bool Framebuffer::formsCopyingFeedbackLoopWith(TextureID copyTextureID,
......
...@@ -127,6 +127,7 @@ class FramebufferState final : angle::NonCopyable ...@@ -127,6 +127,7 @@ class FramebufferState final : angle::NonCopyable
const FramebufferAttachment *getWebGLDepthStencilAttachment() const; const FramebufferAttachment *getWebGLDepthStencilAttachment() const;
const FramebufferAttachment *getWebGLDepthAttachment() const; const FramebufferAttachment *getWebGLDepthAttachment() const;
const FramebufferAttachment *getWebGLStencilAttachment() const; const FramebufferAttachment *getWebGLStencilAttachment() const;
bool updateAttachmentFeedbackLoop(size_t dirtyBit);
friend class Framebuffer; friend class Framebuffer;
...@@ -155,6 +156,11 @@ class FramebufferState final : angle::NonCopyable ...@@ -155,6 +156,11 @@ class FramebufferState final : angle::NonCopyable
FramebufferAttachment mWebGLStencilAttachment; FramebufferAttachment mWebGLStencilAttachment;
bool mWebGLDepthStencilConsistent; bool mWebGLDepthStencilConsistent;
// Tracks rendering feedback loops.
DrawBufferMask mDrawBufferFeedbackLoops;
bool mDepthBufferFeedbackLoop;
bool mStencilBufferFeedbackLoop;
// Tracks if we need to initialize the resources for each attachment. // Tracks if we need to initialize the resources for each attachment.
angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit; angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit;
...@@ -470,7 +476,6 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -470,7 +476,6 @@ class Framebuffer final : public angle::ObserverInterface,
mutable DirtyBits mDirtyBits; mutable DirtyBits mDirtyBits;
DrawBufferMask mFloat32ColorAttachmentBits; DrawBufferMask mFloat32ColorAttachmentBits;
DrawBufferMask mColorAttachmentBits;
// The dirty bits guard is checked when we get a dependent state change message. We verify that // The dirty bits guard is checked when we get a dependent state change message. We verify that
// we don't set a dirty bit that isn't already set, when inside the dirty bits syncState. // we don't set a dirty bit that isn't already set, when inside the dirty bits syncState.
......
...@@ -281,6 +281,17 @@ void FramebufferAttachment::setInitState(InitState initState) const ...@@ -281,6 +281,17 @@ void FramebufferAttachment::setInitState(InitState initState) const
mResource->setInitState(mTarget.textureIndex(), initState); mResource->setInitState(mTarget.textureIndex(), initState);
} }
bool FramebufferAttachment::isBoundAsSamplerOrImage() const
{
if (mType != GL_TEXTURE)
{
return false;
}
const gl::TextureState &textureState = getTexture()->getTextureState();
return textureState.isBoundAsImageTexture() || textureState.isBoundAsSamplerTexture();
}
////// FramebufferAttachmentObject Implementation ////// ////// FramebufferAttachmentObject Implementation //////
FramebufferAttachmentObject::FramebufferAttachmentObject() {} FramebufferAttachmentObject::FramebufferAttachmentObject() {}
......
...@@ -92,6 +92,8 @@ class FramebufferAttachment final ...@@ -92,6 +92,8 @@ class FramebufferAttachment final
GLenum getComponentType() const; GLenum getComponentType() const;
GLenum getColorEncoding() const; GLenum getColorEncoding() const;
bool isBoundAsSamplerOrImage() const;
bool isTextureWithId(TextureID textureId) const bool isTextureWithId(TextureID textureId) const
{ {
return mType == GL_TEXTURE && id() == textureId.value; return mType == GL_TEXTURE && id() == textureId.value;
......
...@@ -33,7 +33,7 @@ using SubjectIndex = size_t; ...@@ -33,7 +33,7 @@ using SubjectIndex = size_t;
enum class SubjectMessage enum class SubjectMessage
{ {
// Used by gl::VertexArray to notify gl::Context of a gl::Buffer binding count change. Triggers // Used by gl::VertexArray to notify gl::Context of a gl::Buffer binding count change. Triggers
// a validation cache update. // a validation cache update. Also used by gl::Texture to notify gl::Framebuffer of loops.
BindingChanged, BindingChanged,
// Only the contents (pixels, bytes, etc) changed in this Subject. Distinct from the object // Only the contents (pixels, bytes, etc) changed in this Subject. Distinct from the object
......
...@@ -426,12 +426,23 @@ class Texture final : public RefCountObject<TextureID>, ...@@ -426,12 +426,23 @@ class Texture final : public RefCountObject<TextureID>,
mState.mImageBindingCount--; mState.mImageBindingCount--;
} }
ANGLE_INLINE void onBindAsSamplerTexture() { mState.mSamplerBindingCount++; } ANGLE_INLINE void onBindAsSamplerTexture()
{
mState.mSamplerBindingCount++;
if (mState.mSamplerBindingCount == 1)
{
onStateChange(angle::SubjectMessage::BindingChanged);
}
}
ANGLE_INLINE void onUnbindAsSamplerTexture() ANGLE_INLINE void onUnbindAsSamplerTexture()
{ {
ASSERT(mState.isBoundAsSamplerTexture()); ASSERT(mState.isBoundAsSamplerTexture());
mState.mSamplerBindingCount--; mState.mSamplerBindingCount--;
if (mState.mSamplerBindingCount == 0)
{
onStateChange(angle::SubjectMessage::BindingChanged);
}
} }
egl::Surface *getBoundSurface() const; egl::Surface *getBoundSurface() const;
......
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