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
default:
if (index < kTextureMaxSubjectIndex)
{
if (message != angle::SubjectMessage::ContentsChanged)
if (message != angle::SubjectMessage::ContentsChanged &&
message != angle::SubjectMessage::BindingChanged)
{
mState.onActiveTextureStateChange(this, index);
mStateCache.onActiveTextureChange(this);
......
......@@ -316,6 +316,8 @@ FramebufferState::FramebufferState()
mDefaultFixedSampleLocations(GL_FALSE),
mDefaultLayers(0),
mWebGLDepthStencilConsistent(true),
mDepthBufferFeedbackLoop(false),
mStencilBufferFeedbackLoop(false),
mDefaultFramebufferReadAttachmentInitialized(false)
{
ASSERT(mDrawBufferStates.size() > 0);
......@@ -335,6 +337,8 @@ FramebufferState::FramebufferState(const Caps &caps, FramebufferID id)
mDefaultFixedSampleLocations(GL_FALSE),
mDefaultLayers(0),
mWebGLDepthStencilConsistent(true),
mDepthBufferFeedbackLoop(false),
mStencilBufferFeedbackLoop(false),
mDefaultFramebufferReadAttachmentInitialized(false)
{
ASSERT(mId != Framebuffer::kDefaultDrawFramebufferHandle);
......@@ -673,6 +677,26 @@ bool FramebufferState::isDefault() const
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};
Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, FramebufferID id)
......@@ -1859,18 +1883,14 @@ void Framebuffer::setAttachmentImpl(const Context *context,
if (!resource)
{
mColorAttachmentBits.reset(colorIndex);
mFloat32ColorAttachmentBits.reset(colorIndex);
}
else
{
mColorAttachmentBits.set(colorIndex);
updateFloat32ColorAttachmentBits(
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);
mState.mEnabledDrawBuffers.set(colorIndex, enabled);
SetComponentTypeMask(getDrawbufferWriteType(colorIndex), colorIndex,
......@@ -1899,6 +1919,7 @@ void Framebuffer::updateAttachment(const Context *context,
mState.mResourceNeedsInit.set(dirtyBit, attachment->initState() == InitState::MayNeedInit);
onDirtyBinding->bind(resource);
mState.updateAttachmentFeedbackLoop(dirtyBit);
invalidateCompletenessCache();
}
......@@ -1931,6 +1952,17 @@ void Framebuffer::onSubjectStateChange(angle::SubjectIndex index, angle::Subject
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.
ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged);
return;
......@@ -1973,68 +2005,16 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject
bool Framebuffer::formsRenderingFeedbackLoopWith(const Context *context) const
{
const State &state = context->getState();
const Program *program = state.getProgram();
// TODO(jmadill): Default framebuffer feedback loops.
// We don't handle tricky cases where the default FBO is bound as a sampler.
// We also don't handle tricky cases with EGLImages and mipmap selection.
// TODO(http://anglebug.com/4500): Tricky rendering feedback loop cases.
if (mState.isDefault())
{
return false;
}
const FramebufferAttachment *depth = getDepthAttachment();
const FramebufferAttachment *stencil = getStencilAttachment();
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;
return mState.mDrawBufferFeedbackLoops.any() || mState.mDepthBufferFeedbackLoop ||
mState.mStencilBufferFeedbackLoop;
}
bool Framebuffer::formsCopyingFeedbackLoopWith(TextureID copyTextureID,
......
......@@ -127,6 +127,7 @@ class FramebufferState final : angle::NonCopyable
const FramebufferAttachment *getWebGLDepthStencilAttachment() const;
const FramebufferAttachment *getWebGLDepthAttachment() const;
const FramebufferAttachment *getWebGLStencilAttachment() const;
bool updateAttachmentFeedbackLoop(size_t dirtyBit);
friend class Framebuffer;
......@@ -155,6 +156,11 @@ class FramebufferState final : angle::NonCopyable
FramebufferAttachment mWebGLStencilAttachment;
bool mWebGLDepthStencilConsistent;
// Tracks rendering feedback loops.
DrawBufferMask mDrawBufferFeedbackLoops;
bool mDepthBufferFeedbackLoop;
bool mStencilBufferFeedbackLoop;
// Tracks if we need to initialize the resources for each attachment.
angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit;
......@@ -470,7 +476,6 @@ class Framebuffer final : public angle::ObserverInterface,
mutable DirtyBits mDirtyBits;
DrawBufferMask mFloat32ColorAttachmentBits;
DrawBufferMask mColorAttachmentBits;
// 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.
......
......@@ -281,6 +281,17 @@ void FramebufferAttachment::setInitState(InitState initState) const
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::FramebufferAttachmentObject() {}
......
......@@ -92,6 +92,8 @@ class FramebufferAttachment final
GLenum getComponentType() const;
GLenum getColorEncoding() const;
bool isBoundAsSamplerOrImage() const;
bool isTextureWithId(TextureID textureId) const
{
return mType == GL_TEXTURE && id() == textureId.value;
......
......@@ -33,7 +33,7 @@ using SubjectIndex = size_t;
enum class SubjectMessage
{
// 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,
// Only the contents (pixels, bytes, etc) changed in this Subject. Distinct from the object
......
......@@ -426,12 +426,23 @@ class Texture final : public RefCountObject<TextureID>,
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()
{
ASSERT(mState.isBoundAsSamplerTexture());
mState.mSamplerBindingCount--;
if (mState.mSamplerBindingCount == 0)
{
onStateChange(angle::SubjectMessage::BindingChanged);
}
}
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