Commit 185d9d08 by Jamie Madill Committed by Commit Bot

Re-land "Feedback Loop Redesign 2/3: Track bound FBOs in Texture."

Re-land fixes the crash when drawing with no bound Program executable. Currently we track feedback loops by counting the times a Texture is bound as a sampler or image in a particular context. This is a bit tricky because Texture bindings change frequently. Relative to the number of times we need to check for a feedback loop this causes excess overhead. Usually Framebuffers have a low number of Textures bound (in many cases just 1). And Textures aren't usually bound to many different FBOs. So instead of counting the number of times a Texture is bound as a sampler or image we will track the Framebuffers that the Texture is bound to. This CL adds a small vector class to gl::Texture which tracks all the Framebufer Serials of its bound Framebuffers. We can use this set to quickly check if there's any potential feedback loop between the a FBO and this Texture. We also update the feedback loop check to use this new method. We will be able to remove the old counting method when we switch the Vulkan feedback loop handling to use the new tracking in this CL. Bug: angleproject:4500 Bug: angleproject:4959 Change-Id: If2bd25b08298a99f5e64b4055137f9154b0f0860 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2365595Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent a20f5b17
...@@ -60,7 +60,7 @@ def _CheckCommitMessageFormatting(input_api, output_api): ...@@ -60,7 +60,7 @@ def _CheckCommitMessageFormatting(input_api, output_api):
def _CheckTabInCommit(lines): def _CheckTabInCommit(lines):
return all([line.find("\t") == -1 for line in lines]) return all([line.find("\t") == -1 for line in lines])
whitelist_strings = ['Revert "', 'Roll ', 'Reland '] whitelist_strings = ['Revert "', 'Roll ', 'Reland ', 'Re-land ']
summary_linelength_warning_lower_limit = 65 summary_linelength_warning_lower_limit = 65
summary_linelength_warning_upper_limit = 70 summary_linelength_warning_upper_limit = 70
description_linelength_limit = 72 description_linelength_limit = 72
......
...@@ -796,19 +796,19 @@ void Framebuffer::onDestroy(const Context *context) ...@@ -796,19 +796,19 @@ void Framebuffer::onDestroy(const Context *context)
{ {
if (isDefault()) if (isDefault())
{ {
mState.mDefaultFramebufferReadAttachment.detach(context); mState.mDefaultFramebufferReadAttachment.detach(context, mState.mFramebufferSerial);
mState.mDefaultFramebufferReadAttachmentInitialized = false; mState.mDefaultFramebufferReadAttachmentInitialized = false;
} }
for (auto &attachment : mState.mColorAttachments) for (auto &attachment : mState.mColorAttachments)
{ {
attachment.detach(context); attachment.detach(context, mState.mFramebufferSerial);
} }
mState.mDepthAttachment.detach(context); mState.mDepthAttachment.detach(context, mState.mFramebufferSerial);
mState.mStencilAttachment.detach(context); mState.mStencilAttachment.detach(context, mState.mFramebufferSerial);
mState.mWebGLDepthAttachment.detach(context); mState.mWebGLDepthAttachment.detach(context, mState.mFramebufferSerial);
mState.mWebGLStencilAttachment.detach(context); mState.mWebGLStencilAttachment.detach(context, mState.mFramebufferSerial);
mState.mWebGLDepthStencilAttachment.detach(context); mState.mWebGLDepthStencilAttachment.detach(context, mState.mFramebufferSerial);
mImpl->destroy(context); mImpl->destroy(context);
} }
...@@ -819,7 +819,7 @@ void Framebuffer::setReadSurface(const Context *context, egl::Surface *readSurfa ...@@ -819,7 +819,7 @@ void Framebuffer::setReadSurface(const Context *context, egl::Surface *readSurfa
mState.mDefaultFramebufferReadAttachment.attach( mState.mDefaultFramebufferReadAttachment.attach(
context, GL_FRAMEBUFFER_DEFAULT, GL_BACK, ImageIndex(), readSurface, context, GL_FRAMEBUFFER_DEFAULT, GL_BACK, ImageIndex(), readSurface,
FramebufferAttachment::kDefaultNumViews, FramebufferAttachment::kDefaultBaseViewIndex, FramebufferAttachment::kDefaultNumViews, FramebufferAttachment::kDefaultBaseViewIndex,
false, FramebufferAttachment::kDefaultRenderToTextureSamples); false, FramebufferAttachment::kDefaultRenderToTextureSamples, mState.mFramebufferSerial);
mDirtyBits.set(DIRTY_BIT_READ_BUFFER); mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
} }
...@@ -1674,19 +1674,21 @@ void Framebuffer::setAttachment(const Context *context, ...@@ -1674,19 +1674,21 @@ void Framebuffer::setAttachment(const Context *context,
{ {
case GL_DEPTH_STENCIL: case GL_DEPTH_STENCIL:
case GL_DEPTH_STENCIL_ATTACHMENT: case GL_DEPTH_STENCIL_ATTACHMENT:
mState.mWebGLDepthStencilAttachment.attach(context, type, binding, textureIndex, mState.mWebGLDepthStencilAttachment.attach(
resource, numViews, baseViewIndex, context, type, binding, textureIndex, resource, numViews, baseViewIndex,
isMultiview, samples); isMultiview, samples, mState.mFramebufferSerial);
break; break;
case GL_DEPTH: case GL_DEPTH:
case GL_DEPTH_ATTACHMENT: case GL_DEPTH_ATTACHMENT:
mState.mWebGLDepthAttachment.attach(context, type, binding, textureIndex, resource, mState.mWebGLDepthAttachment.attach(context, type, binding, textureIndex, resource,
numViews, baseViewIndex, isMultiview, samples); numViews, baseViewIndex, isMultiview, samples,
mState.mFramebufferSerial);
break; break;
case GL_STENCIL: case GL_STENCIL:
case GL_STENCIL_ATTACHMENT: case GL_STENCIL_ATTACHMENT:
mState.mWebGLStencilAttachment.attach(context, type, binding, textureIndex, resource, mState.mWebGLStencilAttachment.attach(context, type, binding, textureIndex, resource,
numViews, baseViewIndex, isMultiview, samples); numViews, baseViewIndex, isMultiview, samples,
mState.mFramebufferSerial);
break; break;
default: default:
setAttachmentImpl(context, type, binding, textureIndex, resource, numViews, setAttachmentImpl(context, type, binding, textureIndex, resource, numViews,
...@@ -1869,7 +1871,7 @@ void Framebuffer::updateAttachment(const Context *context, ...@@ -1869,7 +1871,7 @@ void Framebuffer::updateAttachment(const Context *context,
GLsizei samples) GLsizei samples)
{ {
attachment->attach(context, type, binding, textureIndex, resource, numViews, baseViewIndex, attachment->attach(context, type, binding, textureIndex, resource, numViews, baseViewIndex,
isMultiview, samples); isMultiview, samples, mState.mFramebufferSerial);
mDirtyBits.set(dirtyBit); mDirtyBits.set(dirtyBit);
mState.mResourceNeedsInit.set(dirtyBit, attachment->initState() == InitState::MayNeedInit); mState.mResourceNeedsInit.set(dirtyBit, attachment->initState() == InitState::MayNeedInit);
onDirtyBinding->bind(resource); onDirtyBinding->bind(resource);
...@@ -1967,6 +1969,34 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject ...@@ -1967,6 +1969,34 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject
} }
} }
bool Framebuffer::formsRenderingFeedbackLoopWith(const Context *context) const
{
const State &glState = context->getState();
const ProgramExecutable *executable = glState.getProgramExecutable();
// In some error cases there may be no bound program or executable.
if (!executable)
return false;
const ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
const ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes();
for (size_t textureIndex : activeTextures)
{
unsigned int uintIndex = static_cast<unsigned int>(textureIndex);
Texture *texture = glState.getSamplerTexture(uintIndex, textureTypes[textureIndex]);
const Sampler *sampler = glState.getSampler(uintIndex);
if (texture && texture->isSamplerComplete(context, sampler) &&
texture->isBoundToFramebuffer(mState.mFramebufferSerial))
{
// TODO(jmadill): Subresource check. http://anglebug.com/4500
return true;
}
}
return false;
}
bool Framebuffer::formsCopyingFeedbackLoopWith(TextureID copyTextureID, bool Framebuffer::formsCopyingFeedbackLoopWith(TextureID copyTextureID,
GLint copyTextureLevel, GLint copyTextureLevel,
GLint copyTextureLayer) const GLint copyTextureLayer) const
......
...@@ -171,6 +171,7 @@ class FramebufferState final : angle::NonCopyable ...@@ -171,6 +171,7 @@ class FramebufferState final : angle::NonCopyable
bool mWebGLDepthStencilConsistent; bool mWebGLDepthStencilConsistent;
// Tracks rendering feedback loops. // Tracks rendering feedback loops.
// TODO(jmadill): Remove. http://anglebug.com/4500
DrawBufferMask mDrawBufferFeedbackLoops; DrawBufferMask mDrawBufferFeedbackLoops;
bool mDepthBufferFeedbackLoop; bool mDepthBufferFeedbackLoop;
bool mStencilBufferFeedbackLoop; bool mStencilBufferFeedbackLoop;
...@@ -409,7 +410,10 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -409,7 +410,10 @@ class Framebuffer final : public angle::ObserverInterface,
// Observer implementation // Observer implementation
void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override; void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;
// TODO(jmadill): Remove. http://anglebug.com/4500
bool hasRenderingFeedbackLoop() const { return mState.mHasRenderingFeedbackLoop; } bool hasRenderingFeedbackLoop() const { return mState.mHasRenderingFeedbackLoop; }
bool formsRenderingFeedbackLoopWith(const Context *context) const;
bool formsCopyingFeedbackLoopWith(TextureID copyTextureID, bool formsCopyingFeedbackLoopWith(TextureID copyTextureID,
GLint copyTextureLevel, GLint copyTextureLevel,
GLint copyTextureLayer) const; GLint copyTextureLayer) const;
......
...@@ -60,11 +60,12 @@ FramebufferAttachment::FramebufferAttachment(const Context *context, ...@@ -60,11 +60,12 @@ FramebufferAttachment::FramebufferAttachment(const Context *context,
GLenum type, GLenum type,
GLenum binding, GLenum binding,
const ImageIndex &textureIndex, const ImageIndex &textureIndex,
FramebufferAttachmentObject *resource) FramebufferAttachmentObject *resource,
rx::Serial framebufferSerial)
: mResource(nullptr) : mResource(nullptr)
{ {
attach(context, type, binding, textureIndex, resource, kDefaultNumViews, kDefaultBaseViewIndex, attach(context, type, binding, textureIndex, resource, kDefaultNumViews, kDefaultBaseViewIndex,
false, kDefaultRenderToTextureSamples); false, kDefaultRenderToTextureSamples, framebufferSerial);
} }
FramebufferAttachment::FramebufferAttachment(FramebufferAttachment &&other) FramebufferAttachment::FramebufferAttachment(FramebufferAttachment &&other)
...@@ -90,12 +91,12 @@ FramebufferAttachment::~FramebufferAttachment() ...@@ -90,12 +91,12 @@ FramebufferAttachment::~FramebufferAttachment()
ASSERT(!isAttached()); ASSERT(!isAttached());
} }
void FramebufferAttachment::detach(const Context *context) void FramebufferAttachment::detach(const Context *context, rx::Serial framebufferSerial)
{ {
mType = GL_NONE; mType = GL_NONE;
if (mResource != nullptr) if (mResource != nullptr)
{ {
mResource->onDetach(context); mResource->onDetach(context, framebufferSerial);
mResource = nullptr; mResource = nullptr;
} }
mNumViews = kDefaultNumViews; mNumViews = kDefaultNumViews;
...@@ -114,11 +115,12 @@ void FramebufferAttachment::attach(const Context *context, ...@@ -114,11 +115,12 @@ void FramebufferAttachment::attach(const Context *context,
GLsizei numViews, GLsizei numViews,
GLuint baseViewIndex, GLuint baseViewIndex,
bool isMultiview, bool isMultiview,
GLsizei samples) GLsizei samples,
rx::Serial framebufferSerial)
{ {
if (resource == nullptr) if (resource == nullptr)
{ {
detach(context); detach(context, framebufferSerial);
return; return;
} }
...@@ -128,11 +130,11 @@ void FramebufferAttachment::attach(const Context *context, ...@@ -128,11 +130,11 @@ void FramebufferAttachment::attach(const Context *context,
mBaseViewIndex = baseViewIndex; mBaseViewIndex = baseViewIndex;
mIsMultiview = isMultiview; mIsMultiview = isMultiview;
mRenderToTextureSamples = samples; mRenderToTextureSamples = samples;
resource->onAttach(context); resource->onAttach(context, framebufferSerial);
if (mResource != nullptr) if (mResource != nullptr)
{ {
mResource->onDetach(context); mResource->onDetach(context, framebufferSerial);
} }
mResource = resource; mResource = resource;
......
...@@ -64,14 +64,15 @@ class FramebufferAttachment final ...@@ -64,14 +64,15 @@ class FramebufferAttachment final
GLenum type, GLenum type,
GLenum binding, GLenum binding,
const ImageIndex &textureIndex, const ImageIndex &textureIndex,
FramebufferAttachmentObject *resource); FramebufferAttachmentObject *resource,
rx::Serial framebufferSerial);
FramebufferAttachment(FramebufferAttachment &&other); FramebufferAttachment(FramebufferAttachment &&other);
FramebufferAttachment &operator=(FramebufferAttachment &&other); FramebufferAttachment &operator=(FramebufferAttachment &&other);
~FramebufferAttachment(); ~FramebufferAttachment();
void detach(const Context *context); void detach(const Context *context, rx::Serial framebufferSerial);
void attach(const Context *context, void attach(const Context *context,
GLenum type, GLenum type,
GLenum binding, GLenum binding,
...@@ -80,7 +81,8 @@ class FramebufferAttachment final ...@@ -80,7 +81,8 @@ class FramebufferAttachment final
GLsizei numViews, GLsizei numViews,
GLuint baseViewIndex, GLuint baseViewIndex,
bool isMultiview, bool isMultiview,
GLsizei samples); GLsizei samples,
rx::Serial framebufferSerial);
// Helper methods // Helper methods
GLuint getRedSize() const; GLuint getRedSize() const;
...@@ -208,9 +210,9 @@ class FramebufferAttachmentObject : public angle::Subject, public angle::Observe ...@@ -208,9 +210,9 @@ class FramebufferAttachmentObject : public angle::Subject, public angle::Observe
GLenum binding, GLenum binding,
const ImageIndex &imageIndex) const = 0; const ImageIndex &imageIndex) const = 0;
virtual void onAttach(const Context *context) = 0; virtual void onAttach(const Context *context, rx::Serial framebufferSerial) = 0;
virtual void onDetach(const Context *context) = 0; virtual void onDetach(const Context *context, rx::Serial framebufferSerial) = 0;
virtual GLuint getId() const = 0; virtual GLuint getId() const = 0;
// These are used for robust resource initialization. // These are used for robust resource initialization.
virtual InitState initState(const ImageIndex &imageIndex) const = 0; virtual InitState initState(const ImageIndex &imageIndex) const = 0;
......
...@@ -193,9 +193,9 @@ bool ExternalImageSibling::isTextureable(const gl::Context *context) const ...@@ -193,9 +193,9 @@ bool ExternalImageSibling::isTextureable(const gl::Context *context) const
return mImplementation->isTexturable(context); return mImplementation->isTexturable(context);
} }
void ExternalImageSibling::onAttach(const gl::Context *context) {} void ExternalImageSibling::onAttach(const gl::Context *context, rx::Serial framebufferSerial) {}
void ExternalImageSibling::onDetach(const gl::Context *context) {} void ExternalImageSibling::onDetach(const gl::Context *context, rx::Serial framebufferSerial) {}
GLuint ExternalImageSibling::getId() const GLuint ExternalImageSibling::getId() const
{ {
......
...@@ -98,8 +98,8 @@ class ExternalImageSibling : public ImageSibling ...@@ -98,8 +98,8 @@ class ExternalImageSibling : public ImageSibling
const gl::ImageIndex &imageIndex) const override; const gl::ImageIndex &imageIndex) const override;
bool isTextureable(const gl::Context *context) const; bool isTextureable(const gl::Context *context) const;
void onAttach(const gl::Context *context) override; void onAttach(const gl::Context *context, rx::Serial framebufferSerial) override;
void onDetach(const gl::Context *context) override; void onDetach(const gl::Context *context, rx::Serial framebufferSerial) override;
GLuint getId() const override; GLuint getId() const override;
gl::InitState initState(const gl::ImageIndex &imageIndex) const override; gl::InitState initState(const gl::ImageIndex &imageIndex) const override;
......
...@@ -234,12 +234,12 @@ GLint Renderbuffer::getMemorySize() const ...@@ -234,12 +234,12 @@ GLint Renderbuffer::getMemorySize() const
return size.ValueOrDefault(std::numeric_limits<GLint>::max()); return size.ValueOrDefault(std::numeric_limits<GLint>::max());
} }
void Renderbuffer::onAttach(const Context *context) void Renderbuffer::onAttach(const Context *context, rx::Serial framebufferSerial)
{ {
addRef(); addRef();
} }
void Renderbuffer::onDetach(const Context *context) void Renderbuffer::onDetach(const Context *context, rx::Serial framebufferSerial)
{ {
release(context); release(context);
} }
......
...@@ -110,8 +110,8 @@ class Renderbuffer final : public RefCountObject<RenderbufferID>, ...@@ -110,8 +110,8 @@ class Renderbuffer final : public RefCountObject<RenderbufferID>,
GLenum binding, GLenum binding,
const ImageIndex &imageIndex) const override; const ImageIndex &imageIndex) const override;
void onAttach(const Context *context) override; void onAttach(const Context *context, rx::Serial framebufferSerial) override;
void onDetach(const Context *context) override; void onDetach(const Context *context, rx::Serial framebufferSerial) override;
GLuint getId() const override; GLuint getId() const override;
InitState initState(const ImageIndex &imageIndex) const override; InitState initState(const ImageIndex &imageIndex) const override;
......
...@@ -143,8 +143,8 @@ class Surface : public LabeledObject, public gl::FramebufferAttachmentObject ...@@ -143,8 +143,8 @@ class Surface : public LabeledObject, public gl::FramebufferAttachmentObject
GLenum binding, GLenum binding,
const gl::ImageIndex &imageIndex) const override; const gl::ImageIndex &imageIndex) const override;
void onAttach(const gl::Context *context) override {} void onAttach(const gl::Context *context, rx::Serial framebufferSerial) override {}
void onDetach(const gl::Context *context) override {} void onDetach(const gl::Context *context, rx::Serial framebufferSerial) override {}
GLuint getId() const override; GLuint getId() const override;
bool flexibleSurfaceCompatibilityRequested() const bool flexibleSurfaceCompatibilityRequested() const
......
...@@ -1774,13 +1774,20 @@ GLenum Texture::getGenerateMipmapHint() const ...@@ -1774,13 +1774,20 @@ GLenum Texture::getGenerateMipmapHint() const
return mState.getGenerateMipmapHint(); return mState.getGenerateMipmapHint();
} }
void Texture::onAttach(const Context *context) void Texture::onAttach(const Context *context, rx::Serial framebufferSerial)
{ {
addRef(); addRef();
// Duplicates allowed for multiple attachment points. See the comment in the header.
mBoundFramebufferSerials.push_back(framebufferSerial);
} }
void Texture::onDetach(const Context *context) void Texture::onDetach(const Context *context, rx::Serial framebufferSerial)
{ {
// Erase first instance. If there are multiple bindings, leave the others.
ASSERT(isBoundToFramebuffer(framebufferSerial));
mBoundFramebufferSerials.remove_and_permute(framebufferSerial);
release(context); release(context);
} }
......
...@@ -146,6 +146,8 @@ class TextureState final : private angle::NonCopyable ...@@ -146,6 +146,8 @@ 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; }
// TODO(jmadill): Remove. http://anglebug.com/4500
bool isBoundAsSamplerTexture(ContextID contextID) const bool isBoundAsSamplerTexture(ContextID contextID) const
{ {
return getBindingCount(contextID).samplerBindingCount > 0; return getBindingCount(contextID).samplerBindingCount > 0;
...@@ -545,8 +547,8 @@ class Texture final : public RefCountObject<TextureID>, ...@@ -545,8 +547,8 @@ class Texture final : public RefCountObject<TextureID>,
void setGenerateMipmapHint(GLenum generate); void setGenerateMipmapHint(GLenum generate);
GLenum getGenerateMipmapHint() const; GLenum getGenerateMipmapHint() const;
void onAttach(const Context *context) override; void onAttach(const Context *context, rx::Serial framebufferSerial) override;
void onDetach(const Context *context) override; void onDetach(const Context *context, rx::Serial framebufferSerial) override;
// Used specifically for FramebufferAttachmentObject. // Used specifically for FramebufferAttachmentObject.
GLuint getId() const override; GLuint getId() const override;
...@@ -559,6 +561,17 @@ class Texture final : public RefCountObject<TextureID>, ...@@ -559,6 +561,17 @@ class Texture final : public RefCountObject<TextureID>,
InitState initState() const { return mState.mInitState; } InitState initState() const { return mState.mInitState; }
void setInitState(const ImageIndex &imageIndex, InitState initState) override; void setInitState(const ImageIndex &imageIndex, InitState initState) override;
bool isBoundToFramebuffer(rx::Serial framebufferSerial) const
{
for (size_t index = 0; index < mBoundFramebufferSerials.size(); ++index)
{
if (mBoundFramebufferSerials[index] == framebufferSerial)
return true;
}
return false;
}
enum DirtyBitType enum DirtyBitType
{ {
// Sampler state // Sampler state
...@@ -643,6 +656,14 @@ class Texture final : public RefCountObject<TextureID>, ...@@ -643,6 +656,14 @@ class Texture final : public RefCountObject<TextureID>,
egl::Surface *mBoundSurface; egl::Surface *mBoundSurface;
egl::Stream *mBoundStream; egl::Stream *mBoundStream;
// We track all the serials of the Framebuffers this texture is attached to. Note that this
// allows duplicates because different ranges of a Texture can be bound to the same Framebuffer.
// For the purposes of depth-stencil loops, a simple "isBound" check works fine. For color
// attachment Feedback Loop checks we then need to check further to see when a Texture is bound
// to mulitple bindings that the bindings don't overlap.
static constexpr uint32_t kFastFramebufferSerialCount = 8;
angle::FastVector<rx::Serial, kFastFramebufferSerialCount> mBoundFramebufferSerials;
struct SamplerCompletenessCache struct SamplerCompletenessCache
{ {
SamplerCompletenessCache(); SamplerCompletenessCache();
......
...@@ -362,19 +362,18 @@ const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(const gl: ...@@ -362,19 +362,18 @@ const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(const gl:
// it to be attached to a new binding point. // it to be attached to a new binding point.
if (mDummyAttachment.isAttached()) if (mDummyAttachment.isAttached())
{ {
mDummyAttachment.detach(context); mDummyAttachment.detach(context, Serial());
} }
gl::Texture *dummyTex = nullptr; gl::Texture *dummyTex = nullptr;
// TODO(Jamie): Handle error if dummy texture can't be created. // TODO(jmadill): Handle error if dummy texture can't be created.
(void)mRenderer->getIncompleteTexture(context, gl::TextureType::_2D, &dummyTex); (void)mRenderer->getIncompleteTexture(context, gl::TextureType::_2D, &dummyTex);
if (dummyTex) if (dummyTex)
{ {
gl::ImageIndex index = gl::ImageIndex::Make2D(0); gl::ImageIndex index = gl::ImageIndex::Make2D(0);
mDummyAttachment = gl::FramebufferAttachment( mDummyAttachment = gl::FramebufferAttachment(
context, GL_TEXTURE, GL_COLOR_ATTACHMENT0_EXT + activeProgramLocation, index, context, GL_TEXTURE, GL_COLOR_ATTACHMENT0_EXT + activeProgramLocation, index,
dummyTex); dummyTex, Serial());
colorAttachmentsForRender.push_back(&mDummyAttachment); colorAttachmentsForRender.push_back(&mDummyAttachment);
} }
} }
...@@ -390,7 +389,7 @@ void FramebufferD3D::destroy(const gl::Context *context) ...@@ -390,7 +389,7 @@ void FramebufferD3D::destroy(const gl::Context *context)
{ {
if (mDummyAttachment.isAttached()) if (mDummyAttachment.isAttached())
{ {
mDummyAttachment.detach(context); mDummyAttachment.detach(context, Serial());
} }
} }
......
...@@ -3005,7 +3005,7 @@ const char *ValidateDrawStates(const Context *context) ...@@ -3005,7 +3005,7 @@ const char *ValidateDrawStates(const Context *context)
} }
// Detect rendering feedback loops for WebGL. // Detect rendering feedback loops for WebGL.
if (framebuffer->hasRenderingFeedbackLoop()) if (framebuffer->formsRenderingFeedbackLoopWith(context))
{ {
return kFeedbackLoop; return kFeedbackLoop;
} }
......
...@@ -5070,6 +5070,13 @@ TEST_P(WebGL2CompatibilityTest, UniformVariablesReturnTypes) ...@@ -5070,6 +5070,13 @@ TEST_P(WebGL2CompatibilityTest, UniformVariablesReturnTypes)
EXPECT_GL_ERROR(GL_INVALID_ENUM); EXPECT_GL_ERROR(GL_INVALID_ENUM);
} }
// Tests an error case to ensure we don't crash.
TEST_P(WebGLCompatibilityTest, DrawWithNoProgram)
{
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGLCompatibilityTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGLCompatibilityTest);
......
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