Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / gpu / DrawingBuffer.cpp
1 /*
2  * Copyright (c) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #include "platform/graphics/gpu/DrawingBuffer.h"
34
35 #include <algorithm>
36 #include "platform/TraceEvent.h"
37 #include "platform/graphics/GraphicsLayer.h"
38 #include "platform/graphics/gpu/Extensions3DUtil.h"
39 #include "public/platform/Platform.h"
40 #include "public/platform/WebCompositorSupport.h"
41 #include "public/platform/WebExternalBitmap.h"
42 #include "public/platform/WebExternalTextureLayer.h"
43 #include "public/platform/WebGraphicsContext3D.h"
44
45 using namespace std;
46
47 namespace WebCore {
48
49 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
50 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
51 // exceed the global cap will instead clear the buffer.
52 static const int s_maximumResourceUsePixels = 16 * 1024 * 1024;
53 static int s_currentResourceUsePixels = 0;
54 static const float s_resourceAdjustedRatio = 0.5;
55
56 static const bool s_allowContextEvictionOnCreate = true;
57 static const int s_maxScaleAttempts = 3;
58
59 class ScopedTextureUnit0BindingRestorer {
60 public:
61     ScopedTextureUnit0BindingRestorer(blink::WebGraphicsContext3D* context, GLenum activeTextureUnit, Platform3DObject textureUnitZeroId)
62         : m_context(context)
63         , m_oldActiveTextureUnit(activeTextureUnit)
64         , m_oldTextureUnitZeroId(textureUnitZeroId)
65     {
66         m_context->activeTexture(GL_TEXTURE0);
67     }
68     ~ScopedTextureUnit0BindingRestorer()
69     {
70         m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId);
71         m_context->activeTexture(m_oldActiveTextureUnit);
72     }
73
74 private:
75     blink::WebGraphicsContext3D* m_context;
76     GLenum m_oldActiveTextureUnit;
77     Platform3DObject m_oldTextureUnitZeroId;
78 };
79
80 PassRefPtr<DrawingBuffer> DrawingBuffer::create(blink::WebGraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
81 {
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");
88     }
89 #if defined(OS_TIZEN) && CPU(X86)
90     multisampleSupported = false;
91 #endif
92     bool packedDepthStencilSupported = extensionsUtil.supportsExtension("GL_OES_packed_depth_stencil");
93     if (packedDepthStencilSupported)
94         extensionsUtil.ensureExtensionEnabled("GL_OES_packed_depth_stencil");
95
96     RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
97     return drawingBuffer.release();
98 }
99
100 DrawingBuffer::DrawingBuffer(blink::WebGraphicsContext3D* context,
101     const IntSize& size,
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)
111     , m_context(context)
112     , m_size(-1, -1)
113     , m_multisampleExtensionSupported(multisampleExtensionSupported)
114     , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
115     , m_fbo(0)
116     , m_colorBuffer(0)
117     , m_frontColorBuffer(0)
118     , m_depthStencilBuffer(0)
119     , m_depthBuffer(0)
120     , m_stencilBuffer(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)
127     , m_colorFormat(0)
128     , m_internalRenderbufferFormat(0)
129     , m_maxTextureSize(0)
130     , m_packAlignment(4)
131     , m_contextEvictionManager(contextEvictionManager)
132 {
133     // Used by browser tests to detect the use of a DrawingBuffer.
134     TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
135     initialize(size);
136 }
137
138 DrawingBuffer::~DrawingBuffer()
139 {
140     releaseResources();
141 }
142
143 void DrawingBuffer::markContentsChanged()
144 {
145     m_contentsChanged = true;
146     m_contentsChangeCommitted = false;
147     m_layerComposited = false;
148 }
149
150 bool DrawingBuffer::layerComposited() const
151 {
152     return m_layerComposited;
153 }
154
155 void DrawingBuffer::markLayerComposited()
156 {
157     m_layerComposited = true;
158 }
159
160 blink::WebGraphicsContext3D* DrawingBuffer::context()
161 {
162     return m_context;
163 }
164
165 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
166 {
167     if (!m_context || !m_contentsChanged)
168         return false;
169
170     m_context->makeContextCurrent();
171
172     // Resolve the multisampled buffer into m_colorBuffer texture.
173     if (multisample())
174         commit();
175
176     if (bitmap) {
177         bitmap->setSize(size());
178
179         unsigned char* pixels = bitmap->pixels();
180         bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
181         WebGLImageConversion::AlphaOp op = needPremultiply ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing;
182         if (pixels)
183             readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op);
184     }
185
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);
189
190     // First try to recycle an old buffer.
191     RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox();
192
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.
197         if (!newColorBuffer)
198             return false;
199
200         frontColorBufferMailbox = createNewMailbox(newColorBuffer);
201     }
202
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);
211     } else {
212         m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, frontColorBufferMailbox->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
213     }
214
215     if (multisample() && !m_framebufferBinding)
216         bind();
217     else
218         restoreFramebufferBinding();
219
220     m_contentsChanged = false;
221
222     m_context->bindTexture(GL_TEXTURE_2D, frontColorBufferMailbox->textureId);
223     m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name);
224     m_context->flush();
225     frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
226     markLayerComposited();
227
228     *outMailbox = frontColorBufferMailbox->mailbox;
229     m_frontColorBuffer = frontColorBufferMailbox->textureId;
230     return true;
231 }
232
233 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
234 {
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());
240             return;
241         }
242     }
243     ASSERT_NOT_REACHED();
244 }
245
246 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
247 {
248     if (!m_context || m_recycledMailboxes.isEmpty())
249         return PassRefPtr<MailboxInfo>();
250
251     RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release();
252     m_recycledMailboxes.removeLast();
253
254     if (mailboxInfo->mailbox.syncPoint) {
255         m_context->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
256         mailboxInfo->mailbox.syncPoint = 0;
257     }
258
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;
263     }
264
265     return mailboxInfo.release();
266 }
267
268 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
269 {
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();
276 }
277
278 void DrawingBuffer::initialize(const IntSize& size)
279 {
280     ASSERT(m_context);
281     m_attributes = m_context->getContextAttributes();
282
283     if (m_attributes.alpha) {
284         m_internalColorFormat = GL_RGBA;
285         m_colorFormat = GL_RGBA;
286         m_internalRenderbufferFormat = GL_RGBA8_OES;
287     } else {
288         m_internalColorFormat = GL_RGB;
289         m_colorFormat = GL_RGB;
290         m_internalRenderbufferFormat = GL_RGB8_OES;
291     }
292
293     m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
294
295     int maxSampleCount = 0;
296     if (multisample())
297         m_context->getIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
298     m_sampleCount = std::min(4, maxSampleCount);
299
300     m_fbo = m_context->createFramebuffer();
301
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();
306     reset(size);
307 }
308
309 bool DrawingBuffer::copyToPlatformTexture(blink::WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
310 {
311     if (!m_context || !m_context->makeContextCurrent())
312         return false;
313     if (m_contentsChanged) {
314         if (multisample()) {
315             commit();
316             if (!m_framebufferBinding)
317                 bind();
318             else
319                 restoreFramebufferBinding();
320         }
321         m_context->flush();
322     }
323     Platform3DObject sourceTexture = m_colorBuffer;
324
325     if (!context->makeContextCurrent())
326         return false;
327
328     if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
329         return false;
330
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;
337
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);
345     context->flush();
346
347     return true;
348 }
349
350 Platform3DObject DrawingBuffer::framebuffer() const
351 {
352     return m_fbo;
353 }
354
355 blink::WebLayer* DrawingBuffer::platformLayer()
356 {
357     if (!m_context)
358         return 0;
359
360     if (!m_layer) {
361         m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
362
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());
367     }
368
369     return m_layer->layer();
370 }
371
372 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
373 {
374     if (!m_context || !m_context->makeContextCurrent() || m_context->getGraphicsResetStatusARB() != GL_NO_ERROR)
375         return;
376
377     if (!imageBuffer)
378         return;
379     Platform3DObject tex = imageBuffer->getBackingTexture();
380     if (tex) {
381         m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer,
382             tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
383         return;
384     }
385
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
389     // parameters.
390     unsigned sourceTexture = createColorTexture(m_size);
391     m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE);
392
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);
398
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);
402
403     paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
404     m_context->deleteFramebuffer(framebuffer);
405     m_context->deleteTexture(sourceTexture);
406
407     m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
408 }
409
410 void DrawingBuffer::clearPlatformLayer()
411 {
412     if (m_layer)
413         m_layer->clearTexture();
414
415     if (m_context)
416         m_context->flush();
417 }
418
419 void DrawingBuffer::releaseResources()
420 {
421     if (m_context) {
422         m_context->makeContextCurrent();
423
424         clearPlatformLayer();
425
426         for (size_t i = 0; i < m_textureMailboxes.size(); i++)
427             m_context->deleteTexture(m_textureMailboxes[i]->textureId);
428
429         if (m_multisampleColorBuffer)
430             m_context->deleteRenderbuffer(m_multisampleColorBuffer);
431
432         if (m_depthStencilBuffer)
433             m_context->deleteRenderbuffer(m_depthStencilBuffer);
434
435         if (m_depthBuffer)
436             m_context->deleteRenderbuffer(m_depthBuffer);
437
438         if (m_stencilBuffer)
439             m_context->deleteRenderbuffer(m_stencilBuffer);
440
441         if (m_multisampleFBO)
442             m_context->deleteFramebuffer(m_multisampleFBO);
443
444         if (m_fbo)
445             m_context->deleteFramebuffer(m_fbo);
446
447         m_context = 0;
448     }
449
450     setSize(IntSize());
451
452     m_colorBuffer = 0;
453     m_frontColorBuffer = 0;
454     m_multisampleColorBuffer = 0;
455     m_depthStencilBuffer = 0;
456     m_depthBuffer = 0;
457     m_stencilBuffer = 0;
458     m_multisampleFBO = 0;
459     m_fbo = 0;
460     m_contextEvictionManager.clear();
461
462     m_recycledMailboxes.clear();
463     m_textureMailboxes.clear();
464
465     if (m_layer) {
466         GraphicsLayer::unregisterContentsLayer(m_layer->layer());
467         m_layer.clear();
468     }
469 }
470
471 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
472 {
473     if (!m_context)
474         return 0;
475
476     unsigned offscreenColorTexture = m_context->createTexture();
477     if (!offscreenColorTexture)
478         return 0;
479
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);
485     if (!size.isEmpty())
486         texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
487
488     return offscreenColorTexture;
489 }
490
491 void DrawingBuffer::createSecondaryBuffers()
492 {
493     // create a multisample FBO
494     if (multisample()) {
495         m_multisampleFBO = m_context->createFramebuffer();
496         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
497         m_multisampleColorBuffer = m_context->createRenderbuffer();
498     }
499 }
500
501 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
502 {
503     // resize regular FBO
504     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
505
506     m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
507
508     texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
509
510     m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
511
512     m_context->bindTexture(GL_TEXTURE_2D, 0);
513
514     if (!multisample())
515         resizeDepthStencil(size);
516     if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
517         return false;
518
519     return true;
520 }
521
522 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
523 {
524     if (multisample()) {
525         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
526
527         m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
528         m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
529
530         if (m_context->getError() == GL_OUT_OF_MEMORY)
531             return false;
532
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)
536             return false;
537     }
538
539     return true;
540 }
541
542 void DrawingBuffer::resizeDepthStencil(const IntSize& size)
543 {
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);
548         if (multisample())
549             m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
550         else
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);
554     } else {
555         if (m_attributes.depth) {
556             if (!m_depthBuffer)
557                 m_depthBuffer = m_context->createRenderbuffer();
558             m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
559             if (multisample())
560                 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
561             else
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);
564         }
565         if (m_attributes.stencil) {
566             if (!m_stencilBuffer)
567                 m_stencilBuffer = m_context->createRenderbuffer();
568             m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
569             if (multisample())
570                 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
571             else
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);
574         }
575     }
576     m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
577 }
578
579
580
581 void DrawingBuffer::clearFramebuffers(GLbitfield clearMask)
582 {
583     if (!m_context)
584         return;
585
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);
590     }
591
592     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
593     m_context->clear(clearMask);
594 }
595
596 void DrawingBuffer::setSize(const IntSize& size) {
597     if (m_size == size)
598         return;
599
600     s_currentResourceUsePixels += pixelDelta(size);
601     m_size = size;
602 }
603
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()));
606 }
607
608 IntSize DrawingBuffer::adjustSize(const IntSize& size) {
609     IntSize adjustedSize = size;
610
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);
614
615     if (adjustedSize.width() > m_maxTextureSize)
616         adjustedSize.setWidth(m_maxTextureSize);
617
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) {
621         scaleAttempts++;
622         if (scaleAttempts > s_maxScaleAttempts)
623             return IntSize();
624
625         adjustedSize.scale(s_resourceAdjustedRatio);
626
627         if (adjustedSize.isEmpty())
628             return IntSize();
629     }
630
631     return adjustedSize;
632 }
633
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.
639     }
640
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();
644
645     s_currentResourceUsePixels -= pixelDelta;
646     adjustedSize = adjustSize(size);
647     s_currentResourceUsePixels += pixelDelta;
648
649     evictContext = !adjustedSize.isEmpty();
650     return adjustedSize;
651 }
652
653 void DrawingBuffer::reset(const IntSize& newSize)
654 {
655     if (!m_context)
656         return;
657
658     IntSize adjustedSize;
659     bool evictContext = false;
660     bool isNewContext = m_size.isEmpty();
661     if (s_allowContextEvictionOnCreate && isNewContext)
662         adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
663     else
664         adjustedSize = adjustSize(newSize);
665
666     if (adjustedSize.isEmpty())
667         return;
668
669     if (evictContext)
670         m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
671
672     if (adjustedSize != m_size) {
673         do {
674             // resize multisample FBO
675             if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
676                 adjustedSize.scale(s_resourceAdjustedRatio);
677                 continue;
678             }
679             break;
680         } while (!adjustedSize.isEmpty());
681
682         setSize(adjustedSize);
683
684         if (adjustedSize.isEmpty())
685             return;
686     }
687
688     m_context->disable(GL_SCISSOR_TEST);
689     m_context->clearColor(0, 0, 0, 0);
690     m_context->colorMask(true, true, true, true);
691
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);
697     }
698     if (m_attributes.stencil) {
699         m_context->clearStencil(0);
700         clearMask |= GL_STENCIL_BUFFER_BIT;
701         m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
702     }
703
704     clearFramebuffers(clearMask);
705 }
706
707 void DrawingBuffer::commit(long x, long y, long width, long height)
708 {
709     if (!m_context)
710         return;
711
712     if (width < 0)
713         width = m_size.width();
714     if (height < 0)
715         height = m_size.height();
716
717     m_context->makeContextCurrent();
718
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);
722
723         if (m_scissorEnabled)
724             m_context->disable(GL_SCISSOR_TEST);
725
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);
728
729         if (m_scissorEnabled)
730             m_context->enable(GL_SCISSOR_TEST);
731     }
732
733     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
734     m_contentsChangeCommitted = true;
735 }
736
737 void DrawingBuffer::restoreFramebufferBinding()
738 {
739     if (!m_context || !m_framebufferBinding)
740         return;
741
742     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
743 }
744
745 bool DrawingBuffer::multisample() const
746 {
747     return m_attributes.antialias && m_multisampleExtensionSupported;
748 }
749
750 void DrawingBuffer::bind()
751 {
752     if (!m_context)
753         return;
754
755     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
756 }
757
758 void DrawingBuffer::setPackAlignment(GLint param)
759 {
760     m_packAlignment = param;
761 }
762
763 void DrawingBuffer::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
764 {
765     paintFramebufferToCanvas(framebuffer(), size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
766 }
767
768 PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height)
769 {
770     if (m_attributes.premultipliedAlpha)
771         return 0;
772
773     width = size().width();
774     height = size().height();
775
776     Checked<int, RecordOverflow> dataSize = 4;
777     dataSize *= width;
778     dataSize *= height;
779     if (dataSize.hasOverflowed())
780         return 0;
781
782     RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4);
783
784     m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer());
785     readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, WebGLImageConversion::AlphaDoNothing);
786     flipVertically(pixels->data(), width, height);
787
788     return pixels.release();
789 }
790
791 void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer)
792 {
793     unsigned char* pixels = 0;
794
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();
803     } else {
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())
810                 return;
811         }
812         readbackBitmap = &m_resizingBitmap;
813     }
814
815     // Read back the frame buffer.
816     SkAutoLockPixels bitmapLock(*readbackBitmap);
817     pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
818
819     m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
820     readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing);
821     flipVertically(pixels, width, height);
822
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);
827         SkRect dst;
828         dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height()));
829         canvas.drawBitmapRect(m_resizingBitmap, 0, dst);
830     }
831 }
832
833 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, int width, int height, ReadbackOrder readbackOrder, WebGLImageConversion::AlphaOp op)
834 {
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);
840
841     size_t bufferSize = 4 * width * height;
842
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]);
849         }
850 #endif
851     }
852
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);
858         }
859     } else if (op != WebGLImageConversion::AlphaDoNothing) {
860         ASSERT_NOT_REACHED();
861     }
862 }
863
864 void DrawingBuffer::flipVertically(uint8_t* framebuffer, int width, int height)
865 {
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);
876     }
877 }
878
879 void DrawingBuffer::texImage2DResourceSafe(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLint unpackAlignment)
880 {
881     ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
882     m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0);
883 }
884
885 } // namespace WebCore