From 8a6d29efd0080615f5c53f51268948e31a59bc0a Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Wed, 22 Jun 2016 10:28:11 -0700 Subject: [PATCH] Bug 1281098 - Fix UNPACK_ handling. - r=jrmuizel MozReview-Commit-ID: Au9eiKSxquJ --- dom/canvas/TexUnpackBlob.cpp | 242 ++++++------ dom/canvas/TexUnpackBlob.h | 100 ++--- dom/canvas/WebGLContext.h | 13 + dom/canvas/WebGLTexture.h | 27 +- dom/canvas/WebGLTextureUpload.cpp | 607 +++++++++++++++++++----------- 5 files changed, 591 insertions(+), 398 deletions(-) diff --git a/dom/canvas/TexUnpackBlob.cpp b/dom/canvas/TexUnpackBlob.cpp index 8d9a22b9e157..fd19a1978d60 100644 --- a/dom/canvas/TexUnpackBlob.cpp +++ b/dom/canvas/TexUnpackBlob.cpp @@ -19,6 +19,24 @@ namespace mozilla { namespace webgl { +TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, uint32_t alignment, + uint32_t rowLength, uint32_t imageHeight, uint32_t width, + uint32_t height, uint32_t depth, bool hasData) + : mAlignment(alignment) + , mRowLength(rowLength) + , mImageHeight(imageHeight) + + , mSkipPixels(webgl->mPixelStore_UnpackSkipPixels) + , mSkipRows(webgl->mPixelStore_UnpackSkipRows) + , mSkipImages(webgl->mPixelStore_UnpackSkipImages) + + , mWidth(width) + , mHeight(height) + , mDepth(depth) + + , mHasData(hasData) +{ } + static GLenum DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level, const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset, @@ -48,33 +66,21 @@ TexUnpackBlob::OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src, ////////////////////////////////////////////////////////////////////////////////////////// // TexUnpackBytes -bool -TexUnpackBytes::ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, - const webgl::PackingInfo& pi) +static uint32_t +FallbackOnZero(uint32_t val, uint32_t fallback) { - if (!mBytes) - return true; - - const auto bytesPerPixel = webgl::BytesPerPixel(pi); - const auto bytesNeeded = webgl->GetUnpackSize(isFunc3D, mWidth, mHeight, mDepth, - bytesPerPixel); - if (!bytesNeeded.isValid()) { - webgl->ErrorInvalidOperation("%s: Overflow while computing the needed buffer" - " size.", - funcName); - return false; - } - - if (mByteCount < bytesNeeded.value()) { - webgl->ErrorInvalidOperation("%s: Provided buffer is too small. (needs %u, has" - " %u)", - funcName, bytesNeeded.value(), mByteCount); - return false; - } - - return true; + return (val ? val : fallback); } +TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, uint32_t width, uint32_t height, + uint32_t depth, const void* bytes) + : TexUnpackBlob(webgl, webgl->mPixelStore_UnpackAlignment, + FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width), + FallbackOnZero(webgl->mPixelStore_UnpackImageHeight, height), + width, height, depth, bool(bytes)) + , mBytes(bytes) +{ } + static bool UnpackFormatHasAlpha(GLenum unpackFormat) { @@ -136,11 +142,11 @@ FormatFromPacking(const webgl::PackingInfo& pi) return WebGLTexelFormat::FormatNotSupportingAnyConversion; } -void +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_glError) + GLint yOffset, GLint zOffset, GLenum* const out_error) const { WebGLContext* webgl = tex->mContext; gl::GLContext* gl = webgl->gl; @@ -167,35 +173,63 @@ TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun if (!needsYFlip && !needsAlphaPremult) break; + //////////// // This is literally the worst. + + if (mSkipPixels || mSkipRows || mSkipImages || + mRowLength != mWidth || + mImageHeight != mHeight) + { + webgl->ErrorInvalidOperation("%s: FLIP_Y and PREMULTIPLY_ALPHA are" + " incompatible with WebGL 2's new UNPACK_*" + " settings.", + funcName); + return false; + } + + if (mDepth != 1) { + webgl->ErrorInvalidOperation("%s: FLIP_Y and PREMULTIPLY_ALPHA are" + " incompatible with 3D textures.", + funcName); + return false; + } + webgl->GenerateWarning("%s: Uploading ArrayBuffers with FLIP_Y or" " PREMULTIPLY_ALPHA is slow.", funcName); - tempBuffer = malloc(mByteCount); - if (!tempBuffer) { - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; + 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); + return false; } - const auto bytesPerPixel = webgl::BytesPerPixel(pi); - const auto rowByteAlignment = webgl->mPixelStore_UnpackAlignment; - - const size_t bytesPerRow = bytesPerPixel * mWidth; - const size_t rowStride = RoundUpToMultipleOf(bytesPerRow, rowByteAlignment); + tempBuffer = malloc(imageStride.value()); + if (!tempBuffer) { + webgl->ErrorOutOfMemory("%s: OOM during FLIP_Y/PREMULTIPLY_ALPHA handling.", + funcName); + return false; + } if (!needsAlphaPremult) { MOZ_ASSERT(needsYFlip); const uint8_t* src = (const uint8_t*)mBytes; - const uint8_t* const srcEnd = src + rowStride * mHeight; + const uint8_t* const srcEnd = src + rowStride.value() * mHeight; - uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride * (mHeight - 1); + uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride.value() * (mHeight - 1); while (src != srcEnd) { - memcpy(dst, src, bytesPerRow); - src += rowStride; - dst -= rowStride; + memcpy(dst, src, bytesPerRow.value()); + src += rowStride.value(); + dst -= rowStride.value(); } uploadBytes = tempBuffer.get(); @@ -205,8 +239,10 @@ TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun const auto texelFormat = FormatFromPacking(pi); if (texelFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) { MOZ_ASSERT(false, "Bad texelFormat from pi."); - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; + webgl->ErrorOutOfMemory("%s: FormatFromPacking failed during" + " PREMULTIPLY_ALPHA handling.", + funcName); + return false; } const auto srcOrigin = gl::OriginPos::BottomLeft; @@ -220,44 +256,46 @@ TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun MOZ_ASSERT(srcOrigin != dstOrigin || srcPremultiplied != dstPremultiplied); bool unused_wasTrivial; if (!ConvertImage(mWidth, mHeight, - mBytes, rowStride, srcOrigin, texelFormat, srcPremultiplied, - tempBuffer.get(), rowStride, dstOrigin, texelFormat, + mBytes, rowStride.value(), srcOrigin, texelFormat, + srcPremultiplied, + tempBuffer.get(), rowStride.value(), dstOrigin, texelFormat, dstPremultiplied, &unused_wasTrivial)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; + webgl->ErrorOutOfMemory("%s: ConvertImage failed during PREMULTIPLY_ALPHA" + " handling.", + funcName); + return false; } uploadBytes = tempBuffer.get(); } while (false); - GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, - zOffset, mWidth, mHeight, mDepth, uploadBytes); - *out_glError = error; + *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, + zOffset, mWidth, mHeight, mDepth, uploadBytes); + return true; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // TexUnpackImage -TexUnpackImage::TexUnpackImage(const RefPtr& image, bool isAlphaPremult) - : TexUnpackBlob(image->GetSize().width, image->GetSize().height, 1, true) +TexUnpackImage::TexUnpackImage(const WebGLContext* webgl, uint32_t imageHeight, + uint32_t width, uint32_t height, uint32_t depth, + const RefPtr& image, bool isAlphaPremult) + : TexUnpackBlob(webgl, 0, image->GetSize().width, imageHeight, width, height, depth, + true) , mImage(image) , mIsAlphaPremult(isAlphaPremult) { } -TexUnpackImage::~TexUnpackImage() -{ } - -void +bool TexUnpackImage::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_glError) + GLint yOffset, GLint zOffset, GLenum* const out_error) const { MOZ_ASSERT_IF(needsRespec, !isSubImage); - *out_glError = 0; WebGLContext* webgl = tex->mContext; @@ -265,14 +303,10 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun gl->MakeCurrent(); if (needsRespec) { - GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset, - yOffset, zOffset, mWidth, mHeight, mDepth, - nullptr); - if (error) { - MOZ_ASSERT(!error); - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; - } + *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset, + yOffset, zOffset, mWidth, mHeight, mDepth, + nullptr); + return true; } do { @@ -309,7 +343,9 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun break; } - return; // Blitting was successful, so we're done! + // Blitting was successful, so we're done! + *out_error = 0; + return true; } while (false); webgl->GenerateWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU" @@ -318,20 +354,34 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun RefPtr surface = mImage->GetAsSourceSurface(); if (!surface) { - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; + webgl->ErrorOutOfMemory("%s: GetAsSourceSurface failed after blit failed for" + " TexUnpackImage.", + funcName); + return false; } - TexUnpackSurface surfBlob(surface, mIsAlphaPremult); + TexUnpackSurface surfBlob(webgl, mImageHeight, mWidth, mHeight, mDepth, surface, + mIsAlphaPremult); - surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui, - xOffset, yOffset, zOffset, out_glError); + return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, + dui, xOffset, yOffset, zOffset, out_error); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // TexUnpackSurface +TexUnpackSurface::TexUnpackSurface(const WebGLContext* webgl, uint32_t imageHeight, + uint32_t width, uint32_t height, uint32_t depth, + gfx::SourceSurface* surf, bool isAlphaPremult) + : TexUnpackBlob(webgl, 0, surf->GetSize().width, imageHeight, width, height, depth, + true) + , mSurf(surf) + , mIsAlphaPremult(isAlphaPremult) +{ } + +////////// + static bool GuessAlignment(const void* data, size_t bytesPerRow, size_t stride, size_t maxAlignment, size_t* const out_alignment) @@ -745,24 +795,13 @@ TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackI //////////////////// -TexUnpackSurface::TexUnpackSurface(const RefPtr& surf, - bool isAlphaPremult) - : TexUnpackBlob(surf->GetSize().width, surf->GetSize().height, 1, true) - , mSurf(surf) - , mIsAlphaPremult(isAlphaPremult) -{ } - -TexUnpackSurface::~TexUnpackSurface() -{ } - -void +bool TexUnpackSurface::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_glError) + GLint yOffset, GLint zOffset, + GLenum* const out_error) const { - *out_glError = 0; - WebGLContext* webgl = tex->mContext; // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on @@ -770,23 +809,18 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f // call into GL, instead of trying to keep MakeCurrent-ed. RefPtr dataSurf = mSurf->GetDataSurface(); - if (!dataSurf) { // Since GetDataSurface didn't return error code, assume system // is out of memory - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; + webgl->ErrorOutOfMemory("%s: OOM in GetDataSurface for TexUnpackSurface.", + funcName); + return false; } - GLenum error; if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset, - zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error)) + zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, out_error)) { - return; - } - if (error == LOCAL_GL_OUT_OF_MEMORY) { - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - return; + return true; } // CPU conversion. (++numCopies) @@ -798,13 +832,9 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer, &convertedAlignment, &wasTrivial, &outOfMemory)) { - if (outOfMemory) { - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - } else { - NS_ERROR("Failed to convert surface."); - *out_glError = LOCAL_GL_OUT_OF_MEMORY; - } - return; + webgl->ErrorOutOfMemory("%s: %s in ConvertSurface for TexUnpackSurface.", + funcName, outOfMemory ? "OOM" : "Failure"); + return false; } if (!wasTrivial) { @@ -817,10 +847,10 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f ScopedUnpackReset scopedReset(webgl); webgl->gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, convertedAlignment); - error = DoTexOrSubImage(isSubImage, webgl->gl, target.get(), level, dui, xOffset, - yOffset, zOffset, mWidth, mHeight, mDepth, - convertedBuffer.get()); - *out_glError = error; + *out_error = DoTexOrSubImage(isSubImage, webgl->gl, target.get(), level, dui, xOffset, + yOffset, zOffset, mWidth, mHeight, mDepth, + convertedBuffer.get()); + return true; } } // namespace webgl diff --git a/dom/canvas/TexUnpackBlob.h b/dom/canvas/TexUnpackBlob.h index b9497164533c..21c7f219eae5 100644 --- a/dom/canvas/TexUnpackBlob.h +++ b/dom/canvas/TexUnpackBlob.h @@ -47,30 +47,30 @@ struct DriverUnpackInfo; class TexUnpackBlob { public: - const GLsizei mWidth; - const GLsizei mHeight; - const GLsizei mDepth; + const uint32_t mAlignment; + const uint32_t mRowLength; + 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; const bool mHasData; protected: - TexUnpackBlob(GLsizei width, GLsizei height, GLsizei depth, bool hasData) - : mWidth(width) - , mHeight(height) - , mDepth(depth) - , mHasData(hasData) - { } + TexUnpackBlob(const WebGLContext* webgl, uint32_t alignment, uint32_t rowLength, + uint32_t imageHeight, uint32_t width, uint32_t height, uint32_t depth, + bool hasData); public: - virtual ~TexUnpackBlob() {} + virtual ~TexUnpackBlob() { } - virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, - const webgl::PackingInfo& pi) = 0; - - virtual void 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_glError) = 0; + 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; static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src, gl::OriginPos* const out_dst); @@ -79,24 +79,16 @@ public: class TexUnpackBytes : public TexUnpackBlob { public: - const size_t mByteCount; const void* const mBytes; - TexUnpackBytes(GLsizei width, GLsizei height, GLsizei depth, size_t byteCount, - const void* bytes) - : TexUnpackBlob(width, height, depth, bool(bytes)) - , mByteCount(byteCount) - , mBytes(bytes) - { } + TexUnpackBytes(const WebGLContext* webgl, uint32_t width, uint32_t height, + uint32_t depth, const void* bytes); - virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, - const webgl::PackingInfo& pi) override; - - virtual void 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_glError) override; + 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; }; class TexUnpackImage : public TexUnpackBlob @@ -105,20 +97,15 @@ public: const RefPtr mImage; const bool mIsAlphaPremult; - TexUnpackImage(const RefPtr& image, bool isAlphaPremult); - virtual ~TexUnpackImage() override; + TexUnpackImage(const WebGLContext* webgl, uint32_t imageHeight, uint32_t width, + uint32_t height, uint32_t depth, const RefPtr& image, + bool isAlphaPremult); - virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, - const webgl::PackingInfo& pi) override - { - return true; - } - - virtual void 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_glError) override; + 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; }; class TexUnpackSurface : public TexUnpackBlob @@ -127,20 +114,15 @@ public: const RefPtr mSurf; const bool mIsAlphaPremult; - TexUnpackSurface(const RefPtr& surf, bool isAlphaPremult); - virtual ~TexUnpackSurface() override; + TexUnpackSurface(const WebGLContext* webgl, uint32_t imageHeight, uint32_t width, + uint32_t height, uint32_t depth, gfx::SourceSurface* surf, + bool isAlphaPremult); - virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, - const webgl::PackingInfo& pi) override - { - return true; - } - - virtual void 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_glError) override; + 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; protected: static bool ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui, diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 2322852be13b..c38c2e8e61a5 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -940,7 +940,13 @@ public: &elem, &out_error); } + ////// // WebGLTextureUpload.cpp +public: + bool ValidateUnpackPixels(const char* funcName, uint32_t fullRows, + uint32_t tailPixels, const webgl::TexUnpackBlob* blob); + +protected: bool ValidateTexImageSpecification(const char* funcName, uint8_t funcDims, GLenum texImageTarget, GLint level, GLsizei width, GLsizei height, GLsizei depth, @@ -956,6 +962,13 @@ public: WebGLTexture** const out_texture, WebGLTexture::ImageInfo** const out_imageInfo); + bool GetUnpackValuesForImage(const char* funcName, uint32_t srcImageWidth, + uint32_t srcImageHeight, uint32_t* const out_rowLength, + uint32_t* const out_imageHeight); + + bool ValidateUnpackInfo(const char* funcName, GLenum format, GLenum type, + webgl::PackingInfo* const out); + // ----------------------------------------------------------------------------- // Vertices Feature (WebGLContextVertices.cpp) public: diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index 9a6a8b1cbdad..0d4b0bc33bc1 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -237,31 +237,34 @@ public: dom::Element* elem, ErrorResult* const out_error); protected: - void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, - GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset, - GLint zOffset, GLint border, GLenum unpackFormat, - GLenum unpackType, webgl::TexUnpackBlob* blob); + 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, - GLint level, GLsizei width, GLsizei height, - GLsizei depth, GLint border, + GLint level, uint32_t width, uint32_t height, + uint32_t depth, WebGLTexture::ImageInfo** const out_imageInfo); bool ValidateTexImageSelection(const char* funcName, TexImageTarget target, GLint level, GLint xOffset, GLint yOffset, - GLint zOffset, GLsizei width, GLsizei height, - GLsizei depth, + GLint zOffset, uint32_t width, uint32_t height, + uint32_t depth, WebGLTexture::ImageInfo** const out_imageInfo); + bool ValidateUnpack(const char* funcName, const webgl::TexUnpackBlob* blob, + bool isFunc3D, const webgl::PackingInfo& srcPI) const; public: void TexStorage(const char* funcName, TexTarget target, GLsizei levels, GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth); protected: void TexImage(const char* funcName, TexImageTarget target, GLint level, - GLenum internalFormat, GLint border, GLenum unpackFormat, - GLenum unpackType, webgl::TexUnpackBlob* blob); + GLenum internalFormat, const webgl::PackingInfo& pi, + const webgl::TexUnpackBlob* blob); void TexSubImage(const char* funcName, TexImageTarget target, GLint level, - GLint xOffset, GLint yOffset, GLint zOffset, GLenum unpackFormat, - GLenum unpackType, webgl::TexUnpackBlob* blob); + GLint xOffset, GLint yOffset, GLint zOffset, + const webgl::PackingInfo& pi, const webgl::TexUnpackBlob* blob); public: void CompressedTexImage(const char* funcName, TexImageTarget target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index e167ea9574c1..ddcf04d9735f 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -55,6 +55,33 @@ namespace mozilla { * height) */ +static bool +ValidateExtents(WebGLContext* webgl, const char* funcName, GLsizei width, GLsizei height, + GLsizei depth, GLint border, uint32_t* const out_width, + uint32_t* const out_height, uint32_t* const out_depth) +{ + // Check border + if (border != 0) { + webgl->ErrorInvalidValue("%s: `border` must be 0.", funcName); + return false; + } + + if (width < 0 || height < 0 || depth < 0) { + /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification + * "If wt and ht are the specified image width and height, + * and if either wt or ht are less than zero, then the error + * INVALID_VALUE is generated." + */ + webgl->ErrorInvalidValue("%s: `width`/`height`/`depth` must be >= 0.", funcName); + return false; + } + + *out_width = width; + *out_height = height; + *out_depth = depth; + return true; +} + //////////////////////////////////////// // ArrayBufferView? @@ -98,82 +125,165 @@ DoesJSTypeMatchUnpackType(GLenum unpackType, js::Scalar::Type jsType) } } -static bool -ValidateUnpackArrayType(WebGLContext* webgl, const char* funcName, GLenum unpackType, - js::Scalar::Type jsType) +bool +WebGLContext::ValidateUnpackPixels(const char* funcName, uint32_t fullRows, + uint32_t tailPixels, const webgl::TexUnpackBlob* blob) { - if (DoesJSTypeMatchUnpackType(unpackType, jsType)) - return true; + const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth; + const auto usedRowsPerImage = CheckedUint32(blob->mSkipRows) + blob->mHeight; + const auto usedImages = CheckedUint32(blob->mSkipImages) + blob->mDepth; - const auto& fua = webgl->mFormatUsage; - const GLenum fakeUnpackFormat = LOCAL_GL_RGBA; - if (!fua->AreUnpackEnumsValid(fakeUnpackFormat, unpackType)) { - webgl->ErrorInvalidEnum("%s: Invalid unpack type: 0x%04x", funcName, unpackType); + if (!usedPixelsPerRow.isValid() || + !usedRowsPerImage.isValid() || + !usedImages.isValid()) + { + ErrorOutOfMemory("%s: Invalid calculation for e.g. UNPACK_SKIP_PIXELS + width.", + funcName); return false; } - webgl->ErrorInvalidOperation("%s: `pixels` be compatible with unpack `type`.", - funcName); + ////// + + if (usedPixelsPerRow.value() > blob->mRowLength || + usedRowsPerImage.value() > blob->mImageHeight) + { + ErrorInvalidOperation("%s: UNPACK_ROW_LENGTH or UNPACK_IMAGE_HEIGHT too small.", + funcName); + return false; + } + + ////// + + auto fullRowsNeeded = (usedImages - 1) * blob->mImageHeight; + fullRowsNeeded += usedRowsPerImage - 1; + + if (!fullRowsNeeded.isValid()) { + ErrorOutOfMemory("%s: Invalid calculation for required row count.", + funcName); + return false; + } + + if (fullRows > fullRowsNeeded.value()) + return true; + + if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) + return true; + + 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 -UnpackBlobFromMaybeView(WebGLContext* webgl, const char* funcName, GLsizei width, - GLsizei height, GLsizei depth, GLenum unpackType, - const dom::Nullable& maybeView) +BlobFromView(WebGLContext* webgl, const char* funcName, uint32_t width, uint32_t height, + uint32_t depth, const webgl::PackingInfo& pi, + const dom::Nullable& maybeView) { - size_t dataSize; - const void* data; - if (maybeView.IsNull()) { - dataSize = 0; - data = nullptr; - } else { + const void* bytes = nullptr; + uint32_t byteCount = 0; + + if (!maybeView.IsNull()) { const auto& view = maybeView.Value(); - view.ComputeLengthAndData(); - data = view.DataAllowShared(); - dataSize = view.LengthAllowShared(); - js::Scalar::Type jsType = JS_GetArrayBufferViewType(view.Obj()); - if (!ValidateUnpackArrayType(webgl, funcName, unpackType, jsType)) + + 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(); + } } - UniquePtr ret; - // Warning: Possibly shared memory. See bug 1225033. - ret.reset(new webgl::TexUnpackBytes(width, height, depth, dataSize, data)); - return Move(ret); + UniquePtr blob(new webgl::TexUnpackBytes(webgl, 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 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); +} + +bool +WebGLContext::ValidateUnpackInfo(const char* funcName, GLenum format, GLenum type, + webgl::PackingInfo* const out) +{ + 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; + return true; } void WebGLTexture::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, + GLint yOffset, GLint zOffset, GLsizei rawWidth, + GLsizei rawHeight, GLsizei rawDepth, GLint border, + GLenum unpackFormat, GLenum unpackType, const dom::Nullable& maybeView) { - UniquePtr blob; - blob = UnpackBlobFromMaybeView(mContext, funcName, width, height, depth, unpackType, + uint32_t width, height, depth; + if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, border, + &width, &height, &depth)) + { + return; + } + + webgl::PackingInfo pi; + if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi)) + return; + + const auto blob = BlobFromView(mContext, funcName, width, height, depth, pi, maybeView); if (!blob) return; - TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset, - zOffset, border, unpackFormat, unpackType, blob.get()); + TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, pi, blob.get()); } //////////////////////////////////////// // ImageData -static UniquePtr -UnpackBlobFromImageData(WebGLContext* webgl, const char* funcName, GLenum unpackType, - dom::ImageData* imageData, dom::Uint8ClampedArray* scopedArr) +static already_AddRefed +FromImageData(WebGLContext* webgl, const char* funcName, GLenum unpackType, + dom::ImageData* imageData, dom::Uint8ClampedArray* scopedArr) { - if (!imageData) { - // Spec says to generate an INVALID_VALUE error - webgl->ErrorInvalidValue("%s: null ImageData", funcName); - return nullptr; - } - DebugOnly inited = scopedArr->Init(imageData->GetDataObject()); MOZ_ASSERT(inited); @@ -189,20 +299,46 @@ UnpackBlobFromImageData(WebGLContext* webgl, const char* funcName, GLenum unpack uint8_t* wrappableData = (uint8_t*)data; - const RefPtr surf = + RefPtr surf = gfx::Factory::CreateWrappingDataSourceSurface(wrappableData, stride, size, surfFormat); + if (!surf) { + webgl->ErrorOutOfMemory("%s: OOM in FromImageData.", funcName); + return nullptr; + } - // 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; + return surf.forget(); +} - UniquePtr ret; - ret.reset(new webgl::TexUnpackSurface(surf, surfIsAlphaPremult)); - return Move(ret); +bool +WebGLContext::GetUnpackValuesForImage(const char* funcName, uint32_t srcImageWidth, + uint32_t srcImageHeight, + uint32_t* const out_rowLength, + uint32_t* const out_imageHeight) +{ + uint32_t rowLength = mPixelStore_UnpackRowLength; + if (!rowLength) { + rowLength = srcImageWidth; + } else if (rowLength != srcImageWidth) { + ErrorInvalidOperation("%s: UNPACK_ROW_LENGTH, if set, must be == width of" + " object."); + return false; + } + + uint32_t imageHeight = mPixelStore_UnpackImageHeight; + if (!imageHeight) { + imageHeight = srcImageHeight; + } else if (imageHeight > srcImageHeight) { + ErrorInvalidOperation("%s: UNPACK_IMAGE_HEIGHT, if set, must be <= height of" + " object"); + return false; + } + + *out_rowLength = rowLength; + *out_imageHeight = imageHeight; + return true; } void @@ -211,17 +347,50 @@ WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarge GLint yOffset, GLint zOffset, GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData) { - dom::RootedTypedArray scopedArr( - nsContentUtils::RootingCxForThread()); - - UniquePtr blob; - blob = UnpackBlobFromImageData(mContext, funcName, unpackType, imageData, &scopedArr); - if (!blob) + webgl::PackingInfo pi; + if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi)) return; - const GLint border = 0; - TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset, - zOffset, border, unpackFormat, unpackType, blob.get()); + if (!imageData) { + // Spec says to generate an INVALID_VALUE error + mContext->ErrorInvalidValue("%s: Null ImageData.", funcName); + return; + } + + // Eventually, these will be args. + const uint32_t width = imageData->Width(); + const uint32_t height = imageData->Height(); + const uint32_t depth = 1; + + uint32_t rowLength, imageHeight; + if (!mContext->GetUnpackValuesForImage(funcName, imageData->Width(), + imageData->Height(), &rowLength, &imageHeight)) + { + return; + } + + dom::RootedTypedArray scopedArr( + nsContentUtils::RootingCxForThread()); + const RefPtr surf = FromImageData(mContext, funcName, unpackType, + imageData, &scopedArr); + 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); + + const uint32_t fullRows = imageData->Height(); + const uint32_t tailPixels = 0; + if (!mContext->ValidateUnpackPixels(funcName, fullRows, tailPixels, &blob)) + return; + + TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, pi, &blob); } //////////////////////////////////////// @@ -234,78 +403,113 @@ WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarge GLenum unpackType, dom::Element* elem, ErrorResult* const out_error) { + webgl::PackingInfo pi; + if (!mContext->ValidateUnpackInfo(funcName, unpackFormat, unpackType, &pi)) + return; + auto sfer = mContext->SurfaceFromElement(elem); + uint32_t elemWidth = 0; + uint32_t elemHeight = 0; + layers::Image* layersImage = nullptr; + if (!gfxPrefs::WebGLDisableDOMBlitUploads() && sfer.mLayersImage) { + layersImage = sfer.mLayersImage; + elemWidth = layersImage->GetSize().width; + elemHeight = layersImage->GetSize().height; + } + + gfx::SourceSurface* surf = nullptr; + if (!layersImage && sfer.GetSourceSurface()) { + surf = sfer.GetSourceSurface(); + elemWidth = surf->GetSize().width; + elemHeight = surf->GetSize().height; + } + + // Eventually, these will be args. + const uint32_t width = elemWidth; + const uint32_t height = elemHeight; + const uint32_t depth = 1; + // While it's counter-intuitive, the shape of the SFEResult API means that we should // try to pull out a surface first, and then, if we do pull out a surface, check // CORS/write-only/etc.. - UniquePtr blob; - const bool isAlphaPremult = sfer.mIsPremultiplied; - - const auto& layersImage = sfer.mLayersImage; - if (layersImage && !gfxPrefs::WebGLDisableDOMBlitUploads()) { - blob.reset(new webgl::TexUnpackImage(layersImage, isAlphaPremult)); - } else if (sfer.GetSourceSurface()) { - blob.reset(new webgl::TexUnpackSurface(sfer.GetSourceSurface(), isAlphaPremult)); + if (!layersImage && !surf) { + webgl::TexUnpackBytes blob(mContext, width, height, depth, nullptr); + TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, pi, &blob); + return; } - if (blob) { - if (!sfer.mCORSUsed) { - auto& srcPrincipal = sfer.mPrincipal; - nsIPrincipal* dstPrincipal = mContext->GetCanvas()->NodePrincipal(); + ////// - if (!dstPrincipal->Subsumes(srcPrincipal)) { - mContext->GenerateWarning("%s: Cross-origin elements require CORS.", - funcName); - out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); - return; - } - } + if (!sfer.mCORSUsed) { + auto& srcPrincipal = sfer.mPrincipal; + nsIPrincipal* dstPrincipal = mContext->GetCanvas()->NodePrincipal(); - if (sfer.mIsWriteOnly) { - // mIsWriteOnly defaults to true, and so will be true even if SFE merely - // failed. Thus we must test mIsWriteOnly after successfully retrieving an - // Image or SourceSurface. - mContext->GenerateWarning("%s: Element is write-only, thus cannot be" - " uploaded.", + if (!dstPrincipal->Subsumes(srcPrincipal)) { + mContext->GenerateWarning("%s: Cross-origin elements require CORS.", funcName); out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); return; } - } else { - mContext->GenerateWarning("%s: Failed to get data from DOM element. Implicit" - " width and height for this upload will be zero.", - funcName); - - const uint32_t width = 0; - const uint32_t height = 0; - const uint32_t depth = 1; // Implicit depth for DOM uploads is always 1. - const size_t byteCount = 0; - blob.reset(new webgl::TexUnpackBytes(width, height, depth, byteCount, nullptr)); } - MOZ_ASSERT(blob); - const GLint border = 0; - TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset, - zOffset, border, unpackFormat, unpackType, blob.get()); + if (sfer.mIsWriteOnly) { + // mIsWriteOnly defaults to true, and so will be true even if SFE merely + // failed. Thus we must test mIsWriteOnly after successfully retrieving an + // Image or SourceSurface. + mContext->GenerateWarning("%s: Element is write-only, thus cannot be" + " uploaded.", + funcName); + out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + ////// + // Ok, we're good! + + uint32_t rowLength, imageHeight; + if (!mContext->GetUnpackValuesForImage(funcName, elemWidth, elemHeight, &rowLength, + &imageHeight)) + { + return; + } + + UniquePtr 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, + depth, surf, isAlphaPremult)); + } + + const uint32_t fullRows = elemHeight; + const uint32_t tailPixels = 0; + if (!mContext->ValidateUnpackPixels(funcName, fullRows, tailPixels, blob.get())) + return; + + TexOrSubImageBlob(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, pi, blob.get()); } ////////////////////////////////////////////////////////////////////////////////////////// void -WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, - GLint level, GLenum internalFormat, GLint xOffset, - GLint yOffset, GLint zOffset, GLint border, - GLenum unpackFormat, GLenum unpackType, - webgl::TexUnpackBlob* blob) +WebGLTexture::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) { if (isSubImage) { - TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, unpackFormat, - unpackType, blob); + TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, pi, blob); } else { - TexImage(funcName, target, level, internalFormat, border, unpackFormat, - unpackType, blob); + TexImage(funcName, target, level, internalFormat, pi, blob); } } @@ -334,12 +538,11 @@ ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcNam return true; } - // For *TexImage* bool WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget target, - GLint level, GLsizei width, GLsizei height, - GLsizei depth, GLint border, + GLint level, uint32_t width, uint32_t height, + uint32_t depth, WebGLTexture::ImageInfo** const out_imageInfo) { if (mImmutable) { @@ -352,23 +555,6 @@ WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo)) return false; - // Check border - if (border != 0) { - mContext->ErrorInvalidValue("%s: `border` must be 0.", funcName); - return false; - } - - if (width < 0 || height < 0 || depth < 0) { - /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification - * "If wt and ht are the specified image width and height, - * and if either wt or ht are less than zero, then the error - * INVALID_VALUE is generated." - */ - mContext->ErrorInvalidValue("%s: `width`/`height`/`depth` must be >= 0.", - funcName); - return false; - } - if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && width != height) { @@ -419,9 +605,9 @@ WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget break; } - if (uint32_t(width) > maxWidthHeight || - uint32_t(height) > maxWidthHeight || - uint32_t(depth) > maxDepth) + if (width > maxWidthHeight || + height > maxWidthHeight || + depth > maxDepth) { mContext->ErrorInvalidValue("%s: Requested size at this level is unsupported.", funcName); @@ -439,7 +625,7 @@ WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget bool requirePOT = (!mContext->IsWebGL2() && level != 0); if (requirePOT) { - if (!IsPowerOfTwo(uint32_t(width)) || !IsPowerOfTwo(uint32_t(height))) { + if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) { mContext->ErrorInvalidValue("%s: For level > 0, width and height must be" " powers of two.", funcName); @@ -456,15 +642,13 @@ WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget bool WebGLTexture::ValidateTexImageSelection(const char* funcName, TexImageTarget target, GLint level, GLint xOffset, GLint yOffset, - GLint zOffset, GLsizei width, GLsizei height, - GLsizei depth, + GLint zOffset, uint32_t width, uint32_t height, + uint32_t depth, WebGLTexture::ImageInfo** const out_imageInfo) { // The conformance test wants bad arg checks before imageInfo checks. - if (xOffset < 0 || yOffset < 0 || zOffset < 0 || - width < 0 || height < 0 || depth < 0) - { - mContext->ErrorInvalidValue("%s: Offsets and dimensions must be >=0.", funcName); + if (xOffset < 0 || yOffset < 0 || zOffset < 0) { + mContext->ErrorInvalidValue("%s: Offsets must be >=0.", funcName); return false; } @@ -718,8 +902,10 @@ DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffs static inline GLenum DoCompressedTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLsizei dataSize, const void* data) + GLsizei dataSize, const void* data) { + const GLint border = 0; + gl::GLContext::LocalErrorScope errorScope(*gl); if (Is3D(target)) { @@ -758,9 +944,10 @@ DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, static inline GLenum DoCopyTexImage2D(gl::GLContext* gl, TexImageTarget target, GLint level, - GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, - GLint border) + GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height) { + const GLint border = 0; + gl::GLContext::LocalErrorScope errorScope(*gl); MOZ_ASSERT(!Is3D(target)); @@ -928,11 +1115,10 @@ WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels, const TexImageTarget testTarget = IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X : target.get(); const GLint testLevel = 0; - const GLint border = 0; WebGLTexture::ImageInfo* testImageInfo; if (!ValidateTexImageSpecification(funcName, testTarget, testLevel, width, height, - depth, border, &testImageInfo)) + depth, &testImageInfo)) { return; } @@ -1015,39 +1201,30 @@ WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels, void WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level, - GLenum internalFormat, GLint border, GLenum unpackFormat, - GLenum unpackType, webgl::TexUnpackBlob* blob) + GLenum internalFormat, const webgl::PackingInfo& pi, + const webgl::TexUnpackBlob* blob) { //////////////////////////////////// // Get dest info WebGLTexture::ImageInfo* imageInfo; if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth, - blob->mHeight, blob->mDepth, border, &imageInfo)) + blob->mHeight, blob->mDepth, &imageInfo)) { return; } MOZ_ASSERT(imageInfo); - const webgl::PackingInfo srcPacking = { unpackFormat, unpackType }; - const auto& fua = mContext->mFormatUsage; + if (!fua->IsInternalFormatEnumValid(internalFormat)) { + mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x", + funcName, internalFormat); + return; + } + auto dstUsage = fua->GetSizedTexUsage(internalFormat); if (!dstUsage) { - if (internalFormat != unpackFormat) { - if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) { - mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:" - " 0x%04x/0x%04x", - funcName, unpackFormat, unpackType); - return; - } - - if (!fua->IsInternalFormatEnumValid(internalFormat)) { - mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x", - funcName, internalFormat); - return; - } - + if (internalFormat != pi.format) { /* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification * "Specifying a combination of values for format, type, and * internalformat that is not listed as a valid combination @@ -1059,45 +1236,24 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level, return; } - dstUsage = fua->GetUnsizedTexUsage(srcPacking); + dstUsage = fua->GetUnsizedTexUsage(pi); } if (!dstUsage) { - if (!fua->IsInternalFormatEnumValid(internalFormat)) { - mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x", - funcName, internalFormat); - return; - } - - if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) { - mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:" - " 0x%04x/0x%04x", - funcName, unpackFormat, unpackType); - return; - } - mContext->ErrorInvalidOperation("%s: Invalid internalformat/format/type:" " 0x%04x/0x%04x/0x%04x", - funcName, internalFormat, unpackFormat, - unpackType); + funcName, internalFormat, pi.format, pi.type); return; } const webgl::DriverUnpackInfo* driverUnpackInfo; - if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) { + if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) { mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:" " 0x%04x and 0x%04x/0x%04x", - funcName, internalFormat, unpackFormat, - unpackType); + funcName, internalFormat, pi.format, pi.type); return; } - //////////////////////////////////// - // Get source info - const bool isFunc3D = Is3D(target); - if (!blob->ValidateUnpack(mContext, funcName, isFunc3D, srcPacking)) - return; - //////////////////////////////////// // Check that source and dest info are compatible auto dstFormat = dstUsage->format; @@ -1167,8 +1323,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level, void WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, - GLenum unpackFormat, GLenum unpackType, - webgl::TexUnpackBlob* blob) + const webgl::PackingInfo& pi, const webgl::TexUnpackBlob* blob) { //////////////////////////////////// // Get dest info @@ -1200,28 +1355,14 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev //////////////////////////////////// // Get source info - const webgl::PackingInfo srcPacking = { unpackFormat, unpackType }; const webgl::DriverUnpackInfo* driverUnpackInfo; - if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) { - const auto& fua = mContext->mFormatUsage; - if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) { - mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:" - " 0x%04x/0x%04x", - funcName, unpackFormat, unpackType); - return; - } - + if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) { mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:" " %s and 0x%04x/0x%04x", - funcName, dstFormat->name, unpackFormat, - unpackType); + funcName, dstFormat->name, pi.format, pi.type); return; } - const bool isFunc3D = Is3D(target); - if (!blob->ValidateUnpack(mContext, funcName, isFunc3D, srcPacking)) - return; - //////////////////////////////////// // Do the thing! @@ -1269,16 +1410,23 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev void WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GLint level, - GLenum internalFormat, GLsizei width, GLsizei height, - GLsizei depth, GLint border, + GLenum internalFormat, GLsizei rawWidth, + GLsizei rawHeight, GLsizei rawDepth, GLint border, const dom::ArrayBufferView& view) { + uint32_t width, height, depth; + if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, border, + &width, &height, &depth)) + { + return; + } + //////////////////////////////////// // Get dest info WebGLTexture::ImageInfo* imageInfo; if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth, - border, &imageInfo)) + &imageInfo)) { return; } @@ -1330,7 +1478,7 @@ WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GL // Warning: Possibly shared memory. See bug 1225033. GLenum error = DoCompressedTexImage(mContext->gl, target, level, internalFormat, - width, height, depth, border, dataSize, data); + width, height, depth, dataSize, data); if (error == LOCAL_GL_OUT_OF_MEMORY) { mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName); return; @@ -1375,10 +1523,17 @@ IsSubImageBlockAligned(const webgl::CompressedFormatInfo* compression, void WebGLTexture::CompressedTexSubImage(const char* funcName, TexImageTarget target, GLint level, GLint xOffset, GLint yOffset, - GLint zOffset, GLsizei width, GLsizei height, - GLsizei depth, GLenum sizedUnpackFormat, + GLint zOffset, GLsizei rawWidth, GLsizei rawHeight, + GLsizei rawDepth, GLenum sizedUnpackFormat, const dom::ArrayBufferView& view) { + uint32_t width, height, depth; + if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, rawDepth, 0, &width, + &height, &depth)) + { + return; + } + //////////////////////////////////// // Get dest info @@ -1698,19 +1853,24 @@ ScopedCopyTexImageSource::~ScopedCopyTexImageSource() // There is no CopyTexImage3D. void WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat, - GLint x, GLint y, GLsizei width, GLsizei height, + GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight, GLint border) { const char funcName[] = "CopyTexImage2D"; - const uint8_t depth = 1; - //////////////////////////////////// // Get dest info + uint32_t width, height, depth; + if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, border, &width, + &height, &depth)) + { + return; + } + WebGLTexture::ImageInfo* imageInfo; if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth, - border, &imageInfo)) + &imageInfo)) { return; } @@ -1800,8 +1960,8 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal GLenum error; if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) { MOZ_ASSERT(dstUsage->idealUnpack); - error = DoCopyTexImage2D(gl, target, level, dstUsage->idealUnpack->internalFormat, x, y, width, height, - border); + error = DoCopyTexImage2D(gl, target, level, dstUsage->idealUnpack->internalFormat, + x, y, width, height); } else { // 1. Zero the texture data. // 2. CopyTexSubImage the subrect. @@ -1851,9 +2011,14 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal void WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLint x, - GLint y, GLsizei width, GLsizei height) + GLint y, GLsizei rawWidth, GLsizei rawHeight) { - const GLsizei depth = 1; + uint32_t width, height, depth; + if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, 0, &width, + &height, &depth)) + { + return; + } //////////////////////////////////// // Get dest info