Beispiel #1
0
bool
SharedSurface_IOSurface::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                                    GLenum format, GLenum type, GLvoid* pixels)
{
    // Calling glReadPixels when an IOSurface is bound to the current framebuffer
    // can cause corruption in following glReadPixel calls (even if they aren't
    // reading from an IOSurface).
    // We workaround this by copying to a temporary texture, and doing the readback
    // from that.
    MOZ_ASSERT(mGL->IsCurrent());

    ScopedTexture destTex(mGL);
    {
        ScopedFramebufferForTexture srcFB(mGL, ProdTexture(), ProdTextureTarget());

        ScopedBindFramebuffer bindFB(mGL, srcFB.FB());
        ScopedBindTexture bindTex(mGL, destTex.Texture());
        mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0,
                                 mHasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB,
                                 x, y,
                                 width, height, 0);
    }

    ScopedFramebufferForTexture destFB(mGL, destTex.Texture());

    ScopedBindFramebuffer bindFB(mGL, destFB.FB());
    mGL->raw_fReadPixels(0, 0, width, height, format, type, pixels);
    return true;
}
Beispiel #2
0
bool
SharedSurface_IOSurface::CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
                                        GLint x, GLint y, GLsizei width, GLsizei height,
                                        GLint border)
{
    /* Bug 896693 - OpenGL framebuffers that are backed by IOSurface on OSX expose a bug
     * in glCopyTexImage2D --- internalformats GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA
     * return the wrong results. To work around, copy framebuffer to a temporary texture
     * using GL_RGBA (which works), attach as read framebuffer and glCopyTexImage2D
     * instead.
     */

    // https://www.opengl.org/sdk/docs/man3/xhtml/glCopyTexImage2D.xml says that width or
    // height set to 0 results in a NULL texture. Lets not do any work and punt to
    // original glCopyTexImage2D, since the FBO below will fail when trying to attach a
    // texture of 0 width or height.
    if (width == 0 || height == 0)
        return false;

    switch (internalformat) {
    case LOCAL_GL_ALPHA:
    case LOCAL_GL_LUMINANCE:
    case LOCAL_GL_LUMINANCE_ALPHA:
        break;

    default:
        return false;
    }

    MOZ_ASSERT(mGL->IsCurrent());

    ScopedTexture destTex(mGL);
    {
        ScopedBindTexture bindTex(mGL, destTex.Texture());
        mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
                            LOCAL_GL_NEAREST);
        mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
                            LOCAL_GL_NEAREST);
        mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
                            LOCAL_GL_CLAMP_TO_EDGE);
        mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
                            LOCAL_GL_CLAMP_TO_EDGE);
        mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, x, y, width,
                                 height, 0);
    }

    ScopedFramebufferForTexture tmpFB(mGL, destTex.Texture(), LOCAL_GL_TEXTURE_2D);
    ScopedBindFramebuffer bindFB(mGL, tmpFB.FB());
    mGL->raw_fCopyTexImage2D(target, level, internalformat, x, y, width, height, border);

    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);
}
Beispiel #4
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);
}
Beispiel #5
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);
}