void 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) { *out_glError = 0; WebGLContext* webgl = tex->mContext; // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we // call into GL, instead of trying to keep MakeCurrent-ed. RefPtr<gfx::DataSourceSurface> 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; } GLenum error; if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error)) { return; } if (error == LOCAL_GL_OUT_OF_MEMORY) { *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } // CPU conversion. (++numCopies) UniqueBuffer convertedBuffer; uint8_t convertedAlignment; bool outOfMemory; if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer, &convertedAlignment, &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; } MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); 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; }
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, const webgl::PackingInfo& dstPI, GLenum* const out_error) const { const auto& webgl = tex->mContext; //// const auto rowLength = mSurf->GetSize().width; const auto rowCount = mSurf->GetSize().height; const auto& dstBPP = webgl::BytesPerPixel(dstPI); const auto dstFormat = FormatForPackingInfo(dstPI); //// WebGLTexelFormat srcFormat; uint8_t srcBPP; if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) { webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for" " WebGLTexelFormat::%u.", funcName, uint32_t(mSurf->GetFormat())); return false; } gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ); if (!map.IsMapped()) { webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName); return false; } const auto& srcBegin = map.GetData(); const auto& srcStride = map.GetStride(); //// const auto srcRowLengthBytes = rowLength * srcBPP; const uint8_t maxGLAlignment = 8; uint8_t srcAlignment = 1; for (; srcAlignment <= maxGLAlignment; srcAlignment *= 2) { const auto strideGuess = RoundUpToMultipleOf(srcRowLengthBytes, srcAlignment); if (strideGuess == srcStride) break; } const uint32_t dstAlignment = (srcAlignment > maxGLAlignment) ? 1 : srcAlignment; const auto dstRowLengthBytes = rowLength * dstBPP; const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment); //// const uint8_t* dstBegin = srcBegin; UniqueBuffer tempBuffer; if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, srcFormat, srcBegin, srcStride, dstFormat, dstStride, &dstBegin, &tempBuffer)) { return false; } //// const auto& gl = webgl->gl; if (!gl->MakeCurrent()) { *out_error = LOCAL_GL_CONTEXT_LOST; return true; } gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, dstAlignment); if (webgl->IsWebGL2()) { gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); } *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, dstBegin); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment); if (webgl->IsWebGL2()) { gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength); } return true; }
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, const webgl::PackingInfo& pi, GLenum* const out_error) const { MOZ_ASSERT_IF(needsRespec, !isSubImage); WebGLContext* webgl = tex->mContext; gl::GLContext* gl = webgl->GL(); gl->MakeCurrent(); if (needsRespec) { *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, nullptr); if (*out_error) return true; } const char* fallbackReason; do { if (mDepth != 1) { fallbackReason = "depth is not 1"; break; } if (webgl->mPixelStore_UnpackSkipPixels || webgl->mPixelStore_UnpackSkipRows || webgl->mPixelStore_UnpackSkipImages) { fallbackReason = "non-zero UNPACK_SKIP_* not yet supported"; break; } const auto fnHasPremultMismatch = [&]() { if (mSrcAlphaType == gfxAlphaType::Opaque) return false; const bool srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult); const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha; if (srcIsPremult == dstIsPremult) return false; if (dstIsPremult) { fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true"; } else { fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false"; } return true; }; if (fnHasPremultMismatch()) break; if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) { fallbackReason = "`format` is not RGB or RGBA"; break; } if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) { fallbackReason = "`type` is not UNSIGNED_BYTE"; break; } gl::ScopedFramebuffer scopedFB(gl); gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB()); { gl::GLContext::LocalErrorScope errorScope(*gl); gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, target.get(), tex->mGLName, level); if (errorScope.GetError()) { fallbackReason = "bug: failed to attach to FB for blit"; break; } } const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { fallbackReason = "bug: failed to confirm FB for blit"; break; } const gfx::IntSize destSize(mWidth, mHeight); const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(), dstOrigin)) { fallbackReason = "likely bug: failed to blit"; break; } // Blitting was successful, so we're done! *out_error = 0; return true; } while (false); const nsPrintfCString perfMsg("%s: Failed to hit GPU-copy fast-path: %s (src type %u)", funcName, fallbackReason, uint32_t(mImage->GetFormat())); if (webgl->mPixelStore_RequireFastPath) { webgl->ErrorInvalidOperation("%s", perfMsg.BeginReading()); return false; } webgl->GeneratePerfWarning("%s Falling back to CPU upload.", perfMsg.BeginReading()); const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface(); RefPtr<gfx::DataSourceSurface> dataSurf; if (surf) { // WARNING: OSX can lose our MakeCurrent here. dataSurf = surf->GetDataSurface(); } if (!dataSurf) { webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after" " blit failed for TexUnpackImage.", funcName); return false; } const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf, mSrcAlphaType); return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui, xOffset, yOffset, zOffset, pi, out_error); }
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, const webgl::PackingInfo& pi, GLenum* const out_error) const { WebGLContext* webgl = tex->mContext; const auto format = FormatForPackingInfo(pi); const auto bytesPerPixel = webgl::BytesPerPixel(pi); const uint8_t* uploadPtr = mPtr; UniqueBuffer tempBuffer; do { if (!mIsClientData || !mPtr) break; if (!webgl->mPixelStore_FlipY && !webgl->mPixelStore_PremultiplyAlpha) { break; } if (webgl->mPixelStore_UnpackImageHeight || webgl->mPixelStore_UnpackSkipImages || webgl->mPixelStore_UnpackRowLength || webgl->mPixelStore_UnpackSkipRows || webgl->mPixelStore_UnpackSkipPixels) { webgl->ErrorInvalidOperation("%s: Non-DOM-Element uploads with alpha-premult" " or y-flip do not support subrect selection.", funcName); return false; } webgl->GenerateWarning("%s: Alpha-premult and y-flip are deprecated for" " non-DOM-Element uploads.", funcName); const uint32_t rowLength = mWidth; const uint32_t rowCount = mHeight * mDepth; const auto stride = RoundUpToMultipleOf(rowLength * bytesPerPixel, mAlignment); if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, format, mPtr, stride, format, stride, &uploadPtr, &tempBuffer)) { return false; } } while (false); ////// const auto& gl = webgl->gl; 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) { if (webgl->mBoundPixelUnpackBuffer) { gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, webgl->mBoundPixelUnpackBuffer->mGLName); } *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, uploadPtr); if (webgl->mBoundPixelUnpackBuffer) { gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); } return true; } ////// MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer); if (!isSubImage) { // Alloc first to catch OOMs. AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, nullptr); if (*out_error) return true; } const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, webgl->mBoundPixelUnpackBuffer); ////// // Make our sometimes-implicit values explicit. Also this keeps them constant when we // ask for height=mHeight-1 and such. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength); gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight); if (mDepth > 1) { *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth-1, uploadPtr); } // Skip the images we uploaded. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mSkipImages + mDepth - 1); if (mHeight > 1) { *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, zOffset+mDepth-1, mWidth, mHeight-1, 1, uploadPtr); } const auto totalSkipRows = CheckedUint32(mSkipImages) * mImageHeight + mSkipRows; const auto totalFullRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight - 1; const auto tailOffsetRows = totalSkipRows + totalFullRows; const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel; const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment); if (!rowStride.isValid()) { MOZ_CRASH("Should be checked earlier."); } const auto tailOffsetBytes = tailOffsetRows * rowStride; uploadPtr += tailOffsetBytes.value(); ////// gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // No stride padding. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); // No padding in general. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); // Don't skip images, gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0); // or rows. // Keep skipping pixels though! *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset+mHeight-1, zOffset+mDepth-1, mWidth, 1, 1, uploadPtr); // Reset all our modified state. gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment); gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, webgl->mPixelStore_UnpackImageHeight); gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength); gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, webgl->mPixelStore_UnpackSkipImages); gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, webgl->mPixelStore_UnpackSkipRows); return true; }
bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, WebGLTexture* tex, TexImageTarget target, GLint level, const webgl::DriverUnpackInfo* dstDUI, GLint xOffset, GLint yOffset, GLint zOffset, GLenum* const out_error) const { WebGLContext* webgl = tex->mContext; WebGLTexelFormat srcFormat; uint8_t srcBPP; if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) { webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for" " WebGLTexelFormat::%u.", funcName, uint32_t(mSurf->GetFormat())); return false; } gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ); if (!map.IsMapped()) return false; const auto srcBytes = map.GetData(); const auto srcStride = map.GetStride(); // CPU conversion. (++numCopies) webgl->GenerateWarning("%s: Incurred CPU-side conversion, which is very slow.", funcName); const uint8_t* uploadBytes; UniqueBuffer tempBuffer; if (!ConvertIfNeeded(webgl, funcName, srcBytes, srcStride, srcBPP, srcFormat, dstDUI, &uploadBytes, &tempBuffer)) { return false; } ////// gl::GLContext* const gl = webgl->gl; MOZ_ALWAYS_TRUE( gl->MakeCurrent() ); const auto curEffectiveRowLength = FallbackOnZero(webgl->mPixelStore_UnpackRowLength, mWidth); const bool changeRowLength = (mRowLength != curEffectiveRowLength); if (changeRowLength) { MOZ_ASSERT(webgl->IsWebGL2()); gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength); } *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dstDUI, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes); if (changeRowLength) { gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength); } return true; }
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_error) const { MOZ_ASSERT_IF(needsRespec, !isSubImage); WebGLContext* webgl = tex->mContext; gl::GLContext* gl = webgl->GL(); gl->MakeCurrent(); if (needsRespec) { *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, nullptr); if (*out_error) return false; } do { if (mDepth != 1) break; const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha; if (mIsSrcPremult != isDstPremult) break; if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) break; if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) break; gl::ScopedFramebuffer scopedFB(gl); gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB()); { gl::GLContext::LocalErrorScope errorScope(*gl); gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, target.get(), tex->mGLName, level); if (errorScope.GetError()) break; } const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) break; const gfx::IntSize destSize(mWidth, mHeight); const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(), dstOrigin)) { break; } // 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" " upload.", funcName); const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface(); RefPtr<gfx::DataSourceSurface> dataSurf; if (surf) { // WARNING: OSX can lose our MakeCurrent here. dataSurf = surf->GetDataSurface(); } if (!dataSurf) { webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after" " blit failed for TexUnpackImage.", funcName); return false; } const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf, mIsSrcPremult); return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui, xOffset, yOffset, zOffset, out_error); }
/*static*/ bool TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl, TexImageTarget target, GLint level, const webgl::DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, GLenum* const out_glError) { gl::GLContext* gl = webgl->GL(); MOZ_ASSERT(gl->IsCurrent()); *out_glError = 0; if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha) return false; gl::OriginPos srcOrigin, dstOrigin; OriginsForDOM(webgl, &srcOrigin, &dstOrigin); if (srcOrigin != dstOrigin) return false; // This differs from the raw-data upload in that we choose how we do the unpack. // (alignment, etc.) // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into // RGB on the CPU. However, this is optimization is out-of-scope for now. static const webgl::DriverUnpackInfo kInfoBGRA = { LOCAL_GL_BGRA, LOCAL_GL_BGRA, LOCAL_GL_UNSIGNED_BYTE, }; const webgl::DriverUnpackInfo* chosenDUI = nullptr; switch (surf->GetFormat()) { case gfx::SurfaceFormat::B8G8R8A8: if (SupportsBGRA(gl) && dui->internalFormat == LOCAL_GL_RGBA && dui->unpackFormat == LOCAL_GL_RGBA && dui->unpackType == LOCAL_GL_UNSIGNED_BYTE) { chosenDUI = &kInfoBGRA; } break; case gfx::SurfaceFormat::R8G8B8A8: if (dui->unpackFormat == LOCAL_GL_RGBA && dui->unpackType == LOCAL_GL_UNSIGNED_BYTE) { chosenDUI = dui; } break; case gfx::SurfaceFormat::R5G6B5_UINT16: if (dui->unpackFormat == LOCAL_GL_RGB && dui->unpackType == LOCAL_GL_UNSIGNED_SHORT_5_6_5) { chosenDUI = dui; } break; default: break; } if (!chosenDUI) return false; gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ); if (!map.IsMapped()) return false; const webgl::PackingInfo pi = {chosenDUI->unpackFormat, chosenDUI->unpackType}; const auto bytesPerPixel = webgl::BytesPerPixel(pi); const size_t bytesPerRow = width * bytesPerPixel; const GLint kMaxUnpackAlignment = 8; size_t unpackAlignment; if (!GuessAlignment(map.GetData(), bytesPerRow, map.GetStride(), kMaxUnpackAlignment, &unpackAlignment)) { return false; // TODO: Consider using UNPACK_ settings to set the stride based on the too-large // alignment used for some SourceSurfaces. (D2D allegedy likes alignment=16) } MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, unpackAlignment); const GLsizei depth = 1; GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, chosenDUI, xOffset, yOffset, zOffset, width, height, depth, map.GetData()); if (error) { *out_glError = error; return false; } return true; }
void 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) { MOZ_ASSERT_IF(needsRespec, !isSubImage); *out_glError = 0; WebGLContext* webgl = tex->mContext; gl::GLContext* gl = webgl->GL(); 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; } } do { if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) break; if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) break; gl::ScopedFramebuffer scopedFB(gl); gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB()); { gl::GLContext::LocalErrorScope errorScope(*gl); gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, target.get(), tex->mGLName, level); if (errorScope.GetError()) break; } const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) break; gl::OriginPos srcOrigin, dstOrigin; OriginsForDOM(webgl, &srcOrigin, &dstOrigin); const gfx::IntSize destSize(mWidth, mHeight); if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(), dstOrigin)) { break; } return; // Blitting was successful, so we're done! } while (false); TexUnpackSurface surfBlob(mImage->GetAsSourceSurface(), mIsAlphaPremult); surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui, xOffset, yOffset, zOffset, out_glError); }
void 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) { WebGLContext* webgl = tex->mContext; gl::GLContext* gl = webgl->gl; const void* uploadBytes = mBytes; UniqueBuffer tempBuffer; do { if (!webgl->mPixelStore_FlipY && !webgl->mPixelStore_PremultiplyAlpha) break; if (!mBytes || !mWidth || !mHeight || !mDepth) break; if (webgl->IsWebGL2()) break; MOZ_ASSERT(mDepth == 1); // This is literally the worst. 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 webgl::PackingInfo pi = { dui->unpackFormat, dui->unpackType }; 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); const bool needsYFlip = webgl->mPixelStore_FlipY; bool needsAlphaPremult = webgl->mPixelStore_PremultiplyAlpha; if (!UnpackFormatHasAlpha(pi.format)) needsAlphaPremult = false; if (!needsAlphaPremult) { if (!webgl->mPixelStore_FlipY) break; const uint8_t* src = (const uint8_t*)mBytes; const uint8_t* const srcEnd = src + rowStride * mHeight; uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride * (mHeight - 1); while (src != srcEnd) { memcpy(dst, src, bytesPerRow); src += rowStride; dst -= rowStride; } uploadBytes = tempBuffer.get(); break; } const auto texelFormat = FormatFromPacking(pi); if (texelFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) { MOZ_ASSERT(false, "Bad texelFormat from pi."); *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } const auto srcOrigin = gl::OriginPos::BottomLeft; const auto dstOrigin = (needsYFlip ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); const bool srcPremultiplied = false; const bool dstPremultiplied = needsAlphaPremult; // Always true here. // And go!: if (!ConvertImage(mWidth, mHeight, mBytes, rowStride, srcOrigin, texelFormat, srcPremultiplied, tempBuffer.get(), rowStride, dstOrigin, texelFormat, dstPremultiplied)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } uploadBytes = tempBuffer.get(); } while (false); GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes); *out_glError = error; }