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"
35 #include "platform/RuntimeEnabledFeatures.h"
37 #include "platform/TraceEvent.h"
38 #include "platform/graphics/GraphicsLayer.h"
39 #include "platform/graphics/ImageBuffer.h"
40 #include "platform/graphics/gpu/Extensions3DUtil.h"
41 #include "public/platform/Platform.h"
42 #include "public/platform/WebCompositorSupport.h"
43 #include "public/platform/WebExternalBitmap.h"
44 #include "public/platform/WebExternalTextureLayer.h"
45 #include "public/platform/WebGraphicsContext3D.h"
46 #include "public/platform/WebGraphicsContext3DProvider.h"
48 #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(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 WebGraphicsContext3D* m_context;
83 GLenum m_oldActiveTextureUnit;
84 Platform3DObject m_oldTextureUnitZeroId;
89 PassRefPtr<DrawingBuffer> DrawingBuffer::create(PassOwnPtr<WebGraphicsContext3D> context, const IntSize& size, PreserveDrawingBuffer preserve, WebGraphicsContext3D::Attributes requestedAttributes, 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_EXT_multisampled_render_to_texture"))
99 && extensionsUtil->supportsExtension("GL_OES_rgb8_rgba8");
100 if (multisampleSupported) {
101 extensionsUtil->ensureExtensionEnabled("GL_OES_rgb8_rgba8");
102 if (extensionsUtil->supportsExtension("GL_CHROMIUM_framebuffer_multisample"))
103 extensionsUtil->ensureExtensionEnabled("GL_CHROMIUM_framebuffer_multisample");
105 extensionsUtil->ensureExtensionEnabled("GL_EXT_multisampled_render_to_texture");
107 bool packedDepthStencilSupported = extensionsUtil->supportsExtension("GL_OES_packed_depth_stencil");
108 #if defined(OS_TIZEN) && CPU(X86)
109 multisampleSupported = false;
111 if (packedDepthStencilSupported)
112 extensionsUtil->ensureExtensionEnabled("GL_OES_packed_depth_stencil");
113 bool discardFramebufferSupported = extensionsUtil->supportsExtension("GL_EXT_discard_framebuffer");
114 if (discardFramebufferSupported)
115 extensionsUtil->ensureExtensionEnabled("GL_EXT_discard_framebuffer");
117 RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, extensionsUtil.release(), multisampleSupported, packedDepthStencilSupported, discardFramebufferSupported, preserve, requestedAttributes, contextEvictionManager));
118 if (!drawingBuffer->initialize(size)) {
119 drawingBuffer->beginDestruction();
120 return PassRefPtr<DrawingBuffer>();
122 return drawingBuffer.release();
125 DrawingBuffer::DrawingBuffer(PassOwnPtr<WebGraphicsContext3D> context,
126 PassOwnPtr<Extensions3DUtil> extensionsUtil,
127 bool multisampleExtensionSupported,
128 bool packedDepthStencilExtensionSupported,
129 bool discardFramebufferSupported,
130 PreserveDrawingBuffer preserve,
131 WebGraphicsContext3D::Attributes requestedAttributes,
132 PassRefPtr<ContextEvictionManager> contextEvictionManager)
133 : m_preserveDrawingBuffer(preserve)
134 , m_scissorEnabled(false)
135 , m_texture2DBinding(0)
136 , m_framebufferBinding(0)
137 , m_activeTextureUnit(GL_TEXTURE0)
139 , m_extensionsUtil(extensionsUtil)
141 , m_requestedAttributes(requestedAttributes)
142 , m_multisampleExtensionSupported(multisampleExtensionSupported)
143 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
144 , m_discardFramebufferSupported(discardFramebufferSupported)
146 , m_depthStencilBuffer(0)
149 , m_multisampleFBO(0)
150 , m_multisampleColorBuffer(0)
151 , m_contentsChanged(true)
152 , m_contentsChangeCommitted(false)
153 , m_layerComposited(false)
154 , m_multisampleMode(None)
155 , m_internalColorFormat(0)
157 , m_internalRenderbufferFormat(0)
158 , m_maxTextureSize(0)
161 , m_destructionInProgress(false)
163 , m_contextEvictionManager(contextEvictionManager)
165 // Used by browser tests to detect the use of a DrawingBuffer.
166 TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
168 drawingBufferCounter.increment();
172 DrawingBuffer::~DrawingBuffer()
174 ASSERT(m_destructionInProgress);
175 ASSERT(m_textureMailboxes.isEmpty());
179 drawingBufferCounter.decrement();
183 void DrawingBuffer::markContentsChanged()
185 m_contentsChanged = true;
186 m_contentsChangeCommitted = false;
187 m_layerComposited = false;
190 bool DrawingBuffer::layerComposited() const
192 return m_layerComposited;
195 void DrawingBuffer::markLayerComposited()
197 m_layerComposited = true;
200 WebGraphicsContext3D* DrawingBuffer::context()
202 return m_context.get();
205 void DrawingBuffer::setIsHidden(bool hidden)
207 if (m_isHidden == hidden)
211 freeRecycledMailboxes();
214 void DrawingBuffer::freeRecycledMailboxes()
216 if (m_recycledMailboxQueue.isEmpty())
218 while (!m_recycledMailboxQueue.isEmpty())
219 deleteMailbox(m_recycledMailboxQueue.takeLast());
222 bool DrawingBuffer::prepareMailbox(WebExternalTextureMailbox* outMailbox, WebExternalBitmap* bitmap)
224 if (m_destructionInProgress) {
225 // It can be hit in the following sequence.
226 // 1. WebGL draws something.
227 // 2. The compositor begins the frame.
228 // 3. Javascript makes a context lost using WEBGL_lose_context extension.
233 if (!m_contentsChanged)
236 // Resolve the multisampled buffer into m_colorBuffer texture.
237 if (m_multisampleMode != None)
241 bitmap->setSize(size());
243 unsigned char* pixels = bitmap->pixels();
244 bool needPremultiply = m_actualAttributes.alpha && !m_actualAttributes.premultipliedAlpha;
245 WebGLImageConversion::AlphaOp op = needPremultiply ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing;
247 readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op);
250 // We must restore the texture binding since creating new textures,
251 // consuming and producing mailboxes changes it.
252 ScopedTextureUnit0BindingRestorer restorer(m_context.get(), m_activeTextureUnit, m_texture2DBinding);
254 // First try to recycle an old buffer.
255 RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox();
257 // No buffer available to recycle, create a new one.
258 if (!frontColorBufferMailbox) {
259 TextureInfo newTexture;
260 newTexture.textureId = createColorTexture();
261 allocateTextureMemory(&newTexture, m_size);
262 // Bad things happened, abandon ship.
263 if (!newTexture.textureId)
266 frontColorBufferMailbox = createNewMailbox(newTexture);
269 if (m_preserveDrawingBuffer == Discard) {
270 std::swap(frontColorBufferMailbox->textureInfo, m_colorBuffer);
271 // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a
272 // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding.
273 // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore
274 // it after attaching the new back buffer here.
275 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
276 if (m_multisampleMode == ImplicitResolve)
277 m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer.textureId, 0, m_sampleCount);
279 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer.textureId, 0);
281 if (m_discardFramebufferSupported) {
282 // Explicitly discard framebuffer to save GPU memory bandwidth for tile-based GPU arch.
283 const WGC3Denum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
284 m_context->discardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments);
287 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer.textureId, frontColorBufferMailbox->textureInfo.textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
290 if (m_multisampleMode != None && !m_framebufferBinding)
293 restoreFramebufferBinding();
295 m_contentsChanged = false;
297 m_context->produceTextureDirectCHROMIUM(frontColorBufferMailbox->textureInfo.textureId, GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name);
299 frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
300 frontColorBufferMailbox->mailbox.allowOverlay = frontColorBufferMailbox->textureInfo.imageId != 0;
301 markLayerComposited();
303 // set m_parentDrawingBuffer to make sure 'this' stays alive as long as it has live mailboxes
304 ASSERT(!frontColorBufferMailbox->m_parentDrawingBuffer);
305 frontColorBufferMailbox->m_parentDrawingBuffer = this;
306 *outMailbox = frontColorBufferMailbox->mailbox;
307 m_frontColorBuffer = { frontColorBufferMailbox->textureInfo, frontColorBufferMailbox->mailbox };
311 void DrawingBuffer::mailboxReleased(const WebExternalTextureMailbox& mailbox, bool lostResource)
313 if (m_destructionInProgress || m_context->isContextLost() || lostResource || m_isHidden) {
314 mailboxReleasedWithoutRecycling(mailbox);
318 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
319 RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i];
320 if (nameEquals(mailboxInfo->mailbox, mailbox)) {
321 mailboxInfo->mailbox.syncPoint = mailbox.syncPoint;
322 ASSERT(mailboxInfo->m_parentDrawingBuffer.get() == this);
323 mailboxInfo->m_parentDrawingBuffer.clear();
324 m_recycledMailboxQueue.prepend(mailboxInfo->mailbox);
328 ASSERT_NOT_REACHED();
331 void DrawingBuffer::mailboxReleasedWithoutRecycling(const WebExternalTextureMailbox& mailbox)
333 ASSERT(m_textureMailboxes.size());
334 // Ensure not to call the destructor until deleteMailbox() is completed.
335 RefPtr<DrawingBuffer> self = this;
336 deleteMailbox(mailbox);
339 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
341 if (m_recycledMailboxQueue.isEmpty())
342 return PassRefPtr<MailboxInfo>();
344 WebExternalTextureMailbox mailbox;
345 while (!m_recycledMailboxQueue.isEmpty()) {
346 mailbox = m_recycledMailboxQueue.takeLast();
347 // Never have more than one mailbox in the released state.
348 if (!m_recycledMailboxQueue.isEmpty())
349 deleteMailbox(mailbox);
352 RefPtr<MailboxInfo> mailboxInfo;
353 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
354 if (nameEquals(m_textureMailboxes[i]->mailbox, mailbox)) {
355 mailboxInfo = m_textureMailboxes[i];
361 if (mailboxInfo->mailbox.syncPoint) {
362 m_context->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
363 mailboxInfo->mailbox.syncPoint = 0;
366 if (mailboxInfo->size != m_size) {
367 m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureInfo.textureId);
368 allocateTextureMemory(&mailboxInfo->textureInfo, m_size);
369 mailboxInfo->size = m_size;
372 return mailboxInfo.release();
375 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(const TextureInfo& info)
377 RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo());
378 m_context->genMailboxCHROMIUM(returnMailbox->mailbox.name);
379 returnMailbox->textureInfo = info;
380 returnMailbox->size = m_size;
381 m_textureMailboxes.append(returnMailbox);
382 return returnMailbox.release();
385 void DrawingBuffer::deleteMailbox(const WebExternalTextureMailbox& mailbox)
387 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
388 if (nameEquals(m_textureMailboxes[i]->mailbox, mailbox)) {
389 if (mailbox.syncPoint)
390 m_context->waitSyncPoint(mailbox.syncPoint);
392 deleteChromiumImageForTexture(&m_textureMailboxes[i]->textureInfo);
394 m_context->deleteTexture(m_textureMailboxes[i]->textureInfo.textureId);
395 m_textureMailboxes.remove(i);
399 ASSERT_NOT_REACHED();
402 bool DrawingBuffer::initialize(const IntSize& size)
404 if (m_context->isContextLost()) {
405 // Need to try to restore the context again later.
409 if (m_requestedAttributes.alpha) {
410 m_internalColorFormat = GL_RGBA;
411 m_colorFormat = GL_RGBA;
412 m_internalRenderbufferFormat = GL_RGBA8_OES;
414 m_internalColorFormat = GL_RGB;
415 m_colorFormat = GL_RGB;
416 m_internalRenderbufferFormat = GL_RGB8_OES;
419 m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
421 int maxSampleCount = 0;
422 m_multisampleMode = None;
423 if (m_requestedAttributes.antialias && m_multisampleExtensionSupported) {
424 m_context->getIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
425 m_multisampleMode = ExplicitResolve;
426 if (m_extensionsUtil->supportsExtension("GL_EXT_multisampled_render_to_texture"))
427 m_multisampleMode = ImplicitResolve;
429 m_sampleCount = std::min(4, maxSampleCount);
431 m_fbo = m_context->createFramebuffer();
433 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
434 m_colorBuffer.textureId = createColorTexture();
435 if (m_multisampleMode == ImplicitResolve)
436 m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer.textureId, 0, m_sampleCount);
438 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer.textureId, 0);
439 createSecondaryBuffers();
440 // We first try to initialize everything with the requested attributes.
443 // If that succeeds, we then see what we actually got and update our actual attributes to reflect that.
444 m_actualAttributes = m_requestedAttributes;
445 if (m_requestedAttributes.alpha) {
446 WGC3Dint alphaBits = 0;
447 m_context->getIntegerv(GL_ALPHA_BITS, &alphaBits);
448 m_actualAttributes.alpha = alphaBits > 0;
450 if (m_requestedAttributes.depth) {
451 WGC3Dint depthBits = 0;
452 m_context->getIntegerv(GL_DEPTH_BITS, &depthBits);
453 m_actualAttributes.depth = depthBits > 0;
455 if (m_requestedAttributes.stencil) {
456 WGC3Dint stencilBits = 0;
457 m_context->getIntegerv(GL_STENCIL_BITS, &stencilBits);
458 m_actualAttributes.stencil = stencilBits > 0;
460 m_actualAttributes.antialias = multisample();
464 bool DrawingBuffer::copyToPlatformTexture(WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY, bool fromFrontBuffer)
466 if (m_contentsChanged) {
467 if (m_multisampleMode != None) {
469 if (!m_framebufferBinding)
472 restoreFramebufferBinding();
477 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
480 // Contexts may be in a different share group. We must transfer the texture through a mailbox first
481 WebExternalTextureMailbox mailbox;
483 if (fromFrontBuffer && m_frontColorBuffer.texInfo.textureId) {
484 textureId = m_frontColorBuffer.texInfo.textureId;
485 mailbox = m_frontColorBuffer.mailbox;
487 textureId = m_colorBuffer.textureId;
488 m_context->genMailboxCHROMIUM(mailbox.name);
489 m_context->produceTextureDirectCHROMIUM(textureId, GL_TEXTURE_2D, mailbox.name);
491 mailbox.syncPoint = m_context->insertSyncPoint();
494 context->waitSyncPoint(mailbox.syncPoint);
495 Platform3DObject sourceTexture = context->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
497 bool unpackPremultiplyAlphaNeeded = false;
498 bool unpackUnpremultiplyAlphaNeeded = false;
499 if (m_actualAttributes.alpha && m_actualAttributes.premultipliedAlpha && !premultiplyAlpha)
500 unpackUnpremultiplyAlphaNeeded = true;
501 else if (m_actualAttributes.alpha && !m_actualAttributes.premultipliedAlpha && premultiplyAlpha)
502 unpackPremultiplyAlphaNeeded = true;
504 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpackUnpremultiplyAlphaNeeded);
505 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackPremultiplyAlphaNeeded);
506 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, flipY);
507 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
508 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
509 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
510 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
512 context->deleteTexture(sourceTexture);
515 m_context->waitSyncPoint(context->insertSyncPoint());
520 Platform3DObject DrawingBuffer::framebuffer() const
525 WebLayer* DrawingBuffer::platformLayer()
528 m_layer = adoptPtr(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
530 m_layer->setOpaque(!m_actualAttributes.alpha);
531 m_layer->setBlendBackgroundColor(m_actualAttributes.alpha);
532 m_layer->setPremultipliedAlpha(m_actualAttributes.premultipliedAlpha);
533 GraphicsLayer::registerContentsLayer(m_layer->layer());
536 return m_layer->layer();
539 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
541 if (m_context->getGraphicsResetStatusARB() != GL_NO_ERROR)
544 if (!imageBuffer || !m_frontColorBuffer.texInfo.textureId)
546 Platform3DObject tex = imageBuffer->getBackingTexture();
548 OwnPtr<WebGraphicsContext3DProvider> provider =
549 adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
552 WebGraphicsContext3D* context = provider->context3d();
556 context->waitSyncPoint(m_frontColorBuffer.mailbox.syncPoint);
557 Platform3DObject sourceTexture = context->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer.mailbox.name);
558 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture,
559 tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
560 context->deleteTexture(sourceTexture);
562 m_context->waitSyncPoint(context->insertSyncPoint());
563 imageBuffer->didModifyBackingTexture();
567 Platform3DObject framebuffer = m_context->createFramebuffer();
568 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
569 // We don't need to bind a copy of m_frontColorBuffer since the texture parameters are untouched.
570 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_frontColorBuffer.texInfo.textureId, 0);
572 paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_actualAttributes.premultipliedAlpha, imageBuffer);
573 m_context->deleteFramebuffer(framebuffer);
574 // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding).
575 restoreFramebufferBinding();
578 void DrawingBuffer::clearPlatformLayer()
581 m_layer->clearTexture();
586 void DrawingBuffer::beginDestruction()
588 ASSERT(!m_destructionInProgress);
589 m_destructionInProgress = true;
591 clearPlatformLayer();
593 while (!m_recycledMailboxQueue.isEmpty())
594 deleteMailbox(m_recycledMailboxQueue.takeLast());
596 if (m_multisampleFBO)
597 m_context->deleteFramebuffer(m_multisampleFBO);
600 m_context->deleteFramebuffer(m_fbo);
602 if (m_multisampleColorBuffer)
603 m_context->deleteRenderbuffer(m_multisampleColorBuffer);
605 if (m_depthStencilBuffer)
606 m_context->deleteRenderbuffer(m_depthStencilBuffer);
609 m_context->deleteRenderbuffer(m_depthBuffer);
612 m_context->deleteRenderbuffer(m_stencilBuffer);
614 if (m_colorBuffer.textureId) {
615 deleteChromiumImageForTexture(&m_colorBuffer);
616 m_context->deleteTexture(m_colorBuffer.textureId);
621 m_colorBuffer = TextureInfo();
622 m_frontColorBuffer = FrontBufferInfo();
623 m_multisampleColorBuffer = 0;
624 m_depthStencilBuffer = 0;
627 m_multisampleFBO = 0;
629 m_contextEvictionManager.clear();
632 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
635 unsigned DrawingBuffer::createColorTexture()
637 unsigned offscreenColorTexture = m_context->createTexture();
638 if (!offscreenColorTexture)
641 m_context->bindTexture(GL_TEXTURE_2D, offscreenColorTexture);
642 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
643 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
644 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
645 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
647 return offscreenColorTexture;
650 void DrawingBuffer::createSecondaryBuffers()
652 // create a multisample FBO
653 if (m_multisampleMode == ExplicitResolve) {
654 m_multisampleFBO = m_context->createFramebuffer();
655 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
656 m_multisampleColorBuffer = m_context->createRenderbuffer();
660 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
662 // resize regular FBO
663 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
665 m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer.textureId);
667 allocateTextureMemory(&m_colorBuffer, size);
669 if (m_multisampleMode == ImplicitResolve)
670 m_context->framebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer.textureId, 0, m_sampleCount);
672 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer.textureId, 0);
674 m_context->bindTexture(GL_TEXTURE_2D, 0);
676 if (m_multisampleMode != ExplicitResolve)
677 resizeDepthStencil(size);
678 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
684 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
686 if (m_multisampleMode == ExplicitResolve) {
687 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
689 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
690 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
692 if (m_context->getError() == GL_OUT_OF_MEMORY)
695 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleColorBuffer);
696 resizeDepthStencil(size);
697 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
704 void DrawingBuffer::resizeDepthStencil(const IntSize& size)
706 if (!m_requestedAttributes.depth && !m_requestedAttributes.stencil)
709 if (m_packedDepthStencilExtensionSupported) {
710 if (!m_depthStencilBuffer)
711 m_depthStencilBuffer = m_context->createRenderbuffer();
712 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
713 if (m_multisampleMode == ImplicitResolve)
714 m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
715 else if (m_multisampleMode == ExplicitResolve)
716 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
718 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
719 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
720 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
722 if (m_requestedAttributes.depth) {
724 m_depthBuffer = m_context->createRenderbuffer();
725 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
726 if (m_multisampleMode == ImplicitResolve)
727 m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
728 else if (m_multisampleMode == ExplicitResolve)
729 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
731 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
732 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
734 if (m_requestedAttributes.stencil) {
735 if (!m_stencilBuffer)
736 m_stencilBuffer = m_context->createRenderbuffer();
737 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
738 if (m_multisampleMode == ImplicitResolve)
739 m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
740 else if (m_multisampleMode == ExplicitResolve)
741 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
743 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height());
744 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
747 m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
752 void DrawingBuffer::clearFramebuffers(GLbitfield clearMask)
754 // We will clear the multisample FBO, but we also need to clear the non-multisampled buffer.
755 if (m_multisampleFBO) {
756 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
757 m_context->clear(GL_COLOR_BUFFER_BIT);
760 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
761 m_context->clear(clearMask);
764 void DrawingBuffer::setSize(const IntSize& size)
769 s_currentResourceUsePixels += pixelDelta(size, m_size);
773 int DrawingBuffer::pixelDelta(const IntSize& newSize, const IntSize& curSize)
775 return (std::max(0, newSize.width()) * std::max(0, newSize.height())) - (std::max(0, curSize.width()) * std::max(0, curSize.height()));
778 IntSize DrawingBuffer::adjustSize(const IntSize& desiredSize, const IntSize& curSize, int maxTextureSize)
780 IntSize adjustedSize = desiredSize;
782 // Clamp if the desired size is greater than the maximum texture size for the device.
783 if (adjustedSize.height() > maxTextureSize)
784 adjustedSize.setHeight(maxTextureSize);
786 if (adjustedSize.width() > maxTextureSize)
787 adjustedSize.setWidth(maxTextureSize);
789 // Try progressively smaller sizes until we find a size that fits or reach a scale limit.
790 int scaleAttempts = 0;
791 while ((s_currentResourceUsePixels + pixelDelta(adjustedSize, curSize)) > s_maximumResourceUsePixels) {
793 if (scaleAttempts > s_maxScaleAttempts)
796 adjustedSize.scale(s_resourceAdjustedRatio);
798 if (adjustedSize.isEmpty())
805 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext)
807 IntSize adjustedSize = adjustSize(size, m_size, m_maxTextureSize);
808 if (!adjustedSize.isEmpty()) {
809 evictContext = false;
810 return adjustedSize; // Buffer fits without evicting a context.
813 // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted.
814 IntSize oldestSize = m_contextEvictionManager->oldestContextSize();
815 int pixelDelta = oldestSize.width() * oldestSize.height();
817 s_currentResourceUsePixels -= pixelDelta;
818 adjustedSize = adjustSize(size, m_size, m_maxTextureSize);
819 s_currentResourceUsePixels += pixelDelta;
821 evictContext = !adjustedSize.isEmpty();
825 bool DrawingBuffer::reset(const IntSize& newSize)
827 ASSERT(!newSize.isEmpty());
828 IntSize adjustedSize;
829 bool evictContext = false;
830 bool isNewContext = m_size.isEmpty();
831 if (s_allowContextEvictionOnCreate && isNewContext)
832 adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
834 adjustedSize = adjustSize(newSize, m_size, m_maxTextureSize);
836 if (adjustedSize.isEmpty())
840 m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
842 if (adjustedSize != m_size) {
844 // resize multisample FBO
845 if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
846 adjustedSize.scale(s_resourceAdjustedRatio);
850 } while (!adjustedSize.isEmpty());
852 setSize(adjustedSize);
854 if (adjustedSize.isEmpty())
858 m_context->disable(GL_SCISSOR_TEST);
859 m_context->clearColor(0, 0, 0, 0);
860 m_context->colorMask(true, true, true, true);
862 GLbitfield clearMask = GL_COLOR_BUFFER_BIT;
863 if (m_actualAttributes.depth) {
864 m_context->clearDepth(1.0f);
865 clearMask |= GL_DEPTH_BUFFER_BIT;
866 m_context->depthMask(true);
868 if (m_actualAttributes.stencil) {
869 m_context->clearStencil(0);
870 clearMask |= GL_STENCIL_BUFFER_BIT;
871 m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
874 clearFramebuffers(clearMask);
878 void DrawingBuffer::commit(long x, long y, long width, long height)
881 width = m_size.width();
883 height = m_size.height();
885 if (m_multisampleFBO && !m_contentsChangeCommitted) {
886 m_context->bindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_multisampleFBO);
887 m_context->bindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, m_fbo);
889 if (m_scissorEnabled)
890 m_context->disable(GL_SCISSOR_TEST);
892 // Use NEAREST, because there is no scale performed during the blit.
893 m_context->blitFramebufferCHROMIUM(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
895 if (m_scissorEnabled)
896 m_context->enable(GL_SCISSOR_TEST);
899 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
900 m_contentsChangeCommitted = true;
903 void DrawingBuffer::restoreFramebufferBinding()
905 if (!m_framebufferBinding)
908 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
911 bool DrawingBuffer::multisample() const
913 return m_multisampleMode != None;
916 void DrawingBuffer::bind()
918 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
921 void DrawingBuffer::setPackAlignment(GLint param)
923 m_packAlignment = param;
926 void DrawingBuffer::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
928 paintFramebufferToCanvas(framebuffer(), size().width(), size().height(), !m_actualAttributes.premultipliedAlpha, imageBuffer);
931 PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height)
933 if (m_actualAttributes.premultipliedAlpha)
936 width = size().width();
937 height = size().height();
939 Checked<int, RecordOverflow> dataSize = 4;
942 if (dataSize.hasOverflowed())
945 RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4);
947 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer());
948 readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, WebGLImageConversion::AlphaDoNothing);
949 flipVertically(pixels->data(), width, height);
951 return pixels.release();
954 void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer)
956 unsigned char* pixels = 0;
958 const SkBitmap& canvasBitmap = imageBuffer->bitmap();
959 const SkBitmap* readbackBitmap = 0;
960 ASSERT(canvasBitmap.colorType() == kN32_SkColorType);
961 if (canvasBitmap.width() == width && canvasBitmap.height() == height) {
962 // This is the fastest and most common case. We read back
963 // directly into the canvas's backing store.
964 readbackBitmap = &canvasBitmap;
965 m_resizingBitmap.reset();
967 // We need to allocate a temporary bitmap for reading back the
968 // pixel data. We will then use Skia to rescale this bitmap to
969 // the size of the canvas's backing store.
970 if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) {
971 if (!m_resizingBitmap.tryAllocN32Pixels(width, height))
974 readbackBitmap = &m_resizingBitmap;
977 // Read back the frame buffer.
978 SkAutoLockPixels bitmapLock(*readbackBitmap);
979 pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
981 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
982 readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing);
983 flipVertically(pixels, width, height);
985 readbackBitmap->notifyPixelsChanged();
986 if (m_resizingBitmap.readyToDraw()) {
987 // We need to draw the resizing bitmap into the canvas's backing store.
988 SkCanvas canvas(canvasBitmap);
990 dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height()));
991 canvas.drawBitmapRect(m_resizingBitmap, 0, dst);
995 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, int width, int height, ReadbackOrder readbackOrder, WebGLImageConversion::AlphaOp op)
997 if (m_packAlignment > 4)
998 m_context->pixelStorei(GL_PACK_ALIGNMENT, 1);
999 m_context->readPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
1000 if (m_packAlignment > 4)
1001 m_context->pixelStorei(GL_PACK_ALIGNMENT, m_packAlignment);
1003 size_t bufferSize = 4 * width * height;
1005 if (readbackOrder == ReadbackSkia) {
1006 #if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
1007 // Swizzle red and blue channels to match SkBitmap's byte ordering.
1008 // TODO(kbr): expose GL_BGRA as extension.
1009 for (size_t i = 0; i < bufferSize; i += 4) {
1010 std::swap(pixels[i], pixels[i + 2]);
1015 if (op == WebGLImageConversion::AlphaDoPremultiply) {
1016 for (size_t i = 0; i < bufferSize; i += 4) {
1017 pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
1018 pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
1019 pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
1021 } else if (op != WebGLImageConversion::AlphaDoNothing) {
1022 ASSERT_NOT_REACHED();
1026 void DrawingBuffer::flipVertically(uint8_t* framebuffer, int width, int height)
1028 m_scanline.resize(width * 4);
1029 uint8* scanline = &m_scanline[0];
1030 unsigned rowBytes = width * 4;
1031 unsigned count = height / 2;
1032 for (unsigned i = 0; i < count; i++) {
1033 uint8* rowA = framebuffer + i * rowBytes;
1034 uint8* rowB = framebuffer + (height - i - 1) * rowBytes;
1035 memcpy(scanline, rowB, rowBytes);
1036 memcpy(rowB, rowA, rowBytes);
1037 memcpy(rowA, scanline, rowBytes);
1041 void DrawingBuffer::texImage2DResourceSafe(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLint unpackAlignment)
1043 ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
1044 m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0);
1047 void DrawingBuffer::allocateTextureMemory(TextureInfo* info, const IntSize& size)
1049 if (RuntimeEnabledFeatures::webGLImageChromiumEnabled()) {
1050 deleteChromiumImageForTexture(info);
1052 info->imageId = m_context->createGpuMemoryBufferImageCHROMIUM(size.width(), size.height(), GL_RGBA, GC3D_SCANOUT_CHROMIUM);
1053 if (info->imageId) {
1054 m_context->bindTexImage2DCHROMIUM(GL_TEXTURE_2D, info->imageId);
1059 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
1062 void DrawingBuffer::deleteChromiumImageForTexture(TextureInfo* info)
1064 if (info->imageId) {
1065 m_context->releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, info->imageId);
1066 m_context->destroyImageCHROMIUM(info->imageId);
1071 } // namespace blink