Bug 1250710 - Workaround nvidia when stride is longer than the last row with PBOs. - r=jrmuizel
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 16 Jun 2016 07:30:47 -0700
changeset 388860 40e9d24fd8eab4cd9d9e434ca3d5e815148a7b3f
parent 388859 c70eb1338bf4621388b0187510dd288ca2be6591
child 388861 0a20a55213d28e92a78f8f483aa27c38818732c7
push id23247
push userbmo:jgilbert@mozilla.com
push dateMon, 18 Jul 2016 06:13:58 +0000
reviewersjrmuizel
bugs1250710
milestone50.0a1
Bug 1250710 - Workaround nvidia when stride is longer than the last row with PBOs. - r=jrmuizel MozReview-Commit-ID: FaGumqLiIrA
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextGL.cpp
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -539,17 +539,18 @@ public:
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 protected:
     bool ReadPixels_SharedPrecheck(ErrorResult* const out_error);
     void ReadPixelsImpl(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                         GLenum type, void* data, uint32_t dataLen);
     bool DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
                                 GLsizei width, GLsizei height, GLenum format,
-                                GLenum destType, void* dest);
+                                GLenum destType, void* dest, uint32_t dataLen,
+                                uint32_t rowStride);
 public:
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type,
                     const dom::Nullable<dom::ArrayBufferView>& pixels,
                     ErrorResult& rv);
     void RenderbufferStorage(GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height);
 protected:
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1226,17 +1226,18 @@ IsNeedsANGLEWorkAround(const webgl::Form
     default:
         return false;
     }
 }
 
 bool
 WebGLContext::DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
                                      GLsizei width, GLsizei height, GLenum format,
-                                     GLenum destType, void* dest)
+                                     GLenum destType, void* dest, uint32_t destSize,
+                                     uint32_t rowStride)
 {
     if (gl->WorkAroundDriverBugs() &&
         gl->IsANGLE() &&
         gl->Version() < 300 && // ANGLE ES2 doesn't support HALF_FLOAT reads properly.
         IsNeedsANGLEWorkAround(srcFormat))
     {
         MOZ_RELEASE_ASSERT(!IsWebGL2()); // No SKIP_PIXELS, etc.
         MOZ_ASSERT(!mBoundPixelPackBuffer); // Let's be real clear.
@@ -1300,17 +1301,46 @@ WebGLContext::DoReadPixelsAndConvert(con
 
             srcRow += readStride;
             dstRow += destStride;
         }
 
         return true;
     }
 
-    gl->fReadPixels(x, y, width, height, format, destType, dest);
+    // On at least Win+NV, we'll get PBO errors if we don't have at least
+    // `rowStride * height` bytes available to read into.
+    const auto naiveBytesNeeded = CheckedUint32(rowStride) * height;
+    const bool isDangerCloseToEdge = (!naiveBytesNeeded.isValid() ||
+                                      naiveBytesNeeded.value() > destSize);
+    const bool useParanoidHandling = (gl->WorkAroundDriverBugs() &&
+                                      isDangerCloseToEdge &&
+                                      mBoundPixelPackBuffer);
+    if (!useParanoidHandling) {
+        gl->fReadPixels(x, y, width, height, format, destType, dest);
+        return true;
+    }
+
+    // Read everything but the last row.
+    const auto bodyHeight = height - 1;
+    if (bodyHeight) {
+        gl->fReadPixels(x, y, width, bodyHeight, format, destType, dest);
+    }
+
+    // Now read the last row.
+    gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
+    gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
+    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
+
+    const auto tailRowOffset = (char*)dest + rowStride * bodyHeight;
+    gl->fReadPixels(x, y+bodyHeight, width, 1, format, destType, tailRowOffset);
+
+    gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment);
+    gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
+    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
     return true;
 }
 
 static bool
 IsFormatAndTypeUnpackable(GLenum format, GLenum type, bool isWebGL2)
 {
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
@@ -1759,17 +1789,17 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
     uint32_t readX, readY;
     uint32_t writeX, writeY;
     uint32_t rwWidth, rwHeight;
     Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
     Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
 
     if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
         DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat,
-                               packType, dest);
+                               packType, dest, dataLen, rowStride);
         return;
     }
 
     // Read request contains out-of-bounds pixels. Unfortunately:
     // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
     // "If any of these pixels lies outside of the window allocated to the current GL
     //  context, or outside of the image attached to the currently bound framebuffer
     //  object, then the values obtained for those pixels are undefined."
@@ -1791,29 +1821,29 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
         if (!mPixelStore_PackRowLength) {
             gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
                              mPixelStore_PackSkipPixels + width);
         }
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
 
         DoReadPixelsAndConvert(srcFormat->format, readX, readY, rwWidth, rwHeight,
-                               packFormat, packType, dest);
+                               packFormat, packType, dest, dataLen, rowStride);
 
         gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
     } else {
         // I *did* say "hilariously slow".
 
         uint8_t* row = (uint8_t*)dest + writeX * bytesPerPixel;
         row += writeY * rowStride;
         for (uint32_t j = 0; j < rwHeight; j++) {
             DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1,
-                                   packFormat, packType, row);
+                                   packFormat, packType, row, dataLen, rowStride);
             row += rowStride;
         }
     }
 }
 
 void
 WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
                                        GLsizei samples, GLenum internalFormat,