blob: 3db717c560275f83269fdd6457dadbea04256130 [file] [log] [blame]
/*
* Copyright (c) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DrawingBuffer_h
#define DrawingBuffer_h
#include "cc/layers/texture_layer_client.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "platform/PlatformExport.h"
#include "platform/geometry/IntSize.h"
#include "platform/graphics/GraphicsTypes3D.h"
#include "platform/graphics/gpu/WebGLImageConversion.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "wtf/Deque.h"
#include "wtf/Noncopyable.h"
#include "wtf/RefCounted.h"
#include "wtf/Vector.h"
#include <memory>
namespace cc {
class SharedBitmap;
}
namespace gpu {
namespace gles2 {
class GLES2Interface;
}
}
namespace WTF {
class ArrayBufferContents;
}
namespace blink {
class Extensions3DUtil;
class ImageBuffer;
class StaticBitmapImage;
class WebExternalBitmap;
class WebExternalTextureLayer;
class WebGraphicsContext3DProvider;
class WebLayer;
// Manages a rendering target (framebuffer + attachment) for a canvas. Can
// publish its rendering results to a WebLayer for compositing.
class PLATFORM_EXPORT DrawingBuffer
: public NON_EXPORTED_BASE(cc::TextureLayerClient),
public RefCounted<DrawingBuffer> {
WTF_MAKE_NONCOPYABLE(DrawingBuffer);
public:
enum PreserveDrawingBuffer {
Preserve,
Discard,
};
enum WebGLVersion {
WebGL1,
WebGL2,
};
enum ChromiumImageUsage {
AllowChromiumImage,
DisallowChromiumImage,
};
static PassRefPtr<DrawingBuffer> create(
std::unique_ptr<WebGraphicsContext3DProvider>,
const IntSize&,
bool premultipliedAlpha,
bool wantAlphaChannel,
bool wantDepthBuffer,
bool wantStencilBuffer,
bool wantAntialiasing,
PreserveDrawingBuffer,
WebGLVersion,
ChromiumImageUsage);
static void forceNextDrawingBufferCreationToFail();
~DrawingBuffer() override;
// Destruction will be completed after all mailboxes are released.
void beginDestruction();
// Issues a glClear() on all framebuffers associated with this DrawingBuffer.
// The caller is responsible for making the context current and setting the
// clear values and masks. Modifies the framebuffer binding.
void clearFramebuffers(GLbitfield clearMask);
// Indicates whether the DrawingBuffer internally allocated a packed
// depth-stencil renderbuffer in the situation where the end user only asked
// for a depth buffer. In this case, we need to upgrade clears of the depth
// buffer to clears of the depth and stencil buffers in order to avoid
// performance problems on some GPUs.
bool hasImplicitStencilBuffer() const { return m_hasImplicitStencilBuffer; }
bool hasDepthBuffer() const { return !!m_depthStencilBuffer; }
bool hasStencilBuffer() const { return !!m_depthStencilBuffer; }
// Given the desired buffer size, provides the largest dimensions that will
// fit in the pixel budget.
static IntSize adjustSize(const IntSize& desiredSize,
const IntSize& curSize,
int maxTextureSize);
// Resizes (or allocates if necessary) all buffers attached to the default
// framebuffer. Returns whether the operation was successful. Leaves GL
// bindings dirtied.
bool reset(const IntSize&);
// Bind the default framebuffer to |target|. |target| must be
// GL_FRAMEBUFFER, GL_READ_FRAMEBUFFER, or GL_DRAW_FRAMEBUFFER.
void bind(GLenum target);
IntSize size() const { return m_size; }
// Copies the multisample color buffer to the normal color buffer and leaves
// m_fbo bound.
void commit();
// commit should copy the full multisample buffer, and not respect the
// current scissor bounds. Track the state of the scissor test so that it
// can be disabled during calls to commit.
void setScissorEnabled(bool scissorEnabled) {
m_scissorEnabled = scissorEnabled;
}
// The DrawingBuffer needs to track the texture bound to texture unit 0.
// The bound texture is tracked to avoid costly queries during rendering.
void setTexture2DBinding(GLuint texture) { m_texture2DBinding = texture; }
void setPixelUnpackBufferBinding(GLuint buffer) {
DCHECK(m_webGLVersion > WebGL1);
m_pixelUnpackBufferBinding = buffer;
}
void notifyBufferDeleted(GLuint buffer) {
if (m_webGLVersion > WebGL1 && buffer == m_pixelUnpackBufferBinding) {
setPixelUnpackBufferBinding(0);
}
}
// The DrawingBuffer needs to track the currently bound framebuffer so it
// restore the binding when needed.
void setFramebufferBinding(GLenum target, GLuint fbo) {
switch (target) {
case GL_FRAMEBUFFER:
m_drawFramebufferBinding = fbo;
m_readFramebufferBinding = fbo;
break;
case GL_DRAW_FRAMEBUFFER:
m_drawFramebufferBinding = fbo;
break;
case GL_READ_FRAMEBUFFER:
m_readFramebufferBinding = fbo;
break;
default:
ASSERT(0);
}
}
// The DrawingBuffer needs to track the color mask and clear color so that
// it can restore it when needed.
void setClearColor(GLfloat* clearColor) {
memcpy(m_clearColor, clearColor, 4 * sizeof(GLfloat));
}
void setColorMask(GLboolean* colorMask) {
memcpy(m_colorMask, colorMask, 4 * sizeof(GLboolean));
}
// The DrawingBuffer needs to track the currently bound renderbuffer so it
// restore the binding when needed.
void setRenderbufferBinding(GLuint renderbuffer) {
m_renderbufferBinding = renderbuffer;
}
// Track the currently active texture unit. Texture unit 0 is used as host for
// a scratch texture.
void setActiveTextureUnit(GLint textureUnit) {
m_activeTextureUnit = textureUnit;
}
bool multisample() const;
GLuint framebuffer() const;
bool discardFramebufferSupported() const {
return m_discardFramebufferSupported;
}
void markContentsChanged();
void setBufferClearNeeded(bool);
bool bufferClearNeeded() const;
void setIsHidden(bool);
void setFilterQuality(SkFilterQuality);
// Whether the target for draw operations has format GL_RGBA, but is
// emulating format GL_RGB. When the target's storage is first
// allocated, its alpha channel must be cleared to 1. All future drawing
// operations must use a color mask with alpha=GL_FALSE.
bool requiresAlphaChannelToBePreserved();
// Similar to requiresAlphaChannelToBePreserved(), but always targets the
// default framebuffer.
bool defaultBufferRequiresAlphaChannelToBePreserved();
WebLayer* platformLayer();
gpu::gles2::GLES2Interface* contextGL();
WebGraphicsContext3DProvider* contextProvider();
// cc::TextureLayerClient implementation.
bool PrepareTextureMailbox(
cc::TextureMailbox* outMailbox,
std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback) override;
// Returns a StaticBitmapImage backed by a texture containing the current
// contents of the front buffer. This is done without any pixel copies. The
// texture in the ImageBitmap is from the active ContextProvider on the
// DrawingBuffer.
PassRefPtr<StaticBitmapImage> transferToStaticBitmapImage();
// Destroys the TEXTURE_2D binding for the owned context
bool copyToPlatformTexture(gpu::gles2::GLES2Interface*,
GLuint texture,
GLenum internalFormat,
GLenum destType,
GLint level,
bool premultiplyAlpha,
bool flipY,
SourceDrawingBuffer);
void setPackAlignment(GLint param);
bool paintRenderingResultsToImageData(int&,
int&,
SourceDrawingBuffer,
WTF::ArrayBufferContents&);
int sampleCount() const { return m_sampleCount; }
bool explicitResolveOfMultisampleData() const {
return m_antiAliasingMode == MSAAExplicitResolve;
}
void restorePixelUnpackBufferBindings();
// Bind to m_drawFramebufferBinding or m_readFramebufferBinding if it's not 0.
// Otherwise, bind to the default FBO.
void restoreFramebufferBindings();
void restoreTextureBindings();
void addNewMailboxCallback(std::unique_ptr<WTF::Closure> closure) {
m_newMailboxCallback = std::move(closure);
}
protected: // For unittests
DrawingBuffer(std::unique_ptr<WebGraphicsContext3DProvider>,
std::unique_ptr<Extensions3DUtil>,
bool discardFramebufferSupported,
bool wantAlphaChannel,
bool premultipliedAlpha,
PreserveDrawingBuffer,
WebGLVersion,
bool wantsDepth,
bool wantsStencil,
ChromiumImageUsage);
bool initialize(const IntSize&, bool useMultisampling);
// Shared memory bitmaps that were released by the compositor and can be used
// again by this DrawingBuffer.
struct RecycledBitmap {
std::unique_ptr<cc::SharedBitmap> bitmap;
IntSize size;
};
Vector<RecycledBitmap> m_recycledBitmaps;
private:
// All parameters necessary to generate the texture that will be passed to
// prepareMailbox.
struct TextureParameters {
DISALLOW_NEW();
GLenum target = 0;
GLenum internalColorFormat = 0;
// The internal color format used when allocating storage for the
// texture. This may be different from internalColorFormat if RGB
// emulation is required.
GLenum creationInternalColorFormat = 0;
GLenum colorFormat = 0;
};
// If we used CHROMIUM_image as the backing storage for our buffers,
// we need to know the mapping from texture id to image.
struct TextureInfo {
DISALLOW_NEW();
GLuint textureId = 0;
GLuint imageId = 0;
bool immutable = false;
TextureParameters parameters;
};
struct MailboxInfo : public RefCounted<MailboxInfo> {
MailboxInfo() = default;
gpu::Mailbox mailbox;
TextureInfo textureInfo;
IntSize size;
private:
WTF_MAKE_NONCOPYABLE(MailboxInfo);
};
bool prepareTextureMailboxInternal(
cc::TextureMailbox* outMailbox,
std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback,
bool forceGpuResult);
// Callbacks for mailboxes given to the compositor from PrepareTextureMailbox.
void gpuMailboxReleased(const gpu::Mailbox&,
const gpu::SyncToken&,
bool lostResource);
void softwareMailboxReleased(std::unique_ptr<cc::SharedBitmap>,
const IntSize&,
const gpu::SyncToken&,
bool lostResource);
// The texture parameters to use for a texture that will be backed by a
// CHROMIUM_image.
TextureParameters chromiumImageTextureParameters();
// The texture parameters to use for a default texture.
TextureParameters defaultTextureParameters();
// Creates and binds a texture with the given parameters. Returns 0 on
// failure, or the newly created texture id on success. The caller takes
// ownership of the newly created texture.
GLuint createColorTexture(const TextureParameters&);
// Create the depth/stencil and multisample buffers, if needed.
bool resizeMultisampleFramebuffer(const IntSize&);
void resizeDepthStencil(const IntSize&);
// Attempts to allocator storage for, or resize all buffers. Returns whether
// the operation was successful.
bool resizeDefaultFramebuffer(const IntSize&);
void clearPlatformLayer();
PassRefPtr<MailboxInfo> takeRecycledMailbox();
PassRefPtr<MailboxInfo> createNewMailbox(const TextureInfo&);
void deleteMailbox(const gpu::Mailbox&, const gpu::SyncToken&);
void freeRecycledMailboxes();
std::unique_ptr<cc::SharedBitmap> createOrRecycleBitmap();
// Updates the current size of the buffer, ensuring that
// s_currentResourceUsePixels is updated.
void setSize(const IntSize& size);
// This is the order of bytes to use when doing a readback.
enum ReadbackOrder { ReadbackRGBA, ReadbackSkia };
// Helper function which does a readback from the currently-bound
// framebuffer into a buffer of a certain size with 4-byte pixels.
void readBackFramebuffer(unsigned char* pixels,
int width,
int height,
ReadbackOrder,
WebGLImageConversion::AlphaOp);
// Helper function to flip a bitmap vertically.
void flipVertically(uint8_t* data, int width, int height);
// Allocate a storage texture if possible. Otherwise, allocate a regular
// texture.
void allocateConditionallyImmutableTexture(GLenum target,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type);
// Allocate buffer storage to be sent to compositor using either texImage2D or
// CHROMIUM_image based on available support.
void deleteChromiumImageForTexture(TextureInfo*);
// If RGB emulation is required, then the CHROMIUM image's alpha channel
// must be immediately cleared after it is bound to a texture. Nothing
// should be allowed to change the alpha channel after this.
void clearChromiumImageAlpha(const TextureInfo&);
// Tries to create a CHROMIUM_image backed texture if
// RuntimeEnabledFeatures::webGLImageChromiumEnabled() is true. On failure,
// or if the flag is false, creates a default texture.
TextureInfo createTextureAndAllocateMemory(const IntSize&);
// Creates and allocates space for a default texture.
TextureInfo createDefaultTextureAndAllocateMemory(const IntSize&);
void resizeTextureMemory(TextureInfo*, const IntSize&);
// Attaches |m_colorBuffer| to |m_fbo|, which is always the source for read
// operations.
void attachColorBufferToReadFramebuffer();
// Whether the WebGL client desires an explicit resolve. This is
// implemented by forwarding all draw operations to a multisample
// renderbuffer, which is resolved before any read operations or swaps.
bool wantExplicitResolve();
// Whether the WebGL client wants a depth or stencil buffer.
bool wantDepthOrStencil();
// The format to use when creating a multisampled renderbuffer.
GLenum getMultisampledRenderbufferFormat();
const PreserveDrawingBuffer m_preserveDrawingBuffer;
const WebGLVersion m_webGLVersion;
bool m_scissorEnabled = false;
GLuint m_texture2DBinding = 0;
GLuint m_pixelUnpackBufferBinding = 0;
GLuint m_drawFramebufferBinding = 0;
GLuint m_readFramebufferBinding = 0;
GLuint m_renderbufferBinding = 0;
GLenum m_activeTextureUnit = GL_TEXTURE0;
GLfloat m_clearColor[4];
GLboolean m_colorMask[4];
std::unique_ptr<WebGraphicsContext3DProvider> m_contextProvider;
// Lifetime is tied to the m_contextProvider.
gpu::gles2::GLES2Interface* m_gl;
std::unique_ptr<Extensions3DUtil> m_extensionsUtil;
IntSize m_size = {-1, -1};
const bool m_discardFramebufferSupported;
const bool m_wantAlphaChannel;
const bool m_premultipliedAlpha;
const bool m_softwareRendering;
bool m_hasImplicitStencilBuffer = false;
bool m_storageTextureSupported = false;
struct FrontBufferInfo {
gpu::Mailbox mailbox;
gpu::SyncToken produceSyncToken;
TextureInfo texInfo;
};
FrontBufferInfo m_frontColorBuffer;
std::unique_ptr<WTF::Closure> m_newMailboxCallback;
// This is used when the user requests either a depth or stencil buffer.
GLuint m_depthStencilBuffer = 0;
// When wantExplicitResolve() returns true, the target of all draw
// operations.
GLuint m_multisampleFBO = 0;
// The id of the renderbuffer storage for |m_multisampleFBO|.
GLuint m_multisampleRenderbuffer = 0;
// When wantExplicitResolve() returns false, the target of all draw and
// read operations. When wantExplicitResolve() returns true, the target of
// all read operations. A swap is performed by exchanging |m_colorBuffer|
// with |m_frontColorBuffer|.
GLuint m_fbo = 0;
// All information about the texture storage for |m_fbo|.
TextureInfo m_colorBuffer;
// True if our contents have been modified since the last presentation of this
// buffer.
bool m_contentsChanged = true;
// True if commit() has been called since the last time markContentsChanged()
// had been called.
bool m_contentsChangeCommitted = false;
bool m_bufferClearNeeded = false;
// Whether the client wants a depth or stencil buffer.
const bool m_wantDepth;
const bool m_wantStencil;
enum AntialiasingMode {
None,
MSAAImplicitResolve,
MSAAExplicitResolve,
ScreenSpaceAntialiasing,
};
AntialiasingMode m_antiAliasingMode = None;
int m_maxTextureSize = 0;
int m_sampleCount = 0;
int m_packAlignment = 4;
bool m_destructionInProgress = false;
bool m_isHidden = false;
SkFilterQuality m_filterQuality = kLow_SkFilterQuality;
std::unique_ptr<WebExternalTextureLayer> m_layer;
// All of the mailboxes that this DrawingBuffer has ever created.
Vector<RefPtr<MailboxInfo>> m_textureMailboxes;
struct RecycledMailbox : RefCounted<RecycledMailbox> {
RecycledMailbox(const gpu::Mailbox& mailbox,
const gpu::SyncToken& syncToken)
: mailbox(mailbox), syncToken(syncToken) {}
gpu::Mailbox mailbox;
gpu::SyncToken syncToken;
private:
WTF_MAKE_NONCOPYABLE(RecycledMailbox);
};
// Mailboxes that were released by the compositor can be used again by this
// DrawingBuffer.
Deque<RefPtr<RecycledMailbox>> m_recycledMailboxQueue;
// If the width and height of the Canvas's backing store don't
// match those that we were given in the most recent call to
// reshape(), then we need an intermediate bitmap to read back the
// frame buffer into. This seems to happen when CSS styles are
// used to resize the Canvas.
SkBitmap m_resizingBitmap;
// Used to flip a bitmap vertically.
Vector<uint8_t> m_scanline;
// In the case of OffscreenCanvas, we do not want to enable the
// WebGLImageChromium flag, so we replace all the
// RuntimeEnabledFeatures::webGLImageChromiumEnabled() call with
// shouldUseChromiumImage() calls, and set m_chromiumImageUsage to
// DisallowChromiumImage in the case of OffscreenCanvas.
ChromiumImageUsage m_chromiumImageUsage;
bool shouldUseChromiumImage();
};
} // namespace blink
#endif // DrawingBuffer_h