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 "public/platform/Platform.h"
39 #include "public/platform/WebCompositorSupport.h"
40 #include "public/platform/WebExternalBitmap.h"
41 #include "public/platform/WebExternalTextureLayer.h"
42 #include "public/platform/WebGraphicsContext3D.h"
48 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
49 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
50 // exceed the global cap will instead clear the buffer.
51 static const int s_maximumResourceUsePixels = 16 * 1024 * 1024;
52 static int s_currentResourceUsePixels = 0;
53 static const float s_resourceAdjustedRatio = 0.5;
55 static const bool s_allowContextEvictionOnCreate = true;
56 static const int s_maxScaleAttempts = 3;
58 class ScopedTextureUnit0BindingRestorer {
60 ScopedTextureUnit0BindingRestorer(blink::WebGraphicsContext3D* context, GLenum activeTextureUnit, Platform3DObject textureUnitZeroId)
62 , m_oldActiveTextureUnit(activeTextureUnit)
63 , m_oldTextureUnitZeroId(textureUnitZeroId)
65 m_context->activeTexture(GL_TEXTURE0);
67 ~ScopedTextureUnit0BindingRestorer()
69 m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId);
70 m_context->activeTexture(m_oldActiveTextureUnit);
74 blink::WebGraphicsContext3D* m_context;
75 GLenum m_oldActiveTextureUnit;
76 Platform3DObject m_oldTextureUnitZeroId;
79 PassRefPtr<DrawingBuffer> DrawingBuffer::create(blink::WebGraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
81 RefPtr<GraphicsContext3D> contextSupport(GraphicsContext3D::createContextSupport(context));
83 bool multisampleSupported = contextSupport->supportsExtension("GL_ANGLE_framebuffer_blit")
84 && contextSupport->supportsExtension("GL_ANGLE_framebuffer_multisample")
85 && contextSupport->supportsExtension("GL_OES_rgb8_rgba8");
86 if (multisampleSupported) {
87 contextSupport->ensureExtensionEnabled("GL_ANGLE_framebuffer_blit");
88 contextSupport->ensureExtensionEnabled("GL_ANGLE_framebuffer_multisample");
89 contextSupport->ensureExtensionEnabled("GL_OES_rgb8_rgba8");
91 #if defined(OS_TIZEN) && CPU(X86)
92 multisampleSupported = false;
94 bool packedDepthStencilSupported = contextSupport->supportsExtension("GL_OES_packed_depth_stencil");
95 if (packedDepthStencilSupported)
96 contextSupport->ensureExtensionEnabled("GL_OES_packed_depth_stencil");
98 RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, contextSupport.get(), size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
99 return drawingBuffer.release();
102 DrawingBuffer::DrawingBuffer(blink::WebGraphicsContext3D* context,
103 GraphicsContext3D* contextSupport,
105 bool multisampleExtensionSupported,
106 bool packedDepthStencilExtensionSupported,
107 PreserveDrawingBuffer preserve,
108 PassRefPtr<ContextEvictionManager> contextEvictionManager)
109 : m_preserveDrawingBuffer(preserve)
110 , m_scissorEnabled(false)
111 , m_texture2DBinding(0)
112 , m_framebufferBinding(0)
113 , m_activeTextureUnit(GL_TEXTURE0)
115 , m_contextSupport(contextSupport)
117 , m_multisampleExtensionSupported(multisampleExtensionSupported)
118 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
121 , m_frontColorBuffer(0)
122 , m_depthStencilBuffer(0)
125 , m_multisampleFBO(0)
126 , m_multisampleColorBuffer(0)
127 , m_contentsChanged(true)
128 , m_contentsChangeCommitted(false)
129 , m_layerComposited(false)
130 , m_internalColorFormat(0)
132 , m_internalRenderbufferFormat(0)
133 , m_maxTextureSize(0)
135 , m_contextEvictionManager(contextEvictionManager)
137 // Used by browser tests to detect the use of a DrawingBuffer.
138 TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
142 DrawingBuffer::~DrawingBuffer()
147 void DrawingBuffer::markContentsChanged()
149 m_contentsChanged = true;
150 m_contentsChangeCommitted = false;
151 m_layerComposited = false;
154 bool DrawingBuffer::layerComposited() const
156 return m_layerComposited;
159 void DrawingBuffer::markLayerComposited()
161 m_layerComposited = true;
164 blink::WebGraphicsContext3D* DrawingBuffer::context()
169 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
171 if (!m_context || !m_contentsChanged)
174 m_context->makeContextCurrent();
176 // Resolve the multisampled buffer into m_colorBuffer texture.
181 bitmap->setSize(size());
183 unsigned char* pixels = bitmap->pixels();
184 bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
185 GraphicsContext3D::AlphaOp op = needPremultiply ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing;
187 readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op);
190 // We must restore the texture binding since creating new textures,
191 // consuming and producing mailboxes changes it.
192 ScopedTextureUnit0BindingRestorer restorer(m_context, m_activeTextureUnit, m_texture2DBinding);
194 // First try to recycle an old buffer.
195 RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox();
197 // No buffer available to recycle, create a new one.
198 if (!frontColorBufferMailbox) {
199 unsigned newColorBuffer = createColorTexture(m_size);
200 // Bad things happened, abandon ship.
204 frontColorBufferMailbox = createNewMailbox(newColorBuffer);
207 if (m_preserveDrawingBuffer == Discard) {
208 swap(frontColorBufferMailbox->textureId, m_colorBuffer);
209 // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a
210 // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding.
211 // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore
212 // it after attaching the new back buffer here.
213 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
214 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
216 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, frontColorBufferMailbox->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
219 if (multisample() && !m_framebufferBinding)
222 restoreFramebufferBinding();
224 m_contentsChanged = false;
226 m_context->bindTexture(GL_TEXTURE_2D, frontColorBufferMailbox->textureId);
227 m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name);
229 frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
230 markLayerComposited();
232 *outMailbox = frontColorBufferMailbox->mailbox;
233 m_frontColorBuffer = frontColorBufferMailbox->textureId;
237 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
239 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
240 RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i];
241 if (!memcmp(mailboxInfo->mailbox.name, mailbox.name, sizeof(mailbox.name))) {
242 mailboxInfo->mailbox.syncPoint = mailbox.syncPoint;
243 m_recycledMailboxes.prepend(mailboxInfo.release());
247 ASSERT_NOT_REACHED();
250 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
252 if (!m_context || m_recycledMailboxes.isEmpty())
253 return PassRefPtr<MailboxInfo>();
255 RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release();
256 m_recycledMailboxes.removeLast();
258 if (mailboxInfo->mailbox.syncPoint) {
259 m_context->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
260 mailboxInfo->mailbox.syncPoint = 0;
263 m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureId);
264 m_context->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->mailbox.name);
266 if (mailboxInfo->size != m_size) {
267 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
268 mailboxInfo->size = m_size;
271 return mailboxInfo.release();
274 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
276 RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo());
277 m_context->genMailboxCHROMIUM(returnMailbox->mailbox.name);
278 returnMailbox->textureId = textureId;
279 returnMailbox->size = m_size;
280 m_textureMailboxes.append(returnMailbox);
281 return returnMailbox.release();
284 void DrawingBuffer::initialize(const IntSize& size)
287 m_attributes = m_context->getContextAttributes();
289 if (m_attributes.alpha) {
290 m_internalColorFormat = GL_RGBA;
291 m_colorFormat = GL_RGBA;
292 m_internalRenderbufferFormat = GL_RGBA8_OES;
294 m_internalColorFormat = GL_RGB;
295 m_colorFormat = GL_RGB;
296 m_internalRenderbufferFormat = GL_RGB8_OES;
299 m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
301 int maxSampleCount = 0;
303 m_context->getIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
304 m_sampleCount = std::min(4, maxSampleCount);
306 m_fbo = m_context->createFramebuffer();
308 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
309 m_colorBuffer = createColorTexture();
310 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
311 createSecondaryBuffers();
315 bool DrawingBuffer::copyToPlatformTexture(GraphicsContext3D& contextSupport, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
317 if (!m_context || !m_context->makeContextCurrent())
319 if (m_contentsChanged) {
322 if (!m_framebufferBinding)
325 restoreFramebufferBinding();
329 Platform3DObject sourceTexture = m_colorBuffer;
331 blink::WebGraphicsContext3D* context = contextSupport.webContext();
333 if (!context->makeContextCurrent())
335 if (!contextSupport.supportsExtension("GL_CHROMIUM_copy_texture") || !contextSupport.supportsExtension("GL_CHROMIUM_flipy")
336 || !contextSupport.canUseCopyTextureCHROMIUM(internalFormat, destType, level))
339 bool unpackPremultiplyAlphaNeeded = false;
340 bool unpackUnpremultiplyAlphaNeeded = false;
341 if (m_attributes.alpha && m_attributes.premultipliedAlpha && !premultiplyAlpha)
342 unpackUnpremultiplyAlphaNeeded = true;
343 else if (m_attributes.alpha && !m_attributes.premultipliedAlpha && premultiplyAlpha)
344 unpackPremultiplyAlphaNeeded = true;
346 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpackUnpremultiplyAlphaNeeded);
347 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackPremultiplyAlphaNeeded);
348 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, flipY);
349 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
350 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
351 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
352 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
358 Platform3DObject DrawingBuffer::framebuffer() const
363 blink::WebLayer* DrawingBuffer::platformLayer()
369 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
371 m_layer->setOpaque(!m_attributes.alpha);
372 m_layer->setBlendBackgroundColor(m_attributes.alpha);
373 m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha);
374 GraphicsLayer::registerContentsLayer(m_layer->layer());
377 return m_layer->layer();
380 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
382 if (!m_context || !m_context->makeContextCurrent() || m_context->getGraphicsResetStatusARB() != GL_NO_ERROR)
387 Platform3DObject tex = imageBuffer->getBackingTexture();
389 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer,
390 tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
394 // Since the m_frontColorBuffer was produced and sent to the compositor, it cannot be bound to an fbo.
395 // We have to make a copy of it here and bind that copy instead.
396 // FIXME: That's not true any more, provided we don't change texture
398 unsigned sourceTexture = createColorTexture(m_size);
399 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE);
401 // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding).
402 // FIXME: The WebGLRenderingContext tracks the current framebuffer binding, it would be slightly more efficient to use this value
403 // rather than querying it off of the context.
404 GLint previousFramebuffer = 0;
405 m_context->getIntegerv(GL_FRAMEBUFFER_BINDING, &previousFramebuffer);
407 Platform3DObject framebuffer = m_context->createFramebuffer();
408 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
409 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0);
411 paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
412 m_context->deleteFramebuffer(framebuffer);
413 m_context->deleteTexture(sourceTexture);
415 m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
418 void DrawingBuffer::clearPlatformLayer()
421 m_layer->clearTexture();
427 void DrawingBuffer::releaseResources()
430 m_context->makeContextCurrent();
432 clearPlatformLayer();
434 for (size_t i = 0; i < m_textureMailboxes.size(); i++)
435 m_context->deleteTexture(m_textureMailboxes[i]->textureId);
437 if (m_multisampleColorBuffer)
438 m_context->deleteRenderbuffer(m_multisampleColorBuffer);
440 if (m_depthStencilBuffer)
441 m_context->deleteRenderbuffer(m_depthStencilBuffer);
444 m_context->deleteRenderbuffer(m_depthBuffer);
447 m_context->deleteRenderbuffer(m_stencilBuffer);
449 if (m_multisampleFBO)
450 m_context->deleteFramebuffer(m_multisampleFBO);
453 m_context->deleteFramebuffer(m_fbo);
455 m_contextSupport.clear();
462 m_frontColorBuffer = 0;
463 m_multisampleColorBuffer = 0;
464 m_depthStencilBuffer = 0;
467 m_multisampleFBO = 0;
469 m_contextEvictionManager.clear();
471 m_recycledMailboxes.clear();
472 m_textureMailboxes.clear();
475 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
480 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
485 unsigned offscreenColorTexture = m_context->createTexture();
486 if (!offscreenColorTexture)
489 m_context->bindTexture(GL_TEXTURE_2D, offscreenColorTexture);
490 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
491 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
492 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
493 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
495 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
497 return offscreenColorTexture;
500 void DrawingBuffer::createSecondaryBuffers()
502 // create a multisample FBO
504 m_multisampleFBO = m_context->createFramebuffer();
505 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
506 m_multisampleColorBuffer = m_context->createRenderbuffer();
510 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
512 // resize regular FBO
513 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
515 m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
517 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
519 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
521 m_context->bindTexture(GL_TEXTURE_2D, 0);
524 resizeDepthStencil(size);
525 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
531 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
534 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
536 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
537 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
539 if (m_context->getError() == GL_OUT_OF_MEMORY)
542 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleColorBuffer);
543 resizeDepthStencil(size);
544 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
551 void DrawingBuffer::resizeDepthStencil(const IntSize& size)
553 if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtensionSupported) {
554 if (!m_depthStencilBuffer)
555 m_depthStencilBuffer = m_context->createRenderbuffer();
556 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
558 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
560 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
561 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
562 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
564 if (m_attributes.depth) {
566 m_depthBuffer = m_context->createRenderbuffer();
567 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
569 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
571 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
572 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
574 if (m_attributes.stencil) {
575 if (!m_stencilBuffer)
576 m_stencilBuffer = m_context->createRenderbuffer();
577 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
579 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
581 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height());
582 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
585 m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
590 void DrawingBuffer::clearFramebuffers(GLbitfield clearMask)
595 // We will clear the multisample FBO, but we also need to clear the non-multisampled buffer too.
596 if (m_multisampleFBO) {
597 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
598 m_context->clear(GL_COLOR_BUFFER_BIT);
601 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
602 m_context->clear(clearMask);
605 void DrawingBuffer::setSize(const IntSize& size) {
609 s_currentResourceUsePixels += pixelDelta(size);
613 int DrawingBuffer::pixelDelta(const IntSize& size) {
614 return (max(0, size.width()) * max(0, size.height())) - (max(0, m_size.width()) * max(0, m_size.height()));
617 IntSize DrawingBuffer::adjustSize(const IntSize& size) {
618 IntSize adjustedSize = size;
620 // Clamp if the desired size is greater than the maximum texture size for the device.
621 if (adjustedSize.height() > m_maxTextureSize)
622 adjustedSize.setHeight(m_maxTextureSize);
624 if (adjustedSize.width() > m_maxTextureSize)
625 adjustedSize.setWidth(m_maxTextureSize);
627 // Try progressively smaller sizes until we find a size that fits or reach a scale limit.
628 int scaleAttempts = 0;
629 while ((s_currentResourceUsePixels + pixelDelta(adjustedSize)) > s_maximumResourceUsePixels) {
631 if (scaleAttempts > s_maxScaleAttempts)
634 adjustedSize.scale(s_resourceAdjustedRatio);
636 if (adjustedSize.isEmpty())
643 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext) {
644 IntSize adjustedSize = adjustSize(size);
645 if (!adjustedSize.isEmpty()) {
646 evictContext = false;
647 return adjustedSize; // Buffer fits without evicting a context.
650 // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted.
651 IntSize oldestSize = m_contextEvictionManager->oldestContextSize();
652 int pixelDelta = oldestSize.width() * oldestSize.height();
654 s_currentResourceUsePixels -= pixelDelta;
655 adjustedSize = adjustSize(size);
656 s_currentResourceUsePixels += pixelDelta;
658 evictContext = !adjustedSize.isEmpty();
662 void DrawingBuffer::reset(const IntSize& newSize)
667 IntSize adjustedSize;
668 bool evictContext = false;
669 bool isNewContext = m_size.isEmpty();
670 if (s_allowContextEvictionOnCreate && isNewContext)
671 adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
673 adjustedSize = adjustSize(newSize);
675 if (adjustedSize.isEmpty())
679 m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
681 if (adjustedSize != m_size) {
683 // resize multisample FBO
684 if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
685 adjustedSize.scale(s_resourceAdjustedRatio);
689 } while (!adjustedSize.isEmpty());
691 setSize(adjustedSize);
693 if (adjustedSize.isEmpty())
697 m_context->disable(GL_SCISSOR_TEST);
698 m_context->clearColor(0, 0, 0, 0);
699 m_context->colorMask(true, true, true, true);
701 GLbitfield clearMask = GL_COLOR_BUFFER_BIT;
702 if (m_attributes.depth) {
703 m_context->clearDepth(1.0f);
704 clearMask |= GL_DEPTH_BUFFER_BIT;
705 m_context->depthMask(true);
707 if (m_attributes.stencil) {
708 m_context->clearStencil(0);
709 clearMask |= GL_STENCIL_BUFFER_BIT;
710 m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
713 clearFramebuffers(clearMask);
716 void DrawingBuffer::commit(long x, long y, long width, long height)
722 width = m_size.width();
724 height = m_size.height();
726 m_context->makeContextCurrent();
728 if (m_multisampleFBO && !m_contentsChangeCommitted) {
729 m_context->bindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_multisampleFBO);
730 m_context->bindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, m_fbo);
732 if (m_scissorEnabled)
733 m_context->disable(GL_SCISSOR_TEST);
735 // Use NEAREST, because there is no scale performed during the blit.
736 m_context->blitFramebufferCHROMIUM(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
738 if (m_scissorEnabled)
739 m_context->enable(GL_SCISSOR_TEST);
742 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
743 m_contentsChangeCommitted = true;
746 void DrawingBuffer::restoreFramebufferBinding()
748 if (!m_context || !m_framebufferBinding)
751 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
754 bool DrawingBuffer::multisample() const
756 return m_attributes.antialias && m_multisampleExtensionSupported;
759 void DrawingBuffer::bind()
764 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
767 void DrawingBuffer::setPackAlignment(GLint param)
769 m_packAlignment = param;
772 void DrawingBuffer::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
774 paintFramebufferToCanvas(framebuffer(), size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
777 PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height)
779 if (m_attributes.premultipliedAlpha)
782 width = size().width();
783 height = size().height();
785 Checked<int, RecordOverflow> dataSize = 4;
788 if (dataSize.hasOverflowed())
791 RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4);
793 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer());
794 readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, GraphicsContext3D::AlphaDoNothing);
795 flipVertically(pixels->data(), width, height);
797 return pixels.release();
800 void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer)
802 unsigned char* pixels = 0;
804 const SkBitmap& canvasBitmap = imageBuffer->bitmap();
805 const SkBitmap* readbackBitmap = 0;
806 ASSERT(canvasBitmap.config() == SkBitmap::kARGB_8888_Config);
807 if (canvasBitmap.width() == width && canvasBitmap.height() == height) {
808 // This is the fastest and most common case. We read back
809 // directly into the canvas's backing store.
810 readbackBitmap = &canvasBitmap;
811 m_resizingBitmap.reset();
813 // We need to allocate a temporary bitmap for reading back the
814 // pixel data. We will then use Skia to rescale this bitmap to
815 // the size of the canvas's backing store.
816 if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) {
817 m_resizingBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
818 if (!m_resizingBitmap.allocPixels())
821 readbackBitmap = &m_resizingBitmap;
824 // Read back the frame buffer.
825 SkAutoLockPixels bitmapLock(*readbackBitmap);
826 pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
828 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
829 readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing);
830 flipVertically(pixels, width, height);
832 readbackBitmap->notifyPixelsChanged();
833 if (m_resizingBitmap.readyToDraw()) {
834 // We need to draw the resizing bitmap into the canvas's backing store.
835 SkCanvas canvas(canvasBitmap);
837 dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height()));
838 canvas.drawBitmapRect(m_resizingBitmap, 0, dst);
842 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, int width, int height, ReadbackOrder readbackOrder, GraphicsContext3D::AlphaOp op)
844 if (m_packAlignment > 4)
845 m_context->pixelStorei(GL_PACK_ALIGNMENT, 1);
846 m_context->readPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
847 if (m_packAlignment > 4)
848 m_context->pixelStorei(GL_PACK_ALIGNMENT, m_packAlignment);
850 size_t bufferSize = 4 * width * height;
852 if (readbackOrder == ReadbackSkia) {
853 #if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
854 // Swizzle red and blue channels to match SkBitmap's byte ordering.
855 // TODO(kbr): expose GL_BGRA as extension.
856 for (size_t i = 0; i < bufferSize; i += 4) {
857 std::swap(pixels[i], pixels[i + 2]);
862 if (op == GraphicsContext3D::AlphaDoPremultiply) {
863 for (size_t i = 0; i < bufferSize; i += 4) {
864 pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
865 pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
866 pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
868 } else if (op != GraphicsContext3D::AlphaDoNothing) {
869 ASSERT_NOT_REACHED();
873 void DrawingBuffer::flipVertically(uint8_t* framebuffer, int width, int height)
875 m_scanline.resize(width * 4);
876 uint8* scanline = &m_scanline[0];
877 unsigned rowBytes = width * 4;
878 unsigned count = height / 2;
879 for (unsigned i = 0; i < count; i++) {
880 uint8* rowA = framebuffer + i * rowBytes;
881 uint8* rowB = framebuffer + (height - i - 1) * rowBytes;
882 memcpy(scanline, rowB, rowBytes);
883 memcpy(rowB, rowA, rowBytes);
884 memcpy(rowA, scanline, rowBytes);
888 void DrawingBuffer::texImage2DResourceSafe(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLint unpackAlignment)
890 ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
891 m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0);
894 } // namespace WebCore