gfx/gl/GLScreenBuffer.h
author Ciure Andrei <aciure@mozilla.com>
Mon, 14 Jan 2019 05:55:46 +0200
changeset 453695 d23f6e4d9ba23840b511babbf651e37b297e863d
parent 448963 66eb1f485c1a3ea81372758bc92292c9428b17cd
child 478957 e3151aa0f795d2bf57fea603997a81f7a102b727
permissions -rw-r--r--
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE

/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 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/. */

/* 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 layers {
class KnowsCompositor;
class LayersIPCChannel;
class SharedSurfaceTextureClient;
}  // namespace layers

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<DrawBuffer>* 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<ReadBuffer> 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<GLScreenBuffer> Create(GLContext* gl,
                                          const gfx::IntSize& size,
                                          const SurfaceCaps& caps);

  static UniquePtr<SurfaceFactory> CreateFactory(
      GLContext* gl, const SurfaceCaps& caps,
      layers::KnowsCompositor* compositorConnection,
      const layers::TextureFlags& flags);

 protected:
  GLContext* const mGL;  // Owns us.
 public:
  const SurfaceCaps mCaps;

 protected:
  UniquePtr<SurfaceFactory> mFactory;

  RefPtr<layers::SharedSurfaceTextureClient> mBack;
  RefPtr<layers::SharedSurfaceTextureClient> mFront;

  UniquePtr<DrawBuffer> mDraw;
  UniquePtr<ReadBuffer> mRead;

  bool mNeedsBlit;

  GLenum mUserReadBufferMode;
  GLenum mUserDrawBufferMode;

  // 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<SurfaceFactory> factory);

 public:
  virtual ~GLScreenBuffer();

  SurfaceFactory* Factory() const { return mFactory.get(); }

  const RefPtr<layers::SharedSurfaceTextureClient>& 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 0;

    return mDraw->mSamples;
  }

  uint32_t DepthBits() const;

  void DeletingFB(GLuint fb);

  const gfx::IntSize& Size() const {
    MOZ_ASSERT(mRead);
    MOZ_ASSERT(!mDraw || mDraw->mSize == mRead->Size());
    return mRead->Size();
  }

  bool IsReadBufferReady() const { return mRead.get() != nullptr; }

  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);
  void SetDrawBuffer(GLenum userMode);

  GLenum GetReadBufferMode() const { return mUserReadBufferMode; }
  GLenum GetDrawBufferMode() const { return mUserDrawBufferMode; }

  /**
   * 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<SurfaceFactory> 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<DrawBuffer>* out_buffer);
  UniquePtr<ReadBuffer> 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_