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* 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 TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName, const uint8_t* srcBytes, uint32_t srcStride, uint8_t srcBPP, WebGLTexelFormat srcFormat, const webgl::DriverUnpackInfo* dstDUI, const uint8_t** const out_bytes, UniqueBuffer* const out_anchoredBuffer) const { *out_bytes = srcBytes; if (!HasData() || !mWidth || !mHeight || !mDepth) return true; ////// const auto totalSkipRows = mSkipRows + CheckedUint32(mSkipImages) * mImageHeight; const auto offset = mSkipPixels * CheckedUint32(srcBPP) + totalSkipRows * srcStride; if (!offset.isValid()) { webgl->ErrorOutOfMemory("%s: Invalid offset calculation during conversion.", funcName); return false; } const uint32_t skipBytes = offset.value(); auto const srcBegin = srcBytes + skipBytes; ////// const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); const auto dstOrigin = gl::OriginPos::BottomLeft; const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha; const auto pi = dstDUI->ToPacking(); const auto dstBPP = webgl::BytesPerPixel(pi); const auto dstWidthBytes = CheckedUint32(dstBPP) * mWidth; const auto dstRowLengthBytes = CheckedUint32(dstBPP) * mRowLength; const auto dstAlignment = mAlignment; const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment); ////// const auto dstTotalRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight; const auto dstUsedSizeExceptLastRow = (dstTotalRows - 1) * dstStride; const auto dstSize = skipBytes + dstUsedSizeExceptLastRow + dstWidthBytes; if (!dstSize.isValid()) { webgl->ErrorOutOfMemory("%s: Invalid dstSize calculation during conversion.", funcName); return false; } ////// const auto dstFormat = FormatForPackingInfo(pi); bool premultMatches = (mIsSrcPremult == isDstPremult); if (!UnpackFormatHasColorAndAlpha(dstDUI->unpackFormat)) { premultMatches = true; } const bool needsPixelConversion = (srcFormat != dstFormat || !premultMatches); const bool originsMatch = (srcOrigin == dstOrigin); MOZ_ASSERT_IF(!needsPixelConversion, srcBPP == dstBPP); if (!needsPixelConversion && originsMatch && srcStride == dstStride.value()) { // No conversion needed! return true; } ////// // We need some sort of conversion, so create the dest buffer. *out_anchoredBuffer = calloc(1, dstSize.value()); const auto dstBytes = (uint8_t*)out_anchoredBuffer->get(); if (!dstBytes) { webgl->ErrorOutOfMemory("%s: Unable to allocate buffer during conversion.", funcName); return false; } *out_bytes = dstBytes; const auto dstBegin = dstBytes + skipBytes; ////// // Row conversion if (!needsPixelConversion) { webgl->GenerateWarning("%s: Incurred CPU row conversion, which is slow.", funcName); const uint8_t* srcRow = srcBegin; uint8_t* dstRow = dstBegin; const auto widthBytes = dstWidthBytes.value(); ptrdiff_t dstCopyStride = dstStride.value(); if (!originsMatch) { dstRow += dstUsedSizeExceptLastRow.value(); dstCopyStride = -dstCopyStride; } for (uint32_t i = 0; i < dstTotalRows.value(); i++) { memcpy(dstRow, srcRow, widthBytes); srcRow += srcStride; dstRow += dstCopyStride; } return true; } //////////// // Pixel conversion. MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); webgl->GenerateWarning("%s: Incurred CPU pixel conversion, which is very slow.", funcName); ////// // And go!: bool wasTrivial; if (!ConvertImage(mWidth, dstTotalRows.value(), srcBegin, srcStride, srcOrigin, srcFormat, mIsSrcPremult, dstBegin, dstStride.value(), dstOrigin, dstFormat, isDstPremult, &wasTrivial)) { webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName); return false; } if (!wasTrivial) { webgl->GenerateWarning("%s: Chosen format/type incurred an expensive reformat:" " 0x%04x/0x%04x", funcName, dstDUI->unpackFormat, dstDUI->unpackType); } return true; }