summaryrefslogtreecommitdiffstats
path: root/gfx/gl/ScopedGLHelpers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gl/ScopedGLHelpers.cpp')
-rw-r--r--gfx/gl/ScopedGLHelpers.cpp490
1 files changed, 490 insertions, 0 deletions
diff --git a/gfx/gl/ScopedGLHelpers.cpp b/gfx/gl/ScopedGLHelpers.cpp
new file mode 100644
index 0000000000..d654cae163
--- /dev/null
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -0,0 +1,490 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 {
+
+/* ScopedGLState - Wraps glEnable/glDisable. **********************************/
+
+// Use |newState = true| to enable, |false| to disable.
+ScopedGLState::ScopedGLState(GLContext* aGL, GLenum aCapability, bool aNewState)
+ : mGL(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)
+ : mGL(aGL), mCapability(aCapability) {
+ mOldState = mGL->fIsEnabled(mCapability);
+}
+
+ScopedGLState::~ScopedGLState() {
+ 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) : mGL(aGL) {
+ Init();
+}
+
+ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL, GLuint aNewFB)
+ : mGL(aGL) {
+ Init();
+ mGL->BindFB(aNewFB);
+}
+
+ScopedBindFramebuffer::~ScopedBindFramebuffer() {
+ if (mOldReadFB == mOldDrawFB) {
+ mGL->BindFB(mOldDrawFB);
+ } else {
+ mGL->BindDrawFB(mOldDrawFB);
+ mGL->BindReadFB(mOldReadFB);
+ }
+}
+
+/* ScopedBindTextureUnit ******************************************************/
+
+ScopedBindTextureUnit::ScopedBindTextureUnit(GLContext* aGL, GLenum aTexUnit)
+ : mGL(aGL), mOldTexUnit(0) {
+ MOZ_ASSERT(aTexUnit >= LOCAL_GL_TEXTURE0);
+ mGL->GetUIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &mOldTexUnit);
+ mGL->fActiveTexture(aTexUnit);
+}
+
+ScopedBindTextureUnit::~ScopedBindTextureUnit() {
+ mGL->fActiveTexture(mOldTexUnit);
+}
+
+/* ScopedTexture **************************************************************/
+
+ScopedTexture::ScopedTexture(GLContext* aGL) : mGL(aGL), mTexture(0) {
+ mGL->fGenTextures(1, &mTexture);
+}
+
+ScopedTexture::~ScopedTexture() { mGL->fDeleteTextures(1, &mTexture); }
+
+/* ScopedFramebuffer
+ * **************************************************************/
+
+ScopedFramebuffer::ScopedFramebuffer(GLContext* aGL) : mGL(aGL), mFB(0) {
+ mGL->fGenFramebuffers(1, &mFB);
+}
+
+ScopedFramebuffer::~ScopedFramebuffer() { mGL->fDeleteFramebuffers(1, &mFB); }
+
+/* ScopedRenderbuffer
+ * **************************************************************/
+
+ScopedRenderbuffer::ScopedRenderbuffer(GLContext* aGL) : mGL(aGL), mRB(0) {
+ mGL->fGenRenderbuffers(1, &mRB);
+}
+
+ScopedRenderbuffer::~ScopedRenderbuffer() {
+ 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)
+ : mGL(aGL), mTarget(aTarget), mOldTex(GetBoundTexture(aGL, aTarget)) {
+ mGL->fBindTexture(mTarget, aNewTex);
+}
+
+ScopedBindTexture::~ScopedBindTexture() { mGL->fBindTexture(mTarget, mOldTex); }
+
+/* ScopedBindRenderbuffer *****************************************************/
+
+void ScopedBindRenderbuffer::Init() {
+ mOldRB = 0;
+ mGL->GetUIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &mOldRB);
+}
+
+ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL) : mGL(aGL) {
+ Init();
+}
+
+ScopedBindRenderbuffer::ScopedBindRenderbuffer(GLContext* aGL, GLuint aNewRB)
+ : mGL(aGL) {
+ Init();
+ mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, aNewRB);
+}
+
+ScopedBindRenderbuffer::~ScopedBindRenderbuffer() {
+ mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOldRB);
+}
+
+/* ScopedFramebufferForTexture ************************************************/
+ScopedFramebufferForTexture::ScopedFramebufferForTexture(GLContext* aGL,
+ GLuint aTexture,
+ GLenum aTarget)
+ : mGL(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;
+ }
+}
+
+ScopedFramebufferForTexture::~ScopedFramebufferForTexture() {
+ if (!mFB) return;
+
+ mGL->fDeleteFramebuffers(1, &mFB);
+ mFB = 0;
+}
+
+/* ScopedFramebufferForRenderbuffer *******************************************/
+
+ScopedFramebufferForRenderbuffer::ScopedFramebufferForRenderbuffer(
+ GLContext* aGL, GLuint aRB)
+ : mGL(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;
+ }
+}
+
+ScopedFramebufferForRenderbuffer::~ScopedFramebufferForRenderbuffer() {
+ if (!mFB) return;
+
+ mGL->fDeleteFramebuffers(1, &mFB);
+ mFB = 0;
+}
+
+/* ScopedViewportRect *********************************************************/
+
+ScopedViewportRect::ScopedViewportRect(GLContext* aGL, GLint x, GLint y,
+ GLsizei width, GLsizei height)
+ : mGL(aGL) {
+ mGL->fGetIntegerv(LOCAL_GL_VIEWPORT, mSavedViewportRect);
+ mGL->fViewport(x, y, width, height);
+}
+
+ScopedViewportRect::~ScopedViewportRect() {
+ mGL->fViewport(mSavedViewportRect[0], mSavedViewportRect[1],
+ mSavedViewportRect[2], mSavedViewportRect[3]);
+}
+
+/* ScopedScissorRect **********************************************************/
+
+ScopedScissorRect::ScopedScissorRect(GLContext* aGL, GLint x, GLint y,
+ GLsizei width, GLsizei height)
+ : mGL(aGL) {
+ mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mSavedScissorRect);
+ mGL->fScissor(x, y, width, height);
+}
+
+ScopedScissorRect::ScopedScissorRect(GLContext* aGL) : mGL(aGL) {
+ mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mSavedScissorRect);
+}
+
+ScopedScissorRect::~ScopedScissorRect() {
+ 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)
+ : mGL(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)
+ : mGL(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);
+}
+
+ScopedVertexAttribPointer::~ScopedVertexAttribPointer() {
+ 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)
+ : mGL(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);
+}
+
+bool ScopedPackState::SetForWidthAndStrideRGBA(GLsizei aWidth,
+ GLsizei aStride) {
+ MOZ_ASSERT(aStride % 4 == 0, "RGBA data should always be 4-byte aligned");
+ MOZ_ASSERT(aStride / 4 >= aWidth, "Stride too small");
+ if (aStride / 4 == aWidth) {
+ // No special handling needed.
+ return true;
+ }
+ if (mGL->HasPBOState()) {
+ // HasPBOState implies support for GL_PACK_ROW_LENGTH.
+ mGL->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, aStride / 4);
+ return true;
+ }
+ return false;
+}
+
+ScopedPackState::~ScopedPackState() {
+ 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)
+ : mGL(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);
+}
+
+ResetUnpackState::~ResetUnpackState() {
+ 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)
+ : mGL(gl), mTarget(target), mPBO(GetPBOBinding(mGL, mTarget)) {}
+
+ScopedBindPBO::~ScopedBindPBO() {
+ if (!mGL->HasPBOState()) return;
+
+ mGL->fBindBuffer(mTarget, mPBO);
+}
+
+} /* namespace gl */
+} /* namespace mozilla */