Bug 1280499 - Implement PBOs for textures. - r=jrmuizel
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 21 Jul 2016 19:20:21 -0700
changeset 306580 1326de2b7d1f1fc322bf1bbb0c7a884fb194c64a
parent 306579 4be2f9c41dd318f17bbc5b10b9667a67adc7fc9a
child 306581 07259f1b5eb7f0b69643d115a57ec3418d841d92
push id30489
push usercbook@mozilla.com
push dateTue, 26 Jul 2016 09:56:19 +0000
treeherdermozilla-central@ff1ef8ec0fd8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1280499
milestone50.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 1280499 - Implement PBOs for textures. - r=jrmuizel MozReview-Commit-ID: 3FCinT9hS8j
dom/canvas/TexUnpackBlob.cpp
dom/canvas/TexUnpackBlob.h
dom/canvas/WebGLContext.h
dom/canvas/WebGLTexture.h
dom/canvas/WebGLTextureUpload.cpp
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -314,21 +314,22 @@ DoTexOrSubImage(bool isSubImage, gl::GLC
     }
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // TexUnpackBytes
 
 TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target,
                                uint32_t width, uint32_t height, uint32_t depth,
-                               const void* bytes)
+                               bool isClientData, const void* ptr)
     : TexUnpackBlob(webgl, target,
                     FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width), width,
                     height, depth, false)
-    , mBytes(bytes)
+    , mIsClientData(isClientData)
+    , mPtr(ptr)
 { }
 
 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
 {
@@ -342,18 +343,19 @@ TexUnpackBytes::TexOrSubImage(bool isSub
     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))
+    if (mIsClientData &&
+        !ConvertIfNeeded(webgl, funcName, mPtr, rowStride.value(), bytesPerPixel, format,
+                         dui, &uploadBytes, &tempBuffer))
     {
         return false;
     }
 
     *out_error = DoTexOrSubImage(isSubImage, webgl->gl, target, level, dui, xOffset,
                                  yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes);
     return true;
 }
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -81,22 +81,23 @@ public:
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                GLenum* const out_error) const = 0;
 };
 
 class TexUnpackBytes final : public TexUnpackBlob
 {
 public:
-    const void* const mBytes;
+    const bool mIsClientData;
+    const void* const mPtr;
 
     TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
-                   uint32_t height, uint32_t depth, const void* bytes);
+                   uint32_t height, uint32_t depth, bool isClientData, const void* ptr);
 
-    virtual bool HasData() const override { return bool(mBytes); }
+    virtual bool HasData() const override { return !mIsClientData || bool(mPtr); }
 
     virtual bool 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 override;
 };
 
@@ -121,18 +122,18 @@ class TexUnpackSurface final : public Te
 public:
     const RefPtr<gfx::DataSourceSurface> mSurf;
 
     TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
                      uint32_t height, uint32_t depth, gfx::DataSourceSurface* surf,
                      bool isAlphaPremult);
 
     virtual bool 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 override;
+                               WebGLTexture* tex, TexImageTarget target, GLint level,
+                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
+                               GLint yOffset, GLint zOffset,
+                               GLenum* const out_error) const override;
 };
 
 } // namespace webgl
 } // namespace mozilla
 
 #endif // TEX_UNPACK_BLOB_H_
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -909,18 +909,18 @@ protected:
     bool ValidateTexImageSelection(const char* funcName, uint8_t funcDims,
                                    GLenum texImageTarget, GLint level, GLint xOffset,
                                    GLint yOffset, GLint zOffset, GLsizei width,
                                    GLsizei height, GLsizei depth,
                                    TexImageTarget* const out_target,
                                    WebGLTexture** const out_texture,
                                    WebGLTexture::ImageInfo** const out_imageInfo);
 
-    bool ValidateUnpackInfo(const char* funcName, GLenum format, GLenum type,
-                            webgl::PackingInfo* const out);
+    bool ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format,
+                            GLenum type, webgl::PackingInfo* const out);
 
 // -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
 public:
     void DrawArrays(GLenum mode, GLint first, GLsizei count);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                              GLsizei primcount);
     void DrawElements(GLenum mode, GLsizei count, GLenum type,
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -232,16 +232,22 @@ public:
                        GLint zOffset, GLenum unpackFormat, GLenum unpackType,
                        dom::ImageData* imageData);
 
     void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                        GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLenum unpackFormat, GLenum unpackType,
                        dom::Element* elem, ErrorResult* const out_error);
 
+    void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                       GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
+                       GLint border, GLenum unpackFormat, GLenum unpackType,
+                       WebGLsizeiptr offset);
+
 protected:
     void TexOrSubImageBlob(bool isSubImage, const char* funcName, TexImageTarget target,
                            GLint level, GLenum internalFormat, GLint xOffset,
                            GLint yOffset, GLint zOffset,
                            const webgl::PackingInfo& pi,
                            const webgl::TexUnpackBlob* blob);
 
     bool ValidateTexImageSpecification(const char* funcName, TexImageTarget target,
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -172,78 +172,47 @@ WebGLContext::ValidateUnpackPixels(const
     ErrorInvalidOperation("%s: Desired upload requires more data than is available: (%u"
                           " rows plus %u pixels needed, %u rows plus %u pixels"
                           " available)",
                           funcName, fullRowsNeeded.value(), usedPixelsPerRow.value(),
                           fullRows, tailPixels);
     return false;
 }
 
-static UniquePtr<webgl::TexUnpackBlob>
-BlobFromView(WebGLContext* webgl, const char* funcName, TexImageTarget target,
-             uint32_t width, uint32_t height, uint32_t depth,
-             const webgl::PackingInfo& pi,
-             const dom::Nullable<dom::ArrayBufferView>& maybeView)
+static bool
+ValidateUnpackBytes(WebGLContext* webgl, const char* funcName, uint32_t width,
+                    uint32_t height, uint32_t depth, const webgl::PackingInfo& pi,
+                    uint32_t byteCount, const webgl::TexUnpackBlob* blob)
 {
-    const uint8_t* bytes = nullptr;
-    uint32_t byteCount = 0;
-
-    if (!maybeView.IsNull()) {
-        const auto& view = maybeView.Value();
+    const auto bytesPerPixel = webgl::BytesPerPixel(pi);
+    const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
+    const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
 
-        const auto jsType = JS_GetArrayBufferViewType(view.Obj());
-        if (!DoesJSTypeMatchUnpackType(pi.type, jsType)) {
-            webgl->ErrorInvalidOperation("%s: `pixels` must be compatible with `type`.",
-                                         funcName);
-            return nullptr;
-        }
-
-        if (width && height && depth) {
-            view.ComputeLengthAndData();
-
-            bytes = view.DataAllowShared();
-            byteCount = view.LengthAllowShared();
-        }
+    const auto fullRows = byteCount / rowStride;
+    if (!fullRows.isValid()) {
+        webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.");
+        return false;
     }
 
-    UniquePtr<webgl::TexUnpackBlob> blob(new webgl::TexUnpackBytes(webgl, target, width,
-                                                                   height, depth, bytes));
-
-    //////
-
-    if (bytes) {
-        const auto bytesPerPixel = webgl::BytesPerPixel(pi);
-        const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
-        const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
+    const auto bodyBytes = fullRows.value() * rowStride.value();
+    const auto tailPixels = (byteCount - bodyBytes) / bytesPerPixel;
 
-        const auto fullRows = byteCount / rowStride;
-        if (!fullRows.isValid()) {
-            webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.");
-            return nullptr;
-        }
-
-        const auto bodyBytes = fullRows.value() * rowStride.value();
-        const auto tailPixels = (byteCount - bodyBytes) / bytesPerPixel;
-
-        if (!webgl->ValidateUnpackPixels(funcName, fullRows.value(), tailPixels,
-                                         blob.get()))
-        {
-            return nullptr;
-        }
-    }
-
-    //////
-
-    return Move(blob);
+    return webgl->ValidateUnpackPixels(funcName, fullRows.value(), tailPixels, blob);
 }
 
 bool
-WebGLContext::ValidateUnpackInfo(const char* funcName, GLenum format, GLenum type,
-                                 webgl::PackingInfo* const out)
+WebGLContext::ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format,
+                                 GLenum type, webgl::PackingInfo* const out)
 {
+    if (usePBOs != bool(mBoundPixelUnpackBuffer)) {
+        ErrorInvalidOperation("%s: PACK_BUFFER must be %s.", funcName,
+                              (usePBOs ? "non-null" : "null"));
+        return false;
+    }
+
     if (!mFormatUsage->AreUnpackEnumsValid(format, type)) {
         ErrorInvalidEnum("%s: Invalid unpack format/type: 0x%04x/0x%04x", funcName,
                          format, type);
         return false;
     }
 
     out->format = format;
     out->type = type;
@@ -260,27 +229,107 @@ WebGLTexture::TexOrSubImage(bool isSubIm
 {
     uint32_t width, height, depth;
     if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, border,
                          &width, &height, &depth))
     {
         return;
     }
 
+    const bool usePBOs = false;
     webgl::PackingInfo pi;
-    if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi))
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
-    const auto blob = BlobFromView(mContext, funcName, target, width, height, depth, pi,
-                                   maybeView);
-    if (!blob)
+    ////
+
+    const void* bytes = nullptr;
+    uint32_t byteCount = 0;
+
+    if (!maybeView.IsNull()) {
+        const auto& view = maybeView.Value();
+
+        const auto jsType = JS_GetArrayBufferViewType(view.Obj());
+        if (!DoesJSTypeMatchUnpackType(pi.type, jsType)) {
+            mContext->ErrorInvalidOperation("%s: `pixels` not compatible with `type`.",
+                                            funcName);
+            return;
+        }
+
+        if (width && height && depth) {
+            view.ComputeLengthAndData();
+
+            bytes = view.DataAllowShared();
+            byteCount = view.LengthAllowShared();
+        }
+    }
+
+    const bool isClientData = true;
+    const webgl::TexUnpackBytes blob(mContext, target, width, height, depth, isClientData,
+                                     bytes);
+
+    if (bytes &&
+        !ValidateUnpackBytes(mContext, funcName, width, height, depth, pi, byteCount,
+                             &blob))
+    {
+        return;
+    }
+
+    TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
+                      yOffset, zOffset, pi, &blob);
+}
+
+void
+WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                            GLint level, GLenum internalFormat, GLint xOffset,
+                            GLint yOffset, GLint zOffset, GLsizei rawWidth,
+                            GLsizei rawHeight, GLsizei rawDepth, GLint border,
+                            GLenum unpackFormat, GLenum unpackType,
+                            WebGLsizeiptr offset)
+{
+    uint32_t width, height, depth;
+    if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, border,
+                         &width, &height, &depth))
+    {
+        return;
+    }
+
+    const bool usePBOs = true;
+    webgl::PackingInfo pi;
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
+    ////
+
+    if (offset < 0) {
+        mContext->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
+        return;
+    }
+
+    const bool isClientData = false;
+    const auto ptr = (const void*)offset;
+    const webgl::TexUnpackBytes blob(mContext, target, width, height, depth, isClientData,
+                                     ptr);
+
+    const auto& packBuffer = mContext->mBoundPixelUnpackBuffer;
+    const auto bufferByteCount = packBuffer->ByteLength();
+
+    uint32_t byteCount = 0;
+    if (bufferByteCount >= offset) {
+        byteCount = bufferByteCount - offset;
+    }
+
+    if (!ValidateUnpackBytes(mContext, funcName, width, height, depth, pi, byteCount,
+                             &blob))
+    {
+        return;
+    }
+
     TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
-                      yOffset, zOffset, pi, blob.get());
+                      yOffset, zOffset, pi, &blob);
 }
 
 ////////////////////////////////////////
 // ImageData
 
 static already_AddRefed<gfx::DataSourceSurface>
 FromImageData(WebGLContext* webgl, const char* funcName, GLenum unpackType,
               dom::ImageData* imageData, dom::Uint8ClampedArray* scopedArr)
@@ -314,18 +363,19 @@ FromImageData(WebGLContext* webgl, const
 }
 
 void
 WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                             GLint level, GLenum internalFormat, GLint xOffset,
                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
                             GLenum unpackType, dom::ImageData* imageData)
 {
+    const bool usePBOs = false;
     webgl::PackingInfo pi;
-    if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi))
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
     if (!imageData) {
         // Spec says to generate an INVALID_VALUE error
         mContext->ErrorInvalidValue("%s: Null ImageData.", funcName);
         return;
     }
 
@@ -363,18 +413,19 @@ WebGLTexture::TexOrSubImage(bool isSubIm
 
 void
 WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                             GLint level, GLenum internalFormat, GLint xOffset,
                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
                             GLenum unpackType, dom::Element* elem,
                             ErrorResult* const out_error)
 {
+    const bool usePBOs = false;
     webgl::PackingInfo pi;
-    if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi))
+    if (!mContext->ValidateUnpackInfo(funcName, usePBOs, unpackFormat, unpackType, &pi))
         return;
 
     //////
 
     uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
                      nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
 
     if (mContext->mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
@@ -410,17 +461,19 @@ WebGLTexture::TexOrSubImage(bool isSubIm
     //////
 
     // Eventually, these will be args.
     const uint32_t width = elemWidth;
     const uint32_t height = elemHeight;
     const uint32_t depth = 1;
 
     if (!layersImage && !dataSurf) {
-        const webgl::TexUnpackBytes blob(mContext, target, width, height, depth, nullptr);
+        const bool isClientData = true;
+        const webgl::TexUnpackBytes blob(mContext, target, width, height, depth,
+                                         isClientData, nullptr);
         TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
                           yOffset, zOffset, pi, &blob);
         return;
     }
 
     //////
 
     // While it's counter-intuitive, the shape of the SFEResult API means that we should