gfx/gl/ScopedGLHelpers.cpp
author Daisuke Akatsuka <dakatsuka@mozilla.com>
Tue, 14 Aug 2018 10:35:43 +0900
changeset 432046 86e6704908fde59a926376f6e42161d749e80765
parent 425317 fe0a7d4b6daf29627a8fa3f15cc4070b8a13ebe3
child 448231 ff40e8ca1e42bc69b512e1e3e063ebc2ed394eb3
permissions -rw-r--r--
Bug 1477600 - Part 2: Implement remove/reload button. r=jdescottes Differential Revision: https://phabricator.services.mozilla.com/D3272

/* -*- Mode: C++; tab-width: 8; 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 "mozilla/UniquePtr.h"

#include "GLContext.h"
#include "ScopedGLHelpers.h"

namespace mozilla {
namespace gl {

#ifdef DEBUG
bool
IsContextCurrent(GLContext* gl)
{
    return gl->IsCurrent();
}
#endif

/* ScopedGLState - Wraps glEnable/glDisable. **********************************/

// Use |newState = true| to enable, |false| to disable.
ScopedGLState::ScopedGLState(GLContext* aGL, GLenum aCapability, bool aNewState)
    : ScopedGLWrapper<ScopedGLState>(aGL)
    , mCapability(aCapability)
{
    mOldState = mGL->fIsEnabled(mCapability);

    // Early out if we're already in the right state.
    if (aNewState == mOldState)
        return;

    if (aNewState) {
        mGL->fEnable(mCapability);
    } else {
        mGL->fDisable(mCapability);
    }
}

ScopedGLState::ScopedGLState(GLContext* aGL, GLenum aCapability)
    : ScopedGLWrapper<ScopedGLState>(aGL)
    , mCapability(aCapability)
{
    mOldState = mGL->fIsEnabled(mCapability);
}

void
ScopedGLState::UnwrapImpl()
{
    if (mOldState) {
        mGL->fEnable(mCapability);
    } else {
        mGL->fDisable(mCapability);
    }
}


/* ScopedBindFramebuffer - Saves and restores with GetUserBoundFB and BindUserFB. */

void
ScopedBindFramebuffer::Init()
{
    if (mGL->IsSupported(GLFeature::split_framebuffer)) {
        mOldReadFB = mGL->GetReadFB();
        mOldDrawFB = mGL->GetDrawFB();
    } else {
        mOldReadFB = mOldDrawFB = mGL->GetFB();
    }
}

ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL)
    : ScopedGLWrapper<ScopedBindFramebuffer>(aGL)
{
    Init();
}

ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL, GLuint aNewFB)
    : ScopedGLWrapper<ScopedBindFramebuffer>(aGL)
{
    Init();
    mGL->BindFB(aNewFB);
}

void
ScopedBindFramebuffer::UnwrapImpl()
{
    if (mOldReadFB == mOldDrawFB) {
        mGL->BindFB(mOldDrawFB);
    } else {
        mGL->BindDrawFB(mOldDrawFB);
        mGL->BindReadFB(mOldReadFB);
    }
}


/* ScopedBindTextureUnit ******************************************************/

ScopedBindTextureUnit::ScopedBindTextureUnit(GLContext* aGL, GLenum aTexUnit)
    : ScopedGLWrapper<ScopedBindTextureUnit>(aGL)
    , mOldTexUnit(0)
{
    MOZ_ASSERT(aTexUnit >= LOCAL_GL_TEXTURE0);
    mGL->GetUIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &mOldTexUnit);
    mGL->fActiveTexture(aTexUnit);
}

void
ScopedBindTextureUnit::UnwrapImpl() {
    mGL->fActiveTexture(mOldTexUnit);
}


/* ScopedTexture **************************************************************/

ScopedTexture::ScopedTexture(GLContext* aGL)
    : ScopedGLWrapper<ScopedTexture>(aGL)
    , mTexture(0)
{
    mGL->fGenTextures(1, &mTexture);
}

void
ScopedTexture::UnwrapImpl()
{
    mGL->fDeleteTextures(1, &mTexture);
}

/* ScopedFramebuffer **************************************************************/

ScopedFramebuffer::ScopedFramebuffer(GLContext* aGL)
    : ScopedGLWrapper<ScopedFramebuffer>(aGL)
    , mFB(0)
{
    mGL->fGenFramebuffers(1, &mFB);
}

void
ScopedFramebuffer::UnwrapImpl()
{
    mGL->fDeleteFramebuffers(1, &mFB);
}


/* ScopedRenderbuffer **************************************************************/

ScopedRenderbuffer::ScopedRenderbuffer(GLContext* aGL)
    : ScopedGLWrapper<ScopedRenderbuffer>(aGL)
    , mRB(0)
{
    mGL->fGenRenderbuffers(1, &mRB);
}

void
ScopedRenderbuffer::UnwrapImpl()
{
    mGL->fDeleteRenderbuffers(1, &mRB);
}

/* ScopedBindTexture **********************************************************/

static GLuint
GetBoundTexture(GLContext* gl, GLenum texTarget)
{
    GLenum bindingTarget;
    switch (texTarget) {
    case LOCAL_GL_TEXTURE_2D:
        bindingTarget = LOCAL_GL_TEXTURE_BINDING_2D;
        break;

    case LOCAL_GL_TEXTURE_CUBE_MAP:
        bindingTarget = LOCAL_GL_TEXTURE_BINDING_CUBE_MAP;
        break;

    case LOCAL_GL_TEXTURE_3D:
        bindingTarget = LOCAL_GL_TEXTURE_BINDING_3D;
        break;

    case LOCAL_GL_TEXTURE_2D_ARRAY:
        bindingTarget = LOCAL_GL_TEXTURE_BINDING_2D_ARRAY;
        break;

    case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
        bindingTarget = LOCAL_GL_TEXTURE_BINDING_RECTANGLE_ARB;
        break;

    case LOCAL_GL_TEXTURE_EXTERNAL:
        bindingTarget = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
        break;

    default:
        MOZ_CRASH("bad texTarget");
    }

    GLuint ret = 0;
    gl->GetUIntegerv(bindingTarget, &ret);
    return ret;
}

ScopedBindTexture::ScopedBindTexture(GLContext* aGL, GLuint aNewTex, GLenum aTarget)
        : ScopedGLWrapper<ScopedBindTexture>(aGL)
        , mTarget(aTarget)
        , mOldTex(GetBoundTexture(aGL, aTarget))
{
    mGL->fBindTexture(mTarget, aNewTex);
}

void
ScopedBindTexture::UnwrapImpl()
{
    mGL->fBindTexture(mTarget, mOldTex);
}


/* ScopedBindRenderbuffer *****************************************************/

void
ScopedBindRenderbuffer::Init()
{
    mOldRB = 0;
    mGL->GetUIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &mOldRB);
}

ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL)
        : ScopedGLWrapper<ScopedBindRenderbuffer>(aGL)
{
    Init();
}

ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL, GLuint aNewRB)
    : ScopedGLWrapper<ScopedBindRenderbuffer>(aGL)
{
    Init();
    mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, aNewRB);
}

void
ScopedBindRenderbuffer::UnwrapImpl()
{
    mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOldRB);
}


/* ScopedFramebufferForTexture ************************************************/
ScopedFramebufferForTexture::ScopedFramebufferForTexture(GLContext* aGL,
                                                         GLuint aTexture,
                                                         GLenum aTarget)
    : ScopedGLWrapper<ScopedFramebufferForTexture>(aGL)
    , mComplete(false)
    , mFB(0)
{
    mGL->fGenFramebuffers(1, &mFB);
    ScopedBindFramebuffer autoFB(aGL, mFB);
    mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                               LOCAL_GL_COLOR_ATTACHMENT0,
                               aTarget,
                               aTexture,
                               0);

    GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
    if (status == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
        mComplete = true;
    } else {
        mGL->fDeleteFramebuffers(1, &mFB);
        mFB = 0;
    }
}

void ScopedFramebufferForTexture::UnwrapImpl()
{
    if (!mFB)
        return;

    mGL->fDeleteFramebuffers(1, &mFB);
    mFB = 0;
}


/* ScopedFramebufferForRenderbuffer *******************************************/


ScopedFramebufferForRenderbuffer::ScopedFramebufferForRenderbuffer(GLContext* aGL,
                                                                   GLuint aRB)
    : ScopedGLWrapper<ScopedFramebufferForRenderbuffer>(aGL)
    , mComplete(false)
    , mFB(0)
{
    mGL->fGenFramebuffers(1, &mFB);
    ScopedBindFramebuffer autoFB(aGL, mFB);
    mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_COLOR_ATTACHMENT0,
                                  LOCAL_GL_RENDERBUFFER,
                                  aRB);

    GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
    if (status == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
        mComplete = true;
    } else {
        mGL->fDeleteFramebuffers(1, &mFB);
        mFB = 0;
    }
}

void
ScopedFramebufferForRenderbuffer::UnwrapImpl()
{
    if (!mFB)
        return;

    mGL->fDeleteFramebuffers(1, &mFB);
    mFB = 0;
}

/* ScopedViewportRect *********************************************************/

ScopedViewportRect::ScopedViewportRect(GLContext* aGL,
                                       GLint x, GLint y,
                                       GLsizei width, GLsizei height)
  : ScopedGLWrapper<ScopedViewportRect>(aGL)
{
    mGL->fGetIntegerv(LOCAL_GL_VIEWPORT, mSavedViewportRect);
    mGL->fViewport(x, y, width, height);
}

void ScopedViewportRect::UnwrapImpl()
{
    mGL->fViewport(mSavedViewportRect[0],
                   mSavedViewportRect[1],
                   mSavedViewportRect[2],
                   mSavedViewportRect[3]);
}

/* ScopedScissorRect **********************************************************/

ScopedScissorRect::ScopedScissorRect(GLContext* aGL,
                                     GLint x, GLint y,
                                     GLsizei width, GLsizei height)
  : ScopedGLWrapper<ScopedScissorRect>(aGL)
{
    mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mSavedScissorRect);
    mGL->fScissor(x, y, width, height);
}

ScopedScissorRect::ScopedScissorRect(GLContext* aGL)
  : ScopedGLWrapper<ScopedScissorRect>(aGL)
{
    mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mSavedScissorRect);
}

void ScopedScissorRect::UnwrapImpl()
{
    mGL->fScissor(mSavedScissorRect[0],
                  mSavedScissorRect[1],
                  mSavedScissorRect[2],
                  mSavedScissorRect[3]);
}

/* ScopedVertexAttribPointer **************************************************/

ScopedVertexAttribPointer::ScopedVertexAttribPointer(GLContext* aGL,
                                                     GLuint index,
                                                     GLint size,
                                                     GLenum type,
                                                     realGLboolean normalized,
                                                     GLsizei stride,
                                                     GLuint buffer,
                                                     const GLvoid* pointer)
    : ScopedGLWrapper<ScopedVertexAttribPointer>(aGL)
    , mAttribEnabled(0)
    , mAttribSize(0)
    , mAttribStride(0)
    , mAttribType(0)
    , mAttribNormalized(0)
    , mAttribBufferBinding(0)
    , mAttribPointer(nullptr)
    , mBoundBuffer(0)
{
    WrapImpl(index);
    mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, buffer);
    mGL->fVertexAttribPointer(index, size, type, normalized, stride, pointer);
    mGL->fEnableVertexAttribArray(index);
}

ScopedVertexAttribPointer::ScopedVertexAttribPointer(GLContext* aGL,
                                                     GLuint index)
    : ScopedGLWrapper<ScopedVertexAttribPointer>(aGL)
    , mAttribEnabled(0)
    , mAttribSize(0)
    , mAttribStride(0)
    , mAttribType(0)
    , mAttribNormalized(0)
    , mAttribBufferBinding(0)
    , mAttribPointer(nullptr)
    , mBoundBuffer(0)
{
    WrapImpl(index);
}

void
ScopedVertexAttribPointer::WrapImpl(GLuint index)
{
    mAttribIndex = index;

    /*
     * mGL->fGetVertexAttribiv takes:
     *  VERTEX_ATTRIB_ARRAY_ENABLED
     *  VERTEX_ATTRIB_ARRAY_SIZE,
     *  VERTEX_ATTRIB_ARRAY_STRIDE,
     *  VERTEX_ATTRIB_ARRAY_TYPE,
     *  VERTEX_ATTRIB_ARRAY_NORMALIZED,
     *  VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
     *  CURRENT_VERTEX_ATTRIB
     *
     * CURRENT_VERTEX_ATTRIB is vertex shader state. \o/
     * Others appear to be vertex array state,
     * or alternatively in the internal vertex array state
     * for a buffer object.
    */

    mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &mAttribEnabled);
    mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &mAttribSize);
    mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, &mAttribStride);
    mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, &mAttribType);
    mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &mAttribNormalized);
    mGL->fGetVertexAttribiv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &mAttribBufferBinding);
    mGL->fGetVertexAttribPointerv(mAttribIndex, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &mAttribPointer);

    // Note that uniform values are program state, so we don't need to rebind those.

    mGL->GetUIntegerv(LOCAL_GL_ARRAY_BUFFER_BINDING, &mBoundBuffer);
}

void
ScopedVertexAttribPointer::UnwrapImpl()
{
    mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mAttribBufferBinding);
    mGL->fVertexAttribPointer(mAttribIndex, mAttribSize, mAttribType, mAttribNormalized, mAttribStride, mAttribPointer);
    if (mAttribEnabled)
        mGL->fEnableVertexAttribArray(mAttribIndex);
    else
        mGL->fDisableVertexAttribArray(mAttribIndex);
    mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundBuffer);
}

////////////////////////////////////////////////////////////////////////
// ScopedPackState

ScopedPackState::ScopedPackState(GLContext* gl)
    : ScopedGLWrapper<ScopedPackState>(gl)
    , mAlignment(0)
    , mPixelBuffer(0)
    , mRowLength(0)
    , mSkipPixels(0)
    , mSkipRows(0)
{
    mGL->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &mAlignment);

    if (mAlignment != 4) mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);

    if (!mGL->HasPBOState())
        return;

    mGL->fGetIntegerv(LOCAL_GL_PIXEL_PACK_BUFFER_BINDING, (GLint*)&mPixelBuffer);
    mGL->fGetIntegerv(LOCAL_GL_PACK_ROW_LENGTH, &mRowLength);
    mGL->fGetIntegerv(LOCAL_GL_PACK_SKIP_PIXELS, &mSkipPixels);
    mGL->fGetIntegerv(LOCAL_GL_PACK_SKIP_ROWS, &mSkipRows);

    if (mPixelBuffer != 0) mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, 0);
    if (mRowLength != 0)   mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
    if (mSkipPixels != 0)  mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
    if (mSkipRows != 0)    mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
}

void
ScopedPackState::UnwrapImpl()
{
    mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mAlignment);

    if (!mGL->HasPBOState())
        return;

    mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, mPixelBuffer);
    mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mRowLength);
    mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mSkipPixels);
    mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mSkipRows);
}

////////////////////////////////////////////////////////////////////////
// ResetUnpackState

ResetUnpackState::ResetUnpackState(GLContext* gl)
    : ScopedGLWrapper<ResetUnpackState>(gl)
    , mAlignment(0)
    , mPBO(0)
    , mRowLength(0)
    , mImageHeight(0)
    , mSkipPixels(0)
    , mSkipRows(0)
    , mSkipImages(0)
{
    const auto fnReset = [&](GLenum pname, GLuint val, GLuint* const out_old) {
        mGL->GetUIntegerv(pname, out_old);
        if (*out_old != val) {
            mGL->fPixelStorei(pname, val);
        }
    };

    // Default is 4, but 1 is more useful.
    fnReset(LOCAL_GL_UNPACK_ALIGNMENT, 1, &mAlignment);

    if (!mGL->HasPBOState())
        return;

    mGL->GetUIntegerv(LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING, &mPBO);
    if (mPBO != 0) mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);

    fnReset(LOCAL_GL_UNPACK_ROW_LENGTH  , 0, &mRowLength);
    fnReset(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0, &mImageHeight);
    fnReset(LOCAL_GL_UNPACK_SKIP_PIXELS , 0, &mSkipPixels);
    fnReset(LOCAL_GL_UNPACK_SKIP_ROWS   , 0, &mSkipRows);
    fnReset(LOCAL_GL_UNPACK_SKIP_IMAGES , 0, &mSkipImages);
}

void
ResetUnpackState::UnwrapImpl()
{
    mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mAlignment);

    if (!mGL->HasPBOState())
        return;

    mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);

    mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
    mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight);
    mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, mSkipPixels);
    mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, mSkipRows);
    mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mSkipImages);
}

////////////////////////////////////////////////////////////////////////
// ScopedBindPBO

static GLuint
GetPBOBinding(GLContext* gl, GLenum target)
{
    if (!gl->HasPBOState())
        return 0;

    GLenum targetBinding;
    switch (target) {
    case LOCAL_GL_PIXEL_PACK_BUFFER:
        targetBinding = LOCAL_GL_PIXEL_PACK_BUFFER_BINDING;
        break;

    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
        targetBinding = LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING;
        break;

    default:
        MOZ_CRASH();
    }

    return gl->GetIntAs<GLuint>(targetBinding);
}

ScopedBindPBO::ScopedBindPBO(GLContext* gl, GLenum target)
    : ScopedGLWrapper<ScopedBindPBO>(gl)
    , mTarget(target)
    , mPBO(GetPBOBinding(mGL, mTarget))
{ }

void
ScopedBindPBO::UnwrapImpl()
{
    if (!mGL->HasPBOState())
        return;

    mGL->fBindBuffer(mTarget, mPBO);
}

} /* namespace gl */
} /* namespace mozilla */