示例#1
0
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;
}
示例#2
0
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;
}
示例#3
0
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;
}