| /* |
| * 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 |