Bug 1478216 - Don't init tex images in FBAttachment::IsComplete. r=kvark
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 18 Oct 2018 16:52:39 -0700
changeset 491199 5ef21255897e9c49c234be77b32b07ac3db7dc6c
parent 491198 434f70360933449c52ece7a00a6268391f805f08
child 491200 42961627bc9a1bfd12acb11c62bdbc327ffd7303
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewerskvark
bugs1478216
milestone65.0a1
Bug 1478216 - Don't init tex images in FBAttachment::IsComplete. r=kvark Also: - Only init the base tex level for GenerateMipmap. - Change ZeroTextureData warning into a perf warning. Differential Revision: https://phabricator.services.mozilla.com/D9177
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/test/webgl-conf/generated-mochitest.ini
dom/canvas/test/webgl-conf/mochitest-errata.ini
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -126,17 +126,18 @@ WebGLFBAttachPoint::IsComplete(WebGLCont
     if (tex) {
         // ES 3.0 spec, pg 213 has giant blocks of text that bake down to requiring that
         // attached tex images are within the valid mip-levels of the texture.
         // While it draws distinction to only test non-immutable textures, that's because
         // immutable textures are *always* texture-complete.
         // We need to check immutable textures though, because checking completeness is
         // also when we zero invalidated/no-data tex images.
         const bool complete = [&]() {
-            const auto texCompleteness = tex->CalcCompletenessInfo();
+            const bool ensureInit = false;
+            const auto texCompleteness = tex->CalcCompletenessInfo(ensureInit);
             if (!texCompleteness) // OOM
                 return false;
             if (!texCompleteness->levels)
                 return false;
 
             const auto baseLevel = tex->BaseMipmapLevel();
             const auto maxLevel = baseLevel + texCompleteness->levels - 1;
             return baseLevel <= mTexImageLevel && mTexImageLevel <= maxLevel;
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -150,17 +150,17 @@ WebGLTexture::PopulateMipChain(const uin
 
 static bool
 ZeroTextureData(const WebGLContext* webgl, GLuint tex,
                 TexImageTarget target, uint32_t level,
                 const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height,
                 uint32_t depth);
 
 bool
-WebGLTexture::IsMipAndCubeComplete(const uint32_t maxLevel,
+WebGLTexture::IsMipAndCubeComplete(const uint32_t maxLevel, const bool ensureInit,
                                    bool* const out_initFailed) const
 {
     *out_initFailed = false;
 
     // Reference dimensions based on baseLevel.
     auto ref = BaseImageInfo();
     MOZ_ASSERT(ref.mWidth && ref.mHeight && ref.mDepth);
 
@@ -182,17 +182,17 @@ WebGLTexture::IsMipAndCubeComplete(const
             if (cur.mWidth != ref.mWidth ||
                 cur.mHeight != ref.mHeight ||
                 cur.mDepth != ref.mDepth ||
                 cur.mFormat != ref.mFormat)
             {
                 return false;
             }
 
-            if (MOZ_UNLIKELY( !cur.mHasData )) {
+            if (MOZ_UNLIKELY( ensureInit && !cur.mHasData )) {
                 auto imageTarget = mTarget.get();
                 if (imageTarget == LOCAL_GL_TEXTURE_CUBE_MAP) {
                     imageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
                 }
                 if (!ZeroTextureData(mContext, mGLName, imageTarget, level,
                                      cur.mFormat, cur.mWidth, cur.mHeight, cur.mDepth))
                 {
                     mContext->ErrorOutOfMemory("Failed to zero tex image data.");
@@ -208,17 +208,17 @@ WebGLTexture::IsMipAndCubeComplete(const
             break;
         ref = next.ref();
     }
 
     return true;
 }
 
 Maybe<const WebGLTexture::CompletenessInfo>
-WebGLTexture::CalcCompletenessInfo() const
+WebGLTexture::CalcCompletenessInfo(const bool ensureInit, const bool skipMips) const
 {
     Maybe<CompletenessInfo> ret = Some(CompletenessInfo());
 
     // -
 
     if (mBaseMipmapLevel > kMaxLevelCount - 1) {
         ret->incompleteReason = "`level_base` too high.";
         return ret;
@@ -238,17 +238,17 @@ WebGLTexture::CalcCompletenessInfo() con
 
     if (!baseImageInfo.mWidth || !baseImageInfo.mHeight || !baseImageInfo.mDepth) {
         ret->incompleteReason = "The dimensions of `level_base` are not all positive.";
         return ret;
     }
 
     // "* The texture is a cube map texture, and is not cube complete."
     bool initFailed = false;
-    if (!IsMipAndCubeComplete(mBaseMipmapLevel, &initFailed)) {
+    if (!IsMipAndCubeComplete(mBaseMipmapLevel, ensureInit, &initFailed)) {
         if (initFailed)
             return {};
 
         // Can only fail if not cube-complete.
         ret->incompleteReason = "Cubemaps must be \"cube complete\".";
         return ret;
     }
     ret->levels = 1;
@@ -274,17 +274,20 @@ WebGLTexture::CalcCompletenessInfo() con
     // "* `level_base <= level_max`"
 
     const auto maxLevel = EffectiveMaxLevel();
     if (mBaseMipmapLevel > maxLevel) {
         ret->incompleteReason = "`level_base > level_max`.";
         return ret;
     }
 
-    if (!IsMipAndCubeComplete(maxLevel, &initFailed)) {
+    if (skipMips)
+        return ret;
+
+    if (!IsMipAndCubeComplete(maxLevel, ensureInit, &initFailed)) {
         if (initFailed)
             return {};
 
         ret->incompleteReason = "Bad mipmap dimension or format.";
         return ret;
     }
     ret->levels = maxLevel - mBaseMipmapLevel + 1;
     ret->mipmapComplete = true;
@@ -294,17 +297,18 @@ WebGLTexture::CalcCompletenessInfo() con
     return ret;
 }
 
 Maybe<const webgl::SampleableInfo>
 WebGLTexture::CalcSampleableInfo(const WebGLSampler* const sampler) const
 {
     Maybe<webgl::SampleableInfo> ret = Some(webgl::SampleableInfo());
 
-    const auto completeness = CalcCompletenessInfo();
+    const bool ensureInit = true;
+    const auto completeness = CalcCompletenessInfo(ensureInit);
     if (!completeness)
         return {};
 
     ret->incompleteReason = completeness->incompleteReason;
 
     if (!completeness->levels)
         return ret;
 
@@ -572,18 +576,19 @@ ZeroTextureData(const WebGLContext* webg
     // 1. Lazy zeroing of uninitialized textures:
     //    a. Before draw.
     //    b. Before partial upload. (TexStorage + TexSubImage)
     // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
 
     // We have no sympathy for any of these cases.
 
     // "Doctor, it hurts when I do this!" "Well don't do that!"
-    webgl->GenerateWarning("This operation requires zeroing texture data. This is"
-                           " slow.");
+    const auto targetStr = EnumString(target.get());
+    webgl->GeneratePerfWarning("Tex image %s level %u is incurring lazy initialization.",
+                               targetStr.c_str(), level);
 
     gl::GLContext* gl = webgl->GL();
 
     GLenum scopeBindTarget;
     switch (target.get()) {
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
@@ -732,17 +737,20 @@ WebGLTexture::BindTexture(TexTarget texT
 void
 WebGLTexture::GenerateMipmap()
 {
     // GLES 3.0.4 p160:
     // "Mipmap generation replaces texel array levels level base + 1 through q with arrays
     //  derived from the level base array, regardless of their previous contents. All
     //  other mipmap arrays, including the level base array, are left unchanged by this
     //  computation."
-    const auto completeness = CalcCompletenessInfo();
+    // But only check and init the base level.
+    const bool ensureInit = true;
+    const bool skipMips = true;
+    const auto completeness = CalcCompletenessInfo(ensureInit, skipMips);
     if (!completeness || !completeness->levels) {
         mContext->ErrorInvalidOperation("The texture's base level must be complete.");
         return;
     }
     const auto& usage = completeness->usage;
     const auto& format = usage->format;
     if (!mContext->IsWebGL2()) {
         if (!completeness->powerOfTwo) {
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -140,17 +140,18 @@ protected:
         bool mipmapComplete = false;
         const webgl::FormatUsageInfo* usage = nullptr;
         const char* incompleteReason = nullptr;
     };
 
     mutable CacheWeakMap<const WebGLSampler*, webgl::SampleableInfo> mSamplingCache;
 
 public:
-    Maybe<const CompletenessInfo> CalcCompletenessInfo() const;
+    Maybe<const CompletenessInfo> CalcCompletenessInfo(bool ensureInit,
+                                                       bool skipMips = false) const;
     Maybe<const webgl::SampleableInfo> CalcSampleableInfo(const WebGLSampler*) const;
 
     const webgl::SampleableInfo* GetSampleableInfo(const WebGLSampler*) const;
 
 
     // -
 
     const auto& Immutable() const { return mImmutable; }
@@ -303,17 +304,18 @@ public:
         return ImageInfoAtFace(0, mBaseMipmapLevel);
     }
 
     size_t MemoryUsage() const;
 
     bool EnsureImageDataInitialized(TexImageTarget target,
                                     uint32_t level);
     void PopulateMipChain(uint32_t maxLevel);
-    bool IsMipAndCubeComplete(uint32_t maxLevel, bool* out_initFailed) const;
+    bool IsMipAndCubeComplete(uint32_t maxLevel, bool ensureInit,
+                              bool* out_initFailed) const;
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 };
 
 inline TexImageTarget
 TexImageTargetForTargetAndFace(TexTarget target, uint8_t face)
 {
     switch (target.get()) {
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -5319,16 +5319,17 @@ fail-if = (os == 'win')
 [generated/test_2_conformance2__rendering__element-index-uint.html]
 subsuite = webgl2-core
 [generated/test_2_conformance2__rendering__framebuffer-completeness-draw-framebuffer.html]
 subsuite = webgl2-core
 [generated/test_2_conformance2__rendering__framebuffer-completeness-unaffected.html]
 subsuite = webgl2-core
 [generated/test_2_conformance2__rendering__framebuffer-texture-changing-base-level.html]
 subsuite = webgl2-core
+fail-if = (os == 'win')
 [generated/test_2_conformance2__rendering__framebuffer-texture-level1.html]
 subsuite = webgl2-core
 fail-if = (os == 'mac')
 [generated/test_2_conformance2__rendering__framebuffer-unsupported.html]
 subsuite = webgl2-core
 [generated/test_2_conformance2__rendering__fs-color-type-mismatch-color-buffer-type.html]
 subsuite = webgl2-core
 fail-if = (os == 'mac') || (os == 'win')
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -1096,8 +1096,11 @@ skip-if = (os == 'win')
 [generated/test_conformance__misc__webgl-specific-stencil-settings.html]
 skip-if = (os == 'win')
 [generated/test_conformance__textures__misc__tex-video-using-tex-unit-non-zero.html]
 # Fails on QuantumRender configs, but passes on standard configs?
 # Might be intermittant.
 skip-if = (os == 'win')
 [generated/test_2_conformance__textures__misc__tex-video-using-tex-unit-non-zero.html]
 skip-if = (os == 'win')
+[generated/test_2_conformance2__rendering__framebuffer-texture-changing-base-level.html]
+# https://bugzilla.mozilla.org/show_bug.cgi?id=1501868 (ANGLE bug)
+fail-if = (os == 'win')