Bug 1564293 - Allow efficient slicewise upload of 3D textures r=jgilbert
authorGreyson Gilbert <greyson.gilbert.oss@gmail.com>
Tue, 30 Jul 2019 23:32:29 +0000
changeset 549189 6019fc32fbf36e44a00248aa12f94c6f111a9213
parent 549188 9de8e9eca62071ac3519f9ce65b1d9a21600e876
child 549190 56b1594759719d8d815f91a7017641a131dfa88f
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1564293
milestone70.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 1564293 - Allow efficient slicewise upload of 3D textures r=jgilbert Before this patch any partial upload to a texture would incur a zeroing of the texture first to prevent leakage of information. The texture now tracks, for each image not fully initialized, which z-slices have been initialized, and only zeroes the rest of the slices when the texture is used. Differential Revision: https://phabricator.services.mozilla.com/D37843
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLRenderbuffer.cpp
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/WebGLTextureUpload.cpp
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -835,17 +835,17 @@ void WebGLFramebuffer::ResolveAttachment
   if (mContext->IsWebGL2()) {
     const uint32_t uiZeros[4] = {};
     const int32_t iZeros[4] = {};
     const float fZeros[4] = {};
     const float fOne[] = {1.0f};
 
     for (const auto& cur : mAttachments) {
       const auto& imageInfo = cur->GetImageInfo();
-      if (!imageInfo || imageInfo->mHasData)
+      if (!imageInfo || !imageInfo->mUninitializedSlices)
         continue;  // Nothing attached, or already has data.
 
       const auto fnClearBuffer = [&]() {
         const auto& format = imageInfo->mFormat->format;
         MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(uiZeros));
         MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(iZeros));
         MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(fZeros));
 
@@ -875,40 +875,42 @@ void WebGLFramebuffer::ResolveAttachment
             }
         }
       };
 
       if (imageInfo->mDepth > 1) {
         const auto& tex = cur->Texture();
         const gl::ScopedFramebuffer scopedFB(gl);
         const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
-        for (uint32_t z = 0; z < imageInfo->mDepth; z++) {
-          gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER,
-                                       cur->mAttachmentPoint, tex->mGLName,
-                                       cur->MipLevel(), z);
-          fnClearBuffer();
+        for (const auto z : IntegerRange(imageInfo->mDepth)) {
+          if ((*imageInfo->mUninitializedSlices)[z]) {
+            gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER,
+                                         cur->mAttachmentPoint, tex->mGLName,
+                                         cur->MipLevel(), z);
+            fnClearBuffer();
+          }
         }
       } else {
         fnClearBuffer();
       }
-      imageInfo->mHasData = true;
+      imageInfo->mUninitializedSlices = {};
     }
     return;
   }
 
   uint32_t clearBits = 0;
   std::vector<GLenum> drawBufferForClear;
 
   const auto fnGather = [&](const WebGLFBAttachPoint& attach,
                             const uint32_t attachClearBits) {
     const auto& imageInfo = attach.GetImageInfo();
-    if (!imageInfo || imageInfo->mHasData) return false;
+    if (!imageInfo || !imageInfo->mUninitializedSlices) return false;
 
     clearBits |= attachClearBits;
-    imageInfo->mHasData = true;  // Just mark it now.
+    imageInfo->mUninitializedSlices = {};  // Just mark it now.
     return true;
   };
 
   //////
 
   for (const auto& cur : mColorAttachments) {
     if (fnGather(cur, LOCAL_GL_COLOR_BUFFER_BIT)) {
       const uint32_t id = cur.mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -187,18 +187,20 @@ void WebGLRenderbuffer::RenderbufferStor
       InvalidateCaches();
     }
     return;
   }
 
   mContext->OnDataAllocCall();
 
   const uint32_t depth = 1;
-  const bool hasData = false;
-  mImageInfo = {usage, width, height, depth, hasData, uint8_t(samples)};
+  auto uninitializedSlices = Some(std::vector<bool>(depth, true));
+  mImageInfo = {
+      usage,           width, height, depth, std::move(uninitializedSlices),
+      uint8_t(samples)};
   InvalidateCaches();
 }
 
 void WebGLRenderbuffer::DoFramebufferRenderbuffer(
     const GLenum attachment) const {
   gl::GLContext* gl = mContext->gl;
 
   if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -51,16 +51,19 @@ Maybe<ImageInfo> ImageInfo::NextMip(cons
 
     next.mDepth = std::max(uint32_t(1), next.mDepth / 2);
   } else {
     // TEXTURE_2D_ARRAY may have depth != 1, but that's normal.
     if (mWidth <= 1 && mHeight <= 1) {
       return {};
     }
   }
+  if (next.mUninitializedSlices) {
+    next.mUninitializedSlices = Some(std::vector<bool>(next.mDepth, true));
+  }
 
   next.mWidth = std::max(uint32_t(1), next.mWidth / 2);
   next.mHeight = std::max(uint32_t(1), next.mHeight / 2);
   return Some(next);
 }
 
 }  // namespace webgl
 
@@ -127,18 +130,17 @@ void WebGLTexture::PopulateMipChain(cons
     if (!next) break;
     ref = next.ref();
   }
   InvalidateCaches();
 }
 
 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);
+                            const webgl::ImageInfo& info);
 
 bool 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();
@@ -159,28 +161,27 @@ bool WebGLTexture::IsMipAndCubeComplete(
       // "* The dimensions of the arrays follow the sequence described in
       //    the "Mipmapping" discussion of section 3.8.10."
 
       if (cur.mWidth != ref.mWidth || cur.mHeight != ref.mHeight ||
           cur.mDepth != ref.mDepth || cur.mFormat != ref.mFormat) {
         return false;
       }
 
-      if (MOZ_UNLIKELY(ensureInit && !cur.mHasData)) {
+      if (MOZ_UNLIKELY(ensureInit && cur.mUninitializedSlices)) {
         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)) {
+        if (!ZeroTextureData(mContext, mGLName, imageTarget, level, cur)) {
           mContext->ErrorOutOfMemory("Failed to zero tex image data.");
           *out_initFailed = true;
           return false;
         }
-        cur.mHasData = true;
+        cur.mUninitializedSlices = {};
       }
     }
 
     const auto next = ref.NextMip(mTarget.get());
     if (!next) break;
     ref = next.ref();
   }
 
@@ -456,37 +457,37 @@ void WebGLTexture::RefreshSwizzle() cons
   }
 }
 
 bool WebGLTexture::EnsureImageDataInitialized(const TexImageTarget target,
                                               const uint32_t level) {
   auto& imageInfo = ImageInfoAt(target, level);
   if (!imageInfo.IsDefined()) return true;
 
-  if (imageInfo.mHasData) return true;
+  if (!imageInfo.mUninitializedSlices) return true;
 
-  if (!ZeroTextureData(mContext, mGLName, target, level, imageInfo.mFormat,
-                       imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth)) {
+  if (!ZeroTextureData(mContext, mGLName, target, level, imageInfo)) {
     return false;
   }
-  imageInfo.mHasData = true;
+  imageInfo.mUninitializedSlices = {};
   return true;
 }
 
 static bool ClearDepthTexture(const WebGLContext& webgl, const GLuint tex,
                               const TexImageTarget imageTarget,
                               const uint32_t level,
-                              const webgl::FormatUsageInfo* const usage,
-                              const uint32_t depth) {
+                              const webgl::ImageInfo& info) {
+  const auto& gl = webgl.gl;
+  const auto& usage = info.mFormat;
+  const auto& format = usage->format;
+
   // Depth resources actually clear to 1.0f, not 0.0f!
   // They are also always renderable.
   MOZ_ASSERT(usage->IsRenderable());
-
-  const auto& gl = webgl.gl;
-  const auto& format = usage->format;
+  MOZ_ASSERT(info.mUninitializedSlices);
 
   GLenum attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
   GLbitfield clearBits = LOCAL_GL_DEPTH_BUFFER_BIT;
 
   if (format->s) {
     attachPoint = LOCAL_GL_DEPTH_STENCIL_ATTACHMENT;
     clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
   }
@@ -515,45 +516,51 @@ static bool ClearDepthTexture(const WebG
         } else {
           gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint,
                                     imageTarget.get(), tex, level);
         }
         break;
     }
   };
 
-  for (uint32_t z = 0; z < depth; ++z) {
-    fnAttach(z);
-    gl->fClear(clearBits);
+  for (const auto z : IntegerRange(info.mDepth)) {
+    if ((*info.mUninitializedSlices)[z]) {
+      fnAttach(z);
+      gl->fClear(clearBits);
+    }
   }
   const auto& status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
   const bool isComplete = (status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
   MOZ_ASSERT(isComplete);
   return isComplete;
 }
 
 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) {
-  // This has two usecases:
-  // 1. Lazy zeroing of uninitialized textures:
+                            const webgl::ImageInfo& info) {
+  // This has one usecase:
+  // 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.
+  // We have no sympathy for this case.
 
   // "Doctor, it hurts when I do this!" "Well don't do that!"
+  MOZ_ASSERT(info.mUninitializedSlices);
+
   const auto targetStr = EnumString(target.get());
-  webgl->GeneratePerfWarning(
+  webgl->GenerateWarning(
       "Tex image %s level %u is incurring lazy initialization.",
       targetStr.c_str(), level);
 
   gl::GLContext* gl = webgl->GL();
+  const auto& width = info.mWidth;
+  const auto& height = info.mHeight;
+  const auto& depth = info.mDepth;
+  const auto& usage = info.mFormat;
 
   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:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
@@ -576,67 +583,78 @@ static bool ZeroTextureData(const WebGLC
     };
 
     const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
     const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
 
     CheckedUint32 checkedByteCount = compression->bytesPerBlock;
     checkedByteCount *= widthBlocks;
     checkedByteCount *= heightBlocks;
-    checkedByteCount *= depth;
 
     if (!checkedByteCount.isValid()) return false;
 
-    const size_t byteCount = checkedByteCount.value();
+    const size_t sliceByteCount = checkedByteCount.value();
 
-    UniqueBuffer zeros = calloc(1u, byteCount);
+    UniqueBuffer zeros = calloc(1u, sliceByteCount);
     if (!zeros) return false;
 
     ScopedUnpackReset scopedReset(webgl);
     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);  // Don't bother with
                                                      // striding it well.
 
-    const auto error =
-        DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0, width, height,
-                                depth, sizedFormat, byteCount, zeros.get());
+    GLenum error = 0;
+    for (const auto z : IntegerRange(depth)) {
+      if ((*info.mUninitializedSlices)[z]) {
+        error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, z, width,
+                                        height, 1, sizedFormat, sliceByteCount,
+                                        zeros.get());
+        if (error) break;
+      }
+    }
     return !error;
   }
 
   const auto driverUnpackInfo = usage->idealUnpack;
   MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");
 
   if (usage->format->d) {
     // ANGLE_depth_texture does not allow uploads, so we have to clear.
     // (Restriction because of D3D9)
     // Also, depth resources are cleared to 1.0f and are always renderable, so
     // just use FB clears.
-    return ClearDepthTexture(*webgl, tex, target, level, usage, depth);
+    return ClearDepthTexture(*webgl, tex, target, level, info);
   }
 
   const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
 
   const auto bytesPerPixel = webgl::BytesPerPixel(packing);
 
   CheckedUint32 checkedByteCount = bytesPerPixel;
   checkedByteCount *= width;
   checkedByteCount *= height;
-  checkedByteCount *= depth;
 
   if (!checkedByteCount.isValid()) return false;
 
-  const size_t byteCount = checkedByteCount.value();
+  const size_t sliceByteCount = checkedByteCount.value();
 
-  UniqueBuffer zeros = calloc(1u, byteCount);
+  UniqueBuffer zeros = calloc(1u, sliceByteCount);
   if (!zeros) return false;
 
   ScopedUnpackReset scopedReset(webgl);
   gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
                    1);  // Don't bother with striding it well.
-  const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height,
-                                   depth, packing, zeros.get());
+
+  GLenum error = 0;
+  for (const auto z : IntegerRange(depth)) {
+    if ((*info.mUninitializedSlices)[z]) {
+      error = DoTexSubImage(gl, target, level, 0, 0, z, width, height, 1,
+                            packing, zeros.get());
+      if (error) break;
+    }
+  }
   return !error;
 }
 
 template <typename T, typename R>
 static R Clamp(const T val, const R min, const R max) {
   if (val < min) return min;
   if (val > max) return max;
   return static_cast<R>(val);
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -69,17 +69,17 @@ struct SamplingState final {
 
 struct ImageInfo final {
   static const ImageInfo kUndefined;
 
   const webgl::FormatUsageInfo* mFormat = nullptr;
   uint32_t mWidth = 0;
   uint32_t mHeight = 0;
   uint32_t mDepth = 0;
-  mutable bool mHasData = false;
+  mutable Maybe<std::vector<bool>> mUninitializedSlices;
   uint8_t mSamples = 0;
 
   // -
 
   size_t MemoryUsage() const;
 
   bool IsDefined() const {
     if (!mFormat) {
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -748,31 +748,41 @@ static bool DoChannelsMatchForCopyTexIma
       return false;
   }
 }
 
 static bool EnsureImageDataInitializedForUpload(
     WebGLTexture* tex, TexImageTarget target, GLint level, GLint xOffset,
     GLint yOffset, GLint zOffset, uint32_t width, uint32_t height,
     uint32_t depth, webgl::ImageInfo* imageInfo) {
-  if (!imageInfo->mHasData) {
-    const bool isFullUpload =
-        (!xOffset && !yOffset && !zOffset && width == imageInfo->mWidth &&
-         height == imageInfo->mHeight && depth == imageInfo->mDepth);
-    if (!isFullUpload) {
-      WebGLContext* webgl = tex->mContext;
-      webgl->GenerateWarning(
-          "Texture has not been initialized prior to a"
-          " partial upload, forcing the browser to clear it."
-          " This may be slow.");
-      if (!tex->EnsureImageDataInitialized(target, level)) {
-        MOZ_ASSERT(false, "Unexpected failure to init image data.");
-        return false;
-      }
+  if (!imageInfo->mUninitializedSlices) return true;
+
+  if (width == imageInfo->mWidth && height == imageInfo->mHeight) {
+    for (const auto z :
+         IntegerRange(uint32_t(zOffset), uint32_t(zOffset) + depth)) {
+      (*imageInfo->mUninitializedSlices)[z] = false;
+    }
+    bool hasUninitialized = false;
+    for (const auto z : IntegerRange(imageInfo->mDepth)) {
+      hasUninitialized |= (*imageInfo->mUninitializedSlices)[z];
     }
+    if (!hasUninitialized) {
+      imageInfo->mUninitializedSlices = {};
+    }
+    return true;
+  }
+
+  WebGLContext* webgl = tex->mContext;
+  webgl->GenerateWarning(
+      "Texture has not been initialized prior to a"
+      " partial upload, forcing the browser to clear it."
+      " This may be slow.");
+  if (!tex->EnsureImageDataInitialized(target, level)) {
+    MOZ_ASSERT(false, "Unexpected failure to init image data.");
+    return false;
   }
 
   return true;
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 // Actual calls
@@ -1106,19 +1116,20 @@ void WebGLTexture::TexStorage(TexTarget 
     gfxCriticalError() << "Unexpected error from driver: "
                        << call.BeginReading();
     return;
   }
 
   ////////////////////////////////////
   // Update our specification data.
 
-  const bool isDataInitialized = false;
+  auto uninitializedSlices = Some(std::vector<bool>(depth, true));
   const webgl::ImageInfo newInfo{dstUsage, uint32_t(width), uint32_t(height),
-                                 uint32_t(depth), isDataInitialized};
+                                 uint32_t(depth),
+                                 std::move(uninitializedSlices)};
 
   {
     const auto base_level = mBaseMipmapLevel;
     mBaseMipmapLevel = 0;
 
     ImageInfoAtFace(0, 0) = newInfo;
     PopulateMipChain(levels - 1);
 
@@ -1204,19 +1215,21 @@ void WebGLTexture::TexImage(TexImageTarg
     }
   }
 
   ////////////////////////////////////
   // Do the thing!
 
   // It's tempting to do allocation first, and TexSubImage second, but this is
   // generally slower.
-
+  auto uninitializedSlices =
+      blob->HasData() ? Nothing() : Some(std::vector<bool>(blob->mDepth, true));
   const webgl::ImageInfo newImageInfo{dstUsage, blob->mWidth, blob->mHeight,
-                                      blob->mDepth, blob->HasData()};
+                                      blob->mDepth,
+                                      std::move(uninitializedSlices)};
 
   const bool isSubImage = false;
   const bool needsRespec = (imageInfo->mWidth != newImageInfo.mWidth ||
                             imageInfo->mHeight != newImageInfo.mHeight ||
                             imageInfo->mDepth != newImageInfo.mDepth ||
                             imageInfo->mFormat != newImageInfo.mFormat);
   const GLint xOffset = 0;
   const GLint yOffset = 0;
@@ -1326,18 +1339,16 @@ void WebGLTexture::TexSubImage(TexImageT
         driverUnpackInfo->unpackType);
     mContext->ErrorInvalidOperation("%s", dui.BeginReading());
     gfxCriticalError() << mContext->FuncName() << ": " << dui.BeginReading();
     return;
   }
 
   ////////////////////////////////////
   // Update our specification data?
-
-  imageInfo->mHasData = true;
 }
 
 ////////////////////////////////////////
 // CompressedTex(Sub)Image
 
 UniquePtr<webgl::TexUnpackBytes> WebGLContext::FromCompressed(
     TexImageTarget target, GLsizei rawWidth, GLsizei rawHeight,
     GLsizei rawDepth, GLint border, const TexImageSource& src,
@@ -1433,19 +1444,19 @@ void WebGLTexture::CompressedTexImage(Te
     gfxCriticalError() << "Unexpected error from driver: "
                        << call.BeginReading();
     return;
   }
 
   ////////////////////////////////////
   // Update our specification data.
 
-  const bool isDataInitialized = true;
+  const auto uninitializedSlices = Nothing();
   const webgl::ImageInfo newImageInfo{usage, blob->mWidth, blob->mHeight,
-                                      blob->mDepth, isDataInitialized};
+                                      blob->mDepth, uninitializedSlices};
   *imageInfo = newImageInfo;
   InvalidateCaches();
 }
 
 static inline bool IsSubImageBlockAligned(
     const webgl::CompressedFormatInfo* compression,
     const webgl::ImageInfo* imageInfo, GLint xOffset, GLint yOffset,
     uint32_t width, uint32_t height) {
@@ -1581,18 +1592,16 @@ void WebGLTexture::CompressedTexSubImage
         uint32_t(blob->mAvailBytes), error);
     gfxCriticalError() << "Unexpected error from driver: "
                        << call.BeginReading();
     return;
   }
 
   ////////////////////////////////////
   // Update our specification data?
-
-  imageInfo->mHasData = true;
 }
 
 ////////////////////////////////////////
 // CopyTex(Sub)Image
 
 static bool ValidateCopyTexImageFormats(WebGLContext* webgl,
                                         const webgl::FormatInfo* srcFormat,
                                         const webgl::FormatInfo* dstFormat) {
@@ -2095,19 +2104,19 @@ void WebGLTexture::CopyTexImage2D(TexIma
     return;
   }
 
   mContext->OnDataAllocCall();
 
   ////////////////////////////////////
   // Update our specification data.
 
-  const bool isDataInitialized = true;
+  const auto uninitializedSlices = Nothing();
   const webgl::ImageInfo newImageInfo{dstUsage, width, height, depth,
-                                      isDataInitialized};
+                                      uninitializedSlices};
   *imageInfo = newImageInfo;
   InvalidateCaches();
 }
 
 void WebGLTexture::CopyTexSubImage(TexImageTarget target, GLint level,
                                    GLint xOffset, GLint yOffset, GLint zOffset,
                                    GLint x, GLint y, GLsizei rawWidth,
                                    GLsizei rawHeight) {
@@ -2174,13 +2183,11 @@ void WebGLTexture::CopyTexSubImage(TexIm
   if (!DoCopyTexOrSubImage(mContext, isSubImage, this, target, level, x, y,
                            srcTotalWidth, srcTotalHeight, srcUsage, xOffset,
                            yOffset, zOffset, width, height, dstUsage)) {
     return;
   }
 
   ////////////////////////////////////
   // Update our specification data?
-
-  imageInfo->mHasData = true;
 }
 
 }  // namespace mozilla