예제 #1
0
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;
}
예제 #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
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);
}
예제 #4
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;
}
예제 #5
0
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;
}
예제 #6
0
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);
}
예제 #7
0
/*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;
}
예제 #8
0
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);
}
예제 #9
0
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;
}