Bug 1230089 - If sampler is bound, use parameter of sampler. r=jgilbert
authorMorris Tseng <mtseng@mozilla.com>
Thu, 21 Jan 2016 14:49:41 +0800
changeset 280950 56626252ef750211cfa5f92a93d64555dd01958e
parent 280949 235d8796a27947c371643340162d9dc081cb2cce
child 280951 2cd90b7d3dbd3cbc4759a493da12cfbbb1364c92
push id29925
push userkwierso@gmail.com
push dateFri, 22 Jan 2016 00:27:20 +0000
treeherdermozilla-central@cea2883034cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1230089
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1230089 - If sampler is bound, use parameter of sampler. r=jgilbert
dom/canvas/WebGL2ContextSamplers.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextTextures.cpp
dom/canvas/WebGLSampler.cpp
dom/canvas/WebGLSampler.h
dom/canvas/WebGLStrongTypes.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -42,16 +42,18 @@ WebGL2Context::DeleteSampler(WebGLSample
         return;
 
     if (!sampler || sampler->IsDeleted())
         return;
 
     for (int n = 0; n < mGLMaxTextureUnits; n++) {
         if (mBoundSamplers[n] == sampler) {
             mBoundSamplers[n] = nullptr;
+
+            InvalidateResolveCacheForTextureWithTexUnit(n);
         }
     }
 
     sampler->RequestDelete();
 }
 
 bool
 WebGL2Context::IsSampler(WebGLSampler* sampler)
@@ -83,32 +85,34 @@ WebGL2Context::BindSampler(GLuint unit, 
 
     if (GLint(unit) >= mGLMaxTextureUnits)
         return ErrorInvalidValue("bindSampler: unit must be < %d", mGLMaxTextureUnits);
 
     if (sampler && sampler->IsDeleted())
         return ErrorInvalidOperation("bindSampler: binding deleted sampler");
 
     WebGLContextUnchecked::BindSampler(unit, sampler);
+    InvalidateResolveCacheForTextureWithTexUnit(unit);
 
     mBoundSamplers[unit] = sampler;
 }
 
 void
 WebGL2Context::SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param)
 {
     if (IsContextLost())
         return;
 
     if (!sampler || sampler->IsDeleted())
         return ErrorInvalidOperation("samplerParameteri: invalid sampler");
 
     if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param), "samplerParameteri"))
         return;
 
+    sampler->SamplerParameter1i(pname, param);
     WebGLContextUnchecked::SamplerParameteri(sampler, pname, param);
 }
 
 void
 WebGL2Context::SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Int32Array& param)
 {
     if (IsContextLost())
         return;
@@ -119,16 +123,17 @@ WebGL2Context::SamplerParameteriv(WebGLS
     param.ComputeLengthAndData();
     if (param.Length() < 1)
         return /* TODO(djg): Error message */;
 
     /* TODO(djg): All of these calls in ES3 only take 1 param */
     if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param.Data()[0]), "samplerParameteriv"))
         return;
 
+    sampler->SamplerParameter1i(pname, param.Data()[0]);
     WebGLContextUnchecked::SamplerParameteriv(sampler, pname, param.Data());
 }
 
 void
 WebGL2Context::SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLint>& param)
 {
     if (IsContextLost())
         return;
@@ -138,31 +143,33 @@ WebGL2Context::SamplerParameteriv(WebGLS
 
     if (param.Length() < 1)
         return /* TODO(djg): Error message */;
 
     /* TODO(djg): All of these calls in ES3 only take 1 param */
     if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameteriv"))
         return;
 
+    sampler->SamplerParameter1i(pname, param[0]);
     WebGLContextUnchecked::SamplerParameteriv(sampler, pname, param.Elements());
 }
 
 void
 WebGL2Context::SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param)
 {
     if (IsContextLost())
         return;
 
     if (!sampler || sampler->IsDeleted())
         return ErrorInvalidOperation("samplerParameterf: invalid sampler");
 
     if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param), "samplerParameterf"))
         return;
 
+    sampler->SamplerParameter1f(pname, param);
     WebGLContextUnchecked::SamplerParameterf(sampler, pname, param);
 }
 
 void
 WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Float32Array& param)
 {
     if (IsContextLost())
         return;
@@ -173,16 +180,17 @@ WebGL2Context::SamplerParameterfv(WebGLS
     param.ComputeLengthAndData();
     if (param.Length() < 1)
         return /* TODO(djg): Error message */;
 
     /* TODO(djg): All of these calls in ES3 only take 1 param */
     if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param.Data()[0]), "samplerParameterfv"))
         return;
 
+    sampler->SamplerParameter1f(pname, param.Data()[0]);
     WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Data());
 }
 
 void
 WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLfloat>& param)
 {
     if (IsContextLost())
         return;
@@ -192,16 +200,17 @@ WebGL2Context::SamplerParameterfv(WebGLS
 
     if (param.Length() < 1)
         return /* TODO(djg): Error message */;
 
     /* TODO(djg): All of these calls in ES3 only take 1 param */
     if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameterfv"))
         return;
 
+    sampler->SamplerParameter1f(pname, param[0]);
     WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Elements());
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, WebGLSampler* sampler, GLenum pname, JS::MutableHandleValue retval)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -327,16 +327,18 @@ public:
      */
     WebGLTexture*
     ActiveBoundTextureForTexImageTarget(const TexImageTarget texImgTarget) const
     {
         const TexTarget texTarget = TexImageTargetToTexTarget(texImgTarget);
         return ActiveBoundTextureForTarget(texTarget);
     }
 
+    void InvalidateResolveCacheForTextureWithTexUnit(const GLuint);
+
     already_AddRefed<Layer>
     GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer,
                    LayerManager* manager) override;
 
     // Note that 'clean' here refers to its invalidation state, not the
     // contents of the buffer.
     void MarkContextClean() override { mInvalidated = false; }
 
--- a/dom/canvas/WebGLContextTextures.cpp
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -182,16 +182,29 @@ WebGLContext::IsTexParamValid(GLenum pna
     case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
         return IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic);
 
     default:
         return false;
     }
 }
 
+void
+WebGLContext::InvalidateResolveCacheForTextureWithTexUnit(const GLuint texUnit)
+{
+    if (mBound2DTextures[texUnit])
+        mBound2DTextures[texUnit]->InvalidateResolveCache();
+    if (mBoundCubeMapTextures[texUnit])
+        mBoundCubeMapTextures[texUnit]->InvalidateResolveCache();
+    if (mBound3DTextures[texUnit])
+        mBound3DTextures[texUnit]->InvalidateResolveCache();
+    if (mBound2DArrayTextures[texUnit])
+        mBound2DArrayTextures[texUnit]->InvalidateResolveCache();
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 // GL calls
 
 void
 WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -9,16 +9,25 @@
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSampler::WebGLSampler(WebGLContext* webgl, GLuint sampler)
     : WebGLContextBoundObject(webgl)
     , mGLName(sampler)
+    , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
+    , mMagFilter(LOCAL_GL_LINEAR)
+    , mWrapS(LOCAL_GL_REPEAT)
+    , mWrapT(LOCAL_GL_REPEAT)
+    , mWrapR(LOCAL_GL_REPEAT)
+    , mMinLod(-1000)
+    , mMaxLod(1000)
+    , mCompareMode(LOCAL_GL_NONE)
+    , mCompareFunc(LOCAL_GL_LEQUAL)
 {
     mContext->mSamplers.insertBack(this);
 }
 
 WebGLSampler::~WebGLSampler()
 {
     DeleteOnce();
 }
@@ -39,14 +48,80 @@ WebGLSampler::GetParentObject() const
 }
 
 JSObject*
 WebGLSampler::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLSamplerBinding::Wrap(cx, this, givenProto);
 }
 
+void
+WebGLSampler::SamplerParameter1i(GLenum pname, GLint param)
+{
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+        mMinFilter = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+        mMagFilter = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_S:
+        mWrapS = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_T:
+        mWrapT = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_R:
+        mWrapR = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+        mCompareMode = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        mCompareFunc = param;
+        break;
+
+    default:
+        MOZ_CRASH("Unhandled pname");
+        break;
+    }
+
+    for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
+        if (this == mContext->mBoundSamplers[i])
+            mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
+    }
+}
+
+void
+WebGLSampler::SamplerParameter1f(GLenum pname, GLfloat param)
+{
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+        mMinLod = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+        mMaxLod = param;
+        break;
+
+    default:
+        MOZ_CRASH("Unhandled pname");
+        break;
+    }
+
+    for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
+        if (this == mContext->mBoundSamplers[i])
+            mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
+    }
+}
+
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSampler)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLSampler, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLSampler, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -14,31 +14,45 @@ namespace mozilla {
 
 class WebGLSampler final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSampler>
     , public LinkedListElement<WebGLSampler>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext2;
+    friend class WebGLTexture;
 
 public:
     explicit WebGLSampler(WebGLContext* webgl, GLuint sampler);
 
     const GLuint mGLName;
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
+    void SamplerParameter1i(GLenum pname, GLint param);
+    void SamplerParameter1f(GLenum pname, GLfloat param);
+
 private:
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSampler)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSampler)
 
+    TexMinFilter mMinFilter;
+    TexMagFilter mMagFilter;
+    TexWrap mWrapS;
+    TexWrap mWrapT;
+    TexWrap mWrapR;
+    GLint mMinLod;
+    GLint mMaxLod;
+    TexCompareMode mCompareMode;
+    TexCompareFunc mCompareFunc;
+
 private:
     ~WebGLSampler();
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_SAMPLER_H_
--- a/dom/canvas/WebGLStrongTypes.h
+++ b/dom/canvas/WebGLStrongTypes.h
@@ -264,16 +264,32 @@ STRONG_GLENUM_BEGIN(TexMagFilter)
 STRONG_GLENUM_END(TexMagFilter)
 
 STRONG_GLENUM_BEGIN(TexWrap)
     STRONG_GLENUM_VALUE(REPEAT),
     STRONG_GLENUM_VALUE(CLAMP_TO_EDGE),
     STRONG_GLENUM_VALUE(MIRRORED_REPEAT),
 STRONG_GLENUM_END(TexWrap)
 
+STRONG_GLENUM_BEGIN(TexCompareMode)
+    STRONG_GLENUM_VALUE(NONE),
+    STRONG_GLENUM_VALUE(COMPARE_REF_TO_TEXTURE),
+STRONG_GLENUM_END(TexCompareMode)
+
+STRONG_GLENUM_BEGIN(TexCompareFunc)
+    STRONG_GLENUM_VALUE(LEQUAL),
+    STRONG_GLENUM_VALUE(GEQUAL),
+    STRONG_GLENUM_VALUE(LESS),
+    STRONG_GLENUM_VALUE(GREATER),
+    STRONG_GLENUM_VALUE(EQUAL),
+    STRONG_GLENUM_VALUE(NOTEQUAL),
+    STRONG_GLENUM_VALUE(ALWAYS),
+    STRONG_GLENUM_VALUE(NEVER),
+STRONG_GLENUM_END(TexCompareFunc)
+
 STRONG_GLENUM_BEGIN(TexFormat)
     STRONG_GLENUM_VALUE(NONE),            // 0x0000
     STRONG_GLENUM_VALUE(DEPTH_COMPONENT), // 0x1902
     STRONG_GLENUM_VALUE(RED),             // 0x1903
     STRONG_GLENUM_VALUE(ALPHA),           // 0x1906
     STRONG_GLENUM_VALUE(RGB),             // 0x1907
     STRONG_GLENUM_VALUE(RGBA),            // 0x1908
     STRONG_GLENUM_VALUE(LUMINANCE),       // 0x1909
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -185,22 +185,22 @@ WebGLTexture::SetImageInfosAtLevel(uint3
     for (uint8_t i = 0; i < mFaceCount; i++) {
         ImageInfoAtFace(i, level) = newInfo;
     }
 
     InvalidateResolveCache();
 }
 
 bool
-WebGLTexture::IsMipmapComplete() const
+WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
 {
     MOZ_ASSERT(DoesMinFilterRequireMipmap());
     // GLES 3.0.4, p161
 
-    const uint32_t maxLevel = MaxEffectiveMipmapLevel();
+    const uint32_t maxLevel = MaxEffectiveMipmapLevel(texUnit);
 
     // "* `level_base <= level_max`"
     if (mBaseMipmapLevel > maxLevel)
         return false;
 
     // Make a copy so we can modify it.
     const ImageInfo& baseImageInfo = BaseImageInfo();
     if (!baseImageInfo.IsDefined())
@@ -285,17 +285,17 @@ WebGLTexture::IsCubeComplete() const
             return false;
         }
     }
 
     return true;
 }
 
 bool
-WebGLTexture::IsComplete(const char** const out_reason) const
+WebGLTexture::IsComplete(uint32_t texUnit, const char** const out_reason) const
 {
     // Texture completeness is established at GLES 3.0.4, p160-161.
     // "[A] texture is complete unless any of the following conditions hold true:"
 
     // "* Any dimension of the `level_base` array is not positive."
     const ImageInfo& baseImageInfo = BaseImageInfo();
     if (!baseImageInfo.IsDefined()) {
         // In case of undefined texture image, we don't print any message because this is
@@ -310,29 +310,33 @@ WebGLTexture::IsComplete(const char** co
     }
 
     // "* The texture is a cube map texture, and is not cube complete."
     if (IsCubeMap() && !IsCubeComplete()) {
         *out_reason = "Cubemaps must be \"cube complete\".";
         return false;
     }
 
+    WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
+    TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
+    TexMagFilter magFilter = sampler ? sampler->mMagFilter : mMagFilter;
+
     // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and
     //    the texture is not mipmap complete."
-    const bool requiresMipmap = (mMinFilter != LOCAL_GL_NEAREST &&
-                                 mMinFilter != LOCAL_GL_LINEAR);
-    if (requiresMipmap && !IsMipmapComplete()) {
+    const bool requiresMipmap = (minFilter != LOCAL_GL_NEAREST &&
+                                 minFilter != LOCAL_GL_LINEAR);
+    if (requiresMipmap && !IsMipmapComplete(texUnit)) {
         *out_reason = "Because the minification filter requires mipmapping, the texture"
                       " must be \"mipmap complete\".";
         return false;
     }
 
-    const bool isMinFilteringNearest = (mMinFilter == LOCAL_GL_NEAREST ||
-                                        mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
-    const bool isMagFilteringNearest = (mMagFilter == LOCAL_GL_NEAREST);
+    const bool isMinFilteringNearest = (minFilter == LOCAL_GL_NEAREST ||
+                                        minFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
+    const bool isMagFilteringNearest = (magFilter == LOCAL_GL_NEAREST);
     const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest);
     if (!isFilteringNearestOnly) {
         auto formatUsage = baseImageInfo.mFormat;
         auto format = formatUsage->format;
 
         // "* The effective internal format specified for the texture arrays is a sized
         //    internal color format that is not texture-filterable, and either the
         //    magnification filter is not NEAREST or the minification filter is neither
@@ -389,19 +393,21 @@ WebGLTexture::IsComplete(const char** co
         // "* A two-dimensional sampler is called, the corresponding texture image is a
         //    non-power-of-two image[...], and either the texture wrap mode is not
         //    CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
 
         // "* A cube map sampler is called, any of the corresponding texture images are
         //    non-power-of-two images, and either the texture wrap mode is not
         //    CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
         if (!baseImageInfo.IsPowerOfTwo()) {
+            TexWrap wrapS = sampler ? sampler->mWrapS : mWrapS;
+            TexWrap wrapT = sampler ? sampler->mWrapT : mWrapT;
             // "either the texture wrap mode is not CLAMP_TO_EDGE"
-            if (mWrapS != LOCAL_GL_CLAMP_TO_EDGE ||
-                mWrapT != LOCAL_GL_CLAMP_TO_EDGE)
+            if (wrapS != LOCAL_GL_CLAMP_TO_EDGE ||
+                wrapT != LOCAL_GL_CLAMP_TO_EDGE)
             {
                 *out_reason = "Non-power-of-two textures must have a wrap mode of"
                               " CLAMP_TO_EDGE.";
                 return false;
             }
 
             // "or the minification filter is neither NEAREST nor LINEAR"
             if (requiresMipmap) {
@@ -416,20 +422,22 @@ WebGLTexture::IsComplete(const char** co
         // (already covered)
     }
 
     return true;
 }
 
 
 uint32_t
-WebGLTexture::MaxEffectiveMipmapLevel() const
+WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit) const
 {
-    if (mMinFilter == LOCAL_GL_NEAREST ||
-        mMinFilter == LOCAL_GL_LINEAR)
+    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;
     }
 
     const auto& imageInfo = BaseImageInfo();
     MOZ_ASSERT(imageInfo.IsDefined());
 
@@ -437,33 +445,33 @@ WebGLTexture::MaxEffectiveMipmapLevel() 
     return std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
 }
 
 bool
 WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
                                FakeBlackType* const out_fakeBlack)
 {
     const char* incompleteReason;
-    if (!IsComplete(&incompleteReason)) {
+    if (!IsComplete(texUnit, &incompleteReason)) {
         if (incompleteReason) {
             mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
                                       " 'incomplete', and will be rendered as"
                                       " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
                                       funcName, texUnit, mTarget.get(),
                                       incompleteReason);
         }
         *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();
+    const auto maxLevel = MaxEffectiveMipmapLevel(texUnit);
     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;
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -279,17 +279,17 @@ public:
 
     ////////////////////////////////////
 
 protected:
     void ClampLevelBaseAndMax();
 
     void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel);
 
-    uint32_t MaxEffectiveMipmapLevel() const;
+    uint32_t MaxEffectiveMipmapLevel(uint32_t texUnit) 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:
@@ -364,21 +364,21 @@ public:
     }
 
     void SetGeneratedMipmap();
 
     void SetCustomMipmap();
 
     bool AreAllLevel0ImageInfosEqual() const;
 
-    bool IsMipmapComplete() const;
+    bool IsMipmapComplete(uint32_t texUnit) const;
 
     bool IsCubeComplete() const;
 
-    bool IsComplete(const char** const out_reason) const;
+    bool IsComplete(uint32_t texUnit, const char** const out_reason) const;
 
     bool IsMipmapCubeComplete() const;
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 
     // Resolve cache optimizations
 protected:
     bool GetFakeBlackType(const char* funcName, uint32_t texUnit,