Commit c9c4e4ed by Jamie Madill Committed by Commit Bot

Track rendering feedback loops by-context.

This fixes an issue where feedback loops detection would trigger false positives based on texture use in multiple contexts. 1) there are two contexts, C1 and C2, sharing resources 2) in C1, there is a texture T bound to GL_TEXTURE_2D, and a program in use that will sample C1 3) in C2, a framebuffer is created and T is bound to it This fix indexes each set of active bindings in an object by ContextID. We can potentially redo this solution in the future if this proves to have too much tracking overhead. Includes a test writen by Ken Russell. Bug: angleproject:4517 Change-Id: I67012e68947c42d863dca193972576c82d5f3712 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2134406 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarKenneth Russell <kbr@chromium.org>
parent 96c26c68
...@@ -977,7 +977,7 @@ void Context::bindTexture(TextureType target, TextureID handle) ...@@ -977,7 +977,7 @@ void Context::bindTexture(TextureType target, TextureID handle)
void Context::bindReadFramebuffer(FramebufferID framebufferHandle) void Context::bindReadFramebuffer(FramebufferID framebufferHandle)
{ {
Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation( Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation(
mImplementation.get(), mState.mCaps, framebufferHandle); mImplementation.get(), mState.mCaps, framebufferHandle, mState.getContextID());
mState.setReadFramebufferBinding(framebuffer); mState.setReadFramebufferBinding(framebuffer);
mReadFramebufferObserverBinding.bind(framebuffer); mReadFramebufferObserverBinding.bind(framebuffer);
} }
...@@ -985,7 +985,7 @@ void Context::bindReadFramebuffer(FramebufferID framebufferHandle) ...@@ -985,7 +985,7 @@ void Context::bindReadFramebuffer(FramebufferID framebufferHandle)
void Context::bindDrawFramebuffer(FramebufferID framebufferHandle) void Context::bindDrawFramebuffer(FramebufferID framebufferHandle)
{ {
Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation( Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation(
mImplementation.get(), mState.mCaps, framebufferHandle); mImplementation.get(), mState.mCaps, framebufferHandle, mState.getContextID());
mState.setDrawFramebufferBinding(framebuffer); mState.setDrawFramebufferBinding(framebuffer);
mDrawFramebufferObserverBinding.bind(framebuffer); mDrawFramebufferObserverBinding.bind(framebuffer);
mStateCache.onDrawFramebufferChange(this); mStateCache.onDrawFramebufferChange(this);
......
...@@ -303,8 +303,9 @@ bool IsClearBufferMaskedOut(const Context *context, GLenum buffer, GLint drawbuf ...@@ -303,8 +303,9 @@ bool IsClearBufferMaskedOut(const Context *context, GLenum buffer, GLint drawbuf
} // anonymous namespace } // anonymous namespace
// This constructor is only used for default framebuffers. // This constructor is only used for default framebuffers.
FramebufferState::FramebufferState() FramebufferState::FramebufferState(ContextID owningContextID)
: mId(Framebuffer::kDefaultDrawFramebufferHandle), : mId(Framebuffer::kDefaultDrawFramebufferHandle),
mOwningContextID(owningContextID),
mLabel(), mLabel(),
mColorAttachments(1), mColorAttachments(1),
mDrawBufferStates(1, GL_BACK), mDrawBufferStates(1, GL_BACK),
...@@ -325,8 +326,9 @@ FramebufferState::FramebufferState() ...@@ -325,8 +326,9 @@ FramebufferState::FramebufferState()
mEnabledDrawBuffers.set(0); mEnabledDrawBuffers.set(0);
} }
FramebufferState::FramebufferState(const Caps &caps, FramebufferID id) FramebufferState::FramebufferState(const Caps &caps, FramebufferID id, ContextID owningContextID)
: mId(id), : mId(id),
mOwningContextID(owningContextID),
mLabel(), mLabel(),
mColorAttachments(caps.maxColorAttachments), mColorAttachments(caps.maxColorAttachments),
mDrawBufferStates(caps.maxDrawBuffers, GL_NONE), mDrawBufferStates(caps.maxDrawBuffers, GL_NONE),
...@@ -701,13 +703,13 @@ bool FramebufferState::updateAttachmentFeedbackLoopAndReturnIfChanged(size_t dir ...@@ -701,13 +703,13 @@ bool FramebufferState::updateAttachmentFeedbackLoopAndReturnIfChanged(size_t dir
{ {
case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
previous = mDepthBufferFeedbackLoop; previous = mDepthBufferFeedbackLoop;
loop = mDepthAttachment.isBoundAsSamplerOrImage(); loop = mDepthAttachment.isBoundAsSamplerOrImage(mOwningContextID);
mDepthBufferFeedbackLoop = loop; mDepthBufferFeedbackLoop = loop;
break; break;
case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
previous = mStencilBufferFeedbackLoop; previous = mStencilBufferFeedbackLoop;
loop = mStencilAttachment.isBoundAsSamplerOrImage(); loop = mStencilAttachment.isBoundAsSamplerOrImage(mOwningContextID);
mStencilBufferFeedbackLoop = loop; mStencilBufferFeedbackLoop = loop;
break; break;
...@@ -715,7 +717,7 @@ bool FramebufferState::updateAttachmentFeedbackLoopAndReturnIfChanged(size_t dir ...@@ -715,7 +717,7 @@ bool FramebufferState::updateAttachmentFeedbackLoopAndReturnIfChanged(size_t dir
{ {
ASSERT(dirtyBit <= Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX); ASSERT(dirtyBit <= Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
previous = mDrawBufferFeedbackLoops.test(dirtyBit); previous = mDrawBufferFeedbackLoops.test(dirtyBit);
loop = mColorAttachments[dirtyBit].isBoundAsSamplerOrImage(); loop = mColorAttachments[dirtyBit].isBoundAsSamplerOrImage(mOwningContextID);
mDrawBufferFeedbackLoops[dirtyBit] = loop; mDrawBufferFeedbackLoops[dirtyBit] = loop;
break; break;
} }
...@@ -741,9 +743,12 @@ void FramebufferState::updateHasRenderingFeedbackLoop() ...@@ -741,9 +743,12 @@ void FramebufferState::updateHasRenderingFeedbackLoop()
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,
ContextID owningContextID)
: mSerial(factory->generateSerial()), : mSerial(factory->generateSerial()),
mState(caps, id), mState(caps, id, owningContextID),
mImpl(factory->createFramebuffer(mState)), mImpl(factory->createFramebuffer(mState)),
mCachedStatus(), mCachedStatus(),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT), mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
...@@ -762,7 +767,7 @@ Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, Framebuff ...@@ -762,7 +767,7 @@ Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, Framebuff
Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface) Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface)
: mSerial(context->getImplementation()->generateSerial()), : mSerial(context->getImplementation()->generateSerial()),
mState(), mState(context->id()),
mImpl(surface->getImplementation()->createDefaultFramebuffer(context, mState)), mImpl(surface->getImplementation()->createDefaultFramebuffer(context, mState)),
mCachedStatus(GL_FRAMEBUFFER_COMPLETE), mCachedStatus(GL_FRAMEBUFFER_COMPLETE),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT), mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
...@@ -802,7 +807,7 @@ Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Sur ...@@ -802,7 +807,7 @@ Framebuffer::Framebuffer(const Context *context, egl::Surface *surface, egl::Sur
Framebuffer::Framebuffer(const Context *context, Framebuffer::Framebuffer(const Context *context,
rx::GLImplFactory *factory, rx::GLImplFactory *factory,
egl::Surface *readSurface) egl::Surface *readSurface)
: mState(), : mState(context->id()),
mImpl(factory->createFramebuffer(mState)), mImpl(factory->createFramebuffer(mState)),
mCachedStatus(GL_FRAMEBUFFER_UNDEFINED_OES), mCachedStatus(GL_FRAMEBUFFER_UNDEFINED_OES),
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT), mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
......
...@@ -61,8 +61,8 @@ enum class AttachmentSampleType ...@@ -61,8 +61,8 @@ enum class AttachmentSampleType
class FramebufferState final : angle::NonCopyable class FramebufferState final : angle::NonCopyable
{ {
public: public:
FramebufferState(); explicit FramebufferState(ContextID owningContextID);
explicit FramebufferState(const Caps &caps, FramebufferID id); FramebufferState(const Caps &caps, FramebufferID id, ContextID owningContextID);
~FramebufferState(); ~FramebufferState();
const std::string &getLabel(); const std::string &getLabel();
...@@ -141,6 +141,7 @@ class FramebufferState final : angle::NonCopyable ...@@ -141,6 +141,7 @@ class FramebufferState final : angle::NonCopyable
friend class Framebuffer; friend class Framebuffer;
FramebufferID mId; FramebufferID mId;
ContextID mOwningContextID;
std::string mLabel; std::string mLabel;
std::vector<FramebufferAttachment> mColorAttachments; std::vector<FramebufferAttachment> mColorAttachments;
...@@ -184,7 +185,10 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -184,7 +185,10 @@ class Framebuffer final : public angle::ObserverInterface,
{ {
public: public:
// Constructor to build application-defined framebuffers // Constructor to build application-defined framebuffers
Framebuffer(const Caps &caps, rx::GLImplFactory *factory, FramebufferID id); Framebuffer(const Caps &caps,
rx::GLImplFactory *factory,
FramebufferID id,
ContextID owningContextID);
// Constructor to build default framebuffers for a surface and context pair // Constructor to build default framebuffers for a surface and context pair
Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface); Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface);
// Constructor to build a fake default framebuffer when surfaceless // Constructor to build a fake default framebuffer when surfaceless
......
...@@ -281,7 +281,7 @@ void FramebufferAttachment::setInitState(InitState initState) const ...@@ -281,7 +281,7 @@ void FramebufferAttachment::setInitState(InitState initState) const
mResource->setInitState(mTarget.textureIndex(), initState); mResource->setInitState(mTarget.textureIndex(), initState);
} }
bool FramebufferAttachment::isBoundAsSamplerOrImage() const bool FramebufferAttachment::isBoundAsSamplerOrImage(ContextID contextID) const
{ {
if (mType != GL_TEXTURE) if (mType != GL_TEXTURE)
{ {
...@@ -289,7 +289,8 @@ bool FramebufferAttachment::isBoundAsSamplerOrImage() const ...@@ -289,7 +289,8 @@ bool FramebufferAttachment::isBoundAsSamplerOrImage() const
} }
const gl::TextureState &textureState = getTexture()->getTextureState(); const gl::TextureState &textureState = getTexture()->getTextureState();
return textureState.isBoundAsImageTexture() || textureState.isBoundAsSamplerTexture(); return textureState.isBoundAsImageTexture(contextID) ||
textureState.isBoundAsSamplerTexture(contextID);
} }
////// FramebufferAttachmentObject Implementation ////// ////// FramebufferAttachmentObject Implementation //////
......
...@@ -92,7 +92,7 @@ class FramebufferAttachment final ...@@ -92,7 +92,7 @@ class FramebufferAttachment final
GLenum getComponentType() const; GLenum getComponentType() const;
GLenum getColorEncoding() const; GLenum getColorEncoding() const;
bool isBoundAsSamplerOrImage() const; bool isBoundAsSamplerOrImage(ContextID contextID) const;
bool isTextureWithId(TextureID textureId) const bool isTextureWithId(TextureID textureId) const
{ {
......
...@@ -350,11 +350,12 @@ Sync *SyncManager::getSync(GLuint handle) const ...@@ -350,11 +350,12 @@ Sync *SyncManager::getSync(GLuint handle) const
// static // static
Framebuffer *FramebufferManager::AllocateNewObject(rx::GLImplFactory *factory, Framebuffer *FramebufferManager::AllocateNewObject(rx::GLImplFactory *factory,
FramebufferID handle, FramebufferID handle,
const Caps &caps) const Caps &caps,
ContextID owningContextID)
{ {
// Make sure the caller isn't using a reserved handle. // Make sure the caller isn't using a reserved handle.
ASSERT(handle != Framebuffer::kDefaultDrawFramebufferHandle); ASSERT(handle != Framebuffer::kDefaultDrawFramebufferHandle);
return new Framebuffer(caps, factory, handle); return new Framebuffer(caps, factory, handle, owningContextID);
} }
// static // static
......
...@@ -275,14 +275,16 @@ class FramebufferManager ...@@ -275,14 +275,16 @@ class FramebufferManager
Framebuffer *checkFramebufferAllocation(rx::GLImplFactory *factory, Framebuffer *checkFramebufferAllocation(rx::GLImplFactory *factory,
const Caps &caps, const Caps &caps,
FramebufferID handle) FramebufferID handle,
ContextID owningContextID)
{ {
return checkObjectAllocation<const Caps &>(factory, handle, caps); return checkObjectAllocation<const Caps &>(factory, handle, caps, owningContextID);
} }
static Framebuffer *AllocateNewObject(rx::GLImplFactory *factory, static Framebuffer *AllocateNewObject(rx::GLImplFactory *factory,
FramebufferID handle, FramebufferID handle,
const Caps &caps); const Caps &caps,
ContextID owningContextID);
static void DeleteObject(const Context *context, Framebuffer *framebuffer); static void DeleteObject(const Context *context, Framebuffer *framebuffer);
protected: protected:
......
...@@ -260,11 +260,11 @@ ActiveTexturesCache::~ActiveTexturesCache() ...@@ -260,11 +260,11 @@ ActiveTexturesCache::~ActiveTexturesCache()
ASSERT(empty()); ASSERT(empty());
} }
void ActiveTexturesCache::clear() void ActiveTexturesCache::clear(ContextID contextID)
{ {
for (size_t textureIndex = 0; textureIndex < mTextures.size(); ++textureIndex) for (size_t textureIndex = 0; textureIndex < mTextures.size(); ++textureIndex)
{ {
reset(textureIndex); reset(contextID, textureIndex);
} }
} }
...@@ -281,25 +281,27 @@ bool ActiveTexturesCache::empty() const ...@@ -281,25 +281,27 @@ bool ActiveTexturesCache::empty() const
return true; return true;
} }
ANGLE_INLINE void ActiveTexturesCache::reset(size_t textureIndex) ANGLE_INLINE void ActiveTexturesCache::reset(ContextID contextID, size_t textureIndex)
{ {
if (mTextures[textureIndex]) if (mTextures[textureIndex])
{ {
mTextures[textureIndex]->onUnbindAsSamplerTexture(); mTextures[textureIndex]->onUnbindAsSamplerTexture(contextID);
mTextures[textureIndex] = nullptr; mTextures[textureIndex] = nullptr;
} }
} }
ANGLE_INLINE void ActiveTexturesCache::set(size_t textureIndex, Texture *texture) ANGLE_INLINE void ActiveTexturesCache::set(ContextID contextID,
size_t textureIndex,
Texture *texture)
{ {
// We don't call reset() here to avoid setting nullptr before rebind. // We don't call reset() here to avoid setting nullptr before rebind.
if (mTextures[textureIndex]) if (mTextures[textureIndex])
{ {
mTextures[textureIndex]->onUnbindAsSamplerTexture(); mTextures[textureIndex]->onUnbindAsSamplerTexture(contextID);
} }
ASSERT(texture); ASSERT(texture);
texture->onBindAsSamplerTexture(); texture->onBindAsSamplerTexture(contextID);
mTextures[textureIndex] = texture; mTextures[textureIndex] = texture;
} }
...@@ -516,6 +518,8 @@ void State::initialize(Context *context) ...@@ -516,6 +518,8 @@ void State::initialize(Context *context)
void State::reset(const Context *context) void State::reset(const Context *context)
{ {
mActiveTexturesCache.clear(mID);
for (auto &bindingVec : mSamplerTextures) for (auto &bindingVec : mSamplerTextures)
{ {
for (size_t textureIdx = 0; textureIdx < bindingVec.size(); textureIdx++) for (size_t textureIdx = 0; textureIdx < bindingVec.size(); textureIdx++)
...@@ -577,8 +581,6 @@ void State::reset(const Context *context) ...@@ -577,8 +581,6 @@ void State::reset(const Context *context)
UpdateIndexedBufferBinding(context, &buf, nullptr, BufferBinding::ShaderStorage, 0, 0); UpdateIndexedBufferBinding(context, &buf, nullptr, BufferBinding::ShaderStorage, 0, 0);
} }
mActiveTexturesCache.clear();
setAllDirtyBits(); setAllDirtyBits();
} }
...@@ -587,7 +589,7 @@ ANGLE_INLINE void State::unsetActiveTextures(ActiveTextureMask textureMask) ...@@ -587,7 +589,7 @@ ANGLE_INLINE void State::unsetActiveTextures(ActiveTextureMask textureMask)
// Unset any relevant bound textures. // Unset any relevant bound textures.
for (size_t textureIndex : mExecutable->getActiveSamplersMask()) for (size_t textureIndex : mExecutable->getActiveSamplersMask())
{ {
mActiveTexturesCache.reset(textureIndex); mActiveTexturesCache.reset(mID, textureIndex);
mCompleteTextureBindings[textureIndex].reset(); mCompleteTextureBindings[textureIndex].reset();
} }
} }
...@@ -599,11 +601,11 @@ ANGLE_INLINE void State::updateActiveTextureState(const Context *context, ...@@ -599,11 +601,11 @@ ANGLE_INLINE void State::updateActiveTextureState(const Context *context,
{ {
if (!texture->isSamplerComplete(context, sampler)) if (!texture->isSamplerComplete(context, sampler))
{ {
mActiveTexturesCache.reset(textureIndex); mActiveTexturesCache.reset(mID, textureIndex);
} }
else else
{ {
mActiveTexturesCache.set(textureIndex, texture); mActiveTexturesCache.set(mID, textureIndex, texture);
if (texture->hasAnyDirtyBit()) if (texture->hasAnyDirtyBit())
{ {
...@@ -642,7 +644,7 @@ ANGLE_INLINE void State::updateActiveTexture(const Context *context, ...@@ -642,7 +644,7 @@ ANGLE_INLINE void State::updateActiveTexture(const Context *context,
if (!texture) if (!texture)
{ {
mActiveTexturesCache.reset(textureIndex); mActiveTexturesCache.reset(mID, textureIndex);
mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS);
return; return;
} }
...@@ -3155,11 +3157,11 @@ void State::setImageUnit(const Context *context, ...@@ -3155,11 +3157,11 @@ void State::setImageUnit(const Context *context,
if (imageUnit.texture.get()) if (imageUnit.texture.get())
{ {
imageUnit.texture->onUnbindAsImageTexture(); imageUnit.texture->onUnbindAsImageTexture(mID);
} }
if (texture) if (texture)
{ {
texture->onBindAsImageTexture(); texture->onBindAsImageTexture(mID);
} }
imageUnit.texture.set(context, texture); imageUnit.texture.set(context, texture);
imageUnit.level = level; imageUnit.level = level;
......
...@@ -72,9 +72,9 @@ class ActiveTexturesCache final : angle::NonCopyable ...@@ -72,9 +72,9 @@ class ActiveTexturesCache final : angle::NonCopyable
Texture *operator[](size_t textureIndex) const { return mTextures[textureIndex]; } Texture *operator[](size_t textureIndex) const { return mTextures[textureIndex]; }
void clear(); void clear(ContextID contextID);
void set(size_t textureIndex, Texture *texture); void set(ContextID contextID, size_t textureIndex, Texture *texture);
void reset(size_t textureIndex); void reset(ContextID contextID, size_t textureIndex);
bool empty() const; bool empty() const;
private: private:
......
...@@ -100,8 +100,6 @@ TextureState::TextureState(TextureType type) ...@@ -100,8 +100,6 @@ TextureState::TextureState(TextureType type)
mBaseLevel(0), mBaseLevel(0),
mMaxLevel(1000), mMaxLevel(1000),
mDepthStencilTextureMode(GL_DEPTH_COMPONENT), mDepthStencilTextureMode(GL_DEPTH_COMPONENT),
mSamplerBindingCount(0),
mImageBindingCount(0),
mImmutableFormat(false), mImmutableFormat(false),
mImmutableLevels(0), mImmutableLevels(0),
mUsage(GL_NONE), mUsage(GL_NONE),
...@@ -1953,13 +1951,16 @@ angle::Result Texture::getTexImage(const Context *context, ...@@ -1953,13 +1951,16 @@ angle::Result Texture::getTexImage(const Context *context,
pixels); pixels);
} }
void Texture::onBindAsImageTexture() void Texture::onBindAsImageTexture(ContextID contextID)
{ {
if (!mState.isBoundAsImageTexture()) ContextBindingCount &bindingCount = mState.getBindingCount(contextID);
ASSERT(bindingCount.imageBindingCount < std::numeric_limits<uint32_t>::max());
mState.getBindingCount(contextID).imageBindingCount++;
if (bindingCount.imageBindingCount == 1)
{ {
mDirtyBits.set(DIRTY_BIT_BOUND_AS_IMAGE); mDirtyBits.set(DIRTY_BIT_BOUND_AS_IMAGE);
} }
mState.mImageBindingCount++;
} }
} // namespace gl } // namespace gl
...@@ -92,6 +92,13 @@ struct SwizzleState final ...@@ -92,6 +92,13 @@ struct SwizzleState final
GLenum swizzleAlpha; GLenum swizzleAlpha;
}; };
struct ContextBindingCount
{
ContextID contextID;
uint32_t samplerBindingCount;
uint32_t imageBindingCount;
};
// State from Table 6.9 (state per texture object) in the OpenGL ES 3.0.2 spec. // State from Table 6.9 (state per texture object) in the OpenGL ES 3.0.2 spec.
class TextureState final : private angle::NonCopyable class TextureState final : private angle::NonCopyable
{ {
...@@ -135,8 +142,14 @@ class TextureState final : private angle::NonCopyable ...@@ -135,8 +142,14 @@ class TextureState final : private angle::NonCopyable
GLenum getUsage() const { return mUsage; } GLenum getUsage() const { return mUsage; }
GLenum getDepthStencilTextureMode() const { return mDepthStencilTextureMode; } GLenum getDepthStencilTextureMode() const { return mDepthStencilTextureMode; }
bool isStencilMode() const { return mDepthStencilTextureMode == GL_STENCIL_INDEX; } bool isStencilMode() const { return mDepthStencilTextureMode == GL_STENCIL_INDEX; }
bool isBoundAsSamplerTexture() const { return mSamplerBindingCount > 0; } bool isBoundAsSamplerTexture(ContextID contextID) const
bool isBoundAsImageTexture() const { return mImageBindingCount > 0; } {
return getBindingCount(contextID).samplerBindingCount > 0;
}
bool isBoundAsImageTexture(ContextID contextID) const
{
return getBindingCount(contextID).imageBindingCount > 0;
}
// Returns the desc of the base level. Only valid for cube-complete/mip-complete textures. // Returns the desc of the base level. Only valid for cube-complete/mip-complete textures.
const ImageDesc &getBaseLevelDesc() const; const ImageDesc &getBaseLevelDesc() const;
...@@ -181,6 +194,22 @@ class TextureState final : private angle::NonCopyable ...@@ -181,6 +194,22 @@ class TextureState final : private angle::NonCopyable
void clearImageDesc(TextureTarget target, size_t level); void clearImageDesc(TextureTarget target, size_t level);
void clearImageDescs(); void clearImageDescs();
ContextBindingCount &getBindingCount(ContextID contextID)
{
for (ContextBindingCount &bindingCount : mBindingCounts)
{
if (bindingCount.contextID == contextID)
return bindingCount;
}
mBindingCounts.push_back({contextID, 0, 0});
return mBindingCounts.back();
}
const ContextBindingCount &getBindingCount(ContextID contextID) const
{
return const_cast<TextureState *>(this)->getBindingCount(contextID);
}
const TextureType mType; const TextureType mType;
SwizzleState mSwizzleState; SwizzleState mSwizzleState;
...@@ -192,8 +221,8 @@ class TextureState final : private angle::NonCopyable ...@@ -192,8 +221,8 @@ class TextureState final : private angle::NonCopyable
GLenum mDepthStencilTextureMode; GLenum mDepthStencilTextureMode;
uint32_t mSamplerBindingCount; std::vector<ContextBindingCount> mBindingCounts;
uint32_t mImageBindingCount;
bool mImmutableFormat; bool mImmutableFormat;
GLuint mImmutableLevels; GLuint mImmutableLevels;
...@@ -419,29 +448,33 @@ class Texture final : public RefCountObject<TextureID>, ...@@ -419,29 +448,33 @@ class Texture final : public RefCountObject<TextureID>,
angle::Result generateMipmap(Context *context); angle::Result generateMipmap(Context *context);
void onBindAsImageTexture(); void onBindAsImageTexture(ContextID contextID);
ANGLE_INLINE void onUnbindAsImageTexture() ANGLE_INLINE void onUnbindAsImageTexture(ContextID contextID)
{ {
ASSERT(mState.isBoundAsImageTexture()); ASSERT(mState.isBoundAsImageTexture(contextID));
mState.mImageBindingCount--; mState.getBindingCount(contextID).imageBindingCount--;
} }
ANGLE_INLINE void onBindAsSamplerTexture() ANGLE_INLINE void onBindAsSamplerTexture(ContextID contextID)
{ {
ASSERT(mState.mSamplerBindingCount < std::numeric_limits<uint32_t>::max()); ContextBindingCount &bindingCount = mState.getBindingCount(contextID);
mState.mSamplerBindingCount++;
if (mState.mSamplerBindingCount == 1) ASSERT(bindingCount.samplerBindingCount < std::numeric_limits<uint32_t>::max());
bindingCount.samplerBindingCount++;
if (bindingCount.samplerBindingCount == 1)
{ {
onStateChange(angle::SubjectMessage::BindingChanged); onStateChange(angle::SubjectMessage::BindingChanged);
} }
} }
ANGLE_INLINE void onUnbindAsSamplerTexture() ANGLE_INLINE void onUnbindAsSamplerTexture(ContextID contextID)
{ {
ASSERT(mState.isBoundAsSamplerTexture()); ContextBindingCount &bindingCount = mState.getBindingCount(contextID);
mState.mSamplerBindingCount--;
if (mState.mSamplerBindingCount == 0) ASSERT(mState.isBoundAsSamplerTexture(contextID));
bindingCount.samplerBindingCount--;
if (bindingCount.samplerBindingCount == 0)
{ {
onStateChange(angle::SubjectMessage::BindingChanged); onStateChange(angle::SubjectMessage::BindingChanged);
} }
......
...@@ -20,7 +20,7 @@ namespace rx ...@@ -20,7 +20,7 @@ namespace rx
class MockFramebufferImpl : public rx::FramebufferImpl class MockFramebufferImpl : public rx::FramebufferImpl
{ {
public: public:
MockFramebufferImpl() : rx::FramebufferImpl(gl::FramebufferState()) {} MockFramebufferImpl() : rx::FramebufferImpl(gl::FramebufferState(1)) {}
virtual ~MockFramebufferImpl() { destructor(); } virtual ~MockFramebufferImpl() { destructor(); }
MOCK_METHOD3(discard, angle::Result(const gl::Context *, size_t, const GLenum *)); MOCK_METHOD3(discard, angle::Result(const gl::Context *, size_t, const GLenum *));
......
...@@ -1252,7 +1252,7 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl( ...@@ -1252,7 +1252,7 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(
// Select the appropriate vk::ImageLayout depending on whether the texture is also bound as // Select the appropriate vk::ImageLayout depending on whether the texture is also bound as
// a GL image, and whether the program is a compute or graphics shader. // a GL image, and whether the program is a compute or graphics shader.
vk::ImageLayout textureLayout; vk::ImageLayout textureLayout;
if (textureVk->isBoundAsImageTexture()) if (textureVk->isBoundAsImageTexture(mState.getContextID()))
{ {
textureLayout = executable->isCompute() ? vk::ImageLayout::ComputeShaderWrite textureLayout = executable->isCompute() ? vk::ImageLayout::ComputeShaderWrite
: vk::ImageLayout::AllGraphicsShadersReadWrite; : vk::ImageLayout::AllGraphicsShadersReadWrite;
......
...@@ -227,7 +227,10 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -227,7 +227,10 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
GLenum type, GLenum type,
void *pixels) override; void *pixels) override;
ANGLE_INLINE bool isBoundAsImageTexture() const { return mState.isBoundAsImageTexture(); } ANGLE_INLINE bool isBoundAsImageTexture(gl::ContextID contextID) const
{
return mState.isBoundAsImageTexture(contextID);
}
private: private:
// Transform an image index from the frontend into one that can be used on the backing // Transform an image index from the frontend into one that can be used on the backing
......
...@@ -1369,7 +1369,125 @@ TEST_P(FramebufferTest_ES3, DisabledAttachmentRedefinition) ...@@ -1369,7 +1369,125 @@ TEST_P(FramebufferTest_ES3, DisabledAttachmentRedefinition)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
} }
class FramebufferTest : public ANGLETest
{};
template <typename T>
void FillTexture2D(GLuint texture,
GLsizei width,
GLsizei height,
const T &onePixelData,
GLint level,
GLint internalFormat,
GLenum format,
GLenum type)
{
std::vector<T> allPixelsData(width * height, onePixelData);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, 0, format, type,
allPixelsData.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
// Multi-context uses of textures should not cause rendering feedback loops.
TEST_P(FramebufferTest, MultiContextNoRenderingFeedbackLoops)
{
constexpr char kTextureVS[] =
R"(attribute vec4 a_position;
varying vec2 v_texCoord;
void main() {
gl_Position = a_position;
v_texCoord = (a_position.xy * 0.5) + 0.5;
})";
constexpr char kTextureFS[] =
R"(precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_texCoord).rgba;
})";
ANGLE_GL_PROGRAM(textureProgram, kTextureVS, kTextureFS);
glUseProgram(textureProgram.get());
GLint uniformLoc = glGetUniformLocation(textureProgram.get(), "u_texture");
ASSERT_NE(-1, uniformLoc);
glUniform1i(uniformLoc, 0);
GLTexture texture;
FillTexture2D(texture.get(), 1, 1, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
glBindTexture(GL_TEXTURE_2D, texture.get());
// Note that _texture_ is still bound to GL_TEXTURE_2D in this context at this point.
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
EGLConfig config = window->getConfig();
EGLSurface surface = window->getSurface();
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR,
GetParam().majorVersion,
EGL_CONTEXT_MINOR_VERSION_KHR,
GetParam().minorVersion,
EGL_NONE,
};
EGLContext context1 = eglGetCurrentContext();
// Create context2, sharing resources with context1.
EGLContext context2 = eglCreateContext(display, config, context1, contextAttributes);
ASSERT_NE(context2, EGL_NO_CONTEXT);
eglMakeCurrent(display, surface, surface, context2);
constexpr char kVS[] =
R"(attribute vec4 a_position;
void main() {
gl_Position = a_position;
})";
constexpr char kFS[] =
R"(precision mediump float;
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program.get());
ASSERT_GL_NO_ERROR();
// Render to the texture in context2.
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
// Texture is still a valid name in context2.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// There is no rendering feedback loop at this point.
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
ASSERT_GL_NO_ERROR();
// If draw is no-op'ed, texture will not be filled appropriately.
drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Make context1 current again.
eglMakeCurrent(display, surface, surface, context1);
// Render texture to screen.
drawQuad(textureProgram.get(), "a_position", 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
eglDestroyContext(display, context2);
}
ANGLE_INSTANTIATE_TEST_ES2(AddDummyTextureNoRenderTargetTest); ANGLE_INSTANTIATE_TEST_ES2(AddDummyTextureNoRenderTargetTest);
ANGLE_INSTANTIATE_TEST_ES2(FramebufferTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(FramebufferFormatsTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(FramebufferFormatsTest);
ANGLE_INSTANTIATE_TEST_ES3(FramebufferTest_ES3); ANGLE_INSTANTIATE_TEST_ES3(FramebufferTest_ES3);
ANGLE_INSTANTIATE_TEST_ES31(FramebufferTest_ES31); ANGLE_INSTANTIATE_TEST_ES31(FramebufferTest_ES31);
...@@ -2370,6 +2370,93 @@ void main() { ...@@ -2370,6 +2370,93 @@ void main() {
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
} }
// Multi-context uses of textures should not cause rendering feedback loops.
TEST_P(WebGLCompatibilityTest, MultiContextNoRenderingFeedbackLoops)
{
constexpr char kUnusedTextureVS[] =
R"(attribute vec4 a_position;
varying vec2 v_texCoord;
void main() {
gl_Position = a_position;
v_texCoord = (a_position.xy * 0.5) + 0.5;
})";
constexpr char kUnusedTextureFS[] =
R"(precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_texCoord).rgba;
})";
ANGLE_GL_PROGRAM(unusedProgram, kUnusedTextureVS, kUnusedTextureFS);
glUseProgram(unusedProgram.get());
GLint uniformLoc = glGetUniformLocation(unusedProgram.get(), "u_texture");
ASSERT_NE(-1, uniformLoc);
glUniform1i(uniformLoc, 0);
GLTexture texture;
FillTexture2D(texture.get(), 1, 1, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
glBindTexture(GL_TEXTURE_2D, texture.get());
// Note that _texture_ is still bound to GL_TEXTURE_2D in this context at this point.
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
EGLConfig config = window->getConfig();
EGLSurface surface = window->getSurface();
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR,
GetParam().majorVersion,
EGL_CONTEXT_MINOR_VERSION_KHR,
GetParam().minorVersion,
EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE,
EGL_TRUE,
EGL_NONE,
};
auto context1 = eglGetCurrentContext();
// Create context2, sharing resources with context1.
auto context2 = eglCreateContext(display, config, context1, contextAttributes);
ASSERT_NE(context2, EGL_NO_CONTEXT);
eglMakeCurrent(display, surface, surface, context2);
constexpr char kVS[] =
R"(attribute vec4 a_position;
void main() {
gl_Position = a_position;
})";
constexpr char kFS[] =
R"(precision mediump float;
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program.get());
ASSERT_GL_NO_ERROR();
// Render to the texture in context2.
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
// Texture is still a valid name in context2.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// There is no rendering feedback loop at this point.
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
ASSERT_GL_NO_ERROR();
drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
eglMakeCurrent(display, surface, surface, context1);
eglDestroyContext(display, context2);
}
// Test for the max draw buffers and color attachments. // Test for the max draw buffers and color attachments.
TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints) TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
{ {
......
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