r?jrmuizel - Detect feedback loops in CopyTex*Image.
draft
r?jrmuizel - Detect feedback loops in CopyTex*Image.
From 8de067a13046da0b45fd30b098034c28bcdf7aa8 Mon Sep 17 00:00:00 2001
---
dom/canvas/WebGLContext.h | 6 +++++
dom/canvas/WebGLContextDraw.cpp | 7 +-----
dom/canvas/WebGLFramebuffer.cpp | 18 ++++++++++---
dom/canvas/WebGLFramebuffer.h | 10 +++++++-
dom/canvas/WebGLTextureUpload.cpp | 53 +++++++++++++++++++++++++++++++++++++++
5 files changed, 83 insertions(+), 11 deletions(-)
MozReview-Commit-ID: 4L9dgY6s8g3
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -197,16 +197,22 @@ class WebGLContext
friend class WebGLExtensionCompressedTextureS3TC;
friend class WebGLExtensionDepthTexture;
friend class WebGLExtensionDisjointTimerQuery;
friend class WebGLExtensionDrawBuffers;
friend class WebGLExtensionLoseContext;
friend class WebGLExtensionVertexArray;
friend class WebGLMemoryTracker;
+ friend bool CheckForCopyTexImageFeedback(const char* funcName, WebGLContext* webgl,
+ const WebGLTexture* dstTex,
+ TexImageTarget dstTarget, GLint dstLevel,
+ const webgl::FormatInfo* dstFormat,
+ GLint dstZOffset);
+
enum {
UNPACK_FLIP_Y_WEBGL = 0x9240,
UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
CONTEXT_LOST_WEBGL = 0x9242,
UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
BROWSER_DEFAULT_WEBGL = 0x9244,
UNMASKED_VENDOR_WEBGL = 0x9245,
UNMASKED_RENDERER_WEBGL = 0x9246
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -194,28 +194,23 @@ WebGLContext::Draw_check(const char* fun
if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
return false;
} else {
ClearBackbufferIfNeeded();
}
// Check for sampling+render-to-texture (rtt) feedback loops.
if (mBoundDrawFramebuffer) {
- MOZ_ASSERT(mBoundDrawFramebuffer->mIsKnownFBComplete);
- const auto& rttSet = mBoundDrawFramebuffer->mCached_AttachedTextures;
-
- const auto rttSetEnd = rttSet.cend();
-
for (const auto& sampler : mActiveProgramLinkInfo->activeSamplerUniforms) {
MOZ_ASSERT(sampler->mTexArrayForUniformSampler);
const auto& texArrayForCurSampler = *(sampler->mTexArrayForUniformSampler);
for (const auto& texIndex : sampler->mUniformSamplerValue) {
const auto& tex = texArrayForCurSampler[texIndex];
- if (rttSet.find(tex.get()) != rttSetEnd) {
+ if (mBoundDrawFramebuffer->IsTextureAttached(tex.get())) {
ErrorInvalidOperation("%s: Feedback loop detected: A sampled texture"
" is also attached to draw framebuffer.",
funcName);
return false;
}
}
}
}
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -941,27 +941,26 @@ WebGLFramebuffer::CheckFramebufferStatus
// TODO: This should not be unconditionally GL_FRAMEBUFFER.
ret = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (ret == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
mIsKnownFBComplete = true;
// Cache anything that only invalidates on FB completeness changes.
- auto& attachedTextures = mCached_AttachedTextures;
- attachedTextures.clear();
- const auto fnAddIfTexture = [&attachedTextures](const WebGLFBAttachPoint& attach)
+ mCached_TextureAttachments.clear();
+ const auto fnAddIfTexture = [this](const WebGLFBAttachPoint& attach)
{
if (!attach.HasImage())
return;
if (!attach.Texture())
return;
- attachedTextures.insert(attach.Texture());
+ this->mCached_TextureAttachments.push_back(&attach);
};
fnAddIfTexture(mColorAttachment0);
fnAddIfTexture(mDepthAttachment);
fnAddIfTexture(mStencilAttachment);
fnAddIfTexture(mDepthStencilAttachment);
for (const auto& cur : mMoreColorAttachments) {
fnAddIfTexture(cur);
@@ -969,16 +968,27 @@ WebGLFramebuffer::CheckFramebufferStatus
} else {
out_info->AssignLiteral("Bad status according to the driver");
}
return ret;
}
bool
+WebGLFramebuffer::IsTextureAttached(const WebGLTexture* tex) const
+{
+ MOZ_ASSERT(mIsKnownFBComplete);
+ for (const auto& cur : mCached_TextureAttachments) {
+ if (cur->Texture() == tex)
+ return true;
+ }
+ return false;
+}
+
+bool
WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName)
{
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
nsCString fbStatusInfo;
const auto fbStatus = CheckFramebufferStatus(&fbStatusInfo);
if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -164,24 +164,30 @@ class WebGLFramebuffer final
: public nsWrapperCache
, public WebGLRefCountedObject<WebGLFramebuffer>
, public LinkedListElement<WebGLFramebuffer>
, public WebGLContextBoundObject
, public SupportsWeakPtr<WebGLFramebuffer>
{
friend class WebGLContext;
+ friend bool CheckForCopyTexImageFeedback(const char* funcName, WebGLContext* webgl,
+ const WebGLTexture* dstTex,
+ TexImageTarget dstTarget, GLint dstLevel,
+ const webgl::FormatInfo* dstFormat,
+ GLint dstZOffset);
+
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
const GLuint mGLName;
private:
mutable bool mIsKnownFBComplete;
- mutable std::set<const WebGLTexture*> mCached_AttachedTextures;
+ mutable std::vector<const WebGLFBAttachPoint*> mCached_TextureAttachments;
GLenum mReadBufferMode;
// No need to chase pointers for the oft-used color0.
WebGLFBAttachPoint mColorAttachment0;
WebGLFBAttachPoint mDepthAttachment;
WebGLFBAttachPoint mStencilAttachment;
WebGLFBAttachPoint mDepthStencilAttachment;
@@ -253,16 +259,18 @@ public:
const WebGLFBAttachPoint& DepthStencilAttachment() const {
return mDepthStencilAttachment;
}
void SetReadBufferMode(GLenum readBufferMode) {
mReadBufferMode = readBufferMode;
}
+ bool IsTextureAttached(const WebGLTexture* tex) const;
+
protected:
WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible
public:
void DetachTexture(const WebGLTexture* tex);
void DetachRenderbuffer(const WebGLRenderbuffer* rb);
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1687,16 +1687,56 @@ ScopedCopyTexImageSource::~ScopedCopyTex
ToGLHandle(mWebGL->mBoundReadFramebuffer));
gl->fDeleteFramebuffers(1, &mFB);
gl->fDeleteRenderbuffers(1, &mRB);
}
////////////////////////////////////////////////////////////////////////////////
+static bool
+CheckForCopyTexImageFeedback(const char* funcName, WebGLContext* webgl,
+ const WebGLTexture* dstTex, TexImageTarget dstTarget,
+ GLint dstLevel, const webgl::FormatInfo* dstFormat,
+ GLint dstZOffset)
+{
+ const auto& srcFB = webgl->mBoundReadFramebuffer;
+ if (!srcFB)
+ return true;
+
+ const WebGLFBAttachPoint* srcAttachPoint;
+ if (dstFormat->isColorFormat) {
+ srcAttachPoint = &(srcFB->mColorAttachment0);
+
+ } else if (dstFormat->hasDepth) {
+ MOZ_ASSERT(!srcFB->mDepthStencilAttachment.IsDefined());
+ srcAttachPoint = &(srcFB->mDepthAttachment);
+
+ } else if (dstFormat->hasStencil) {
+ MOZ_ASSERT(!srcFB->mDepthStencilAttachment.IsDefined());
+ srcAttachPoint = &(srcFB->mStencilAttachment);
+
+ } else {
+ MOZ_CRASH("Bad dstUsage.");
+ }
+
+ if (dstTex != srcAttachPoint->Texture() ||
+ dstTarget != srcAttachPoint->ImageTarget() ||
+ dstLevel != srcAttachPoint->MipLevel() ||
+ dstZOffset != srcAttachPoint->Layer())
+ {
+ return true;
+ }
+
+ webgl->ErrorInvalidOperation("%s: Destination texImage is attached to the read"
+ " framebuffer, creating a feedback loop.",
+ funcName);
+ return false;
+}
+
// There is no CopyTexImage3D.
void
WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
GLint x, GLint y, GLsizei width, GLsizei height,
GLint border)
{
const char funcName[] = "CopyTexImage2D";
@@ -1774,16 +1814,23 @@ WebGLTexture::CopyTexImage2D(TexImageTar
mContext->ErrorInvalidOperation("%s: Function may not be called with format %s.",
funcName, dstFormat->name);
return;
}
if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
return;
+ const GLint zOffset = 0;
+ if (!CheckForCopyTexImageFeedback(funcName, mContext, this, target, level, dstFormat,
+ zOffset))
+ {
+ return;
+ }
+
////////////////////////////////////
// Do the thing!
gl::GLContext* gl = mContext->gl;
gl->MakeCurrent();
ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
srcFormat, dstUsage);
@@ -1896,16 +1943,22 @@ WebGLTexture::CopyTexSubImage(const char
}
////////////////////////////////////
// Check that source and dest info are compatible
if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
return;
+ if (!CheckForCopyTexImageFeedback(funcName, mContext, this, target, level, dstFormat,
+ zOffset))
+ {
+ return;
+ }
+
////////////////////////////////////
// Do the thing!
mContext->gl->MakeCurrent();
ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
srcFormat, dstUsage);