void AdditiveCipherTemplate<S>::GenerateBlock(byte *outString, size_t length) { if (m_leftOver > 0) { const size_t len = STDMIN(m_leftOver, length); memcpy(outString, PtrSub(KeystreamBufferEnd(), m_leftOver), len); length -= len; m_leftOver -= len; outString = PtrAdd(outString, len); if (!length) {return;} } PolicyInterface &policy = this->AccessPolicy(); unsigned int bytesPerIteration = policy.GetBytesPerIteration(); if (length >= bytesPerIteration) { const size_t iterations = length / bytesPerIteration; policy.WriteKeystream(outString, iterations); length -= iterations * bytesPerIteration; outString = PtrAdd(outString, iterations * bytesPerIteration); } if (length > 0) { size_t bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration); size_t bufferIterations = bufferByteSize / bytesPerIteration; policy.WriteKeystream(PtrSub(KeystreamBufferEnd(), bufferByteSize), bufferIterations); memcpy(outString, PtrSub(KeystreamBufferEnd(), bufferByteSize), length); m_leftOver = bufferByteSize - length; } }
void AdditiveCipherTemplate<S>::ProcessData(byte *outString, const byte *inString, size_t length) { if (m_leftOver > 0) { size_t len = STDMIN(m_leftOver, length); xorbuf(outString, inString, KeystreamBufferEnd()-m_leftOver, len); length -= len; m_leftOver -= len; inString += len; outString += len; if (!length) return; } CRYPTOPP_ASSERT(m_leftOver == 0); PolicyInterface &policy = this->AccessPolicy(); unsigned int bytesPerIteration = policy.GetBytesPerIteration(); if (policy.CanOperateKeystream() && length >= bytesPerIteration) { size_t iterations = length / bytesPerIteration; unsigned int alignment = policy.GetAlignment(); KeystreamOperation operation = KeystreamOperation((IsAlignedOn(inString, alignment) * 2) | (int)IsAlignedOn(outString, alignment)); policy.OperateKeystream(operation, outString, inString, iterations); inString += iterations * bytesPerIteration; outString += iterations * bytesPerIteration; length -= iterations * bytesPerIteration; if (!length) return; } size_t bufferByteSize = m_buffer.size(); size_t bufferIterations = bufferByteSize / bytesPerIteration; while (length >= bufferByteSize) { policy.WriteKeystream(m_buffer, bufferIterations); xorbuf(outString, inString, KeystreamBufferBegin(), bufferByteSize); length -= bufferByteSize; inString += bufferByteSize; outString += bufferByteSize; } if (length > 0) { bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration); bufferIterations = bufferByteSize / bytesPerIteration; policy.WriteKeystream(KeystreamBufferEnd()-bufferByteSize, bufferIterations); xorbuf(outString, inString, KeystreamBufferEnd()-bufferByteSize, length); m_leftOver = bufferByteSize - length; } }
void RDSEED::DiscardBytes(size_t n) { // RoundUpToMultipleOf is used because a full word is read, and its cheaper // to discard full words. There's no sense in dealing with tail bytes. CRYPTOPP_ASSERT(HasRDSEED()); #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 FixedSizeSecBlock<word64, 16> discard; n = RoundUpToMultipleOf(n, sizeof(word64)); #else FixedSizeSecBlock<word32, 16> discard; n = RoundUpToMultipleOf(n, sizeof(word32)); #endif size_t count = STDMIN(n, discard.SizeInBytes()); while (count) { GenerateBlock(discard.BytePtr(), count); n -= count; count = STDMIN(n, discard.SizeInBytes()); } }
static bool GuessAlignment(const void* data, size_t bytesPerRow, size_t stride, size_t maxAlignment, size_t* const out_alignment) { size_t alignmentGuess = maxAlignment; while (alignmentGuess) { size_t guessStride = RoundUpToMultipleOf(bytesPerRow, alignmentGuess); if (guessStride == stride && uintptr_t(data) % alignmentGuess == 0) { *out_alignment = alignmentGuess; return true; } alignmentGuess /= 2; } return false; }
void BenchMark(const char *name, StreamTransformation &cipher, double timeTotal) { const int BUF_SIZE=RoundUpToMultipleOf(2048U, cipher.OptimalBlockSize()); AlignedSecByteBlock buf(BUF_SIZE); GlobalRNG().GenerateBlock(buf, BUF_SIZE); clock_t start = clock(); unsigned long i=0, blocks=1; double timeTaken; do { blocks *= 2; for (; i<blocks; i++) cipher.ProcessString(buf, BUF_SIZE); timeTaken = double(clock() - start) / CLOCK_TICKS_PER_SECOND; } while (timeTaken < 2.0/3*timeTotal); OutputResultBytes(name, double(blocks) * BUF_SIZE, timeTaken); }
static bool ValidateUnpackBytes(WebGLContext* webgl, const char* funcName, const webgl::PackingInfo& pi, size_t availByteCount, webgl::TexUnpackBlob* blob) { if (!blob->mWidth || !blob->mHeight || !blob->mDepth) return true; const auto bytesPerPixel = webgl::BytesPerPixel(pi); const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel; const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment); const auto fullRows = availByteCount / rowStride; if (!fullRows.isValid()) { webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.", funcName); return false; } const auto bodyBytes = fullRows.value() * rowStride.value(); const auto tailPixels = (availByteCount - bodyBytes) / bytesPerPixel; return ValidateUnpackPixels(webgl, funcName, fullRows.value(), tailPixels, blob); }
void AdditiveCipherTemplate<S>::GenerateBlock(byte *outString, size_t length) { if (m_leftOver > 0) { size_t len = STDMIN(m_leftOver, length); memcpy(outString, KeystreamBufferEnd()-m_leftOver, len); length -= len; m_leftOver -= len; outString += len; if (!length) return; } CRYPTOPP_ASSERT(m_leftOver == 0); PolicyInterface &policy = this->AccessPolicy(); unsigned int bytesPerIteration = policy.GetBytesPerIteration(); if (length >= bytesPerIteration) { size_t iterations = length / bytesPerIteration; policy.WriteKeystream(outString, iterations); outString += iterations * bytesPerIteration; length -= iterations * bytesPerIteration; } if (length > 0) { size_t bufferByteSize = RoundUpToMultipleOf(length, bytesPerIteration); size_t bufferIterations = bufferByteSize / bytesPerIteration; policy.WriteKeystream(KeystreamBufferEnd()-bufferByteSize, bufferIterations); memcpy(outString, KeystreamBufferEnd()-bufferByteSize, length); m_leftOver = bufferByteSize - length; } }
void AdditiveCipherTemplate<S>::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) { PolicyInterface &policy = this->AccessPolicy(); policy.CipherSetKey(params, key, length); m_leftOver = 0; unsigned int bufferByteSize = policy.CanOperateKeystream() ? GetBufferByteSize(policy) : RoundUpToMultipleOf(1024U, GetBufferByteSize(policy)); m_buffer.New(bufferByteSize); if (this->IsResynchronizable()) { size_t ivLength; const byte *iv = this->GetIVAndThrowIfInvalid(params, ivLength); policy.CipherResynchronize(m_buffer, iv, ivLength); } }
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 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; }
static bool ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex, TexImageTarget target, uint32_t level, const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height, uint32_t depth) { // This has two usecases: // 1. Lazy zeroing of uninitialized textures: // a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*) // b. Before partial upload. (TexStorage + TexSubImage) // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image) // We have no sympathy for any of these cases. // "Doctor, it hurts when I do this!" "Well don't do that!" webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is" " slow.", funcName); gl::GLContext* gl = webgl->GL(); gl->MakeCurrent(); GLenum scopeBindTarget; switch (target.get()) { case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP; break; default: scopeBindTarget = target.get(); break; } const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget); auto compression = usage->format->compression; if (compression) { auto sizedFormat = usage->format->sizedFormat; MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set"); const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) { return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock; }; const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth); const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight); CheckedUint32 checkedByteCount = compression->bytesPerBlock; checkedByteCount *= widthBlocks; checkedByteCount *= heightBlocks; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it // well. const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0, width, height, depth, sizedFormat, byteCount, zeros.get()); return !error; } const auto driverUnpackInfo = usage->idealUnpack; MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set."); if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) && gl->IsANGLE() && usage->format->d) { // ANGLE_depth_texture does not allow uploads, so we have to clear. // (Restriction because of D3D9) MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D); MOZ_ASSERT(level == 0); ZeroANGLEDepthTexture(webgl, tex, usage, width, height); return true; } const webgl::PackingInfo packing = driverUnpackInfo->ToPacking(); const auto bytesPerPixel = webgl::BytesPerPixel(packing); CheckedUint32 checkedByteCount = bytesPerPixel; checkedByteCount *= width; checkedByteCount *= height; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well. const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth, packing, zeros.get()); return !error; }
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; }
/*static*/ bool TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui, gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, UniqueBuffer* const out_convertedBuffer, uint8_t* const out_convertedAlignment, bool* const out_outOfMemory) { *out_outOfMemory = false; const size_t width = surf->GetSize().width; const size_t height = surf->GetSize().height; // Source args: // After we call this, on OSX, our GLContext will no longer be current. gfx::DataSourceSurface::ScopedMap srcMap(surf, gfx::DataSourceSurface::MapType::READ); if (!srcMap.IsMapped()) return false; const void* const srcBegin = srcMap.GetData(); const size_t srcStride = srcMap.GetStride(); WebGLTexelFormat srcFormat; if (!GetFormatForSurf(surf, &srcFormat)) return false; const bool srcPremultiplied = isSurfAlphaPremult; // Dest args: WebGLTexelFormat dstFormat; if (!GetFormatForPackingTuple(dui->unpackFormat, dui->unpackType, &dstFormat)) return false; const auto bytesPerPixel = webgl::BytesPerPixel({dui->unpackFormat, dui->unpackType}); const size_t dstRowBytes = bytesPerPixel * width; const size_t dstAlignment = 8; // Just use the max! const size_t dstStride = RoundUpToMultipleOf(dstRowBytes, dstAlignment); CheckedUint32 checkedDstSize = dstStride; checkedDstSize *= height; if (!checkedDstSize.isValid()) { *out_outOfMemory = true; return false; } const size_t dstSize = checkedDstSize.value(); UniqueBuffer dstBuffer = malloc(dstSize); if (!dstBuffer) { *out_outOfMemory = true; return false; } void* const dstBegin = dstBuffer.get(); gl::OriginPos srcOrigin, dstOrigin; OriginsForDOM(webgl, &srcOrigin, &dstOrigin); const bool dstPremultiplied = webgl->mPixelStore_PremultiplyAlpha; // And go!: if (!ConvertImage(width, height, srcBegin, srcStride, srcOrigin, srcFormat, srcPremultiplied, dstBegin, dstStride, dstOrigin, dstFormat, dstPremultiplied)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); NS_ERROR("ConvertImage failed unexpectedly."); *out_outOfMemory = true; return false; } *out_convertedBuffer = Move(dstBuffer); *out_convertedAlignment = dstAlignment; return true; }
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; }
static bool ZeroTextureData(const WebGLContext* webgl, GLuint tex, TexImageTarget target, uint32_t level, const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height, uint32_t depth) { // This has two usecases: // 1. Lazy zeroing of uninitialized textures: // a. Before draw. // b. Before partial upload. (TexStorage + TexSubImage) // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image) // We have no sympathy for any of these cases. // "Doctor, it hurts when I do this!" "Well don't do that!" const auto targetStr = EnumString(target.get()); webgl->GeneratePerfWarning( "Tex image %s level %u is incurring lazy initialization.", targetStr.c_str(), level); gl::GLContext* gl = webgl->GL(); GLenum scopeBindTarget; switch (target.get()) { case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP; break; default: scopeBindTarget = target.get(); break; } const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget); const auto& compression = usage->format->compression; if (compression) { auto sizedFormat = usage->format->sizedFormat; MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set"); const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) { return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock; }; const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth); const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight); CheckedUint32 checkedByteCount = compression->bytesPerBlock; checkedByteCount *= widthBlocks; checkedByteCount *= heightBlocks; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with // striding it well. const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0, width, height, depth, sizedFormat, byteCount, zeros.get()); return !error; } const auto driverUnpackInfo = usage->idealUnpack; MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set."); if (usage->format->d) { // ANGLE_depth_texture does not allow uploads, so we have to clear. // (Restriction because of D3D9) // Also, depth resources are cleared to 1.0f and are always renderable, so // just use FB clears. return ClearDepthTexture(*webgl, tex, target, level, usage, depth); } const webgl::PackingInfo packing = driverUnpackInfo->ToPacking(); const auto bytesPerPixel = webgl::BytesPerPixel(packing); CheckedUint32 checkedByteCount = bytesPerPixel; checkedByteCount *= width; checkedByteCount *= height; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well. const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth, packing, zeros.get()); return !error; }