Upstream version 5.34.97.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 "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"
43
44 using namespace std;
45
46 namespace WebCore {
47
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;
54
55 static const bool s_allowContextEvictionOnCreate = true;
56 static const int s_maxScaleAttempts = 3;
57
58 class ScopedTextureUnit0BindingRestorer {
59 public:
60     ScopedTextureUnit0BindingRestorer(blink::WebGraphicsContext3D* context, GLenum activeTextureUnit, Platform3DObject textureUnitZeroId)
61         : m_context(context)
62         , m_oldActiveTextureUnit(activeTextureUnit)
63         , m_oldTextureUnitZeroId(textureUnitZeroId)
64     {
65         m_context->activeTexture(GL_TEXTURE0);
66     }
67     ~ScopedTextureUnit0BindingRestorer()
68     {
69         m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId);
70         m_context->activeTexture(m_oldActiveTextureUnit);
71     }
72
73 private:
74     blink::WebGraphicsContext3D* m_context;
75     GLenum m_oldActiveTextureUnit;
76     Platform3DObject m_oldTextureUnitZeroId;
77 };
78
79 PassRefPtr<DrawingBuffer> DrawingBuffer::create(blink::WebGraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
80 {
81     RefPtr<GraphicsContext3D> contextSupport(GraphicsContext3D::createContextSupport(context));
82
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");
90     }
91 #if defined(OS_TIZEN) && CPU(X86)
92     multisampleSupported = false;
93 #endif
94     bool packedDepthStencilSupported = contextSupport->supportsExtension("GL_OES_packed_depth_stencil");
95     if (packedDepthStencilSupported)
96         contextSupport->ensureExtensionEnabled("GL_OES_packed_depth_stencil");
97
98     RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, contextSupport.get(), size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
99     return drawingBuffer.release();
100 }
101
102 DrawingBuffer::DrawingBuffer(blink::WebGraphicsContext3D* context,
103     GraphicsContext3D* contextSupport,
104     const IntSize& size,
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)
114     , m_context(context)
115     , m_contextSupport(contextSupport)
116     , m_size(-1, -1)
117     , m_multisampleExtensionSupported(multisampleExtensionSupported)
118     , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
119     , m_fbo(0)
120     , m_colorBuffer(0)
121     , m_frontColorBuffer(0)
122     , m_depthStencilBuffer(0)
123     , m_depthBuffer(0)
124     , m_stencilBuffer(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)
131     , m_colorFormat(0)
132     , m_internalRenderbufferFormat(0)
133     , m_maxTextureSize(0)
134     , m_packAlignment(4)
135     , m_contextEvictionManager(contextEvictionManager)
136 {
137     // Used by browser tests to detect the use of a DrawingBuffer.
138     TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
139     initialize(size);
140 }
141
142 DrawingBuffer::~DrawingBuffer()
143 {
144     releaseResources();
145 }
146
147 void DrawingBuffer::markContentsChanged()
148 {
149     m_contentsChanged = true;
150     m_contentsChangeCommitted = false;
151     m_layerComposited = false;
152 }
153
154 bool DrawingBuffer::layerComposited() const
155 {
156     return m_layerComposited;
157 }
158
159 void DrawingBuffer::markLayerComposited()
160 {
161     m_layerComposited = true;
162 }
163
164 blink::WebGraphicsContext3D* DrawingBuffer::context()
165 {
166     return m_context;
167 }
168
169 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
170 {
171     if (!m_context || !m_contentsChanged)
172         return false;
173
174     m_context->makeContextCurrent();
175
176     // Resolve the multisampled buffer into m_colorBuffer texture.
177     if (multisample())
178         commit();
179
180     if (bitmap) {
181         bitmap->setSize(size());
182
183         unsigned char* pixels = bitmap->pixels();
184         bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
185         GraphicsContext3D::AlphaOp op = needPremultiply ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing;
186         if (pixels)
187             readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, op);
188     }
189
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);
193
194     // First try to recycle an old buffer.
195     RefPtr<MailboxInfo> frontColorBufferMailbox = recycledMailbox();
196
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.
201         if (!newColorBuffer)
202             return false;
203
204         frontColorBufferMailbox = createNewMailbox(newColorBuffer);
205     }
206
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);
215     } else {
216         m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, frontColorBufferMailbox->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
217     }
218
219     if (multisample() && !m_framebufferBinding)
220         bind();
221     else
222         restoreFramebufferBinding();
223
224     m_contentsChanged = false;
225
226     m_context->bindTexture(GL_TEXTURE_2D, frontColorBufferMailbox->textureId);
227     m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, frontColorBufferMailbox->mailbox.name);
228     m_context->flush();
229     frontColorBufferMailbox->mailbox.syncPoint = m_context->insertSyncPoint();
230     markLayerComposited();
231
232     *outMailbox = frontColorBufferMailbox->mailbox;
233     m_frontColorBuffer = frontColorBufferMailbox->textureId;
234     return true;
235 }
236
237 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
238 {
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());
244             return;
245         }
246     }
247     ASSERT_NOT_REACHED();
248 }
249
250 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
251 {
252     if (!m_context || m_recycledMailboxes.isEmpty())
253         return PassRefPtr<MailboxInfo>();
254
255     RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release();
256     m_recycledMailboxes.removeLast();
257
258     if (mailboxInfo->mailbox.syncPoint) {
259         m_context->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
260         mailboxInfo->mailbox.syncPoint = 0;
261     }
262
263     m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureId);
264     m_context->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->mailbox.name);
265
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;
269     }
270
271     return mailboxInfo.release();
272 }
273
274 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
275 {
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();
282 }
283
284 void DrawingBuffer::initialize(const IntSize& size)
285 {
286     ASSERT(m_context);
287     m_attributes = m_context->getContextAttributes();
288
289     if (m_attributes.alpha) {
290         m_internalColorFormat = GL_RGBA;
291         m_colorFormat = GL_RGBA;
292         m_internalRenderbufferFormat = GL_RGBA8_OES;
293     } else {
294         m_internalColorFormat = GL_RGB;
295         m_colorFormat = GL_RGB;
296         m_internalRenderbufferFormat = GL_RGB8_OES;
297     }
298
299     m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
300
301     int maxSampleCount = 0;
302     if (multisample())
303         m_context->getIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSampleCount);
304     m_sampleCount = std::min(4, maxSampleCount);
305
306     m_fbo = m_context->createFramebuffer();
307
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();
312     reset(size);
313 }
314
315 bool DrawingBuffer::copyToPlatformTexture(GraphicsContext3D& contextSupport, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
316 {
317     if (!m_context || !m_context->makeContextCurrent())
318         return false;
319     if (m_contentsChanged) {
320         if (multisample()) {
321             commit();
322             if (!m_framebufferBinding)
323                 bind();
324             else
325                 restoreFramebufferBinding();
326         }
327         m_context->flush();
328     }
329     Platform3DObject sourceTexture = m_colorBuffer;
330
331     blink::WebGraphicsContext3D* context = contextSupport.webContext();
332
333     if (!context->makeContextCurrent())
334         return false;
335     if (!contextSupport.supportsExtension("GL_CHROMIUM_copy_texture") || !contextSupport.supportsExtension("GL_CHROMIUM_flipy")
336         || !contextSupport.canUseCopyTextureCHROMIUM(internalFormat, destType, level))
337         return false;
338
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;
345
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);
353     context->flush();
354
355     return true;
356 }
357
358 Platform3DObject DrawingBuffer::framebuffer() const
359 {
360     return m_fbo;
361 }
362
363 blink::WebLayer* DrawingBuffer::platformLayer()
364 {
365     if (!m_context)
366         return 0;
367
368     if (!m_layer) {
369         m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
370
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());
375     }
376
377     return m_layer->layer();
378 }
379
380 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
381 {
382     if (!m_context || !m_context->makeContextCurrent() || m_context->getGraphicsResetStatusARB() != GL_NO_ERROR)
383         return;
384
385     if (!imageBuffer)
386         return;
387     Platform3DObject tex = imageBuffer->getBackingTexture();
388     if (tex) {
389         m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer,
390             tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
391         return;
392     }
393
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
397     // parameters.
398     unsigned sourceTexture = createColorTexture(m_size);
399     m_context->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE);
400
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);
406
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);
410
411     paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
412     m_context->deleteFramebuffer(framebuffer);
413     m_context->deleteTexture(sourceTexture);
414
415     m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
416 }
417
418 void DrawingBuffer::clearPlatformLayer()
419 {
420     if (m_layer)
421         m_layer->clearTexture();
422
423     if (m_context)
424         m_context->flush();
425 }
426
427 void DrawingBuffer::releaseResources()
428 {
429     if (m_context) {
430         m_context->makeContextCurrent();
431
432         clearPlatformLayer();
433
434         for (size_t i = 0; i < m_textureMailboxes.size(); i++)
435             m_context->deleteTexture(m_textureMailboxes[i]->textureId);
436
437         if (m_multisampleColorBuffer)
438             m_context->deleteRenderbuffer(m_multisampleColorBuffer);
439
440         if (m_depthStencilBuffer)
441             m_context->deleteRenderbuffer(m_depthStencilBuffer);
442
443         if (m_depthBuffer)
444             m_context->deleteRenderbuffer(m_depthBuffer);
445
446         if (m_stencilBuffer)
447             m_context->deleteRenderbuffer(m_stencilBuffer);
448
449         if (m_multisampleFBO)
450             m_context->deleteFramebuffer(m_multisampleFBO);
451
452         if (m_fbo)
453             m_context->deleteFramebuffer(m_fbo);
454
455         m_contextSupport.clear();
456         m_context = 0;
457     }
458
459     setSize(IntSize());
460
461     m_colorBuffer = 0;
462     m_frontColorBuffer = 0;
463     m_multisampleColorBuffer = 0;
464     m_depthStencilBuffer = 0;
465     m_depthBuffer = 0;
466     m_stencilBuffer = 0;
467     m_multisampleFBO = 0;
468     m_fbo = 0;
469     m_contextEvictionManager.clear();
470
471     m_recycledMailboxes.clear();
472     m_textureMailboxes.clear();
473
474     if (m_layer) {
475         GraphicsLayer::unregisterContentsLayer(m_layer->layer());
476         m_layer.clear();
477     }
478 }
479
480 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
481 {
482     if (!m_context)
483         return 0;
484
485     unsigned offscreenColorTexture = m_context->createTexture();
486     if (!offscreenColorTexture)
487         return 0;
488
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);
494     if (!size.isEmpty())
495         texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
496
497     return offscreenColorTexture;
498 }
499
500 void DrawingBuffer::createSecondaryBuffers()
501 {
502     // create a multisample FBO
503     if (multisample()) {
504         m_multisampleFBO = m_context->createFramebuffer();
505         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
506         m_multisampleColorBuffer = m_context->createRenderbuffer();
507     }
508 }
509
510 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
511 {
512     // resize regular FBO
513     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
514
515     m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
516
517     texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
518
519     m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
520
521     m_context->bindTexture(GL_TEXTURE_2D, 0);
522
523     if (!multisample())
524         resizeDepthStencil(size);
525     if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
526         return false;
527
528     return true;
529 }
530
531 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
532 {
533     if (multisample()) {
534         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
535
536         m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
537         m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
538
539         if (m_context->getError() == GL_OUT_OF_MEMORY)
540             return false;
541
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)
545             return false;
546     }
547
548     return true;
549 }
550
551 void DrawingBuffer::resizeDepthStencil(const IntSize& size)
552 {
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);
557         if (multisample())
558             m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height());
559         else
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);
563     } else {
564         if (m_attributes.depth) {
565             if (!m_depthBuffer)
566                 m_depthBuffer = m_context->createRenderbuffer();
567             m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
568             if (multisample())
569                 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
570             else
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);
573         }
574         if (m_attributes.stencil) {
575             if (!m_stencilBuffer)
576                 m_stencilBuffer = m_context->createRenderbuffer();
577             m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
578             if (multisample())
579                 m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
580             else
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);
583         }
584     }
585     m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
586 }
587
588
589
590 void DrawingBuffer::clearFramebuffers(GLbitfield clearMask)
591 {
592     if (!m_context)
593         return;
594
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);
599     }
600
601     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
602     m_context->clear(clearMask);
603 }
604
605 void DrawingBuffer::setSize(const IntSize& size) {
606     if (m_size == size)
607         return;
608
609     s_currentResourceUsePixels += pixelDelta(size);
610     m_size = size;
611 }
612
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()));
615 }
616
617 IntSize DrawingBuffer::adjustSize(const IntSize& size) {
618     IntSize adjustedSize = size;
619
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);
623
624     if (adjustedSize.width() > m_maxTextureSize)
625         adjustedSize.setWidth(m_maxTextureSize);
626
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) {
630         scaleAttempts++;
631         if (scaleAttempts > s_maxScaleAttempts)
632             return IntSize();
633
634         adjustedSize.scale(s_resourceAdjustedRatio);
635
636         if (adjustedSize.isEmpty())
637             return IntSize();
638     }
639
640     return adjustedSize;
641 }
642
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.
648     }
649
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();
653
654     s_currentResourceUsePixels -= pixelDelta;
655     adjustedSize = adjustSize(size);
656     s_currentResourceUsePixels += pixelDelta;
657
658     evictContext = !adjustedSize.isEmpty();
659     return adjustedSize;
660 }
661
662 void DrawingBuffer::reset(const IntSize& newSize)
663 {
664     if (!m_context)
665         return;
666
667     IntSize adjustedSize;
668     bool evictContext = false;
669     bool isNewContext = m_size.isEmpty();
670     if (s_allowContextEvictionOnCreate && isNewContext)
671         adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
672     else
673         adjustedSize = adjustSize(newSize);
674
675     if (adjustedSize.isEmpty())
676         return;
677
678     if (evictContext)
679         m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
680
681     if (adjustedSize != m_size) {
682         do {
683             // resize multisample FBO
684             if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
685                 adjustedSize.scale(s_resourceAdjustedRatio);
686                 continue;
687             }
688             break;
689         } while (!adjustedSize.isEmpty());
690
691         setSize(adjustedSize);
692
693         if (adjustedSize.isEmpty())
694             return;
695     }
696
697     m_context->disable(GL_SCISSOR_TEST);
698     m_context->clearColor(0, 0, 0, 0);
699     m_context->colorMask(true, true, true, true);
700
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);
706     }
707     if (m_attributes.stencil) {
708         m_context->clearStencil(0);
709         clearMask |= GL_STENCIL_BUFFER_BIT;
710         m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
711     }
712
713     clearFramebuffers(clearMask);
714 }
715
716 void DrawingBuffer::commit(long x, long y, long width, long height)
717 {
718     if (!m_context)
719         return;
720
721     if (width < 0)
722         width = m_size.width();
723     if (height < 0)
724         height = m_size.height();
725
726     m_context->makeContextCurrent();
727
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);
731
732         if (m_scissorEnabled)
733             m_context->disable(GL_SCISSOR_TEST);
734
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);
737
738         if (m_scissorEnabled)
739             m_context->enable(GL_SCISSOR_TEST);
740     }
741
742     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
743     m_contentsChangeCommitted = true;
744 }
745
746 void DrawingBuffer::restoreFramebufferBinding()
747 {
748     if (!m_context || !m_framebufferBinding)
749         return;
750
751     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
752 }
753
754 bool DrawingBuffer::multisample() const
755 {
756     return m_attributes.antialias && m_multisampleExtensionSupported;
757 }
758
759 void DrawingBuffer::bind()
760 {
761     if (!m_context)
762         return;
763
764     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
765 }
766
767 void DrawingBuffer::setPackAlignment(GLint param)
768 {
769     m_packAlignment = param;
770 }
771
772 void DrawingBuffer::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
773 {
774     paintFramebufferToCanvas(framebuffer(), size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
775 }
776
777 PassRefPtr<Uint8ClampedArray> DrawingBuffer::paintRenderingResultsToImageData(int& width, int& height)
778 {
779     if (m_attributes.premultipliedAlpha)
780         return 0;
781
782     width = size().width();
783     height = size().height();
784
785     Checked<int, RecordOverflow> dataSize = 4;
786     dataSize *= width;
787     dataSize *= height;
788     if (dataSize.hasOverflowed())
789         return 0;
790
791     RefPtr<Uint8ClampedArray> pixels = Uint8ClampedArray::createUninitialized(width * height * 4);
792
793     m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer());
794     readBackFramebuffer(pixels->data(), width, height, ReadbackRGBA, GraphicsContext3D::AlphaDoNothing);
795     flipVertically(pixels->data(), width, height);
796
797     return pixels.release();
798 }
799
800 void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer)
801 {
802     unsigned char* pixels = 0;
803
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();
812     } else {
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())
819                 return;
820         }
821         readbackBitmap = &m_resizingBitmap;
822     }
823
824     // Read back the frame buffer.
825     SkAutoLockPixels bitmapLock(*readbackBitmap);
826     pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
827
828     m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
829     readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing);
830     flipVertically(pixels, width, height);
831
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);
836         SkRect dst;
837         dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height()));
838         canvas.drawBitmapRect(m_resizingBitmap, 0, dst);
839     }
840 }
841
842 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, int width, int height, ReadbackOrder readbackOrder, GraphicsContext3D::AlphaOp op)
843 {
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);
849
850     size_t bufferSize = 4 * width * height;
851
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]);
858         }
859 #endif
860     }
861
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);
867         }
868     } else if (op != GraphicsContext3D::AlphaDoNothing) {
869         ASSERT_NOT_REACHED();
870     }
871 }
872
873 void DrawingBuffer::flipVertically(uint8_t* framebuffer, int width, int height)
874 {
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);
885     }
886 }
887
888 void DrawingBuffer::texImage2DResourceSafe(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLint unpackAlignment)
889 {
890     ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
891     m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0);
892 }
893
894 } // namespace WebCore