Bug 1286758 - Handle y-flip for unconvertable types. - r=mtseng
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 13 Jul 2016 12:36:51 -0700
changeset 330423 0cd6f350309eb1125c3d85a98b717365ff8ee674
parent 330422 b658098634b8ae8de414d5baac04b4645bf4c525
child 330424 7a99ed13ea7f91cadc80bc593dabb7b0a075a0ce
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmtseng
bugs1286758
milestone50.0a1
Bug 1286758 - Handle y-flip for unconvertable types. - r=mtseng MozReview-Commit-ID: 2zK3qzUtHQx
dom/canvas/TexUnpackBlob.cpp
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -186,64 +186,103 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
     //////
 
     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                      : gl::OriginPos::BottomLeft);
     const auto dstOrigin = gl::OriginPos::BottomLeft;
     const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha;
 
     const auto pi = dstDUI->ToPacking();
-    const auto dstFormat = FormatForPackingInfo(pi);
 
     const auto dstBPP = webgl::BytesPerPixel(pi);
     const auto dstWidthBytes = CheckedUint32(dstBPP) * mWidth;
     const auto dstRowLengthBytes = CheckedUint32(dstBPP) * mRowLength;
 
     const auto dstAlignment = mAlignment;
     const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
 
     //////
 
     const auto dstTotalRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight;
+    const auto dstUsedSizeExceptLastRow = (dstTotalRows - 1) * dstStride;
 
-    const auto dstSize = skipBytes + (dstTotalRows - 1) * dstStride + dstWidthBytes;
+    const auto dstSize = skipBytes + dstUsedSizeExceptLastRow + dstWidthBytes;
     if (!dstSize.isValid()) {
         webgl->ErrorOutOfMemory("%s: Invalid dstSize calculation during conversion.",
                                 funcName);
         return false;
     }
 
     //////
 
-    bool needsConvert = (srcOrigin != dstOrigin ||
-                         srcFormat != dstFormat ||
-                         srcStride != dstStride.value());
+    const auto dstFormat = FormatForPackingInfo(pi);
 
-    if (UnpackFormatHasAlpha(dstDUI->unpackFormat)) {
-        needsConvert |= (mIsSrcPremult != isDstPremult);
+    bool premultMatches = (mIsSrcPremult == isDstPremult);
+    if (!UnpackFormatHasAlpha(dstDUI->unpackFormat)) {
+        premultMatches = true;
     }
 
-    if (!needsConvert)
-        return true;
+    const bool needsPixelConversion = (srcFormat != dstFormat || !premultMatches);
+    const bool originsMatch = (srcOrigin == dstOrigin);
+
+    MOZ_ASSERT_IF(!needsPixelConversion, srcBPP == dstBPP);
 
-    ////////////
-    // Ugh, ok, fine!
-
-    webgl->GenerateWarning("%s: Incurred CPU data conversion, which is slow.",
-                           funcName);
+    if (!needsPixelConversion &&
+        originsMatch &&
+        srcStride == dstStride.value())
+    {
+        // No conversion needed!
+        return true;
+    }
 
     //////
+    // We need some sort of conversion, so create the dest buffer.
 
     *out_anchoredBuffer = calloc(1, dstSize.value());
-    if (!out_anchoredBuffer->get()) {
+    *out_bytes = out_anchoredBuffer->get();
+    if (!*out_bytes) {
         webgl->ErrorOutOfMemory("%s: Unable to allocate buffer during conversion.",
                                 funcName);
         return false;
     }
-    const auto dstBegin = (uint8_t*)out_anchoredBuffer->get() + skipBytes;
+    const auto dstBegin = (uint8_t*)(*out_bytes) + skipBytes;
+
+    //////
+    // Row conversion
+
+    if (!needsPixelConversion) {
+        webgl->GenerateWarning("%s: Incurred CPU row conversion, which is slow.",
+                               funcName);
+
+        const uint8_t* srcRow = srcBegin;
+        uint8_t* dstRow = dstBegin;
+        const auto widthBytes = dstWidthBytes.value();
+        ptrdiff_t dstCopyStride = dstStride.value();
+
+        if (!originsMatch) {
+            dstRow += dstUsedSizeExceptLastRow.value();
+            dstCopyStride = -dstCopyStride;
+        }
+
+        for (uint32_t i = 0; i < dstTotalRows.value(); i++) {
+            memcpy(dstRow, srcRow, widthBytes);
+            srcRow += srcStride;
+            dstRow += dstCopyStride;
+        }
+        return true;
+    }
+
+    ////////////
+    // Pixel conversion.
+
+    MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
+    MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
+
+    webgl->GenerateWarning("%s: Incurred CPU pixel conversion, which is very slow.",
+                           funcName);
 
     //////
 
     // And go!:
     bool wasTrivial;
     if (!ConvertImage(mWidth, dstTotalRows.value(),
                       srcBegin, srcStride, srcOrigin, srcFormat, mIsSrcPremult,
                       dstBegin, dstStride.value(), dstOrigin, dstFormat, isDstPremult,
@@ -254,17 +293,16 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
     }
 
     if (!wasTrivial) {
         webgl->GenerateWarning("%s: Chosen format/type incurred an expensive reformat:"
                                " 0x%04x/0x%04x",
                                funcName, dstDUI->unpackFormat, dstDUI->unpackType);
     }
 
-    *out_bytes = out_anchoredBuffer->get();
     return true;
 }
 
 static GLenum
 DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level,
                 const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset,
                 GLsizei width, GLsizei height, GLsizei depth, const void* data)
 {
@@ -292,25 +330,26 @@ bool
 TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                               WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
                               GLint yOffset, GLint zOffset, GLenum* const out_error) const
 {
     WebGLContext* webgl = tex->mContext;
 
     const auto pi = dui->ToPacking();
-    const auto format = FormatForPackingInfo(pi);
 
     const auto bytesPerPixel = webgl::BytesPerPixel(pi);
     const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
     const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
     if (!rowStride.isValid()) {
         MOZ_CRASH("Should be checked earlier.");
     }
 
+    const auto format = FormatForPackingInfo(pi);
+
     const void* uploadBytes;
     UniqueBuffer tempBuffer;
     if (!ConvertIfNeeded(webgl, funcName, mBytes, rowStride.value(), bytesPerPixel,
                          format, dui, &uploadBytes, &tempBuffer))
     {
         return false;
     }