/* -*- 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 "SurfaceTypes.h" #include "SurfaceStream.h" #include "GLContextTypes.h" #include "GLDefs.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Point.h" // Forwards: class gfxImageSurface; namespace mozilla { namespace gfx { class SurfaceStream; class SharedSurface; } namespace gl { class GLContext; class SharedSurface_GL; class SurfaceFactory_GL; } } namespace mozilla { namespace gl { class DrawBuffer { protected: typedef struct gfx::SurfaceCaps SurfaceCaps; 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, DrawBuffer** out_buffer); protected: GLContext* const mGL; const gfx::IntSize mSize; const GLuint mFB; const GLuint mColorMSRB; const GLuint mDepthRB; const GLuint mStencilRB; DrawBuffer(GLContext* gl, const gfx::IntSize& size, GLuint fb, GLuint colorMSRB, GLuint depthRB, GLuint stencilRB) : mGL(gl) , mSize(size) , mFB(fb) , mColorMSRB(colorMSRB) , mDepthRB(depthRB) , mStencilRB(stencilRB) {} public: virtual ~DrawBuffer(); const gfx::IntSize& Size() const { return mSize; } GLuint FB() const { return mFB; } }; class ReadBuffer { protected: typedef struct gfx::SurfaceCaps SurfaceCaps; public: // Infallible, always non-null. static ReadBuffer* Create(GLContext* gl, const SurfaceCaps& caps, const GLFormats& formats, SharedSurface_GL* surf); protected: GLContext* const mGL; const GLuint mFB; // mFB has the following attachments: const GLuint mDepthRB; const GLuint mStencilRB; // note no mColorRB here: this is provided by mSurf. SharedSurface_GL* mSurf; // Owned by GLScreenBuffer's SurfaceStream. ReadBuffer(GLContext* gl, GLuint fb, GLuint depthRB, GLuint stencilRB, SharedSurface_GL* 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_GL* surf); const gfx::IntSize& Size() const; GLuint FB() const { return mFB; } SharedSurface_GL* SharedSurf() const { return mSurf; } }; class GLScreenBuffer { protected: typedef class gfx::SurfaceStream SurfaceStream; typedef class gfx::SharedSurface SharedSurface; typedef gfx::SurfaceStreamType SurfaceStreamType; typedef gfx::SharedSurfaceType SharedSurfaceType; typedef struct gfx::SurfaceCaps SurfaceCaps; public: // Infallible. static GLScreenBuffer* Create(GLContext* gl, const gfx::IntSize& size, const SurfaceCaps& caps); protected: GLContext* const mGL; // Owns us. SurfaceCaps mCaps; SurfaceFactory_GL* mFactory; // Owned by us. RefPtr mStream; DrawBuffer* mDraw; // Owned by us. ReadBuffer* mRead; // Owned by us. bool mNeedsBlit; // 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, SurfaceFactory_GL* factory, SurfaceStream* stream) : mGL(gl) , mCaps(caps) , mFactory(factory) , mStream(stream) , mDraw(nullptr) , mRead(nullptr) , mNeedsBlit(true) , mUserDrawFB(0) , mUserReadFB(0) , mInternalDrawFB(0) , mInternalReadFB(0) #ifdef DEBUG , mInInternalMode_DrawFB(true) , mInInternalMode_ReadFB(true) #endif {} public: virtual ~GLScreenBuffer(); SurfaceStream* Stream() const { return mStream; } SurfaceFactory_GL* Factory() const { return mFactory; } SharedSurface_GL* SharedSurf() const { MOZ_ASSERT(mRead); return mRead->SharedSurf(); } bool PreserveBuffer() const { return mCaps.preserve; } const SurfaceCaps& Caps() const { return mCaps; } GLuint DrawFB() const { if (!mDraw) return ReadFB(); return mDraw->FB(); } GLuint ReadFB() const { return mRead->FB(); } void DeletingFB(GLuint fb); const gfx::IntSize& Size() const { MOZ_ASSERT(mRead); MOZ_ASSERT(!mDraw || mDraw->Size() == mRead->Size()); return mRead->Size(); } void BindAsFramebuffer(GLContext* const gl, GLenum target) const; void RequireBlit(); void AssureBlitted(); void AfterDrawCall(); void BeforeReadCall(); /** * Attempts to read pixels from the current bound framebuffer, if * it is backed by a SharedSurface_GL. * * 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 swaps out our SurfaceStream mechanism and replaces it with * one best suited to our platform and compositor configuration. * * Must be called on the producing thread. * We haven't made any guarantee that rendering is actually * done when Morph is run, just that it can't run concurrently * with rendering. This means that we can't just drop the contents * of the buffer, since we may only be partially done rendering. * * Once you pass newFactory into Morph, newFactory will be owned by * GLScreenBuffer, so `forget` any references to it that still exist. */ void Morph(SurfaceFactory_GL* newFactory, SurfaceStreamType streamType); 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); void Readback(SharedSurface_GL* src, gfx::DataSourceSurface* dest); protected: bool Attach(SharedSurface* surface, const gfx::IntSize& size); bool CreateDraw(const gfx::IntSize& size, DrawBuffer** out_buffer); ReadBuffer* CreateRead(SharedSurface_GL* 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 BindDrawFB_Internal(GLuint fb); void BindReadFB_Internal(GLuint fb); }; } // namespace gl } // namespace mozilla #endif // SCREEN_BUFFER_H_