2 * Copyright (c) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "platform/graphics/gpu/DrawingBuffer.h"
36 #include "platform/TraceEvent.h"
37 #include "platform/graphics/GraphicsLayer.h"
38 #include "platform/graphics/gpu/Extensions3DUtil.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 #include "public/platform/WebGraphicsContext3DProvider.h"
46 #include "wtf/RefCountedLeakCounter.h"
54 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
55 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
56 // exceed the global cap will instead clear the buffer.
57 const int s_maximumResourceUsePixels = 16 * 1024 * 1024;
58 int s_currentResourceUsePixels = 0;
59 const float s_resourceAdjustedRatio = 0.5;
61 const bool s_allowContextEvictionOnCreate = true;
62 const int s_maxScaleAttempts = 3;
64 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, drawingBufferCounter, ("DrawingBuffer"));
66 class ScopedTextureUnit0BindingRestorer {
68 ScopedTextureUnit0BindingRestorer(blink::WebGraphicsContext3D* context, GLenum activeTextureUnit, Platform3DObject textureUnitZeroId)
70 , m_oldActiveTextureUnit(activeTextureUnit)
71 , m_oldTextureUnitZeroId(textureUnitZeroId)
73 m_context->activeTexture(GL_TEXTURE0);
75 ~ScopedTextureUnit0BindingRestorer()
77 m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId);
78 m_context->activeTexture(m_oldActiveTextureUnit);
82 blink::WebGraphicsContext3D* m_context;
83 GLenum m_oldActiveTextureUnit;
84 Platform3DObject m_oldTextureUnitZeroId;
89 PassRefPtr<DrawingBuffer> DrawingBuffer::create(PassOwnPtr<blink::WebGraphicsContext3D> context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
92 OwnPtr<Extensions3DUtil> extensionsUtil = Extensions3DUtil::create(context.get());
93 if (!extensionsUtil) {
94 // This might be the first time we notice that the WebGraphicsContext3D is lost.
97 bool multisampleSupported = extensionsUtil->supportsExtension("GL_CHROMIUM_framebuffer_multisample")
98 && extensionsUtil->supportsExtension("GL_OES_rgb8_rgba8");
99 if (multisampleSupported) {
100 extensionsUtil->ensureExtensionEnabled("GL_CHROMIUM_framebuffer_multisample");
101 extensionsUtil->ensureExtensionEnabled("GL_OES_rgb8_rgba8");
103 bool packedDepthStencilSupported = extensionsUtil->supportsExtension("GL_OES_packed_depth_stencil");
104 #if defined(OS_TIZEN) && CPU(X86)
105 multisampleSupported = false;
107 if (packedDepthStencilSupported)
108 extensionsUtil->ensureExtensionEnabled("GL_OES_packed_depth_stencil");
110 RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, extensionsUtil.release(), multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
111 if (!drawingBuffer->initialize(size)) {
112 drawingBuffer->beginDestruction();
113 return PassRefPtr<DrawingBuffer>();
115 return drawingBuffer.release();
118 DrawingBuffer::DrawingBuffer(PassOwnPtr<blink::WebGraphicsContext3D> context,
119 PassOwnPtr<Extensions3DUtil> extensionsUtil,
120 bool multisampleExtensionSupported,
121 bool packedDepthStencilExtensionSupported,
122 PreserveDrawingBuffer preserve,
123 PassRefPtr<ContextEvictionManager> contextEvictionManager)
124 : m_preserveDrawingBuffer(preserve)
125 , m_scissorEnabled(false)
126 , m_texture2DBinding(0)
127 , m_framebufferBinding(0)
128 , m_activeTextureUnit(GL_TEXTURE0)
130 , m_extensionsUtil(extensionsUtil)
132 , m_multisampleExtensionSupported(multisampleExtensionSupported)
133 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
136 , m_frontColorBuffer(0)
137 , m_depthStencilBuffer(0)
140 , m_multisampleFBO(0)
141 , m_multisampleColorBuffer(0)
142 , m_contentsChanged(true)
143 , m_contentsChangeCommitted(false)
144 , m_layerComposited(false)
145 , m_multisampleMode(None)
146 , m_internalColorFormat(0)
148 , m_internalRenderbufferFormat(0)
149 , m_maxTextureSize(0)
152 , m_destructionInProgress(false)
153 , m_contextEvictionManager(contextEvictionManager)
155 // Used by browser tests to detect the use of a DrawingBuffer.
156 TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
158 drawingBufferCounter.increment();
162 DrawingBuffer::~DrawingBuffer()
164 ASSERT(m_destructionInProgress);
165 ASSERT(m_textureMailboxes.isEmpty());
169 drawingBufferCounter.decrement();
173 void DrawingBuffer::markContentsChanged()
175 m_contentsChanged = true;
176 m_contentsChangeCommitted = false;
177 m_layerComposited = false;
180 bool DrawingBuffer::layerComposited() const
182 return m_layerComposited;
185 void DrawingBuffer::markLayerComposited()
187 m_layerComposited = true;
190 blink::WebGraphicsContext3D* DrawingBuffer::context()
192 return m_context.get();
195 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
197 if (!m_contentsChanged)
200 if (m_destructionInProgress) {
201 // It can be hit in the following sequence.
202 // 1. WebGL draws something.
203 // 2. The compositor begins the frame.
204 // 3. Javascript makes a context lost using WEBGL_lose_context extension.
209 m_context->makeContextCurrent();
211 // Resolve the multisampled buffer into m_colorBuffer texture.
212 if (m_multisampleMode != None)
216 bitmap->setSize(size());
218 unsigned char* pixels = bitmap->pixels();
219 bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
220 WebGLImageConversion::AlphaOp op = needPremultiply ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing;
222 readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op);
225 // We must restore the texture binding since creating new textures,
226 // consuming and producing mailboxes changes it.
227 ScopedTextureUnit0BindingRestorer restorer(m_context.get(), m_activeTextureUnit, m_texture2DBinding);
229 // First try to recycle an old buffer.
230 RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox();
232 // No buffer available to recycle, create a new one.
233 if (!frontColorBufferMailbox) {
234 unsigned newColorBuffer = createColorTexture(m_size);
235 // Bad things happened, abandon ship.
239 frontColorBufferMailbox = createNewMailbox(newColorBuffer);
242 if (m_preserveDrawingBuffer == Discard) {
243 swap(frontColorBufferMailbox->textureId, m_colorBuffer);
244 // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a
245 // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding.
246 // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore
247 // it after attaching the new back buffer here.
248 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
249 if (m_multisampleMode == ImplicitResolve)
250 m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0, m_sampleCount);
252 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
254 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, frontColorBufferMailbox->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
257 if (m_multisampleMode != None && !m_framebufferBinding)
260 restoreFramebufferBinding();
262 m_contentsChanged = false;
264 m_context->bindTexture(GL_TEXTURE_2D, frontColorBufferMailbox->textureId);
265 m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name);
267 frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
268 markLayerComposited();
270 // set m_parentDrawingBuffer to make sure 'this' stays alive as long as it has live mailboxes
271 ASSERT(!frontColorBufferMailbox->m_parentDrawingBuffer);
272 frontColorBufferMailbox->m_parentDrawingBuffer = this;
273 *outMailbox = frontColorBufferMailbox->mailbox;
274 m_frontColorBuffer = frontColorBufferMailbox->textureId;
278 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
280 if (m_destructionInProgress) {
281 mailboxReleasedWhileDestructionInProgress(mailbox);
285 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
286 RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i];
287 if (nameEquals(mailboxInfo->mailbox, mailbox)) {
288 mailboxInfo->mailbox.syncPoint = mailbox.syncPoint;
289 ASSERT(mailboxInfo->m_parentDrawingBuffer.get() == this);
290 mailboxInfo->m_parentDrawingBuffer.clear();
291 m_recycledMailboxQueue.prepend(mailboxInfo->mailbox);
295 ASSERT_NOT_REACHED();
298 void DrawingBuffer::mailboxReleasedWhileDestructionInProgress(const blink::WebExternalTextureMailbox& mailbox)
300 ASSERT(m_textureMailboxes.size());
301 m_context->makeContextCurrent();
302 // Ensure not to call the destructor until deleteMailbox() is completed.
303 RefPtr<DrawingBuffer> self = this;
304 deleteMailbox(mailbox);
307 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
309 if (m_recycledMailboxQueue.isEmpty())
310 return PassRefPtr<MailboxInfo>();
312 blink::WebExternalTextureMailbox mailbox = m_recycledMailboxQueue.takeLast();
313 RefPtr<MailboxInfo> mailboxInfo;
314 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
315 if (nameEquals(m_textureMailboxes[i]->mailbox, mailbox)) {
316 mailboxInfo = m_textureMailboxes[i];
322 if (mailboxInfo->mailbox.syncPoint) {
323 m_context->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
324 mailboxInfo->mailbox.syncPoint = 0;
327 if (mailboxInfo->size != m_size) {
328 m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureId);
329 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
330 mailboxInfo->size = m_size;
333 return mailboxInfo.release();
336 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
338 RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo());
339 m_context->genMailboxCHROMIUM(returnMailbox->mailbox.name);
340 returnMailbox->textureId = textureId;
341 returnMailbox->size = m_size;
342 m_textureMailboxes.append(returnMailbox);
343 return returnMailbox.release();
346 void DrawingBuffer::deleteMailbox(const blink::WebExternalTextureMailbox& mailbox)
348 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
349 if (nameEquals(m_textureMailboxes[i]->mailbox, mailbox)) {
350 if (mailbox.syncPoint)
351 m_context->waitSyncPoint(mailbox.syncPoint);
352 m_context->deleteTexture(m_textureMailboxes[i]->textureId);
353 m_textureMailboxes.remove(i);
357 ASSERT_NOT_REACHED();
360 bool DrawingBuffer::initialize(const IntSize& size)
362 if (!m_context->makeContextCurrent()) {
363 // Most likely the GPU process exited and the attempt to reconnect to it failed.
364 // Need to try to restore the context again later.
368 if (m_context->isContextLost()) {
369 // Need to try to restore the context again later.
373 m_attributes = m_context->getContextAttributes();
375 if (m_attributes.alpha) {
376 m_internalColorFormat = GL_RGBA;
377 m_colorFormat = GL_RGBA;
378 m_internalRenderbufferFormat = GL_RGBA8_OES;
380 m_internalColorFormat = GL_RGB;
381 m_colorFormat = GL_RGB;
382 m_internalRenderbufferFormat = GL_RGB8_OES;
385 m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
387 int maxSampleCount = 0;
388 m_multisampleMode = None;
389 if (m_attributes.antialias && m_multisampleExtensionSupported) {
390 m_context->getIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
391 m_multisampleMode = ExplicitResolve;
392 if (m_extensionsUtil->supportsExtension("GL_EXT_multisampled_render_to_texture"))
393 m_multisampleMode = ImplicitResolve;
395 m_sampleCount = std::min(4, maxSampleCount);
397 m_fbo = m_context->createFramebuffer();
399 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
400 m_colorBuffer = createColorTexture();
401 if (m_multisampleMode == ImplicitResolve)
402 m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0, m_sampleCount);
404 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
405 createSecondaryBuffers();
409 bool DrawingBuffer::copyToPlatformTexture(blink::WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
411 if (!m_context->makeContextCurrent())
413 if (m_contentsChanged) {
414 if (m_multisampleMode != None) {
416 if (!m_framebufferBinding)
419 restoreFramebufferBinding();
424 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
427 // Contexts may be in a different share group. We must transfer the texture through a mailbox first
428 RefPtr<MailboxInfo> bufferMailbox = adoptRef(new MailboxInfo());
429 m_context->genMailboxCHROMIUM(bufferMailbox->mailbox.name);
430 m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
431 m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, bufferMailbox->mailbox.name);
434 bufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
436 if (!context->makeContextCurrent())
439 Platform3DObject sourceTexture = context->createTexture();
441 // TODO(bajones): Should be able to change the texture bindings here without reverting but
442 // something else in the system is depending on it. Failing to revert causes WebGL
443 // tests to fail. We should find out why and fix it.
444 GLint boundTexture = 0;
445 context->getIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
446 context->bindTexture(GL_TEXTURE_2D, sourceTexture);
447 context->waitSyncPoint(bufferMailbox->mailbox.syncPoint);
448 context->consumeTextureCHROMIUM(GL_TEXTURE_2D, bufferMailbox->mailbox.name);
450 bool unpackPremultiplyAlphaNeeded = false;
451 bool unpackUnpremultiplyAlphaNeeded = false;
452 if (m_attributes.alpha && m_attributes.premultipliedAlpha && !premultiplyAlpha)
453 unpackUnpremultiplyAlphaNeeded = true;
454 else if (m_attributes.alpha && !m_attributes.premultipliedAlpha && premultiplyAlpha)
455 unpackPremultiplyAlphaNeeded = true;
457 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpackUnpremultiplyAlphaNeeded);
458 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackPremultiplyAlphaNeeded);
459 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, flipY);
460 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
461 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
462 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
463 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
465 context->bindTexture(GL_TEXTURE_2D, boundTexture);
466 context->deleteTexture(sourceTexture);
469 m_context->waitSyncPoint(context->insertSyncPoint());
474 Platform3DObject DrawingBuffer::framebuffer() const
479 blink::WebLayer* DrawingBuffer::platformLayer()
482 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
484 m_layer->setOpaque(!m_attributes.alpha);
485 m_layer->setBlendBackgroundColor(m_attributes.alpha);
486 m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha);
487 GraphicsLayer::registerContentsLayer(m_layer->layer());
490 return m_layer->layer();
493 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
495 if (!m_context->makeContextCurrent() || m_context->getGraphicsResetStatusARB() != GL_NO_ERROR)
500 Platform3DObject tex = imageBuffer->getBackingTexture();
502 RefPtr<MailboxInfo> bufferMailbox = adoptRef(new MailboxInfo());
503 m_context->genMailboxCHROMIUM(bufferMailbox->mailbox.name);
504 m_context->bindTexture(GL_TEXTURE_2D, m_frontColorBuffer);
505 m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, bufferMailbox->mailbox.name);
508 bufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
509 OwnPtr<blink::WebGraphicsContext3DProvider> provider =
510 adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
513 blink::WebGraphicsContext3D* context = provider->context3d();
514 if (!context || !context->makeContextCurrent())
517 Platform3DObject sourceTexture = context->createTexture();
518 GLint boundTexture = 0;
519 context->getIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
520 context->bindTexture(GL_TEXTURE_2D, sourceTexture);
521 context->waitSyncPoint(bufferMailbox->mailbox.syncPoint);
522 context->consumeTextureCHROMIUM(GL_TEXTURE_2D, bufferMailbox->mailbox.name);
523 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture,
524 tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
525 context->bindTexture(GL_TEXTURE_2D, boundTexture);
526 context->deleteTexture(sourceTexture);
528 m_context->waitSyncPoint(context->insertSyncPoint());
532 // Since the m_frontColorBuffer was produced and sent to the compositor, it cannot be bound to an fbo.
533 // We have to make a copy of it here and bind that copy instead.
534 // FIXME: That's not true any more, provided we don't change texture
536 unsigned sourceTexture = createColorTexture(m_size);
537 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE);
539 // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding).
540 // FIXME: The WebGLRenderingContext tracks the current framebuffer binding, it would be slightly more efficient to use this value
541 // rather than querying it off of the context.
542 GLint previousFramebuffer = 0;
543 m_context->getIntegerv(GL_FRAMEBUFFER_BINDING, &previousFramebuffer);
545 Platform3DObject framebuffer = m_context->createFramebuffer();
546 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
547 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0);
549 paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
550 m_context->deleteFramebuffer(framebuffer);
551 m_context->deleteTexture(sourceTexture);
553 m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
556 void DrawingBuffer::clearPlatformLayer()
559 m_layer->clearTexture();
564 void DrawingBuffer::beginDestruction()
566 ASSERT(!m_destructionInProgress);
567 m_destructionInProgress = true;
569 m_context->makeContextCurrent();
571 clearPlatformLayer();
573 while (!m_recycledMailboxQueue.isEmpty())
574 deleteMailbox(m_recycledMailboxQueue.takeLast());
576 if (m_multisampleFBO)
577 m_context->deleteFramebuffer(m_multisampleFBO);
580 m_context->deleteFramebuffer(m_fbo);
582 if (m_multisampleColorBuffer)
583 m_context->deleteRenderbuffer(m_multisampleColorBuffer);
585 if (m_depthStencilBuffer)
586 m_context->deleteRenderbuffer(m_depthStencilBuffer);
589 m_context->deleteRenderbuffer(m_depthBuffer);
592 m_context->deleteRenderbuffer(m_stencilBuffer);
595 m_context->deleteTexture(m_colorBuffer);
600 m_frontColorBuffer = 0;
601 m_multisampleColorBuffer = 0;
602 m_depthStencilBuffer = 0;
605 m_multisampleFBO = 0;
607 m_contextEvictionManager.clear();
610 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
613 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
615 unsigned offscreenColorTexture = m_context->createTexture();
616 if (!offscreenColorTexture)
619 m_context->bindTexture(GL_TEXTURE_2D, offscreenColorTexture);
620 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
621 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
622 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
623 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
625 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
627 return offscreenColorTexture;
630 void DrawingBuffer::createSecondaryBuffers()
632 // create a multisample FBO
633 if (m_multisampleMode == ExplicitResolve) {
634 m_multisampleFBO = m_context->createFramebuffer();
635 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
636 m_multisampleColorBuffer = m_context->createRenderbuffer();
640 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
642 // resize regular FBO
643 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
645 m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
647 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
649 if (m_multisampleMode == ImplicitResolve)
650 m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0, m_sampleCount);
652 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
654 m_context->bindTexture(GL_TEXTURE_2D, 0);
656 if (m_multisampleMode != ExplicitResolve)
657 resizeDepthStencil(size);
658 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
664 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
666 if (m_multisampleMode == ExplicitResolve) {
667 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
669 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
670 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
672 if (m_context->getError() == GL_OUT_OF_MEMORY)
675 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleColorBuffer);
676 resizeDepthStencil(size);
677 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
684 void DrawingBuffer::resizeDepthStencil(const IntSize& size)
686 if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtensionSupported) {
687 if (!m_depthStencilBuffer)
688 m_depthStencilBuffer = m_context->createRenderbuffer();
689 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
690 if (m_multisampleMode == ImplicitResolve)
691 m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
692 else if (m_multisampleMode == ExplicitResolve)
693 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
695 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
696 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
697 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
699 if (m_attributes.depth) {
701 m_depthBuffer = m_context->createRenderbuffer();
702 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
703 if (m_multisampleMode == ImplicitResolve)
704 m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
705 else if (m_multisampleMode == ExplicitResolve)
706 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
708 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
709 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
711 if (m_attributes.stencil) {
712 if (!m_stencilBuffer)
713 m_stencilBuffer = m_context->createRenderbuffer();
714 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
715 if (m_multisampleMode == ImplicitResolve)
716 m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
717 else if (m_multisampleMode == ExplicitResolve)
718 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
720 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height());
721 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
724 m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
729 void DrawingBuffer::clearFramebuffers(GLbitfield clearMask)
731 // We will clear the multisample FBO, but we also need to clear the non-multisampled buffer.
732 if (m_multisampleFBO) {
733 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
734 m_context->clear(GL_COLOR_BUFFER_BIT);
737 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
738 m_context->clear(clearMask);
741 void DrawingBuffer::setSize(const IntSize& size)
746 s_currentResourceUsePixels += pixelDelta(size, m_size);
750 int DrawingBuffer::pixelDelta(const IntSize& newSize, const IntSize& curSize)
752 return (max(0, newSize.width()) * max(0, newSize.height())) - (max(0, curSize.width()) * max(0, curSize.height()));
755 IntSize DrawingBuffer::adjustSize(const IntSize& desiredSize, const IntSize& curSize, int maxTextureSize)
757 IntSize adjustedSize = desiredSize;
759 // Clamp if the desired size is greater than the maximum texture size for the device.
760 if (adjustedSize.height() > maxTextureSize)
761 adjustedSize.setHeight(maxTextureSize);
763 if (adjustedSize.width() > maxTextureSize)
764 adjustedSize.setWidth(maxTextureSize);
766 // Try progressively smaller sizes until we find a size that fits or reach a scale limit.
767 int scaleAttempts = 0;
768 while ((s_currentResourceUsePixels + pixelDelta(adjustedSize, curSize)) > s_maximumResourceUsePixels) {
770 if (scaleAttempts > s_maxScaleAttempts)
773 adjustedSize.scale(s_resourceAdjustedRatio);
775 if (adjustedSize.isEmpty())
782 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext)
784 IntSize adjustedSize = adjustSize(size, m_size, m_maxTextureSize);
785 if (!adjustedSize.isEmpty()) {
786 evictContext = false;
787 return adjustedSize; // Buffer fits without evicting a context.
790 // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted.
791 IntSize oldestSize = m_contextEvictionManager->oldestContextSize();
792 int pixelDelta = oldestSize.width() * oldestSize.height();
794 s_currentResourceUsePixels -= pixelDelta;
795 adjustedSize = adjustSize(size, m_size, m_maxTextureSize);
796 s_currentResourceUsePixels += pixelDelta;
798 evictContext = !adjustedSize.isEmpty();
802 bool DrawingBuffer::reset(const IntSize& newSize)
804 ASSERT(!newSize.isEmpty());
805 IntSize adjustedSize;
806 bool evictContext = false;
807 bool isNewContext = m_size.isEmpty();
808 if (s_allowContextEvictionOnCreate && isNewContext)
809 adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
811 adjustedSize = adjustSize(newSize, m_size, m_maxTextureSize);
813 if (adjustedSize.isEmpty())
817 m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
819 if (adjustedSize != m_size) {
821 // resize multisample FBO
822 if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
823 adjustedSize.scale(s_resourceAdjustedRatio);
827 } while (!adjustedSize.isEmpty());
829 setSize(adjustedSize);
831 if (adjustedSize.isEmpty())
835 m_context->disable(GL_SCISSOR_TEST);
836 m_context->clearColor(0, 0, 0, 0);
837 m_context->colorMask(true, true, true, true);
839 GLbitfield clearMask = GL_COLOR_BUFFER_BIT;
840 if (m_attributes.depth) {
841 m_context->clearDepth(1.0f);
842 clearMask |= GL_DEPTH_BUFFER_BIT;
843 m_context->depthMask(true);
845 if (m_attributes.stencil) {
846 m_context->clearStencil(0);
847 clearMask |= GL_STENCIL_BUFFER_BIT;
848 m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
851 clearFramebuffers(clearMask);
855 void DrawingBuffer::commit(long x, long y, long width, long height)
858 width = m_size.width();
860 height = m_size.height();
862 m_context->makeContextCurrent();
864 if (m_multisampleFBO && !m_contentsChangeCommitted) {
865 m_context->bindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_multisampleFBO);
866 m_context->bindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, m_fbo);
868 if (m_scissorEnabled)
869 m_context->disable(GL_SCISSOR_TEST);
871 // Use NEAREST, because there is no scale performed during the blit.
872 m_context->blitFramebufferCHROMIUM(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
874 if (m_scissorEnabled)
875 m_context->enable(GL_SCISSOR_TEST);
878 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
879 m_contentsChangeCommitted = true;
882 void DrawingBuffer::restoreFramebufferBinding()
884 if (!m_framebufferBinding)
887 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
890 bool DrawingBuffer::multisample() const
892 return m_multisampleMode != None;
895 void DrawingBuffer::bind()
897 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
900 void DrawingBuffer::setPackAlignment(GLint param)
902 m_packAlignment = param;
905 void DrawingBuffer::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
907 paintFramebufferToCanvas(framebuffer(), size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
910 PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height)
912 if (m_attributes.premultipliedAlpha)
915 width = size().width();
916 height = size().height();
918 Checked<int, RecordOverflow> dataSize = 4;
921 if (dataSize.hasOverflowed())
924 RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4);
926 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer());
927 readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, WebGLImageConversion::AlphaDoNothing);
928 flipVertically(pixels->data(), width, height);
930 return pixels.release();
933 void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer)
935 unsigned char* pixels = 0;
937 const SkBitmap& canvasBitmap = imageBuffer->bitmap();
938 const SkBitmap* readbackBitmap = 0;
939 ASSERT(canvasBitmap.colorType() == kPMColor_SkColorType);
940 if (canvasBitmap.width() == width && canvasBitmap.height() == height) {
941 // This is the fastest and most common case. We read back
942 // directly into the canvas's backing store.
943 readbackBitmap = &canvasBitmap;
944 m_resizingBitmap.reset();
946 // We need to allocate a temporary bitmap for reading back the
947 // pixel data. We will then use Skia to rescale this bitmap to
948 // the size of the canvas's backing store.
949 if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) {
950 if (!m_resizingBitmap.allocN32Pixels(width, height))
953 readbackBitmap = &m_resizingBitmap;
956 // Read back the frame buffer.
957 SkAutoLockPixels bitmapLock(*readbackBitmap);
958 pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
960 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
961 readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing);
962 flipVertically(pixels, width, height);
964 readbackBitmap->notifyPixelsChanged();
965 if (m_resizingBitmap.readyToDraw()) {
966 // We need to draw the resizing bitmap into the canvas's backing store.
967 SkCanvas canvas(canvasBitmap);
969 dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height()));
970 canvas.drawBitmapRect(m_resizingBitmap, 0, dst);
974 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, int width, int height, ReadbackOrder readbackOrder, WebGLImageConversion::AlphaOp op)
976 if (m_packAlignment > 4)
977 m_context->pixelStorei(GL_PACK_ALIGNMENT, 1);
978 m_context->readPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
979 if (m_packAlignment > 4)
980 m_context->pixelStorei(GL_PACK_ALIGNMENT, m_packAlignment);
982 size_t bufferSize = 4 * width * height;
984 if (readbackOrder == ReadbackSkia) {
985 #if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
986 // Swizzle red and blue channels to match SkBitmap's byte ordering.
987 // TODO(kbr): expose GL_BGRA as extension.
988 for (size_t i = 0; i < bufferSize; i += 4) {
989 std::swap(pixels[i], pixels[i + 2]);
994 if (op == WebGLImageConversion::AlphaDoPremultiply) {
995 for (size_t i = 0; i < bufferSize; i += 4) {
996 pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
997 pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
998 pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
1000 } else if (op != WebGLImageConversion::AlphaDoNothing) {
1001 ASSERT_NOT_REACHED();
1005 void DrawingBuffer::flipVertically(uint8_t* framebuffer, int width, int height)
1007 m_scanline.resize(width * 4);
1008 uint8* scanline = &m_scanline[0];
1009 unsigned rowBytes = width * 4;
1010 unsigned count = height / 2;
1011 for (unsigned i = 0; i < count; i++) {
1012 uint8* rowA = framebuffer + i * rowBytes;
1013 uint8* rowB = framebuffer + (height - i - 1) * rowBytes;
1014 memcpy(scanline, rowB, rowBytes);
1015 memcpy(rowB, rowA, rowBytes);
1016 memcpy(rowA, scanline, rowBytes);
1020 void DrawingBuffer::texImage2DResourceSafe(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLint unpackAlignment)
1022 ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
1023 m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0);
1026 } // namespace WebCore