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