Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #include "platform/graphics/Canvas2DLayerBridge.h"
     29 
     30 #include "GrContext.h"
     31 #include "SkDevice.h"
     32 #include "SkSurface.h"
     33 #include "platform/TraceEvent.h"
     34 #include "platform/graphics/Canvas2DLayerManager.h"
     35 #include "platform/graphics/GraphicsLayer.h"
     36 #include "platform/graphics/gpu/SharedGraphicsContext3D.h"
     37 #include "public/platform/Platform.h"
     38 #include "public/platform/WebCompositorSupport.h"
     39 #include "public/platform/WebGraphicsContext3D.h"
     40 
     41 using blink::WebExternalTextureLayer;
     42 using blink::WebGraphicsContext3D;
     43 
     44 namespace WebCore {
     45 
     46 static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount)
     47 {
     48     ASSERT(!context3D->webContext()->isContextLost());
     49     GrContext* gr = context3D->grContext();
     50     if (!gr)
     51         return 0;
     52     gr->resetContext();
     53     SkImageInfo info;
     54     info.fWidth = size.width();
     55     info.fHeight = size.height();
     56     info.fColorType = kPMColor_SkColorType;
     57     info.fAlphaType = kPremul_SkAlphaType;
     58     return adoptRef(SkSurface::NewRenderTarget(gr, info,  msaaSampleCount));
     59 }
     60 
     61 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
     62 {
     63     TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
     64     RefPtr<GraphicsContext3D> context = SharedGraphicsContext3D::get();
     65     RefPtr<SkSurface> surface(createSkSurface(context.get(), size, msaaSampleCount));
     66     if (!surface)
     67         return 0;
     68     RefPtr<Canvas2DLayerBridge> layerBridge;
     69     OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
     70     layerBridge = adoptRef(new Canvas2DLayerBridge(context, canvas.release(), msaaSampleCount, opacityMode));
     71     return layerBridge.release();
     72 }
     73 
     74 Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
     75     : m_canvas(canvas)
     76     , m_context(context)
     77     , m_msaaSampleCount(msaaSampleCount)
     78     , m_bytesAllocated(0)
     79     , m_didRecordDrawCommand(false)
     80     , m_surfaceIsValid(true)
     81     , m_framesPending(0)
     82     , m_destructionInProgress(false)
     83     , m_rateLimitingEnabled(false)
     84     , m_next(0)
     85     , m_prev(0)
     86     , m_lastImageId(0)
     87 {
     88     ASSERT(m_canvas);
     89     // Used by browser tests to detect the use of a Canvas2DLayerBridge.
     90     TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
     91     m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
     92     m_layer->setOpaque(opacityMode == Opaque);
     93     m_layer->setBlendBackgroundColor(opacityMode != Opaque);
     94     GraphicsLayer::registerContentsLayer(m_layer->layer());
     95     m_layer->setRateLimitContext(m_rateLimitingEnabled);
     96     m_canvas->setNotificationClient(this);
     97 }
     98 
     99 Canvas2DLayerBridge::~Canvas2DLayerBridge()
    100 {
    101     ASSERT(m_destructionInProgress);
    102     m_layer.clear();
    103     Vector<MailboxInfo>::iterator mailboxInfo;
    104     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
    105         ASSERT(mailboxInfo->m_status != MailboxInUse);
    106         if (mailboxInfo->m_status == MailboxReleased) {
    107             if (mailboxInfo->m_mailbox.syncPoint) {
    108                 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
    109                 mailboxInfo->m_mailbox.syncPoint = 0;
    110             }
    111             // Invalidate texture state in case the compositor altered it since the copy-on-write.
    112             mailboxInfo->m_image->getTexture()->invalidateCachedState();
    113         }
    114     }
    115     m_mailboxes.clear();
    116 }
    117 
    118 void Canvas2DLayerBridge::beginDestruction()
    119 {
    120     ASSERT(!m_destructionInProgress);
    121     m_destructionInProgress = true;
    122     GraphicsLayer::unregisterContentsLayer(m_layer->layer());
    123     m_canvas->setNotificationClient(0);
    124     m_layer->clearTexture();
    125     Canvas2DLayerManager::get().layerToBeDestroyed(this);
    126     // Orphaning the layer is required to trigger the recration of a new layer
    127     // in the case where destruction is caused by a canvas resize. Test:
    128     // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
    129     m_layer->layer()->removeFromParent();
    130 }
    131 
    132 void Canvas2DLayerBridge::limitPendingFrames()
    133 {
    134     ASSERT(!m_destructionInProgress);
    135     if (m_didRecordDrawCommand) {
    136         m_framesPending++;
    137         m_didRecordDrawCommand = false;
    138         if (m_framesPending > 1) {
    139             // Turn on the rate limiter if this layer tends to accumulate a
    140             // non-discardable multi-frame backlog of draw commands.
    141             setRateLimitingEnabled(true);
    142         }
    143         if (m_rateLimitingEnabled) {
    144             flush();
    145         }
    146     }
    147 }
    148 
    149 void Canvas2DLayerBridge::prepareForDraw()
    150 {
    151     ASSERT(!m_destructionInProgress);
    152     ASSERT(m_layer);
    153     if (!isValid()) {
    154         if (m_canvas) {
    155             // drop pending commands because there is no surface to draw to
    156             m_canvas->silentFlush();
    157         }
    158         return;
    159     }
    160     m_context->makeContextCurrent();
    161 }
    162 
    163 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
    164 {
    165     ASSERT(!m_destructionInProgress);
    166     intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
    167     m_bytesAllocated = bytesAllocated;
    168     Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, delta);
    169 }
    170 
    171 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
    172 {
    173     ASSERT(!m_destructionInProgress);
    174     return m_canvas->storageAllocatedForRecording();
    175 }
    176 
    177 void Canvas2DLayerBridge::flushedDrawCommands()
    178 {
    179     ASSERT(!m_destructionInProgress);
    180     storageAllocatedForRecordingChanged(storageAllocatedForRecording());
    181     m_framesPending = 0;
    182 }
    183 
    184 void Canvas2DLayerBridge::skippedPendingDrawCommands()
    185 {
    186     ASSERT(!m_destructionInProgress);
    187     // Stop triggering the rate limiter if SkDeferredCanvas is detecting
    188     // and optimizing overdraw.
    189     setRateLimitingEnabled(false);
    190     flushedDrawCommands();
    191 }
    192 
    193 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
    194 {
    195     ASSERT(!m_destructionInProgress || !enabled);
    196     if (m_rateLimitingEnabled != enabled) {
    197         m_rateLimitingEnabled = enabled;
    198         m_layer->setRateLimitContext(m_rateLimitingEnabled);
    199     }
    200 }
    201 
    202 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
    203 {
    204     ASSERT(!m_destructionInProgress);
    205     size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
    206     if (bytesFreed)
    207         Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed));
    208     m_bytesAllocated -= bytesFreed;
    209     return bytesFreed;
    210 }
    211 
    212 void Canvas2DLayerBridge::flush()
    213 {
    214     ASSERT(!m_destructionInProgress);
    215     if (m_canvas->hasPendingCommands()) {
    216         TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
    217         m_canvas->flush();
    218         m_context->flush();
    219     }
    220 }
    221 
    222 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
    223 {
    224     // Check on m_layer is necessary because context() may be called during
    225     // the destruction of m_layer
    226     if (m_layer) {
    227         isValid(); // To ensure rate limiter is disabled if context is lost.
    228     }
    229     return m_context->webContext();
    230 }
    231 
    232 bool Canvas2DLayerBridge::isValid()
    233 {
    234     ASSERT(m_layer);
    235     if (m_destructionInProgress)
    236         return false;
    237     if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) {
    238         // Attempt to recover.
    239         m_layer->clearTexture();
    240         m_mailboxes.clear();
    241         RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get();
    242         if (!sharedContext || sharedContext->webContext()->isContextLost()) {
    243             m_surfaceIsValid = false;
    244         } else {
    245             m_context = sharedContext;
    246             IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
    247             RefPtr<SkSurface> surface(createSkSurface(m_context.get(), size, m_msaaSampleCount));
    248             if (surface.get()) {
    249                 m_canvas->setSurface(surface.get());
    250                 m_surfaceIsValid = true;
    251                 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
    252             } else {
    253                 // Surface allocation failed. Set m_surfaceIsValid to false to
    254                 // trigger subsequent retry.
    255                 m_surfaceIsValid = false;
    256             }
    257         }
    258     }
    259     if (!m_surfaceIsValid)
    260         setRateLimitingEnabled(false);
    261     return m_surfaceIsValid;
    262 }
    263 
    264 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
    265 {
    266     if (bitmap) {
    267         // Using accelerated 2d canvas with software renderer, which
    268         // should only happen in tests that use fake graphics contexts
    269         // or in Android WebView in software mode. In this case, we do
    270         // not care about producing any results for this canvas.
    271         m_canvas->silentFlush();
    272         m_lastImageId = 0;
    273         return false;
    274     }
    275     if (!isValid())
    276         return false;
    277     // Release to skia textures that were previouosly released by the
    278     // compositor. We do this before acquiring the next snapshot in
    279     // order to cap maximum gpu memory consumption.
    280     m_context->makeContextCurrent();
    281     flush();
    282     Vector<MailboxInfo>::iterator mailboxInfo;
    283     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
    284         if (mailboxInfo->m_status == MailboxReleased) {
    285             if (mailboxInfo->m_mailbox.syncPoint) {
    286                 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
    287                 mailboxInfo->m_mailbox.syncPoint = 0;
    288             }
    289             // Invalidate texture state in case the compositor altered it since the copy-on-write.
    290             mailboxInfo->m_image->getTexture()->invalidateCachedState();
    291             mailboxInfo->m_image.reset(0);
    292             mailboxInfo->m_status = MailboxAvailable;
    293         }
    294     }
    295     SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot());
    296     // Early exit if canvas was not drawn to since last prepareMailbox
    297     if (image->uniqueID() == m_lastImageId)
    298         return false;
    299     m_lastImageId = image->uniqueID();
    300 
    301     mailboxInfo = createMailboxInfo();
    302     mailboxInfo->m_status = MailboxInUse;
    303     mailboxInfo->m_image.swap(&image);
    304     // Because of texture sharing with the compositor, we must invalidate
    305     // the state cached in skia so that the deferred copy on write
    306     // in SkSurface_Gpu does not make any false assumptions.
    307     mailboxInfo->m_image->getTexture()->invalidateCachedState();
    308 
    309     ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
    310     ASSERT(mailboxInfo->m_image.get());
    311     ASSERT(mailboxInfo->m_image->getTexture());
    312 
    313     m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
    314     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    315     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    316     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    317     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    318     context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
    319     context()->flush();
    320     mailboxInfo->m_mailbox.syncPoint = context()->insertSyncPoint();
    321     m_context->bindTexture(GL_TEXTURE_2D, 0);
    322     // Because we are changing the texture binding without going through skia,
    323     // we must dirty the context.
    324     m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState);
    325 
    326     // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
    327     // live mailboxes
    328     ASSERT(!mailboxInfo->m_parentLayerBridge);
    329     mailboxInfo->m_parentLayerBridge = this;
    330     *outMailbox = mailboxInfo->m_mailbox;
    331     return true;
    332 }
    333 
    334 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
    335     ASSERT(!m_destructionInProgress);
    336     MailboxInfo* mailboxInfo;
    337     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
    338         if (mailboxInfo->m_status == MailboxAvailable) {
    339             return mailboxInfo;
    340         }
    341     }
    342 
    343     // No available mailbox: create one.
    344     m_mailboxes.grow(m_mailboxes.size() + 1);
    345     mailboxInfo = &m_mailboxes.last();
    346     context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
    347     // Worst case, canvas is triple buffered.  More than 3 active mailboxes
    348     // means there is a problem.
    349     // For the single-threaded case, this value needs to be at least
    350     // kMaxSwapBuffersPending+1 (in render_widget.h).
    351     // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
    352     // TODO(piman): fix this.
    353     ASSERT(m_mailboxes.size() <= 4);
    354     ASSERT(mailboxInfo < m_mailboxes.end());
    355     return mailboxInfo;
    356 }
    357 
    358 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
    359 {
    360     Vector<MailboxInfo>::iterator mailboxInfo;
    361     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
    362         if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
    363             mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
    364             ASSERT(mailboxInfo->m_status == MailboxInUse);
    365             mailboxInfo->m_status = MailboxReleased;
    366             // Trigger Canvas2DLayerBridge self-destruction if this is the
    367             // last live mailbox and the layer bridge is not externally
    368             // referenced.
    369             ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
    370             mailboxInfo->m_parentLayerBridge.clear();
    371             return;
    372         }
    373     }
    374 }
    375 
    376 blink::WebLayer* Canvas2DLayerBridge::layer() const
    377 {
    378     ASSERT(m_layer);
    379     return m_layer->layer();
    380 }
    381 
    382 void Canvas2DLayerBridge::willUse()
    383 {
    384     ASSERT(!m_destructionInProgress);
    385     Canvas2DLayerManager::get().layerDidDraw(this);
    386     m_didRecordDrawCommand = true;
    387 }
    388 
    389 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
    390 {
    391     ASSERT(!m_destructionInProgress);
    392     if (!isValid())
    393         return 0;
    394     willUse();
    395     m_canvas->flush();
    396     m_context->flush();
    397     GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
    398     if (renderTarget) {
    399         return renderTarget->asTexture()->getTextureHandle();
    400     }
    401     return 0;
    402 }
    403 
    404 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
    405     // This copy constructor should only be used for Vector reallocation
    406     // Assuming 'other' is to be destroyed, we swap m_image ownership
    407     // rather than do a refcount dance.
    408     memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
    409     m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image));
    410     m_status = other.m_status;
    411 }
    412 
    413 }
    414