Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright (c) 2010, 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 are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 
     33 #include "core/platform/graphics/gpu/DrawingBuffer.h"
     34 
     35 #include <algorithm>
     36 #include "core/platform/chromium/TraceEvent.h"
     37 #include "core/platform/graphics/Extensions3D.h"
     38 #include "core/platform/graphics/GraphicsContext3D.h"
     39 #include "core/platform/graphics/GraphicsLayer.h"
     40 #include "public/platform/Platform.h"
     41 #include "public/platform/WebCompositorSupport.h"
     42 #include "public/platform/WebExternalBitmap.h"
     43 #include "public/platform/WebExternalTextureLayer.h"
     44 #include "public/platform/WebGraphicsContext3D.h"
     45 
     46 using namespace std;
     47 
     48 namespace WebCore {
     49 
     50 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
     51 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
     52 // exceed the global cap will instead clear the buffer.
     53 static const int s_maximumResourceUsePixels = 16 * 1024 * 1024;
     54 static int s_currentResourceUsePixels = 0;
     55 static const float s_resourceAdjustedRatio = 0.5;
     56 
     57 static const bool s_allowContextEvictionOnCreate = true;
     58 static const int s_maxScaleAttempts = 3;
     59 
     60 class ScopedTextureUnit0BindingRestorer {
     61 public:
     62     ScopedTextureUnit0BindingRestorer(GraphicsContext3D* context, GC3Denum activeTextureUnit, Platform3DObject textureUnitZeroId)
     63         : m_context(context)
     64         , m_oldActiveTextureUnit(activeTextureUnit)
     65         , m_oldTextureUnitZeroId(textureUnitZeroId)
     66     {
     67         m_context->activeTexture(GraphicsContext3D::TEXTURE0);
     68     }
     69     ~ScopedTextureUnit0BindingRestorer()
     70     {
     71         m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_oldTextureUnitZeroId);
     72         m_context->activeTexture(m_oldActiveTextureUnit);
     73     }
     74 
     75 private:
     76     GraphicsContext3D* m_context;
     77     GC3Denum m_oldActiveTextureUnit;
     78     Platform3DObject m_oldTextureUnitZeroId;
     79 };
     80 
     81 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
     82 {
     83     Extensions3D* extensions = context->getExtensions();
     84     bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit")
     85         && extensions->supports("GL_ANGLE_framebuffer_multisample")
     86         && extensions->supports("GL_OES_rgb8_rgba8");
     87     if (multisampleSupported) {
     88         extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
     89         extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
     90         extensions->ensureEnabled("GL_OES_rgb8_rgba8");
     91     }
     92     bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil");
     93     if (packedDepthStencilSupported)
     94         extensions->ensureEnabled("GL_OES_packed_depth_stencil");
     95 
     96     RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
     97     return drawingBuffer.release();
     98 }
     99 
    100 DrawingBuffer::DrawingBuffer(GraphicsContext3D* context,
    101                              const IntSize& size,
    102                              bool multisampleExtensionSupported,
    103                              bool packedDepthStencilExtensionSupported,
    104                              PreserveDrawingBuffer preserve,
    105                              PassRefPtr<ContextEvictionManager> contextEvictionManager)
    106     : m_preserveDrawingBuffer(preserve)
    107     , m_scissorEnabled(false)
    108     , m_texture2DBinding(0)
    109     , m_framebufferBinding(0)
    110     , m_activeTextureUnit(GraphicsContext3D::TEXTURE0)
    111     , m_context(context)
    112     , m_size(-1, -1)
    113     , m_multisampleExtensionSupported(multisampleExtensionSupported)
    114     , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
    115     , m_fbo(0)
    116     , m_colorBuffer(0)
    117     , m_frontColorBuffer(0)
    118     , m_depthStencilBuffer(0)
    119     , m_depthBuffer(0)
    120     , m_stencilBuffer(0)
    121     , m_multisampleFBO(0)
    122     , m_multisampleColorBuffer(0)
    123     , m_contentsChanged(true)
    124     , m_internalColorFormat(0)
    125     , m_colorFormat(0)
    126     , m_internalRenderbufferFormat(0)
    127     , m_maxTextureSize(0)
    128     , m_contextEvictionManager(contextEvictionManager)
    129 {
    130     // Used by browser tests to detect the use of a DrawingBuffer.
    131     TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
    132     initialize(size);
    133 }
    134 
    135 DrawingBuffer::~DrawingBuffer()
    136 {
    137     releaseResources();
    138 }
    139 
    140 WebKit::WebGraphicsContext3D* DrawingBuffer::context()
    141 {
    142     if (!m_context)
    143         return 0;
    144     return m_context->webContext();
    145 }
    146 
    147 bool DrawingBuffer::prepareMailbox(WebKit::WebExternalTextureMailbox* outMailbox, WebKit::WebExternalBitmap* bitmap)
    148 {
    149     if (!m_context || !m_contentsChanged || !m_lastColorBuffer)
    150         return false;
    151 
    152     m_context->makeContextCurrent();
    153 
    154     // Resolve the multisampled buffer into the texture referenced by m_lastColorBuffer mailbox.
    155     if (multisample())
    156         commit();
    157 
    158     if (bitmap) {
    159         bitmap->setSize(size());
    160 
    161         unsigned char* pixels = bitmap->pixels();
    162         bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
    163         GraphicsContext3D::AlphaOp op = needPremultiply ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing;
    164         if (pixels)
    165             m_context->readBackFramebuffer(pixels, size().width(), size().height(), GraphicsContext3D::ReadbackSkia, op);
    166     }
    167 
    168     // We must restore the texture binding since creating new textures,
    169     // consuming and producing mailboxes changes it.
    170     ScopedTextureUnit0BindingRestorer restorer(m_context.get(), m_activeTextureUnit, m_texture2DBinding);
    171 
    172     // First try to recycle an old buffer.
    173     RefPtr<MailboxInfo> nextFrontColorBuffer = getRecycledMailbox();
    174 
    175     // No buffer available to recycle, create a new one.
    176     if (!nextFrontColorBuffer) {
    177         unsigned newColorBuffer = createColorTexture(m_size);
    178         // Bad things happened, abandon ship.
    179         if (!newColorBuffer)
    180             return false;
    181 
    182         nextFrontColorBuffer = createNewMailbox(newColorBuffer);
    183     }
    184 
    185     if (m_preserveDrawingBuffer == Discard) {
    186         m_colorBuffer = nextFrontColorBuffer->textureId;
    187         swap(nextFrontColorBuffer, m_lastColorBuffer);
    188         // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a
    189         // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding.
    190         // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore
    191         // it after attaching the new back buffer here.
    192         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
    193         m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
    194     } else {
    195         Extensions3D* extensions = m_context->getExtensions();
    196         extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, m_colorBuffer, nextFrontColorBuffer->textureId, 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE);
    197     }
    198 
    199     if (multisample() && !m_framebufferBinding)
    200         bind();
    201     else
    202         restoreFramebufferBinding();
    203 
    204     m_contentsChanged = false;
    205 
    206     context()->bindTexture(GraphicsContext3D::TEXTURE_2D, nextFrontColorBuffer->textureId);
    207     context()->produceTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, nextFrontColorBuffer->mailbox.name);
    208     context()->flush();
    209     m_context->markLayerComposited();
    210 
    211     *outMailbox = nextFrontColorBuffer->mailbox;
    212     m_frontColorBuffer = nextFrontColorBuffer->textureId;
    213     return true;
    214 }
    215 
    216 void DrawingBuffer::mailboxReleased(const WebKit::WebExternalTextureMailbox& mailbox)
    217 {
    218     for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
    219          RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i];
    220          if (!memcmp(mailboxInfo->mailbox.name, mailbox.name, sizeof(mailbox.name))) {
    221              mailboxInfo->mailbox.syncPoint = mailbox.syncPoint;
    222              m_recycledMailboxes.append(mailboxInfo.release());
    223              return;
    224          }
    225      }
    226      ASSERT_NOT_REACHED();
    227 }
    228 
    229 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::getRecycledMailbox()
    230 {
    231     if (!m_context || m_recycledMailboxes.isEmpty())
    232         return PassRefPtr<MailboxInfo>();
    233 
    234     RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release();
    235     m_recycledMailboxes.removeLast();
    236 
    237     if (mailboxInfo->mailbox.syncPoint) {
    238         context()->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
    239         mailboxInfo->mailbox.syncPoint = 0;
    240     }
    241 
    242     context()->bindTexture(GraphicsContext3D::TEXTURE_2D, mailboxInfo->textureId);
    243     context()->consumeTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, mailboxInfo->mailbox.name);
    244 
    245     if (mailboxInfo->size != m_size) {
    246         m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, m_internalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GraphicsContext3D::UNSIGNED_BYTE);
    247         mailboxInfo->size = m_size;
    248     }
    249 
    250     return mailboxInfo.release();
    251 }
    252 
    253 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
    254 {
    255     RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo());
    256     context()->genMailboxCHROMIUM(returnMailbox->mailbox.name);
    257     returnMailbox->textureId = textureId;
    258     returnMailbox->size = m_size;
    259     m_textureMailboxes.append(returnMailbox);
    260     return returnMailbox.release();
    261 }
    262 
    263 void DrawingBuffer::initialize(const IntSize& size)
    264 {
    265     ASSERT(m_context);
    266     m_attributes = m_context->getContextAttributes();
    267 
    268     if (m_attributes.alpha) {
    269         m_internalColorFormat = GraphicsContext3D::RGBA;
    270         m_colorFormat = GraphicsContext3D::RGBA;
    271         m_internalRenderbufferFormat = Extensions3D::RGBA8_OES;
    272     } else {
    273         m_internalColorFormat = GraphicsContext3D::RGB;
    274         m_colorFormat = GraphicsContext3D::RGB;
    275         m_internalRenderbufferFormat = Extensions3D::RGB8_OES;
    276     }
    277 
    278     m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize);
    279 
    280     m_fbo = m_context->createFramebuffer();
    281 
    282     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
    283     m_colorBuffer = createColorTexture();
    284     m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
    285     createSecondaryBuffers();
    286     reset(size);
    287     m_lastColorBuffer = createNewMailbox(m_colorBuffer);
    288 }
    289 
    290 unsigned DrawingBuffer::frontColorBuffer() const
    291 {
    292     return m_colorBuffer;
    293 }
    294 
    295 Platform3DObject DrawingBuffer::framebuffer() const
    296 {
    297     return m_fbo;
    298 }
    299 
    300 WebKit::WebLayer* DrawingBuffer::platformLayer()
    301 {
    302     if (!m_context)
    303         return 0;
    304 
    305     if (!m_layer) {
    306         m_layer = adoptPtr(WebKit::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
    307 
    308         m_layer->setOpaque(!m_attributes.alpha);
    309         m_layer->setBlendBackgroundColor(m_attributes.alpha);
    310         m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha);
    311         GraphicsLayer::registerContentsLayer(m_layer->layer());
    312     }
    313 
    314     return m_layer->layer();
    315 }
    316 
    317 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
    318 {
    319     if (!m_context || !m_context->makeContextCurrent() || m_context->getExtensions()->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR)
    320         return;
    321 
    322     Extensions3D* extensions = m_context->getExtensions();
    323     // Since the m_frontColorBuffer was produced and sent to the compositor, it cannot be bound to an fbo.
    324     // We have to make a copy of it here and bind that copy instead.
    325     // FIXME: That's not true any more, provided we don't change texture
    326     // parameters.
    327     unsigned sourceTexture = createColorTexture(m_size);
    328     extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE);
    329 
    330     // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding).
    331     // FIXME: The WebGLRenderingContext tracks the current framebuffer binding, it would be slightly more efficient to use this value
    332     // rather than querying it off of the context.
    333     GC3Dint previousFramebuffer = 0;
    334     m_context->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &previousFramebuffer);
    335 
    336     Platform3DObject framebuffer = m_context->createFramebuffer();
    337     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, framebuffer);
    338     m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, sourceTexture, 0);
    339 
    340     extensions->paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
    341     m_context->deleteFramebuffer(framebuffer);
    342     m_context->deleteTexture(sourceTexture);
    343 
    344     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, previousFramebuffer);
    345 }
    346 
    347 void DrawingBuffer::clearPlatformLayer()
    348 {
    349     if (m_layer)
    350         m_layer->clearTexture();
    351 
    352     if (m_context)
    353         m_context->flush();
    354 }
    355 
    356 void DrawingBuffer::releaseResources()
    357 {
    358     if (m_context) {
    359         m_context->makeContextCurrent();
    360 
    361         clearPlatformLayer();
    362 
    363         for (size_t i = 0; i < m_textureMailboxes.size(); i++)
    364             m_context->deleteTexture(m_textureMailboxes[i]->textureId);
    365 
    366         if (m_multisampleColorBuffer)
    367             m_context->deleteRenderbuffer(m_multisampleColorBuffer);
    368 
    369         if (m_depthStencilBuffer)
    370             m_context->deleteRenderbuffer(m_depthStencilBuffer);
    371 
    372         if (m_depthBuffer)
    373             m_context->deleteRenderbuffer(m_depthBuffer);
    374 
    375         if (m_stencilBuffer)
    376             m_context->deleteRenderbuffer(m_stencilBuffer);
    377 
    378         if (m_multisampleFBO)
    379             m_context->deleteFramebuffer(m_multisampleFBO);
    380 
    381         if (m_fbo)
    382             m_context->deleteFramebuffer(m_fbo);
    383 
    384         m_context.clear();
    385     }
    386 
    387     setSize(IntSize());
    388 
    389     m_colorBuffer = 0;
    390     m_frontColorBuffer = 0;
    391     m_multisampleColorBuffer = 0;
    392     m_depthStencilBuffer = 0;
    393     m_depthBuffer = 0;
    394     m_stencilBuffer = 0;
    395     m_multisampleFBO = 0;
    396     m_fbo = 0;
    397     m_contextEvictionManager.clear();
    398 
    399     m_lastColorBuffer.clear();
    400     m_recycledMailboxes.clear();
    401     m_textureMailboxes.clear();
    402 
    403     if (m_layer) {
    404         GraphicsLayer::unregisterContentsLayer(m_layer->layer());
    405         m_layer.clear();
    406     }
    407 }
    408 
    409 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
    410 {
    411     if (!m_context)
    412         return 0;
    413 
    414     unsigned offscreenColorTexture = m_context->createTexture();
    415     if (!offscreenColorTexture)
    416         return 0;
    417 
    418     m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, offscreenColorTexture);
    419     m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
    420     m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
    421     m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
    422     m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
    423     if (!size.isEmpty())
    424         m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GraphicsContext3D::UNSIGNED_BYTE);
    425 
    426     return offscreenColorTexture;
    427 }
    428 
    429 void DrawingBuffer::createSecondaryBuffers()
    430 {
    431     // create a multisample FBO
    432     if (multisample()) {
    433         m_multisampleFBO = m_context->createFramebuffer();
    434         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
    435         m_multisampleColorBuffer = m_context->createRenderbuffer();
    436     }
    437 }
    438 
    439 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
    440 {
    441     // resize regular FBO
    442     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
    443 
    444     m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer);
    445     m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GraphicsContext3D::UNSIGNED_BYTE);
    446     if (m_lastColorBuffer)
    447         m_lastColorBuffer->size = m_size;
    448 
    449     m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
    450 
    451     m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
    452 
    453     if (!multisample())
    454         resizeDepthStencil(size, 0);
    455     if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
    456         return false;
    457 
    458     return true;
    459 }
    460 
    461 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
    462 {
    463     if (multisample()) {
    464         int maxSampleCount = 0;
    465 
    466         m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
    467         int sampleCount = std::min(4, maxSampleCount);
    468 
    469         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
    470 
    471         m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
    472         m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
    473 
    474         if (m_context->getError() == GraphicsContext3D::OUT_OF_MEMORY)
    475             return false;
    476 
    477         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
    478         resizeDepthStencil(size, sampleCount);
    479         if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
    480             return false;
    481     }
    482 
    483     return true;
    484 }
    485 
    486 void DrawingBuffer::resizeDepthStencil(const IntSize& size, int sampleCount)
    487 {
    488     if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtensionSupported) {
    489         if (!m_depthStencilBuffer)
    490             m_depthStencilBuffer = m_context->createRenderbuffer();
    491         m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
    492         if (multisample())
    493             m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, size.width(), size.height());
    494         else
    495             m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, size.width(), size.height());
    496         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
    497         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
    498     } else {
    499         if (m_attributes.depth) {
    500             if (!m_depthBuffer)
    501                 m_depthBuffer = m_context->createRenderbuffer();
    502             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
    503             if (multisample())
    504                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, size.width(), size.height());
    505             else
    506                 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, size.width(), size.height());
    507             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
    508         }
    509         if (m_attributes.stencil) {
    510             if (!m_stencilBuffer)
    511                 m_stencilBuffer = m_context->createRenderbuffer();
    512             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
    513             if (multisample())
    514                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, size.width(), size.height());
    515             else
    516                 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, size.width(), size.height());
    517             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
    518         }
    519     }
    520     m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
    521 }
    522 
    523 
    524 
    525 void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask)
    526 {
    527     if (!m_context)
    528         return;
    529 
    530     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
    531 
    532     m_context->clear(clearMask);
    533 
    534     // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too.
    535     if (m_multisampleFBO) {
    536         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
    537         m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
    538         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
    539     }
    540 }
    541 
    542 // Only way to ensure that we're not getting a bad framebuffer on some AMD/OSX devices.
    543 // FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly.
    544 bool DrawingBuffer::checkBufferIntegrity()
    545 {
    546     if (!m_multisampleFBO)
    547         return true;
    548 
    549     if (m_scissorEnabled)
    550         m_context->disable(GraphicsContext3D::SCISSOR_TEST);
    551 
    552     m_context->colorMask(true, true, true, true);
    553 
    554     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
    555     m_context->clearColor(1.0f, 0.0f, 1.0f, 1.0f);
    556     m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
    557 
    558     commit(0, 0, 1, 1);
    559 
    560     unsigned char pixel[4] = {0, 0, 0, 0};
    561     m_context->readPixels(0, 0, 1, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, &pixel);
    562 
    563     if (m_scissorEnabled)
    564         m_context->enable(GraphicsContext3D::SCISSOR_TEST);
    565 
    566     return (pixel[0] == 0xFF && pixel[1] == 0x00 && pixel[2] == 0xFF && pixel[3] == 0xFF);
    567 }
    568 
    569 void DrawingBuffer::setSize(const IntSize& size) {
    570     if (m_size == size)
    571         return;
    572 
    573     s_currentResourceUsePixels += pixelDelta(size);
    574     m_size = size;
    575 }
    576 
    577 int DrawingBuffer::pixelDelta(const IntSize& size) {
    578     return (max(0, size.width()) * max(0, size.height())) - (max(0, m_size.width()) * max(0, m_size.height()));
    579 }
    580 
    581 IntSize DrawingBuffer::adjustSize(const IntSize& size) {
    582     IntSize adjustedSize = size;
    583 
    584     // Clamp if the desired size is greater than the maximum texture size for the device.
    585     if (adjustedSize.height() > m_maxTextureSize)
    586         adjustedSize.setHeight(m_maxTextureSize);
    587 
    588     if (adjustedSize.width() > m_maxTextureSize)
    589         adjustedSize.setWidth(m_maxTextureSize);
    590 
    591     // Try progressively smaller sizes until we find a size that fits or reach a scale limit.
    592     int scaleAttempts = 0;
    593     while ((s_currentResourceUsePixels + pixelDelta(adjustedSize)) > s_maximumResourceUsePixels) {
    594         scaleAttempts++;
    595         if (scaleAttempts > s_maxScaleAttempts)
    596             return IntSize();
    597 
    598         adjustedSize.scale(s_resourceAdjustedRatio);
    599 
    600         if (adjustedSize.isEmpty())
    601             return IntSize();
    602     }
    603 
    604     return adjustedSize;
    605 }
    606 
    607 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext) {
    608     IntSize adjustedSize = adjustSize(size);
    609     if (!adjustedSize.isEmpty()) {
    610         evictContext = false;
    611         return adjustedSize; // Buffer fits without evicting a context.
    612     }
    613 
    614     // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted.
    615     IntSize oldestSize = m_contextEvictionManager->oldestContextSize();
    616     int pixelDelta = oldestSize.width() * oldestSize.height();
    617 
    618     s_currentResourceUsePixels -= pixelDelta;
    619     adjustedSize = adjustSize(size);
    620     s_currentResourceUsePixels += pixelDelta;
    621 
    622     evictContext = !adjustedSize.isEmpty();
    623     return adjustedSize;
    624 }
    625 
    626 void DrawingBuffer::reset(const IntSize& newSize)
    627 {
    628     if (!m_context)
    629         return;
    630 
    631     IntSize adjustedSize;
    632     bool evictContext = false;
    633     bool isNewContext = m_size.isEmpty();
    634     if (s_allowContextEvictionOnCreate && isNewContext)
    635         adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
    636     else
    637         adjustedSize = adjustSize(newSize);
    638 
    639     if (adjustedSize.isEmpty())
    640         return;
    641 
    642     if (evictContext)
    643         m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
    644 
    645     if (adjustedSize != m_size) {
    646         do {
    647             // resize multisample FBO
    648             if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
    649                 adjustedSize.scale(s_resourceAdjustedRatio);
    650                 continue;
    651             }
    652 
    653 #if OS(DARWIN)
    654             // FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly on OSX.
    655             if (!checkBufferIntegrity()) {
    656                 adjustedSize.scale(s_resourceAdjustedRatio);
    657                 continue;
    658             }
    659 #endif
    660             break;
    661         } while (!adjustedSize.isEmpty());
    662 
    663         setSize(adjustedSize);
    664 
    665         if (adjustedSize.isEmpty())
    666             return;
    667     }
    668 
    669     m_context->disable(GraphicsContext3D::SCISSOR_TEST);
    670     m_context->clearColor(0, 0, 0, 0);
    671     m_context->colorMask(true, true, true, true);
    672 
    673     GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT;
    674     if (m_attributes.depth) {
    675         m_context->clearDepth(1.0f);
    676         clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
    677         m_context->depthMask(true);
    678     }
    679     if (m_attributes.stencil) {
    680         m_context->clearStencil(0);
    681         clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
    682         m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF);
    683     }
    684 
    685     clearFramebuffers(clearMask);
    686 }
    687 
    688 void DrawingBuffer::commit(long x, long y, long width, long height)
    689 {
    690     if (!m_context)
    691         return;
    692 
    693     if (width < 0)
    694         width = m_size.width();
    695     if (height < 0)
    696         height = m_size.height();
    697 
    698     m_context->makeContextCurrent();
    699 
    700     if (m_multisampleFBO) {
    701         m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
    702         m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
    703 
    704         if (m_scissorEnabled)
    705             m_context->disable(GraphicsContext3D::SCISSOR_TEST);
    706 
    707         // Use NEAREST, because there is no scale performed during the blit.
    708         m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST);
    709 
    710         if (m_scissorEnabled)
    711             m_context->enable(GraphicsContext3D::SCISSOR_TEST);
    712     }
    713 
    714     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
    715 }
    716 
    717 void DrawingBuffer::restoreFramebufferBinding()
    718 {
    719     if (!m_context || !m_framebufferBinding)
    720         return;
    721 
    722     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding);
    723 }
    724 
    725 bool DrawingBuffer::multisample() const
    726 {
    727     return m_attributes.antialias && m_multisampleExtensionSupported;
    728 }
    729 
    730 void DrawingBuffer::bind()
    731 {
    732     if (!m_context)
    733         return;
    734 
    735     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
    736 }
    737 
    738 } // namespace WebCore
    739