Bug 1136494 - Add WebGLTexture::IsFeedback. - r=mtseng
MozReview-Commit-ID: 9kyomMFgDmS
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -37,58 +37,109 @@ class ScopedResolveTexturesForDraw
std::vector<TexRebindRequest> mRebindRequests;
public:
ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
bool* const out_error);
~ScopedResolveTexturesForDraw();
};
+bool
+WebGLTexture::IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+ const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const
+{
+ auto itr = fbAttachments.cbegin();
+ for (; itr != fbAttachments.cend(); ++itr) {
+ const auto& attach = *itr;
+ if (attach->Texture() == this)
+ break;
+ }
+
+ if (itr == fbAttachments.cend())
+ return false;
+
+ ////
+
+ const auto minLevel = mBaseMipmapLevel;
+ uint32_t maxLevel;
+ if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel)) {
+ // No valid mips. Will need fake-black.
+ return false;
+ }
+
+ ////
+
+ for (; itr != fbAttachments.cend(); ++itr) {
+ const auto& attach = *itr;
+ if (attach->Texture() != this)
+ continue;
+
+ const auto dstLevel = attach->MipLevel();
+
+ if (minLevel <= dstLevel && dstLevel <= maxLevel) {
+ webgl->ErrorInvalidOperation("%s: Feedback loop detected between tex target"
+ " 0x%04x, tex unit %u, levels %u-%u; and"
+ " framebuffer attachment 0x%04x, level %u.",
+ funcName, mTarget.get(), texUnit, minLevel,
+ maxLevel, attach->mAttachmentPoint, dstLevel);
+ return true;
+ }
+ }
+
+ return false;
+}
+
ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
const char* funcName,
bool* const out_error)
: mWebGL(webgl)
{
- MOZ_ASSERT(webgl->gl->IsCurrent());
+ MOZ_ASSERT(mWebGL->gl->IsCurrent());
- typedef decltype(WebGLContext::mBound2DTextures) TexturesT;
+ std::vector<const WebGLFBAttachPoint*> fbAttachments;
+ if (mWebGL->mBoundDrawFramebuffer) {
+ const auto& fb = mWebGL->mBoundDrawFramebuffer;
+ fb->GatherAttachments(&fbAttachments);
+ }
- const auto fnResolveAll = [this, funcName](const TexturesT& textures)
- {
- const auto len = textures.Length();
- for (uint32_t texUnit = 0; texUnit < len; ++texUnit) {
- WebGLTexture* tex = textures[texUnit];
+ MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo);
+ const auto& uniformSamplers = mWebGL->mActiveProgramLinkInfo->uniformSamplers;
+ for (const auto& uniform : uniformSamplers) {
+ const auto& texList = *(uniform->mSamplerTexList);
+
+ for (const auto& texUnit : uniform->mSamplerValues) {
+ if (texUnit >= texList.Length())
+ continue;
+
+ const auto& tex = texList[texUnit];
if (!tex)
continue;
+ if (tex->IsFeedback(mWebGL, funcName, texUnit, fbAttachments)) {
+ *out_error = true;
+ return;
+ }
+
FakeBlackType fakeBlack;
- if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack))
- return false;
+ if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) {
+ mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.",
+ funcName);
+ *out_error = true;
+ return;
+ }
if (fakeBlack == FakeBlackType::None)
continue;
mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack);
mRebindRequests.push_back({texUnit, tex});
}
-
- return true;
- };
-
- bool ok = true;
- ok &= fnResolveAll(mWebGL->mBound2DTextures);
- ok &= fnResolveAll(mWebGL->mBoundCubeMapTextures);
- ok &= fnResolveAll(mWebGL->mBound3DTextures);
- ok &= fnResolveAll(mWebGL->mBound2DArrayTextures);
-
- if (!ok) {
- mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName);
}
- *out_error = !ok;
+ *out_error = false;
}
ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw()
{
if (!mRebindRequests.size())
return;
gl::GLContext* gl = mWebGL->gl;
@@ -598,18 +649,18 @@ WebGLContext::ValidateBufferFetching(con
return false;
}
++i;
}
mBufferFetch_IsAttrib0Active = false;
- for (const auto& pair : mActiveProgramLinkInfo->activeAttribLocs) {
- const uint32_t attribLoc = pair.second;
+ for (const auto& attrib : mActiveProgramLinkInfo->attribs) {
+ const auto& attribLoc = attrib.mLoc;
if (attribLoc >= attribCount)
continue;
if (attribLoc == 0) {
mBufferFetch_IsAttrib0Active = true;
}
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1276,16 +1276,44 @@ WebGLFramebuffer::GetAttachmentParameter
}
FinalizeAttachments();
return attachPoint->GetParameter(funcName, mContext, cx, target, attachment, pname,
out_error);
}
+
+void
+WebGLFramebuffer::GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const
+{
+ auto itr = mDrawBuffers.cbegin();
+ if (itr != mDrawBuffers.cend() &&
+ *itr != LOCAL_GL_NONE)
+ {
+ out->push_back(&mColorAttachment0);
+ ++itr;
+ }
+
+ size_t i = 0;
+ for (; itr != mDrawBuffers.cend(); ++itr) {
+ if (i >= mMoreColorAttachments.Size())
+ break;
+
+ if (*itr != LOCAL_GL_NONE) {
+ out->push_back(&mMoreColorAttachments[i]);
+ }
+ ++i;
+ }
+
+ out->push_back(&mDepthAttachment);
+ out->push_back(&mStencilAttachment);
+ out->push_back(&mDepthStencilAttachment);
+}
+
////////////////////////////////////////////////////////////////////////////////
// Goop.
JSObject*
WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
{
return dom::WebGLFramebufferBinding::Wrap(cx, this, givenProto);
}
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -33,17 +33,17 @@ class WebGLFBAttachPoint
public:
WebGLFramebuffer* const mFB;
const GLenum mAttachmentPoint;
private:
WebGLRefPtr<WebGLTexture> mTexturePtr;
WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
TexImageTarget mTexImageTarget;
GLint mTexImageLayer;
- GLint mTexImageLevel;
+ uint32_t mTexImageLevel;
// PlacementArray needs a default constructor.
template<typename T>
friend class PlacementArray;
WebGLFBAttachPoint()
: mFB(nullptr)
, mAttachmentPoint(0)
@@ -84,17 +84,17 @@ public:
return mRenderbufferPtr;
}
TexImageTarget ImageTarget() const {
return mTexImageTarget;
}
GLint Layer() const {
return mTexImageLayer;
}
- GLint MipLevel() const {
+ uint32_t MipLevel() const {
return mTexImageLevel;
}
void AttachmentName(nsCString* out) const;
bool HasUninitializedImageData() const;
void SetImageDataStatus(WebGLImageDataStatus x);
void Size(uint32_t* const out_width, uint32_t* const out_height) const;
@@ -254,16 +254,18 @@ public:
}
void SetReadBufferMode(GLenum readBufferMode) {
mReadBufferMode = readBufferMode;
}
GLenum ReadBufferMode() const { return mReadBufferMode; }
+ void GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const;
+
protected:
WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible
public:
void DetachTexture(const WebGLTexture* tex);
void DetachRenderbuffer(const WebGLRenderbuffer* rb);
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -190,26 +190,26 @@ WebGLTexture::SetImageInfosAtLevel(uint3
}
bool
WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
{
MOZ_ASSERT(DoesMinFilterRequireMipmap());
// GLES 3.0.4, p161
- const uint32_t maxLevel = MaxEffectiveMipmapLevel(texUnit);
+ uint32_t maxLevel;
+ if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
+ return false;
// "* `level_base <= level_max`"
if (mBaseMipmapLevel > maxLevel)
return false;
// Make a copy so we can modify it.
const ImageInfo& baseImageInfo = BaseImageInfo();
- if (!baseImageInfo.IsDefined())
- return false;
// Reference dimensions based on the current level.
uint32_t refWidth = baseImageInfo.mWidth;
uint32_t refHeight = baseImageInfo.mHeight;
uint32_t refDepth = baseImageInfo.mDepth;
MOZ_ASSERT(refWidth && refHeight && refDepth);
for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
@@ -420,34 +420,36 @@ WebGLTexture::IsComplete(uint32_t texUni
// image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a
// mipmap and the texture is not mipmap cube complete."
// (already covered)
}
return true;
}
-
-uint32_t
-WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit) const
+bool
+WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const
{
WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
if (minFilter == LOCAL_GL_NEAREST ||
minFilter == LOCAL_GL_LINEAR)
{
- // No mips used.
- return mBaseMipmapLevel;
+ // No extra mips used.
+ *out = mBaseMipmapLevel;
+ return true;
}
const auto& imageInfo = BaseImageInfo();
- MOZ_ASSERT(imageInfo.IsDefined());
+ if (!imageInfo.IsDefined())
+ return false;
- uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.MaxMipmapLevels() - 1;
- return std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
+ uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.PossibleMipmapLevels() - 1;
+ *out = std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
+ return true;
}
bool
WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
FakeBlackType* const out_fakeBlack)
{
const char* incompleteReason;
if (!IsComplete(texUnit, &incompleteReason)) {
@@ -461,17 +463,19 @@ WebGLTexture::GetFakeBlackType(const cha
*out_fakeBlack = FakeBlackType::RGBA0001;
return true;
}
// We may still want FakeBlack as an optimization for uninitialized image data.
bool hasUninitializedData = false;
bool hasInitializedData = false;
- const auto maxLevel = MaxEffectiveMipmapLevel(texUnit);
+ uint32_t maxLevel;
+ MOZ_ALWAYS_TRUE( MaxEffectiveMipmapLevel(texUnit, &maxLevel) );
+
MOZ_ASSERT(mBaseMipmapLevel <= maxLevel);
for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
for (uint8_t face = 0; face < mFaceCount; face++) {
const auto& cur = ImageInfoAtFace(face, level);
if (cur.IsDataInitialized())
hasInitializedData = true;
else
hasUninitializedData = true;
@@ -785,18 +789,18 @@ WebGLTexture::GenerateMipmap(TexTarget t
mMinFilter.get());
} else {
gl->fGenerateMipmap(texTarget.get());
}
// Record the results.
// Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
// mBaseMipmapLevel if the min filter doesn't require mipmaps.
- const uint32_t lastLevel = mBaseMipmapLevel + baseImageInfo.MaxMipmapLevels() - 1;
- PopulateMipChain(mBaseMipmapLevel, lastLevel);
+ const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
+ PopulateMipChain(mBaseMipmapLevel, maxLevel);
}
JS::Value
WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
{
mContext->MakeContextCurrent();
GLint i = 0;
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -149,19 +149,20 @@ public:
if (!IsDefined())
Clear();
}
protected:
ImageInfo& operator =(const ImageInfo& a);
public:
- uint32_t MaxMipmapLevels() const {
+ uint32_t PossibleMipmapLevels() const {
// GLES 3.0.4, 3.8 - Mipmapping: `floor(log2(largest_of_dims)) + 1`
- uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
+ const uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
+ MOZ_ASSERT(largest != 0);
return FloorLog2Size(largest) + 1;
}
bool IsPowerOfTwo() const;
void AddAttachPoint(WebGLFBAttachPoint* attachPoint);
void RemoveAttachPoint(WebGLFBAttachPoint* attachPoint);
void OnRespecify() const;
@@ -283,17 +284,17 @@ public:
////////////////////////////////////
protected:
void ClampLevelBaseAndMax();
void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel);
- uint32_t MaxEffectiveMipmapLevel(uint32_t texUnit) const;
+ bool MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const;
static uint8_t FaceForTarget(TexImageTarget texImageTarget) {
GLenum rawTexImageTarget = texImageTarget.get();
switch (rawTexImageTarget) {
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
@@ -383,16 +384,19 @@ public:
bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
// Resolve cache optimizations
protected:
bool GetFakeBlackType(const char* funcName, uint32_t texUnit,
FakeBlackType* const out_fakeBlack);
public:
+ bool IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+ const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const;
+
bool ResolveForDraw(const char* funcName, uint32_t texUnit,
FakeBlackType* const out_fakeBlack);
void InvalidateResolveCache() { mIsResolved = false; }
};
inline TexImageTarget
TexImageTargetForTargetAndFace(TexTarget target, uint8_t face)