dom/canvas/WebGL2ContextFramebuffers.cpp
author Brian Hackett <bhackett1024@gmail.com>
Wed, 03 Oct 2018 15:03:02 -1000
changeset 495437 00c56e070d3835c77fd0138a5c641d04a72dcb89
parent 486013 b807f63147aeb40dc4139b258d630cc1739662e0
child 498719 f4fe9800063b5110cb59f2eafdc1ffc45feb5a60
permissions -rw-r--r--
Bug 1495272 Part 1 - Don't register deferred finalizer in BindingJSObjectCreator until initialization succeeds, r=bz.

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "WebGL2Context.h"

#include "GLContext.h"
#include "GLScreenBuffer.h"
#include "mozilla/CheckedInt.h"
#include "WebGLContextUtils.h"
#include "WebGLFormats.h"
#include "WebGLFramebuffer.h"

namespace mozilla {

void
WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                               GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                               GLbitfield mask, GLenum filter)
{
    const FuncScope funcScope(*this, "blitFramebuffer");
    if (IsContextLost())
        return;

    const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT |
                                 LOCAL_GL_DEPTH_BUFFER_BIT |
                                 LOCAL_GL_STENCIL_BUFFER_BIT;
    if ((mask | validBits) != validBits) {
        ErrorInvalidValue("Invalid bit set in mask.");
        return;
    }

    switch (filter) {
    case LOCAL_GL_NEAREST:
    case LOCAL_GL_LINEAR:
        break;
    default:
        ErrorInvalidEnumInfo("filter", filter);
        return;
    }

    // --

    const auto fnLikelyOverflow = [](GLint p0, GLint p1) {
        auto checked = CheckedInt<GLint>(p1) - p0;
        checked = -checked; // And check the negation!
        return !checked.isValid();
    };

    if (fnLikelyOverflow(srcX0, srcX1) || fnLikelyOverflow(srcY0, srcY1) ||
        fnLikelyOverflow(dstX0, dstX1) || fnLikelyOverflow(dstY0, dstY1))
    {
        ErrorInvalidValue("Likely-to-overflow large ranges are forbidden.");
        return;
    }

    // --

    if (!ValidateAndInitFB(mBoundReadFramebuffer) ||
        !ValidateAndInitFB(mBoundDrawFramebuffer))
    {
        return;
    }

    DoBindFB(mBoundReadFramebuffer, LOCAL_GL_READ_FRAMEBUFFER);
    DoBindFB(mBoundDrawFramebuffer, LOCAL_GL_DRAW_FRAMEBUFFER);

    WebGLFramebuffer::BlitFramebuffer(this,
                                      srcX0, srcY0, srcX1, srcY1,
                                      dstX0, dstY0, dstX1, dstY1,
                                      mask, filter);
}

void
WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
                                       WebGLTexture* texture, GLint level, GLint layer)
{
    const FuncScope funcScope(*this, "framebufferTextureLayer");
    if (IsContextLost())
        return;

    if (!ValidateFramebufferTarget(target))
        return;

    WebGLFramebuffer* fb;
    switch (target) {
    case LOCAL_GL_FRAMEBUFFER:
    case LOCAL_GL_DRAW_FRAMEBUFFER:
        fb = mBoundDrawFramebuffer;
        break;

    case LOCAL_GL_READ_FRAMEBUFFER:
        fb = mBoundReadFramebuffer;
        break;

    default:
        MOZ_CRASH("GFX: Bad target.");
    }

    if (!fb)
        return ErrorInvalidOperation("Cannot modify framebuffer 0.");

    fb->FramebufferTextureLayer(attachment, texture, level, layer);
}

JS::Value
WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
                                                 GLenum target,
                                                 GLenum attachment,
                                                 GLenum pname,
                                                 ErrorResult& out_error)
{
    return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname,
                                                           out_error);
}

////

static bool
ValidateBackbufferAttachmentEnum(WebGLContext* webgl, GLenum attachment)
{
    switch (attachment) {
    case LOCAL_GL_COLOR:
    case LOCAL_GL_DEPTH:
    case LOCAL_GL_STENCIL:
        return true;

    default:
        webgl->ErrorInvalidEnumInfo("attachment", attachment);
        return false;
    }
}

static bool
ValidateFramebufferAttachmentEnum(WebGLContext* webgl,
                                  GLenum attachment)
{
    switch (attachment) {
    case LOCAL_GL_DEPTH_ATTACHMENT:
    case LOCAL_GL_STENCIL_ATTACHMENT:
    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
        return true;
    }

    if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) {
        webgl->ErrorInvalidEnumInfo("attachment", attachment);
        return false;
    }

    if (attachment > webgl->LastColorAttachmentEnum()) {
        // That these errors have different types is ridiculous.
        webgl->ErrorInvalidOperation("Too-large LOCAL_GL_COLOR_ATTACHMENTn.");
        return false;
    }

    return true;
}

bool
WebGLContext::ValidateInvalidateFramebuffer(GLenum target,
                                            const dom::Sequence<GLenum>& attachments,
                                            ErrorResult* const out_rv,
                                            std::vector<GLenum>* const scopedVector,
                                            GLsizei* const out_glNumAttachments,
                                            const GLenum** const out_glAttachments)
{
    if (IsContextLost())
        return false;

    if (!ValidateFramebufferTarget(target))
        return false;

    const WebGLFramebuffer* fb;
    bool isDefaultFB = false;
    switch (target) {
    case LOCAL_GL_FRAMEBUFFER:
    case LOCAL_GL_DRAW_FRAMEBUFFER:
        fb = mBoundDrawFramebuffer;
        break;

    case LOCAL_GL_READ_FRAMEBUFFER:
        fb = mBoundReadFramebuffer;
        break;

    default:
        MOZ_CRASH("GFX: Bad target.");
    }

    if (fb) {
        const auto fbStatus = fb->CheckFramebufferStatus();
        if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
            return false; // Not an error, but don't run forward to driver either.
    } else {
        if (!EnsureDefaultFB())
            return false;
    }
    DoBindFB(fb, target);

    *out_glNumAttachments = attachments.Length();
    *out_glAttachments = attachments.Elements();

    if (fb) {
        for (const auto& attachment : attachments) {
            if (!ValidateFramebufferAttachmentEnum(this, attachment))
                return false;
        }
    } else {
        for (const auto& attachment : attachments) {
            if (!ValidateBackbufferAttachmentEnum(this, attachment))
                return false;
        }

        if (!isDefaultFB) {
            MOZ_ASSERT(scopedVector->empty());
            scopedVector->reserve(attachments.Length());
            for (const auto& attachment : attachments) {
                switch (attachment) {
                case LOCAL_GL_COLOR:
                    scopedVector->push_back(LOCAL_GL_COLOR_ATTACHMENT0);
                    break;

                case LOCAL_GL_DEPTH:
                    scopedVector->push_back(LOCAL_GL_DEPTH_ATTACHMENT);
                    break;

                case LOCAL_GL_STENCIL:
                    scopedVector->push_back(LOCAL_GL_STENCIL_ATTACHMENT);
                    break;

                default:
                    MOZ_CRASH();
                }
            }
            *out_glNumAttachments = scopedVector->size();
            *out_glAttachments = scopedVector->data();
        }
    }

    ////

    if (!fb) {
        mDefaultFB_IsInvalid = true;
        mResolvedDefaultFB = nullptr;
    }
    return true;
}

void
WebGL2Context::InvalidateFramebuffer(GLenum target,
                                     const dom::Sequence<GLenum>& attachments,
                                     ErrorResult& rv)
{
    const FuncScope funcScope(*this, "invalidateFramebuffer");

    std::vector<GLenum> scopedVector;
    GLsizei glNumAttachments;
    const GLenum* glAttachments;
    if (!ValidateInvalidateFramebuffer(target, attachments, &rv, &scopedVector,
                                       &glNumAttachments, &glAttachments))
    {
        return;
    }

    ////

    // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
    const bool useFBInvalidation = (mAllowFBInvalidation &&
                                    gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
    if (useFBInvalidation) {
        gl->fInvalidateFramebuffer(target, glNumAttachments, glAttachments);
        return;
    }

    // Use clear instead?
    // No-op for now.
}

void
WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
                                        GLint x, GLint y, GLsizei width, GLsizei height,
                                        ErrorResult& rv)
{
    const FuncScope funcScope(*this, "invalidateSubFramebuffer");

    std::vector<GLenum> scopedVector;
    GLsizei glNumAttachments;
    const GLenum* glAttachments;
    if (!ValidateInvalidateFramebuffer(target, attachments, &rv, &scopedVector,
                                       &glNumAttachments, &glAttachments))
    {
        return;
    }

    if (!ValidateNonNegative("width", width) ||
        !ValidateNonNegative("height", height))
    {
        return;
    }

    ////

    // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
    const bool useFBInvalidation = (mAllowFBInvalidation &&
                                    gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
    if (useFBInvalidation) {
        gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y,
                                      width, height);
        return;
    }

    // Use clear instead?
    // No-op for now.
}

void
WebGL2Context::ReadBuffer(GLenum mode)
{
    const FuncScope funcScope(*this, "readBuffer");
    if (IsContextLost())
        return;

    if (mBoundReadFramebuffer) {
        mBoundReadFramebuffer->ReadBuffer(mode);
        return;
    }

    // Operating on the default framebuffer.
    if (mode != LOCAL_GL_NONE &&
        mode != LOCAL_GL_BACK)
    {
        nsCString enumName;
        EnumName(mode, &enumName);
        ErrorInvalidOperation("If READ_FRAMEBUFFER is null, `mode` must be BACK or"
                              " NONE. Was %s.",
                              enumName.BeginReading());
        return;
    }

    mDefaultFB_ReadBuffer = mode;
}

} // namespace mozilla