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 "platform/graphics/gpu/DrawingBuffer.h" 34 35 #include <algorithm> 36 #include "platform/TraceEvent.h" 37 #include "platform/graphics/Extensions3D.h" 38 #include "platform/graphics/GraphicsLayer.h" 39 #include "public/platform/Platform.h" 40 #include "public/platform/WebCompositorSupport.h" 41 #include "public/platform/WebExternalBitmap.h" 42 #include "public/platform/WebExternalTextureLayer.h" 43 #include "public/platform/WebGraphicsContext3D.h" 44 45 using namespace std; 46 47 namespace WebCore { 48 49 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize. 50 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would 51 // exceed the global cap will instead clear the buffer. 52 static const int s_maximumResourceUsePixels = 16 * 1024 * 1024; 53 static int s_currentResourceUsePixels = 0; 54 static const float s_resourceAdjustedRatio = 0.5; 55 56 static const bool s_allowContextEvictionOnCreate = true; 57 static const int s_maxScaleAttempts = 3; 58 59 class ScopedTextureUnit0BindingRestorer { 60 public: 61 ScopedTextureUnit0BindingRestorer(GraphicsContext3D* context, GC3Denum activeTextureUnit, Platform3DObject textureUnitZeroId) 62 : m_context(context) 63 , m_oldActiveTextureUnit(activeTextureUnit) 64 , m_oldTextureUnitZeroId(textureUnitZeroId) 65 { 66 m_context->activeTexture(GL_TEXTURE0); 67 } 68 ~ScopedTextureUnit0BindingRestorer() 69 { 70 m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId); 71 m_context->activeTexture(m_oldActiveTextureUnit); 72 } 73 74 private: 75 GraphicsContext3D* m_context; 76 GC3Denum m_oldActiveTextureUnit; 77 Platform3DObject m_oldTextureUnitZeroId; 78 }; 79 80 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager) 81 { 82 Extensions3D* extensions = context->extensions(); 83 bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit") 84 && extensions->supports("GL_ANGLE_framebuffer_multisample") 85 && extensions->supports("GL_OES_rgb8_rgba8"); 86 if (multisampleSupported) { 87 extensions->ensureEnabled("GL_ANGLE_framebuffer_blit"); 88 extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample"); 89 extensions->ensureEnabled("GL_OES_rgb8_rgba8"); 90 } 91 bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil"); 92 if (packedDepthStencilSupported) 93 extensions->ensureEnabled("GL_OES_packed_depth_stencil"); 94 95 RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager)); 96 return drawingBuffer.release(); 97 } 98 99 DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, 100 const IntSize& size, 101 bool multisampleExtensionSupported, 102 bool packedDepthStencilExtensionSupported, 103 PreserveDrawingBuffer preserve, 104 PassRefPtr<ContextEvictionManager> contextEvictionManager) 105 : m_preserveDrawingBuffer(preserve) 106 , m_scissorEnabled(false) 107 , m_texture2DBinding(0) 108 , m_framebufferBinding(0) 109 , m_activeTextureUnit(GL_TEXTURE0) 110 , m_context(context) 111 , m_size(-1, -1) 112 , m_multisampleExtensionSupported(multisampleExtensionSupported) 113 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported) 114 , m_fbo(0) 115 , m_colorBuffer(0) 116 , m_frontColorBuffer(0) 117 , m_depthStencilBuffer(0) 118 , m_depthBuffer(0) 119 , m_stencilBuffer(0) 120 , m_multisampleFBO(0) 121 , m_multisampleColorBuffer(0) 122 , m_contentsChanged(true) 123 , m_contentsChangeCommitted(false) 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 void DrawingBuffer::markContentsChanged() 141 { 142 m_contentsChanged = true; 143 m_contentsChangeCommitted = false; 144 } 145 146 blink::WebGraphicsContext3D* DrawingBuffer::context() 147 { 148 if (!m_context) 149 return 0; 150 return m_context->webContext(); 151 } 152 153 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap) 154 { 155 if (!m_context || !m_contentsChanged || !m_lastColorBuffer) 156 return false; 157 158 m_context->makeContextCurrent(); 159 160 // Resolve the multisampled buffer into the texture referenced by m_lastColorBuffer mailbox. 161 if (multisample()) 162 commit(); 163 164 if (bitmap) { 165 bitmap->setSize(size()); 166 167 unsigned char* pixels = bitmap->pixels(); 168 bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha; 169 GraphicsContext3D::AlphaOp op = needPremultiply ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing; 170 if (pixels) 171 m_context->readBackFramebuffer(pixels, size().width(), size().height(), GraphicsContext3D::ReadbackSkia, op); 172 } 173 174 // We must restore the texture binding since creating new textures, 175 // consuming and producing mailboxes changes it. 176 ScopedTextureUnit0BindingRestorer restorer(m_context.get(), m_activeTextureUnit, m_texture2DBinding); 177 178 // First try to recycle an old buffer. 179 RefPtr<MailboxInfo> nextFrontColorBuffer = recycledMailbox(); 180 181 // No buffer available to recycle, create a new one. 182 if (!nextFrontColorBuffer) { 183 unsigned newColorBuffer = createColorTexture(m_size); 184 // Bad things happened, abandon ship. 185 if (!newColorBuffer) 186 return false; 187 188 nextFrontColorBuffer = createNewMailbox(newColorBuffer); 189 } 190 191 if (m_preserveDrawingBuffer == Discard) { 192 m_colorBuffer = nextFrontColorBuffer->textureId; 193 swap(nextFrontColorBuffer, m_lastColorBuffer); 194 // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a 195 // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding. 196 // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore 197 // it after attaching the new back buffer here. 198 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo); 199 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0); 200 } else { 201 Extensions3D* extensions = m_context->extensions(); 202 extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, nextFrontColorBuffer->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE); 203 } 204 205 if (multisample() && !m_framebufferBinding) 206 bind(); 207 else 208 restoreFramebufferBinding(); 209 210 m_contentsChanged = false; 211 212 context()->bindTexture(GL_TEXTURE_2D, nextFrontColorBuffer->textureId); 213 context()->produceTextureCHROMIUM(GL_TEXTURE_2D, nextFrontColorBuffer->mailbox.name); 214 context()->flush(); 215 m_context->markLayerComposited(); 216 217 *outMailbox = nextFrontColorBuffer->mailbox; 218 m_frontColorBuffer = nextFrontColorBuffer->textureId; 219 return true; 220 } 221 222 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox) 223 { 224 for (size_t i = 0; i < m_textureMailboxes.size(); i++) { 225 RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i]; 226 if (!memcmp(mailboxInfo->mailbox.name, mailbox.name, sizeof(mailbox.name))) { 227 mailboxInfo->mailbox.syncPoint = mailbox.syncPoint; 228 m_recycledMailboxes.append(mailboxInfo.release()); 229 return; 230 } 231 } 232 ASSERT_NOT_REACHED(); 233 } 234 235 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox() 236 { 237 if (!m_context || m_recycledMailboxes.isEmpty()) 238 return PassRefPtr<MailboxInfo>(); 239 240 RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release(); 241 m_recycledMailboxes.removeLast(); 242 243 if (mailboxInfo->mailbox.syncPoint) { 244 context()->waitSyncPoint(mailboxInfo->mailbox.syncPoint); 245 mailboxInfo->mailbox.syncPoint = 0; 246 } 247 248 context()->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureId); 249 context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->mailbox.name); 250 251 if (mailboxInfo->size != m_size) { 252 m_context->texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE); 253 mailboxInfo->size = m_size; 254 } 255 256 return mailboxInfo.release(); 257 } 258 259 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId) 260 { 261 RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo()); 262 context()->genMailboxCHROMIUM(returnMailbox->mailbox.name); 263 returnMailbox->textureId = textureId; 264 returnMailbox->size = m_size; 265 m_textureMailboxes.append(returnMailbox); 266 return returnMailbox.release(); 267 } 268 269 void DrawingBuffer::initialize(const IntSize& size) 270 { 271 ASSERT(m_context); 272 m_attributes = m_context->getContextAttributes(); 273 274 if (m_attributes.alpha) { 275 m_internalColorFormat = GL_RGBA; 276 m_colorFormat = GL_RGBA; 277 m_internalRenderbufferFormat = Extensions3D::RGBA8_OES; 278 } else { 279 m_internalColorFormat = GL_RGB; 280 m_colorFormat = GL_RGB; 281 m_internalRenderbufferFormat = Extensions3D::RGB8_OES; 282 } 283 284 m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); 285 286 m_fbo = m_context->createFramebuffer(); 287 288 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo); 289 m_colorBuffer = createColorTexture(); 290 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0); 291 createSecondaryBuffers(); 292 reset(size); 293 m_lastColorBuffer = createNewMailbox(m_colorBuffer); 294 } 295 296 unsigned DrawingBuffer::frontColorBuffer() const 297 { 298 return m_frontColorBuffer; 299 } 300 301 bool DrawingBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DObject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool premultiplyAlpha, bool flipY) 302 { 303 if (!m_context || !m_context->makeContextCurrent()) 304 return false; 305 if (m_contentsChanged) { 306 if (multisample()) { 307 commit(); 308 if (!m_framebufferBinding) 309 bind(); 310 else 311 restoreFramebufferBinding(); 312 } 313 m_context->flush(); 314 } 315 Platform3DObject sourceTexture = colorBuffer(); 316 317 if (!context.makeContextCurrent()) 318 return false; 319 Extensions3D* extensions = context.extensions(); 320 if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->supports("GL_CHROMIUM_flipy") 321 || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, level)) 322 return false; 323 324 bool unpackPremultiplyAlphaNeeded = false; 325 bool unpackUnpremultiplyAlphaNeeded = false; 326 if (m_attributes.alpha && m_attributes.premultipliedAlpha && !premultiplyAlpha) 327 unpackUnpremultiplyAlphaNeeded = true; 328 else if (m_attributes.alpha && !m_attributes.premultipliedAlpha && premultiplyAlpha) 329 unpackPremultiplyAlphaNeeded = true; 330 331 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpackUnpremultiplyAlphaNeeded); 332 context.pixelStorei(Extensions3D::UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackPremultiplyAlphaNeeded); 333 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, flipY); 334 extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType); 335 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false); 336 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false); 337 context.pixelStorei(Extensions3D::UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); 338 context.flush(); 339 340 return true; 341 } 342 343 Platform3DObject DrawingBuffer::framebuffer() const 344 { 345 return m_fbo; 346 } 347 348 blink::WebLayer* DrawingBuffer::platformLayer() 349 { 350 if (!m_context) 351 return 0; 352 353 if (!m_layer) { 354 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this)); 355 356 m_layer->setOpaque(!m_attributes.alpha); 357 m_layer->setBlendBackgroundColor(m_attributes.alpha); 358 m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha); 359 GraphicsLayer::registerContentsLayer(m_layer->layer()); 360 } 361 362 return m_layer->layer(); 363 } 364 365 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer) 366 { 367 if (!m_context || !m_context->makeContextCurrent() || m_context->extensions()->getGraphicsResetStatusARB() != GL_NO_ERROR) 368 return; 369 370 Extensions3D* extensions = m_context->extensions(); 371 372 if (!imageBuffer) 373 return; 374 Platform3DObject tex = imageBuffer->getBackingTexture(); 375 if (tex) { 376 extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, 377 tex, 0, GL_RGBA, GL_UNSIGNED_BYTE); 378 return; 379 } 380 381 // Since the m_frontColorBuffer was produced and sent to the compositor, it cannot be bound to an fbo. 382 // We have to make a copy of it here and bind that copy instead. 383 // FIXME: That's not true any more, provided we don't change texture 384 // parameters. 385 unsigned sourceTexture = createColorTexture(m_size); 386 extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE); 387 388 // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding). 389 // FIXME: The WebGLRenderingContext tracks the current framebuffer binding, it would be slightly more efficient to use this value 390 // rather than querying it off of the context. 391 GC3Dint previousFramebuffer = 0; 392 m_context->getIntegerv(GL_FRAMEBUFFER_BINDING, &previousFramebuffer); 393 394 Platform3DObject framebuffer = m_context->createFramebuffer(); 395 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); 396 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); 397 398 extensions->paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer); 399 m_context->deleteFramebuffer(framebuffer); 400 m_context->deleteTexture(sourceTexture); 401 402 m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer); 403 } 404 405 void DrawingBuffer::clearPlatformLayer() 406 { 407 if (m_layer) 408 m_layer->clearTexture(); 409 410 if (m_context) 411 m_context->flush(); 412 } 413 414 void DrawingBuffer::releaseResources() 415 { 416 if (m_context) { 417 m_context->makeContextCurrent(); 418 419 clearPlatformLayer(); 420 421 for (size_t i = 0; i < m_textureMailboxes.size(); i++) 422 m_context->deleteTexture(m_textureMailboxes[i]->textureId); 423 424 if (m_multisampleColorBuffer) 425 m_context->deleteRenderbuffer(m_multisampleColorBuffer); 426 427 if (m_depthStencilBuffer) 428 m_context->deleteRenderbuffer(m_depthStencilBuffer); 429 430 if (m_depthBuffer) 431 m_context->deleteRenderbuffer(m_depthBuffer); 432 433 if (m_stencilBuffer) 434 m_context->deleteRenderbuffer(m_stencilBuffer); 435 436 if (m_multisampleFBO) 437 m_context->deleteFramebuffer(m_multisampleFBO); 438 439 if (m_fbo) 440 m_context->deleteFramebuffer(m_fbo); 441 442 m_context.clear(); 443 } 444 445 setSize(IntSize()); 446 447 m_colorBuffer = 0; 448 m_frontColorBuffer = 0; 449 m_multisampleColorBuffer = 0; 450 m_depthStencilBuffer = 0; 451 m_depthBuffer = 0; 452 m_stencilBuffer = 0; 453 m_multisampleFBO = 0; 454 m_fbo = 0; 455 m_contextEvictionManager.clear(); 456 457 m_lastColorBuffer.clear(); 458 m_recycledMailboxes.clear(); 459 m_textureMailboxes.clear(); 460 461 if (m_layer) { 462 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); 463 m_layer.clear(); 464 } 465 } 466 467 unsigned DrawingBuffer::createColorTexture(const IntSize& size) 468 { 469 if (!m_context) 470 return 0; 471 472 unsigned offscreenColorTexture = m_context->createTexture(); 473 if (!offscreenColorTexture) 474 return 0; 475 476 m_context->bindTexture(GL_TEXTURE_2D, offscreenColorTexture); 477 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 478 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 479 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 480 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 481 if (!size.isEmpty()) 482 m_context->texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE); 483 484 return offscreenColorTexture; 485 } 486 487 void DrawingBuffer::createSecondaryBuffers() 488 { 489 // create a multisample FBO 490 if (multisample()) { 491 m_multisampleFBO = m_context->createFramebuffer(); 492 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); 493 m_multisampleColorBuffer = m_context->createRenderbuffer(); 494 } 495 } 496 497 bool DrawingBuffer::resizeFramebuffer(const IntSize& size) 498 { 499 // resize regular FBO 500 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo); 501 502 m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer); 503 m_context->texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE); 504 if (m_lastColorBuffer) 505 m_lastColorBuffer->size = size; 506 507 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0); 508 509 m_context->bindTexture(GL_TEXTURE_2D, 0); 510 511 if (!multisample()) 512 resizeDepthStencil(size, 0); 513 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 514 return false; 515 516 return true; 517 } 518 519 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size) 520 { 521 if (multisample()) { 522 int maxSampleCount = 0; 523 524 m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount); 525 int sampleCount = std::min(4, maxSampleCount); 526 527 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); 528 529 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer); 530 m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, m_internalRenderbufferFormat, size.width(), size.height()); 531 532 if (m_context->getError() == GL_OUT_OF_MEMORY) 533 return false; 534 535 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleColorBuffer); 536 resizeDepthStencil(size, sampleCount); 537 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 538 return false; 539 } 540 541 return true; 542 } 543 544 void DrawingBuffer::resizeDepthStencil(const IntSize& size, int sampleCount) 545 { 546 if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtensionSupported) { 547 if (!m_depthStencilBuffer) 548 m_depthStencilBuffer = m_context->createRenderbuffer(); 549 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); 550 if (multisample()) 551 m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, size.width(), size.height()); 552 else 553 m_context->renderbufferStorage(GL_RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, size.width(), size.height()); 554 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); 555 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); 556 } else { 557 if (m_attributes.depth) { 558 if (!m_depthBuffer) 559 m_depthBuffer = m_context->createRenderbuffer(); 560 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); 561 if (multisample()) 562 m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height()); 563 else 564 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); 565 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); 566 } 567 if (m_attributes.stencil) { 568 if (!m_stencilBuffer) 569 m_stencilBuffer = m_context->createRenderbuffer(); 570 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer); 571 if (multisample()) 572 m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_STENCIL_INDEX8, size.width(), size.height()); 573 else 574 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height()); 575 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer); 576 } 577 } 578 m_context->bindRenderbuffer(GL_RENDERBUFFER, 0); 579 } 580 581 582 583 void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask) 584 { 585 if (!m_context) 586 return; 587 588 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo); 589 590 m_context->clear(clearMask); 591 592 // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too. 593 if (m_multisampleFBO) { 594 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo); 595 m_context->clear(GL_COLOR_BUFFER_BIT); 596 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); 597 } 598 } 599 600 void DrawingBuffer::setSize(const IntSize& size) { 601 if (m_size == size) 602 return; 603 604 s_currentResourceUsePixels += pixelDelta(size); 605 m_size = size; 606 } 607 608 int DrawingBuffer::pixelDelta(const IntSize& size) { 609 return (max(0, size.width()) * max(0, size.height())) - (max(0, m_size.width()) * max(0, m_size.height())); 610 } 611 612 IntSize DrawingBuffer::adjustSize(const IntSize& size) { 613 IntSize adjustedSize = size; 614 615 // Clamp if the desired size is greater than the maximum texture size for the device. 616 if (adjustedSize.height() > m_maxTextureSize) 617 adjustedSize.setHeight(m_maxTextureSize); 618 619 if (adjustedSize.width() > m_maxTextureSize) 620 adjustedSize.setWidth(m_maxTextureSize); 621 622 // Try progressively smaller sizes until we find a size that fits or reach a scale limit. 623 int scaleAttempts = 0; 624 while ((s_currentResourceUsePixels + pixelDelta(adjustedSize)) > s_maximumResourceUsePixels) { 625 scaleAttempts++; 626 if (scaleAttempts > s_maxScaleAttempts) 627 return IntSize(); 628 629 adjustedSize.scale(s_resourceAdjustedRatio); 630 631 if (adjustedSize.isEmpty()) 632 return IntSize(); 633 } 634 635 return adjustedSize; 636 } 637 638 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext) { 639 IntSize adjustedSize = adjustSize(size); 640 if (!adjustedSize.isEmpty()) { 641 evictContext = false; 642 return adjustedSize; // Buffer fits without evicting a context. 643 } 644 645 // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted. 646 IntSize oldestSize = m_contextEvictionManager->oldestContextSize(); 647 int pixelDelta = oldestSize.width() * oldestSize.height(); 648 649 s_currentResourceUsePixels -= pixelDelta; 650 adjustedSize = adjustSize(size); 651 s_currentResourceUsePixels += pixelDelta; 652 653 evictContext = !adjustedSize.isEmpty(); 654 return adjustedSize; 655 } 656 657 void DrawingBuffer::reset(const IntSize& newSize) 658 { 659 if (!m_context) 660 return; 661 662 IntSize adjustedSize; 663 bool evictContext = false; 664 bool isNewContext = m_size.isEmpty(); 665 if (s_allowContextEvictionOnCreate && isNewContext) 666 adjustedSize = adjustSizeWithContextEviction(newSize, evictContext); 667 else 668 adjustedSize = adjustSize(newSize); 669 670 if (adjustedSize.isEmpty()) 671 return; 672 673 if (evictContext) 674 m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost."); 675 676 if (adjustedSize != m_size) { 677 do { 678 // resize multisample FBO 679 if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) { 680 adjustedSize.scale(s_resourceAdjustedRatio); 681 continue; 682 } 683 break; 684 } while (!adjustedSize.isEmpty()); 685 686 setSize(adjustedSize); 687 688 if (adjustedSize.isEmpty()) 689 return; 690 } 691 692 m_context->disable(GL_SCISSOR_TEST); 693 m_context->clearColor(0, 0, 0, 0); 694 m_context->colorMask(true, true, true, true); 695 696 GC3Dbitfield clearMask = GL_COLOR_BUFFER_BIT; 697 if (m_attributes.depth) { 698 m_context->clearDepth(1.0f); 699 clearMask |= GL_DEPTH_BUFFER_BIT; 700 m_context->depthMask(true); 701 } 702 if (m_attributes.stencil) { 703 m_context->clearStencil(0); 704 clearMask |= GL_STENCIL_BUFFER_BIT; 705 m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF); 706 } 707 708 clearFramebuffers(clearMask); 709 } 710 711 void DrawingBuffer::commit(long x, long y, long width, long height) 712 { 713 if (!m_context) 714 return; 715 716 if (width < 0) 717 width = m_size.width(); 718 if (height < 0) 719 height = m_size.height(); 720 721 m_context->makeContextCurrent(); 722 723 if (m_multisampleFBO && !m_contentsChangeCommitted) { 724 m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO); 725 m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo); 726 727 if (m_scissorEnabled) 728 m_context->disable(GL_SCISSOR_TEST); 729 730 // Use NEAREST, because there is no scale performed during the blit. 731 m_context->extensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 732 733 if (m_scissorEnabled) 734 m_context->enable(GL_SCISSOR_TEST); 735 } 736 737 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo); 738 m_contentsChangeCommitted = true; 739 } 740 741 void DrawingBuffer::restoreFramebufferBinding() 742 { 743 if (!m_context || !m_framebufferBinding) 744 return; 745 746 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding); 747 } 748 749 bool DrawingBuffer::multisample() const 750 { 751 return m_attributes.antialias && m_multisampleExtensionSupported; 752 } 753 754 void DrawingBuffer::bind() 755 { 756 if (!m_context) 757 return; 758 759 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo); 760 } 761 762 } // namespace WebCore 763