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"
49 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
50 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
51 // exceed the global cap will instead clear the buffer.
52 static const int s_maximumResourceUsePixels = 16 * 1024 * 1024;
53 static int s_currentResourceUsePixels = 0;
54 static const float s_resourceAdjustedRatio = 0.5;
56 static const bool s_allowContextEvictionOnCreate = true;
57 static const int s_maxScaleAttempts = 3;
59 class ScopedTextureUnit0BindingRestorer {
61 ScopedTextureUnit0BindingRestorer(blink::WebGraphicsContext3D* context, GLenum activeTextureUnit, Platform3DObject textureUnitZeroId)
63 , m_oldActiveTextureUnit(activeTextureUnit)
64 , m_oldTextureUnitZeroId(textureUnitZeroId)
66 m_context->activeTexture(GL_TEXTURE0);
68 ~ScopedTextureUnit0BindingRestorer()
70 m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId);
71 m_context->activeTexture(m_oldActiveTextureUnit);
75 blink::WebGraphicsContext3D* m_context;
76 GLenum m_oldActiveTextureUnit;
77 Platform3DObject m_oldTextureUnitZeroId;
80 PassRefPtr<DrawingBuffer> DrawingBuffer::create(blink::WebGraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
82 Extensions3DUtil extensionsUtil(context);
83 bool multisampleSupported = extensionsUtil.supportsExtension("GL_CHROMIUM_framebuffer_multisample")
84 && extensionsUtil.supportsExtension("GL_OES_rgb8_rgba8");
85 if (multisampleSupported) {
86 extensionsUtil.ensureExtensionEnabled("GL_CHROMIUM_framebuffer_multisample");
87 extensionsUtil.ensureExtensionEnabled("GL_OES_rgb8_rgba8");
89 #if defined(OS_TIZEN) && CPU(X86)
90 multisampleSupported = false;
92 bool packedDepthStencilSupported = extensionsUtil.supportsExtension("GL_OES_packed_depth_stencil");
93 if (packedDepthStencilSupported)
94 extensionsUtil.ensureExtensionEnabled("GL_OES_packed_depth_stencil");
96 RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
97 return drawingBuffer.release();
100 DrawingBuffer::DrawingBuffer(blink::WebGraphicsContext3D* context,
102 bool multisampleExtensionSupported,
103 bool packedDepthStencilExtensionSupported,
104 PreserveDrawingBuffer preserve,
105 PassRefPtr<ContextEvictionManager> contextEvictionManager)
106 : m_preserveDrawingBuffer(preserve)
107 , m_scissorEnabled(false)
108 , m_texture2DBinding(0)
109 , m_framebufferBinding(0)
110 , m_activeTextureUnit(GL_TEXTURE0)
113 , m_multisampleExtensionSupported(multisampleExtensionSupported)
114 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
117 , m_frontColorBuffer(0)
118 , m_depthStencilBuffer(0)
121 , m_multisampleFBO(0)
122 , m_multisampleColorBuffer(0)
123 , m_contentsChanged(true)
124 , m_contentsChangeCommitted(false)
125 , m_layerComposited(false)
126 , m_internalColorFormat(0)
128 , m_internalRenderbufferFormat(0)
129 , m_maxTextureSize(0)
131 , m_contextEvictionManager(contextEvictionManager)
133 // Used by browser tests to detect the use of a DrawingBuffer.
134 TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
138 DrawingBuffer::~DrawingBuffer()
143 void DrawingBuffer::markContentsChanged()
145 m_contentsChanged = true;
146 m_contentsChangeCommitted = false;
147 m_layerComposited = false;
150 bool DrawingBuffer::layerComposited() const
152 return m_layerComposited;
155 void DrawingBuffer::markLayerComposited()
157 m_layerComposited = true;
160 blink::WebGraphicsContext3D* DrawingBuffer::context()
165 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
167 if (!m_context || !m_contentsChanged)
170 m_context->makeContextCurrent();
172 // Resolve the multisampled buffer into m_colorBuffer texture.
177 bitmap->setSize(size());
179 unsigned char* pixels = bitmap->pixels();
180 bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
181 WebGLImageConversion::AlphaOp op = needPremultiply ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing;
183 readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op);
186 // We must restore the texture binding since creating new textures,
187 // consuming and producing mailboxes changes it.
188 ScopedTextureUnit0BindingRestorer restorer(m_context, m_activeTextureUnit, m_texture2DBinding);
190 // First try to recycle an old buffer.
191 RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox();
193 // No buffer available to recycle, create a new one.
194 if (!frontColorBufferMailbox) {
195 unsigned newColorBuffer = createColorTexture(m_size);
196 // Bad things happened, abandon ship.
200 frontColorBufferMailbox = createNewMailbox(newColorBuffer);
203 if (m_preserveDrawingBuffer == Discard) {
204 swap(frontColorBufferMailbox->textureId, m_colorBuffer);
205 // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a
206 // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding.
207 // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore
208 // it after attaching the new back buffer here.
209 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
210 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
212 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, frontColorBufferMailbox->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
215 if (multisample() && !m_framebufferBinding)
218 restoreFramebufferBinding();
220 m_contentsChanged = false;
222 m_context->bindTexture(GL_TEXTURE_2D, frontColorBufferMailbox->textureId);
223 m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name);
225 frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
226 markLayerComposited();
228 *outMailbox = frontColorBufferMailbox->mailbox;
229 m_frontColorBuffer = frontColorBufferMailbox->textureId;
233 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
235 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
236 RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i];
237 if (!memcmp(mailboxInfo->mailbox.name, mailbox.name, sizeof(mailbox.name))) {
238 mailboxInfo->mailbox.syncPoint = mailbox.syncPoint;
239 m_recycledMailboxes.prepend(mailboxInfo.release());
243 ASSERT_NOT_REACHED();
246 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
248 if (!m_context || m_recycledMailboxes.isEmpty())
249 return PassRefPtr<MailboxInfo>();
251 RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release();
252 m_recycledMailboxes.removeLast();
254 if (mailboxInfo->mailbox.syncPoint) {
255 m_context->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
256 mailboxInfo->mailbox.syncPoint = 0;
259 if (mailboxInfo->size != m_size) {
260 m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureId);
261 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
262 mailboxInfo->size = m_size;
265 return mailboxInfo.release();
268 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
270 RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo());
271 m_context->genMailboxCHROMIUM(returnMailbox->mailbox.name);
272 returnMailbox->textureId = textureId;
273 returnMailbox->size = m_size;
274 m_textureMailboxes.append(returnMailbox);
275 return returnMailbox.release();
278 void DrawingBuffer::initialize(const IntSize& size)
281 m_attributes = m_context->getContextAttributes();
283 if (m_attributes.alpha) {
284 m_internalColorFormat = GL_RGBA;
285 m_colorFormat = GL_RGBA;
286 m_internalRenderbufferFormat = GL_RGBA8_OES;
288 m_internalColorFormat = GL_RGB;
289 m_colorFormat = GL_RGB;
290 m_internalRenderbufferFormat = GL_RGB8_OES;
293 m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
295 int maxSampleCount = 0;
297 m_context->getIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
298 m_sampleCount = std::min(4, maxSampleCount);
300 m_fbo = m_context->createFramebuffer();
302 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
303 m_colorBuffer = createColorTexture();
304 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
305 createSecondaryBuffers();
309 bool DrawingBuffer::copyToPlatformTexture(blink::WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
311 if (!m_context || !m_context->makeContextCurrent())
313 if (m_contentsChanged) {
316 if (!m_framebufferBinding)
319 restoreFramebufferBinding();
323 Platform3DObject sourceTexture = m_colorBuffer;
325 if (!context->makeContextCurrent())
328 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
331 bool unpackPremultiplyAlphaNeeded = false;
332 bool unpackUnpremultiplyAlphaNeeded = false;
333 if (m_attributes.alpha && m_attributes.premultipliedAlpha && !premultiplyAlpha)
334 unpackUnpremultiplyAlphaNeeded = true;
335 else if (m_attributes.alpha && !m_attributes.premultipliedAlpha && premultiplyAlpha)
336 unpackPremultiplyAlphaNeeded = true;
338 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpackUnpremultiplyAlphaNeeded);
339 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackPremultiplyAlphaNeeded);
340 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, flipY);
341 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
342 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
343 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
344 context->pixelStorei(GC3D_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
350 Platform3DObject DrawingBuffer::framebuffer() const
355 blink::WebLayer* DrawingBuffer::platformLayer()
361 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
363 m_layer->setOpaque(!m_attributes.alpha);
364 m_layer->setBlendBackgroundColor(m_attributes.alpha);
365 m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha);
366 GraphicsLayer::registerContentsLayer(m_layer->layer());
369 return m_layer->layer();
372 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
374 if (!m_context || !m_context->makeContextCurrent() || m_context->getGraphicsResetStatusARB() != GL_NO_ERROR)
379 Platform3DObject tex = imageBuffer->getBackingTexture();
381 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer,
382 tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
386 // Since the m_frontColorBuffer was produced and sent to the compositor, it cannot be bound to an fbo.
387 // We have to make a copy of it here and bind that copy instead.
388 // FIXME: That's not true any more, provided we don't change texture
390 unsigned sourceTexture = createColorTexture(m_size);
391 m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE);
393 // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding).
394 // FIXME: The WebGLRenderingContext tracks the current framebuffer binding, it would be slightly more efficient to use this value
395 // rather than querying it off of the context.
396 GLint previousFramebuffer = 0;
397 m_context->getIntegerv(GL_FRAMEBUFFER_BINDING, &previousFramebuffer);
399 Platform3DObject framebuffer = m_context->createFramebuffer();
400 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
401 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0);
403 paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
404 m_context->deleteFramebuffer(framebuffer);
405 m_context->deleteTexture(sourceTexture);
407 m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
410 void DrawingBuffer::clearPlatformLayer()
413 m_layer->clearTexture();
419 void DrawingBuffer::releaseResources()
422 m_context->makeContextCurrent();
424 clearPlatformLayer();
426 for (size_t i = 0; i < m_textureMailboxes.size(); i++)
427 m_context->deleteTexture(m_textureMailboxes[i]->textureId);
429 if (m_multisampleColorBuffer)
430 m_context->deleteRenderbuffer(m_multisampleColorBuffer);
432 if (m_depthStencilBuffer)
433 m_context->deleteRenderbuffer(m_depthStencilBuffer);
436 m_context->deleteRenderbuffer(m_depthBuffer);
439 m_context->deleteRenderbuffer(m_stencilBuffer);
441 if (m_multisampleFBO)
442 m_context->deleteFramebuffer(m_multisampleFBO);
445 m_context->deleteFramebuffer(m_fbo);
453 m_frontColorBuffer = 0;
454 m_multisampleColorBuffer = 0;
455 m_depthStencilBuffer = 0;
458 m_multisampleFBO = 0;
460 m_contextEvictionManager.clear();
462 m_recycledMailboxes.clear();
463 m_textureMailboxes.clear();
466 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
471 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
476 unsigned offscreenColorTexture = m_context->createTexture();
477 if (!offscreenColorTexture)
480 m_context->bindTexture(GL_TEXTURE_2D, offscreenColorTexture);
481 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
482 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
483 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
484 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
486 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
488 return offscreenColorTexture;
491 void DrawingBuffer::createSecondaryBuffers()
493 // create a multisample FBO
495 m_multisampleFBO = m_context->createFramebuffer();
496 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
497 m_multisampleColorBuffer = m_context->createRenderbuffer();
501 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
503 // resize regular FBO
504 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
506 m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
508 texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
510 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
512 m_context->bindTexture(GL_TEXTURE_2D, 0);
515 resizeDepthStencil(size);
516 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
522 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
525 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
527 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
528 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
530 if (m_context->getError() == GL_OUT_OF_MEMORY)
533 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleColorBuffer);
534 resizeDepthStencil(size);
535 if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
542 void DrawingBuffer::resizeDepthStencil(const IntSize& size)
544 if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtensionSupported) {
545 if (!m_depthStencilBuffer)
546 m_depthStencilBuffer = m_context->createRenderbuffer();
547 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
549 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
551 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
552 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
553 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
555 if (m_attributes.depth) {
557 m_depthBuffer = m_context->createRenderbuffer();
558 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
560 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
562 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
563 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
565 if (m_attributes.stencil) {
566 if (!m_stencilBuffer)
567 m_stencilBuffer = m_context->createRenderbuffer();
568 m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
570 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
572 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height());
573 m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
576 m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
581 void DrawingBuffer::clearFramebuffers(GLbitfield clearMask)
586 // We will clear the multisample FBO, but we also need to clear the non-multisampled buffer too.
587 if (m_multisampleFBO) {
588 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
589 m_context->clear(GL_COLOR_BUFFER_BIT);
592 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
593 m_context->clear(clearMask);
596 void DrawingBuffer::setSize(const IntSize& size) {
600 s_currentResourceUsePixels += pixelDelta(size);
604 int DrawingBuffer::pixelDelta(const IntSize& size) {
605 return (max(0, size.width()) * max(0, size.height())) - (max(0, m_size.width()) * max(0, m_size.height()));
608 IntSize DrawingBuffer::adjustSize(const IntSize& size) {
609 IntSize adjustedSize = size;
611 // Clamp if the desired size is greater than the maximum texture size for the device.
612 if (adjustedSize.height() > m_maxTextureSize)
613 adjustedSize.setHeight(m_maxTextureSize);
615 if (adjustedSize.width() > m_maxTextureSize)
616 adjustedSize.setWidth(m_maxTextureSize);
618 // Try progressively smaller sizes until we find a size that fits or reach a scale limit.
619 int scaleAttempts = 0;
620 while ((s_currentResourceUsePixels + pixelDelta(adjustedSize)) > s_maximumResourceUsePixels) {
622 if (scaleAttempts > s_maxScaleAttempts)
625 adjustedSize.scale(s_resourceAdjustedRatio);
627 if (adjustedSize.isEmpty())
634 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext) {
635 IntSize adjustedSize = adjustSize(size);
636 if (!adjustedSize.isEmpty()) {
637 evictContext = false;
638 return adjustedSize; // Buffer fits without evicting a context.
641 // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted.
642 IntSize oldestSize = m_contextEvictionManager->oldestContextSize();
643 int pixelDelta = oldestSize.width() * oldestSize.height();
645 s_currentResourceUsePixels -= pixelDelta;
646 adjustedSize = adjustSize(size);
647 s_currentResourceUsePixels += pixelDelta;
649 evictContext = !adjustedSize.isEmpty();
653 void DrawingBuffer::reset(const IntSize& newSize)
658 IntSize adjustedSize;
659 bool evictContext = false;
660 bool isNewContext = m_size.isEmpty();
661 if (s_allowContextEvictionOnCreate && isNewContext)
662 adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
664 adjustedSize = adjustSize(newSize);
666 if (adjustedSize.isEmpty())
670 m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
672 if (adjustedSize != m_size) {
674 // resize multisample FBO
675 if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
676 adjustedSize.scale(s_resourceAdjustedRatio);
680 } while (!adjustedSize.isEmpty());
682 setSize(adjustedSize);
684 if (adjustedSize.isEmpty())
688 m_context->disable(GL_SCISSOR_TEST);
689 m_context->clearColor(0, 0, 0, 0);
690 m_context->colorMask(true, true, true, true);
692 GLbitfield clearMask = GL_COLOR_BUFFER_BIT;
693 if (m_attributes.depth) {
694 m_context->clearDepth(1.0f);
695 clearMask |= GL_DEPTH_BUFFER_BIT;
696 m_context->depthMask(true);
698 if (m_attributes.stencil) {
699 m_context->clearStencil(0);
700 clearMask |= GL_STENCIL_BUFFER_BIT;
701 m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
704 clearFramebuffers(clearMask);
707 void DrawingBuffer::commit(long x, long y, long width, long height)
713 width = m_size.width();
715 height = m_size.height();
717 m_context->makeContextCurrent();
719 if (m_multisampleFBO && !m_contentsChangeCommitted) {
720 m_context->bindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_multisampleFBO);
721 m_context->bindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, m_fbo);
723 if (m_scissorEnabled)
724 m_context->disable(GL_SCISSOR_TEST);
726 // Use NEAREST, because there is no scale performed during the blit.
727 m_context->blitFramebufferCHROMIUM(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
729 if (m_scissorEnabled)
730 m_context->enable(GL_SCISSOR_TEST);
733 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
734 m_contentsChangeCommitted = true;
737 void DrawingBuffer::restoreFramebufferBinding()
739 if (!m_context || !m_framebufferBinding)
742 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
745 bool DrawingBuffer::multisample() const
747 return m_attributes.antialias && m_multisampleExtensionSupported;
750 void DrawingBuffer::bind()
755 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
758 void DrawingBuffer::setPackAlignment(GLint param)
760 m_packAlignment = param;
763 void DrawingBuffer::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
765 paintFramebufferToCanvas(framebuffer(), size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
768 PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height)
770 if (m_attributes.premultipliedAlpha)
773 width = size().width();
774 height = size().height();
776 Checked<int, RecordOverflow> dataSize = 4;
779 if (dataSize.hasOverflowed())
782 RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4);
784 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer());
785 readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, WebGLImageConversion::AlphaDoNothing);
786 flipVertically(pixels->data(), width, height);
788 return pixels.release();
791 void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer)
793 unsigned char* pixels = 0;
795 const SkBitmap& canvasBitmap = imageBuffer->bitmap();
796 const SkBitmap* readbackBitmap = 0;
797 ASSERT(canvasBitmap.config() == SkBitmap::kARGB_8888_Config);
798 if (canvasBitmap.width() == width && canvasBitmap.height() == height) {
799 // This is the fastest and most common case. We read back
800 // directly into the canvas's backing store.
801 readbackBitmap = &canvasBitmap;
802 m_resizingBitmap.reset();
804 // We need to allocate a temporary bitmap for reading back the
805 // pixel data. We will then use Skia to rescale this bitmap to
806 // the size of the canvas's backing store.
807 if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) {
808 m_resizingBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
809 if (!m_resizingBitmap.allocPixels())
812 readbackBitmap = &m_resizingBitmap;
815 // Read back the frame buffer.
816 SkAutoLockPixels bitmapLock(*readbackBitmap);
817 pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
819 m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
820 readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing);
821 flipVertically(pixels, width, height);
823 readbackBitmap->notifyPixelsChanged();
824 if (m_resizingBitmap.readyToDraw()) {
825 // We need to draw the resizing bitmap into the canvas's backing store.
826 SkCanvas canvas(canvasBitmap);
828 dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height()));
829 canvas.drawBitmapRect(m_resizingBitmap, 0, dst);
833 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, int width, int height, ReadbackOrder readbackOrder, WebGLImageConversion::AlphaOp op)
835 if (m_packAlignment > 4)
836 m_context->pixelStorei(GL_PACK_ALIGNMENT, 1);
837 m_context->readPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
838 if (m_packAlignment > 4)
839 m_context->pixelStorei(GL_PACK_ALIGNMENT, m_packAlignment);
841 size_t bufferSize = 4 * width * height;
843 if (readbackOrder == ReadbackSkia) {
844 #if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
845 // Swizzle red and blue channels to match SkBitmap's byte ordering.
846 // TODO(kbr): expose GL_BGRA as extension.
847 for (size_t i = 0; i < bufferSize; i += 4) {
848 std::swap(pixels[i], pixels[i + 2]);
853 if (op == WebGLImageConversion::AlphaDoPremultiply) {
854 for (size_t i = 0; i < bufferSize; i += 4) {
855 pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
856 pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
857 pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
859 } else if (op != WebGLImageConversion::AlphaDoNothing) {
860 ASSERT_NOT_REACHED();
864 void DrawingBuffer::flipVertically(uint8_t* framebuffer, int width, int height)
866 m_scanline.resize(width * 4);
867 uint8* scanline = &m_scanline[0];
868 unsigned rowBytes = width * 4;
869 unsigned count = height / 2;
870 for (unsigned i = 0; i < count; i++) {
871 uint8* rowA = framebuffer + i * rowBytes;
872 uint8* rowB = framebuffer + (height - i - 1) * rowBytes;
873 memcpy(scanline, rowB, rowBytes);
874 memcpy(rowB, rowA, rowBytes);
875 memcpy(rowA, scanline, rowBytes);
879 void DrawingBuffer::texImage2DResourceSafe(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLint unpackAlignment)
881 ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
882 m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0);
885 } // namespace WebCore