/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ /* 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/. */ /* GLScreenBuffer is the abstraction for the "default framebuffer" used * by an offscreen GLContext. Since it's only for offscreen GLContext's, * it's only useful for things like WebGL, and is NOT used by the * compositor's GLContext. Remember that GLContext provides an abstraction * so that even if you want to draw to the 'screen', even if that's not * actually the screen, just draw to 0. This GLScreenBuffer class takes the * logic handling out of GLContext. */ #ifndef SCREEN_BUFFER_H_ #define SCREEN_BUFFER_H_ #include "GLContextTypes.h" #include "GLDefs.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Point.h" #include "mozilla/UniquePtr.h" #include "SharedSurface.h" #include "SurfaceTypes.h" namespace mozilla { namespace gl { class GLContext; class SharedSurface; class ShSurfHandle; class SurfaceFactory; class DrawBuffer { public: // Fallible! // But it may return true with *out_buffer==nullptr if unneeded. static bool Create(GLContext* const gl, const SurfaceCaps& caps, const GLFormats& formats, const gfx::IntSize& size, UniquePtr* out_buffer); protected: GLContext* const mGL; public: const gfx::IntSize mSize; const GLsizei mSamples; const GLuint mFB; protected: const GLuint mColorMSRB; const GLuint mDepthRB; const GLuint mStencilRB; DrawBuffer(GLContext* gl, const gfx::IntSize& size, GLsizei samples, GLuint fb, GLuint colorMSRB, GLuint depthRB, GLuint stencilRB) : mGL(gl) , mSize(size) , mSamples(samples) , mFB(fb) , mColorMSRB(colorMSRB) , mDepthRB(depthRB) , mStencilRB(stencilRB) {} public: virtual ~DrawBuffer(); }; class ReadBuffer { public: // Infallible, always non-null. static UniquePtr Create(GLContext* gl, const SurfaceCaps& caps, const GLFormats& formats, SharedSurface* surf); protected: GLContext* const mGL; public: const GLuint mFB; protected: // mFB has the following attachments: const GLuint mDepthRB; const GLuint mStencilRB; // note no mColorRB here: this is provided by mSurf. SharedSurface* mSurf; ReadBuffer(GLContext* gl, GLuint fb, GLuint depthRB, GLuint stencilRB, SharedSurface* surf) : mGL(gl) , mFB(fb) , mDepthRB(depthRB) , mStencilRB(stencilRB) , mSurf(surf) {} public: virtual ~ReadBuffer(); // Cannot attach a surf of a different AttachType or Size than before. void Attach(SharedSurface* surf); const gfx::IntSize& Size() const; SharedSurface* SharedSurf() const { return mSurf; } void SetReadBuffer(GLenum mode) const; }; class GLScreenBuffer { public: // Infallible. static UniquePtr Create(GLContext* gl, const gfx::IntSize& size, const SurfaceCaps& caps); protected: GLContext* const mGL; // Owns us. public: const SurfaceCaps mCaps; protected: UniquePtr mFactory; RefPtr mBack; RefPtr mFront; UniquePtr mDraw; UniquePtr mRead; bool mNeedsBlit; GLenum mUserReadBufferMode; // Below are the parts that help us pretend to be framebuffer 0: GLuint mUserDrawFB; GLuint mUserReadFB; GLuint mInternalDrawFB; GLuint mInternalReadFB; #ifdef DEBUG bool mInInternalMode_DrawFB; bool mInInternalMode_ReadFB; #endif GLScreenBuffer(GLContext* gl, const SurfaceCaps& caps, UniquePtr factory) : mGL(gl) , mCaps(caps) , mFactory(Move(factory)) , mNeedsBlit(true) , mUserReadBufferMode(LOCAL_GL_BACK) , mUserDrawFB(0) , mUserReadFB(0) , mInternalDrawFB(0) , mInternalReadFB(0) #ifdef DEBUG , mInInternalMode_DrawFB(true) , mInInternalMode_ReadFB(true) #endif {} public: virtual ~GLScreenBuffer(); SurfaceFactory* Factory() const { return mFactory.get(); } ShSurfHandle* Front() const { return mFront; } SharedSurface* SharedSurf() const { MOZ_ASSERT(mRead); return mRead->SharedSurf(); } bool ShouldPreserveBuffer() const { return mCaps.preserve; } GLuint DrawFB() const { if (!mDraw) return ReadFB(); return mDraw->mFB; } GLuint ReadFB() const { return mRead->mFB; } GLsizei Samples() const { if (!mDraw) return 1; return mDraw->mSamples; } void DeletingFB(GLuint fb); const gfx::IntSize& Size() const { MOZ_ASSERT(mRead); MOZ_ASSERT(!mDraw || mDraw->mSize == mRead->Size()); return mRead->Size(); } void BindAsFramebuffer(GLContext* const gl, GLenum target) const; void RequireBlit(); void AssureBlitted(); void AfterDrawCall(); void BeforeReadCall(); bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); void SetReadBuffer(GLenum userMode); /** * Attempts to read pixels from the current bound framebuffer, if * it is backed by a SharedSurface. * * Returns true if the pixel data has been read back, false * otherwise. */ bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); // Morph changes the factory used to create surfaces. void Morph(UniquePtr newFactory); protected: // Returns false on error or inability to resize. bool Swap(const gfx::IntSize& size); public: bool PublishFrame(const gfx::IntSize& size); bool Resize(const gfx::IntSize& size); protected: bool Attach(SharedSurface* surf, const gfx::IntSize& size); bool CreateDraw(const gfx::IntSize& size, UniquePtr* out_buffer); UniquePtr CreateRead(SharedSurface* surf); public: /* `fb` in these functions is the framebuffer the GLContext is hoping to * bind. When this is 0, we intercept the call and bind our own * framebuffers. As a client of these functions, just bind 0 when you want * to draw to the default framebuffer/'screen'. */ void BindFB(GLuint fb); void BindDrawFB(GLuint fb); void BindReadFB(GLuint fb); GLuint GetFB() const; GLuint GetDrawFB() const; GLuint GetReadFB() const; // Here `fb` is the actual framebuffer you want bound. Binding 0 will // bind the (generally useless) default framebuffer. void BindFB_Internal(GLuint fb); void BindDrawFB_Internal(GLuint fb); void BindReadFB_Internal(GLuint fb); bool IsDrawFramebufferDefault() const; bool IsReadFramebufferDefault() const; }; } // namespace gl } // namespace mozilla #endif // SCREEN_BUFFER_H_