--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -7,16 +7,17 @@
#include "GLBlitHelper.h"
#include "GLContext.h"
#include "GLDefs.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/RefPtr.h"
#include "nsLayoutUtils.h"
+#include "WebGLBuffer.h"
#include "WebGLContext.h"
#include "WebGLTexelConversions.h"
#include "WebGLTexture.h"
namespace mozilla {
namespace webgl {
TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, uint32_t alignment,
@@ -28,16 +29,18 @@ TexUnpackBlob::TexUnpackBlob(const WebGL
, mSkipPixels(webgl->mPixelStore_UnpackSkipPixels)
, mSkipRows(webgl->mPixelStore_UnpackSkipRows)
, mSkipImages(webgl->mPixelStore_UnpackSkipImages)
, mWidth(width)
, mHeight(height)
, mDepth(depth)
+
+ , mNeedsExactUpload(false)
{ }
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)
{
if (isSubImage) {
@@ -145,33 +148,34 @@ 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;
gl::GLContext* gl = webgl->gl;
- const void* uploadPtr = mPtr;
+ const webgl::PackingInfo pi = { dui->unpackFormat, dui->unpackType };
+ const auto bytesPerPixel = webgl::BytesPerPixel(pi);
+
+ auto uploadPtr = (const uint8_t*)mPtr;
UniqueBuffer tempBuffer;
do {
if (!mWidth || !mHeight || !mDepth)
break;
if (webgl->IsWebGL2())
break;
MOZ_ASSERT(mDepth == 1);
MOZ_ASSERT(mIsClientData);
if (!mPtr)
break;
- const webgl::PackingInfo pi = { dui->unpackFormat, dui->unpackType };
-
const bool needsYFlip = webgl->mPixelStore_FlipY;
bool needsAlphaPremult = webgl->mPixelStore_PremultiplyAlpha;
if (!UnpackFormatHasAlpha(pi.format))
needsAlphaPremult = false;
if (!needsYFlip && !needsAlphaPremult)
break;
@@ -196,18 +200,16 @@ TexUnpackBytes::TexOrSubImage(bool isSub
funcName);
return false;
}
webgl->GenerateWarning("%s: Uploading ArrayBuffers with FLIP_Y or"
" PREMULTIPLY_ALPHA is slow.",
funcName);
- const auto bytesPerPixel = webgl::BytesPerPixel(pi);
-
const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
const auto imageStride = rowStride * mImageHeight;
if (!imageStride.isValid()) {
webgl->ErrorOutOfMemory("%s: Invalid calculation during"
" FLIP_Y/PREMULTIPLY_ALPHA handling.",
funcName);
@@ -230,17 +232,17 @@ TexUnpackBytes::TexOrSubImage(bool isSub
uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride.value() * (mHeight - 1);
while (src != srcEnd) {
memcpy(dst, src, bytesPerRow.value());
src += rowStride.value();
dst -= rowStride.value();
}
- uploadPtr = tempBuffer.get();
+ uploadPtr = (const uint8_t*)tempBuffer.get();
break;
}
const auto texelFormat = FormatFromPacking(pi);
if (texelFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) {
MOZ_ASSERT(false, "Bad texelFormat from pi.");
webgl->ErrorOutOfMemory("%s: FormatFromPacking failed during"
" PREMULTIPLY_ALPHA handling.",
@@ -266,21 +268,90 @@ TexUnpackBytes::TexOrSubImage(bool isSub
{
MOZ_ASSERT(false, "ConvertImage failed unexpectedly.");
webgl->ErrorOutOfMemory("%s: ConvertImage failed during PREMULTIPLY_ALPHA"
" handling.",
funcName);
return false;
}
- uploadPtr = tempBuffer.get();
+ uploadPtr = (const uint8_t*)tempBuffer.get();
} while (false);
- *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
- zOffset, mWidth, mHeight, mDepth, uploadPtr);
+ //////
+
+ bool useParanoidHandling = false;
+ if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) {
+ webgl->GenerateWarning("%s: Uploads from a buffer with a final row with a byte"
+ " count smaller than the row stride can incur extra"
+ " overhead.",
+ funcName);
+
+ if (gl->WorkAroundDriverBugs()) {
+ useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
+ }
+ }
+
+ if (!useParanoidHandling) {
+ *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
+ zOffset, mWidth, mHeight, mDepth, uploadPtr);
+ return true;
+ }
+
+ //////
+
+ MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
+
+ if (!isSubImage) {
+ // Alloc first to catch OOMs.
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+ *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
+ zOffset, mWidth, mHeight, mDepth, nullptr);
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER,
+ webgl->mBoundPixelUnpackBuffer->mGLName);
+ }
+
+ //////
+
+ if (mDepth > 1) {
+ *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
+ zOffset, mWidth, mHeight, mDepth-1, uploadPtr);
+ }
+
+ if (mHeight > 1) {
+ *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
+ zOffset+mDepth-1, mWidth, mHeight-1, 1, uploadPtr);
+ }
+
+ const uint32_t bytesPerRow = mRowLength * bytesPerPixel;
+ const uint32_t rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
+ const uint32_t imageStride = rowStride * mImageHeight;
+
+ const uint32_t usedImages = mSkipImages + mDepth;
+ const uint32_t usedRows = mSkipRows + mHeight;
+
+ uploadPtr += (usedImages - 1) * imageStride;
+ uploadPtr += (usedRows - 1) * rowStride;
+
+ //////
+
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0);
+
+ *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset,
+ yOffset+mHeight-1, zOffset+mDepth-1, mWidth, 1, 1,
+ uploadPtr);
+
+ gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, webgl->mPixelStore_UnpackImageHeight);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, webgl->mPixelStore_UnpackSkipImages);
+ gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, webgl->mPixelStore_UnpackSkipRows);
+
return true;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// TexUnpackImage
TexUnpackImage::TexUnpackImage(const WebGLContext* webgl, uint32_t imageHeight,
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -52,28 +52,30 @@ public:
const uint32_t mImageHeight;
const uint32_t mSkipPixels;
const uint32_t mSkipRows;
const uint32_t mSkipImages;
const uint32_t mWidth;
const uint32_t mHeight;
const uint32_t mDepth;
+ bool mNeedsExactUpload;
+
protected:
TexUnpackBlob(const WebGLContext* webgl, uint32_t alignment, uint32_t rowLength,
uint32_t imageHeight, uint32_t width, uint32_t height, uint32_t depth);
public:
virtual bool HasData() const { return true; }
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 = 0;
+ WebGLTexture* tex, TexImageTarget target, GLint level,
+ const webgl::DriverUnpackInfo* dui, GLint xOffset,
+ GLint yOffset, GLint zOffset,
+ GLenum* const out_error) const = 0;
static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
gl::OriginPos* const out_dst);
};
class TexUnpackBytes : public TexUnpackBlob
{
public:
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -79,17 +79,17 @@ ValidateExtents(WebGLContext* webgl, con
*out_width = width;
*out_height = height;
*out_depth = depth;
return true;
}
static bool
ValidateUnpackPixels(WebGLContext* webgl, const char* funcName, uint32_t fullRows,
- uint32_t tailPixels, const webgl::TexUnpackBlob* blob)
+ uint32_t tailPixels, webgl::TexUnpackBlob* blob)
{
const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth;
const auto usedRowsPerImage = CheckedUint32(blob->mSkipRows) + blob->mHeight;
const auto usedImages = CheckedUint32(blob->mSkipImages) + blob->mDepth;
bool unpackVarsValid = false;
if (!usedPixelsPerRow.isValid() ||
!usedRowsPerImage.isValid() ||
@@ -121,40 +121,42 @@ ValidateUnpackPixels(WebGLContext* webgl
webgl->ErrorOutOfMemory("%s: Invalid calculation for required row count.",
funcName);
return false;
}
if (fullRows > fullRowsNeeded.value())
return true;
- if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value())
+ if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) {
+ blob->mNeedsExactUpload = true;
return true;
+ }
webgl->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 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)
+ uint32_t byteCount, webgl::TexUnpackBlob* blob)
{
const auto bytesPerPixel = webgl::BytesPerPixel(pi);
const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
const auto fullRows = byteCount / rowStride;
if (!fullRows.isValid()) {
webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.");
- return nullptr;
+ return false;
}
const auto bodyBytes = fullRows.value() * rowStride.value();
const auto tailPixels = (byteCount - bodyBytes) / bytesPerPixel;
return ValidateUnpackPixels(webgl, funcName, fullRows.value(), tailPixels, blob);
}
@@ -262,17 +264,17 @@ WebGLTexture::TexOrSubImage(bool isSubIm
view.ComputeLengthAndData();
bytes = view.DataAllowShared();
byteCount = view.LengthAllowShared();
}
}
const bool isClientData = true;
- const webgl::TexUnpackBytes blob(mContext, width, height, depth, isClientData, bytes);
+ webgl::TexUnpackBytes blob(mContext, width, height, depth, isClientData, bytes);
//////
if (bytes && !ValidateUnpackBytes(mContext, funcName, width, height, depth, pi,
byteCount, &blob))
{
return;
}
@@ -308,17 +310,17 @@ WebGLTexture::TexOrSubImage(bool isSubIm
if (offset < 0) {
mContext->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
return;
}
const bool isClientData = false;
const void* ptr = (const void*)offset;
- const webgl::TexUnpackBytes blob(mContext, width, height, depth, isClientData, ptr);
+ webgl::TexUnpackBytes blob(mContext, width, height, depth, isClientData, ptr);
//////
const auto& packBuffer = mContext->mBoundPixelUnpackBuffer;
const auto bufferByteCount = packBuffer->ByteLength();
uint32_t byteCount = 0;
if (bufferByteCount >= offset) {
@@ -436,18 +438,18 @@ WebGLTexture::TexOrSubImage(bool isSubIm
if (!surf)
return;
// WhatWG "HTML Living Standard" (30 October 2015):
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
// non-premultiplied alpha values."
const bool surfIsAlphaPremult = false;
- const webgl::TexUnpackSurface blob(mContext, imageHeight, width, height, depth, surf,
- surfIsAlphaPremult);
+ webgl::TexUnpackSurface blob(mContext, imageHeight, width, height, depth, surf,
+ surfIsAlphaPremult);
const uint32_t fullRows = imageData->Height();
const uint32_t tailPixels = 0;
if (!ValidateUnpackPixels(mContext, funcName, fullRows, tailPixels, &blob))
return;
TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset,
yOffset, zOffset, pi, &blob);
@@ -534,17 +536,17 @@ WebGLTexture::TexOrSubImage(bool isSubIm
uint32_t rowLength, imageHeight;
if (!mContext->GetUnpackValuesForImage(funcName, elemWidth, elemHeight, &rowLength,
&imageHeight))
{
return;
}
- UniquePtr<const webgl::TexUnpackBlob> blob;
+ UniquePtr<webgl::TexUnpackBlob> blob;
const bool isAlphaPremult = sfer.mIsPremultiplied;
if (layersImage) {
blob.reset(new webgl::TexUnpackImage(mContext, imageHeight, width, height, depth,
layersImage, isAlphaPremult));
} else {
MOZ_ASSERT(surf);
blob.reset(new webgl::TexUnpackSurface(mContext, imageHeight, width, height,