• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/Extensions3D.h"
38 #include "platform/graphics/GraphicsLayer.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:
ScopedTextureUnit0BindingRestorer(GraphicsContext3D * context,GC3Denum activeTextureUnit,Platform3DObject textureUnitZeroId)61     ScopedTextureUnit0BindingRestorer(GraphicsContext3D* context, GC3Denum activeTextureUnit, Platform3DObject textureUnitZeroId)
62         : m_context(context)
63         , m_oldActiveTextureUnit(activeTextureUnit)
64         , m_oldTextureUnitZeroId(textureUnitZeroId)
65     {
66         m_context->activeTexture(GL_TEXTURE0);
67     }
~ScopedTextureUnit0BindingRestorer()68     ~ScopedTextureUnit0BindingRestorer()
69     {
70         m_context->bindTexture(GL_TEXTURE_2D, m_oldTextureUnitZeroId);
71         m_context->activeTexture(m_oldActiveTextureUnit);
72     }
73 
74 private:
75     GraphicsContext3D* m_context;
76     GC3Denum m_oldActiveTextureUnit;
77     Platform3DObject m_oldTextureUnitZeroId;
78 };
79 
create(GraphicsContext3D * context,const IntSize & size,PreserveDrawingBuffer preserve,PassRefPtr<ContextEvictionManager> contextEvictionManager)80 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager)
81 {
82     Extensions3D* extensions = context->extensions();
83     bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit")
84         && extensions->supports("GL_ANGLE_framebuffer_multisample")
85         && extensions->supports("GL_OES_rgb8_rgba8");
86     if (multisampleSupported) {
87         extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
88         extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
89         extensions->ensureEnabled("GL_OES_rgb8_rgba8");
90     }
91     bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil");
92     if (packedDepthStencilSupported)
93         extensions->ensureEnabled("GL_OES_packed_depth_stencil");
94 
95     RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, contextEvictionManager));
96     return drawingBuffer.release();
97 }
98 
DrawingBuffer(GraphicsContext3D * context,const IntSize & size,bool multisampleExtensionSupported,bool packedDepthStencilExtensionSupported,PreserveDrawingBuffer preserve,PassRefPtr<ContextEvictionManager> contextEvictionManager)99 DrawingBuffer::DrawingBuffer(GraphicsContext3D* context,
100                              const IntSize& size,
101                              bool multisampleExtensionSupported,
102                              bool packedDepthStencilExtensionSupported,
103                              PreserveDrawingBuffer preserve,
104                              PassRefPtr<ContextEvictionManager> contextEvictionManager)
105     : m_preserveDrawingBuffer(preserve)
106     , m_scissorEnabled(false)
107     , m_texture2DBinding(0)
108     , m_framebufferBinding(0)
109     , m_activeTextureUnit(GL_TEXTURE0)
110     , m_context(context)
111     , m_size(-1, -1)
112     , m_multisampleExtensionSupported(multisampleExtensionSupported)
113     , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
114     , m_fbo(0)
115     , m_colorBuffer(0)
116     , m_frontColorBuffer(0)
117     , m_depthStencilBuffer(0)
118     , m_depthBuffer(0)
119     , m_stencilBuffer(0)
120     , m_multisampleFBO(0)
121     , m_multisampleColorBuffer(0)
122     , m_contentsChanged(true)
123     , m_contentsChangeCommitted(false)
124     , m_internalColorFormat(0)
125     , m_colorFormat(0)
126     , m_internalRenderbufferFormat(0)
127     , m_maxTextureSize(0)
128     , m_contextEvictionManager(contextEvictionManager)
129 {
130     // Used by browser tests to detect the use of a DrawingBuffer.
131     TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation");
132     initialize(size);
133 }
134 
~DrawingBuffer()135 DrawingBuffer::~DrawingBuffer()
136 {
137     releaseResources();
138 }
139 
markContentsChanged()140 void DrawingBuffer::markContentsChanged()
141 {
142     m_contentsChanged = true;
143     m_contentsChangeCommitted = false;
144 }
145 
context()146 blink::WebGraphicsContext3D* DrawingBuffer::context()
147 {
148     if (!m_context)
149         return 0;
150     return m_context->webContext();
151 }
152 
prepareMailbox(blink::WebExternalTextureMailbox * outMailbox,blink::WebExternalBitmap * bitmap)153 bool DrawingBuffer::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
154 {
155     if (!m_context || !m_contentsChanged || !m_lastColorBuffer)
156         return false;
157 
158     m_context->makeContextCurrent();
159 
160     // Resolve the multisampled buffer into the texture referenced by m_lastColorBuffer mailbox.
161     if (multisample())
162         commit();
163 
164     if (bitmap) {
165         bitmap->setSize(size());
166 
167         unsigned char* pixels = bitmap->pixels();
168         bool needPremultiply = m_attributes.alpha && !m_attributes.premultipliedAlpha;
169         GraphicsContext3D::AlphaOp op = needPremultiply ? GraphicsContext3D::AlphaDoPremultiply : GraphicsContext3D::AlphaDoNothing;
170         if (pixels)
171             m_context->readBackFramebuffer(pixels, size().width(), size().height(), GraphicsContext3D::ReadbackSkia, op);
172     }
173 
174     // We must restore the texture binding since creating new textures,
175     // consuming and producing mailboxes changes it.
176     ScopedTextureUnit0BindingRestorer restorer(m_context.get(), m_activeTextureUnit, m_texture2DBinding);
177 
178     // First try to recycle an old buffer.
179     RefPtr<MailboxInfo> nextFrontColorBuffer = recycledMailbox();
180 
181     // No buffer available to recycle, create a new one.
182     if (!nextFrontColorBuffer) {
183         unsigned newColorBuffer = createColorTexture(m_size);
184         // Bad things happened, abandon ship.
185         if (!newColorBuffer)
186             return false;
187 
188         nextFrontColorBuffer = createNewMailbox(newColorBuffer);
189     }
190 
191     if (m_preserveDrawingBuffer == Discard) {
192         m_colorBuffer = nextFrontColorBuffer->textureId;
193         swap(nextFrontColorBuffer, m_lastColorBuffer);
194         // It appears safe to overwrite the context's framebuffer binding in the Discard case since there will always be a
195         // WebGLRenderingContext::clearIfComposited() call made before the next draw call which restores the framebuffer binding.
196         // If this stops being true at some point, we should track the current framebuffer binding in the DrawingBuffer and restore
197         // it after attaching the new back buffer here.
198         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
199         m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
200     } else {
201         Extensions3D* extensions = m_context->extensions();
202         extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, m_colorBuffer, nextFrontColorBuffer->textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE);
203     }
204 
205     if (multisample() && !m_framebufferBinding)
206         bind();
207     else
208         restoreFramebufferBinding();
209 
210     m_contentsChanged = false;
211 
212     context()->bindTexture(GL_TEXTURE_2D, nextFrontColorBuffer->textureId);
213     context()->produceTextureCHROMIUM(GL_TEXTURE_2D, nextFrontColorBuffer->mailbox.name);
214     context()->flush();
215     m_context->markLayerComposited();
216 
217     *outMailbox = nextFrontColorBuffer->mailbox;
218     m_frontColorBuffer = nextFrontColorBuffer->textureId;
219     return true;
220 }
221 
mailboxReleased(const blink::WebExternalTextureMailbox & mailbox)222 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
223 {
224     for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
225          RefPtr<MailboxInfo> mailboxInfo = m_textureMailboxes[i];
226          if (!memcmp(mailboxInfo->mailbox.name, mailbox.name, sizeof(mailbox.name))) {
227              mailboxInfo->mailbox.syncPoint = mailbox.syncPoint;
228              m_recycledMailboxes.append(mailboxInfo.release());
229              return;
230          }
231      }
232      ASSERT_NOT_REACHED();
233 }
234 
recycledMailbox()235 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::recycledMailbox()
236 {
237     if (!m_context || m_recycledMailboxes.isEmpty())
238         return PassRefPtr<MailboxInfo>();
239 
240     RefPtr<MailboxInfo> mailboxInfo = m_recycledMailboxes.last().release();
241     m_recycledMailboxes.removeLast();
242 
243     if (mailboxInfo->mailbox.syncPoint) {
244         context()->waitSyncPoint(mailboxInfo->mailbox.syncPoint);
245         mailboxInfo->mailbox.syncPoint = 0;
246     }
247 
248     context()->bindTexture(GL_TEXTURE_2D, mailboxInfo->textureId);
249     context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->mailbox.name);
250 
251     if (mailboxInfo->size != m_size) {
252         m_context->texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
253         mailboxInfo->size = m_size;
254     }
255 
256     return mailboxInfo.release();
257 }
258 
createNewMailbox(unsigned textureId)259 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox(unsigned textureId)
260 {
261     RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo());
262     context()->genMailboxCHROMIUM(returnMailbox->mailbox.name);
263     returnMailbox->textureId = textureId;
264     returnMailbox->size = m_size;
265     m_textureMailboxes.append(returnMailbox);
266     return returnMailbox.release();
267 }
268 
initialize(const IntSize & size)269 void DrawingBuffer::initialize(const IntSize& size)
270 {
271     ASSERT(m_context);
272     m_attributes = m_context->getContextAttributes();
273 
274     if (m_attributes.alpha) {
275         m_internalColorFormat = GL_RGBA;
276         m_colorFormat = GL_RGBA;
277         m_internalRenderbufferFormat = Extensions3D::RGBA8_OES;
278     } else {
279         m_internalColorFormat = GL_RGB;
280         m_colorFormat = GL_RGB;
281         m_internalRenderbufferFormat = Extensions3D::RGB8_OES;
282     }
283 
284     m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
285 
286     m_fbo = m_context->createFramebuffer();
287 
288     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
289     m_colorBuffer = createColorTexture();
290     m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
291     createSecondaryBuffers();
292     reset(size);
293     m_lastColorBuffer = createNewMailbox(m_colorBuffer);
294 }
295 
frontColorBuffer() const296 unsigned DrawingBuffer::frontColorBuffer() const
297 {
298     return m_frontColorBuffer;
299 }
300 
copyToPlatformTexture(GraphicsContext3D & context,Platform3DObject texture,GC3Denum internalFormat,GC3Denum destType,GC3Dint level,bool premultiplyAlpha,bool flipY)301 bool DrawingBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DObject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool premultiplyAlpha, bool flipY)
302 {
303     if (!m_context || !m_context->makeContextCurrent())
304         return false;
305     if (m_contentsChanged) {
306         if (multisample()) {
307             commit();
308             if (!m_framebufferBinding)
309                 bind();
310             else
311                 restoreFramebufferBinding();
312         }
313         m_context->flush();
314     }
315     Platform3DObject sourceTexture = colorBuffer();
316 
317     if (!context.makeContextCurrent())
318         return false;
319     Extensions3D* extensions = context.extensions();
320     if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->supports("GL_CHROMIUM_flipy")
321         || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, level))
322         return false;
323 
324     bool unpackPremultiplyAlphaNeeded = false;
325     bool unpackUnpremultiplyAlphaNeeded = false;
326     if (m_attributes.alpha && m_attributes.premultipliedAlpha && !premultiplyAlpha)
327         unpackUnpremultiplyAlphaNeeded = true;
328     else if (m_attributes.alpha && !m_attributes.premultipliedAlpha && premultiplyAlpha)
329         unpackPremultiplyAlphaNeeded = true;
330 
331     context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpackUnpremultiplyAlphaNeeded);
332     context.pixelStorei(Extensions3D::UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackPremultiplyAlphaNeeded);
333     context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, flipY);
334     extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
335     context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false);
336     context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
337     context.pixelStorei(Extensions3D::UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
338     context.flush();
339 
340     return true;
341 }
342 
framebuffer() const343 Platform3DObject DrawingBuffer::framebuffer() const
344 {
345     return m_fbo;
346 }
347 
platformLayer()348 blink::WebLayer* DrawingBuffer::platformLayer()
349 {
350     if (!m_context)
351         return 0;
352 
353     if (!m_layer) {
354         m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
355 
356         m_layer->setOpaque(!m_attributes.alpha);
357         m_layer->setBlendBackgroundColor(m_attributes.alpha);
358         m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha);
359         GraphicsLayer::registerContentsLayer(m_layer->layer());
360     }
361 
362     return m_layer->layer();
363 }
364 
paintCompositedResultsToCanvas(ImageBuffer * imageBuffer)365 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer)
366 {
367     if (!m_context || !m_context->makeContextCurrent() || m_context->extensions()->getGraphicsResetStatusARB() != GL_NO_ERROR)
368         return;
369 
370     Extensions3D* extensions = m_context->extensions();
371 
372     if (!imageBuffer)
373         return;
374     Platform3DObject tex = imageBuffer->getBackingTexture();
375     if (tex) {
376         extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer,
377             tex, 0, GL_RGBA, GL_UNSIGNED_BYTE);
378         return;
379     }
380 
381     // Since the m_frontColorBuffer was produced and sent to the compositor, it cannot be bound to an fbo.
382     // We have to make a copy of it here and bind that copy instead.
383     // FIXME: That's not true any more, provided we don't change texture
384     // parameters.
385     unsigned sourceTexture = createColorTexture(m_size);
386     extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, m_frontColorBuffer, sourceTexture, 0, GL_RGBA, GL_UNSIGNED_BYTE);
387 
388     // Since we're using the same context as WebGL, we have to restore any state we change (in this case, just the framebuffer binding).
389     // FIXME: The WebGLRenderingContext tracks the current framebuffer binding, it would be slightly more efficient to use this value
390     // rather than querying it off of the context.
391     GC3Dint previousFramebuffer = 0;
392     m_context->getIntegerv(GL_FRAMEBUFFER_BINDING, &previousFramebuffer);
393 
394     Platform3DObject framebuffer = m_context->createFramebuffer();
395     m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
396     m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0);
397 
398     extensions->paintFramebufferToCanvas(framebuffer, size().width(), size().height(), !m_attributes.premultipliedAlpha, imageBuffer);
399     m_context->deleteFramebuffer(framebuffer);
400     m_context->deleteTexture(sourceTexture);
401 
402     m_context->bindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
403 }
404 
clearPlatformLayer()405 void DrawingBuffer::clearPlatformLayer()
406 {
407     if (m_layer)
408         m_layer->clearTexture();
409 
410     if (m_context)
411         m_context->flush();
412 }
413 
releaseResources()414 void DrawingBuffer::releaseResources()
415 {
416     if (m_context) {
417         m_context->makeContextCurrent();
418 
419         clearPlatformLayer();
420 
421         for (size_t i = 0; i < m_textureMailboxes.size(); i++)
422             m_context->deleteTexture(m_textureMailboxes[i]->textureId);
423 
424         if (m_multisampleColorBuffer)
425             m_context->deleteRenderbuffer(m_multisampleColorBuffer);
426 
427         if (m_depthStencilBuffer)
428             m_context->deleteRenderbuffer(m_depthStencilBuffer);
429 
430         if (m_depthBuffer)
431             m_context->deleteRenderbuffer(m_depthBuffer);
432 
433         if (m_stencilBuffer)
434             m_context->deleteRenderbuffer(m_stencilBuffer);
435 
436         if (m_multisampleFBO)
437             m_context->deleteFramebuffer(m_multisampleFBO);
438 
439         if (m_fbo)
440             m_context->deleteFramebuffer(m_fbo);
441 
442         m_context.clear();
443     }
444 
445     setSize(IntSize());
446 
447     m_colorBuffer = 0;
448     m_frontColorBuffer = 0;
449     m_multisampleColorBuffer = 0;
450     m_depthStencilBuffer = 0;
451     m_depthBuffer = 0;
452     m_stencilBuffer = 0;
453     m_multisampleFBO = 0;
454     m_fbo = 0;
455     m_contextEvictionManager.clear();
456 
457     m_lastColorBuffer.clear();
458     m_recycledMailboxes.clear();
459     m_textureMailboxes.clear();
460 
461     if (m_layer) {
462         GraphicsLayer::unregisterContentsLayer(m_layer->layer());
463         m_layer.clear();
464     }
465 }
466 
createColorTexture(const IntSize & size)467 unsigned DrawingBuffer::createColorTexture(const IntSize& size)
468 {
469     if (!m_context)
470         return 0;
471 
472     unsigned offscreenColorTexture = m_context->createTexture();
473     if (!offscreenColorTexture)
474         return 0;
475 
476     m_context->bindTexture(GL_TEXTURE_2D, offscreenColorTexture);
477     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
478     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
479     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
480     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
481     if (!size.isEmpty())
482         m_context->texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
483 
484     return offscreenColorTexture;
485 }
486 
createSecondaryBuffers()487 void DrawingBuffer::createSecondaryBuffers()
488 {
489     // create a multisample FBO
490     if (multisample()) {
491         m_multisampleFBO = m_context->createFramebuffer();
492         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
493         m_multisampleColorBuffer = m_context->createRenderbuffer();
494     }
495 }
496 
resizeFramebuffer(const IntSize & size)497 bool DrawingBuffer::resizeFramebuffer(const IntSize& size)
498 {
499     // resize regular FBO
500     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
501 
502     m_context->bindTexture(GL_TEXTURE_2D, m_colorBuffer);
503     m_context->texImage2DResourceSafe(GL_TEXTURE_2D, 0, m_internalColorFormat, size.width(), size.height(), 0, m_colorFormat, GL_UNSIGNED_BYTE);
504     if (m_lastColorBuffer)
505         m_lastColorBuffer->size = size;
506 
507     m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
508 
509     m_context->bindTexture(GL_TEXTURE_2D, 0);
510 
511     if (!multisample())
512         resizeDepthStencil(size, 0);
513     if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
514         return false;
515 
516     return true;
517 }
518 
resizeMultisampleFramebuffer(const IntSize & size)519 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size)
520 {
521     if (multisample()) {
522         int maxSampleCount = 0;
523 
524         m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
525         int sampleCount = std::min(4, maxSampleCount);
526 
527         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
528 
529         m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleColorBuffer);
530         m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, m_internalRenderbufferFormat, size.width(), size.height());
531 
532         if (m_context->getError() == GL_OUT_OF_MEMORY)
533             return false;
534 
535         m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleColorBuffer);
536         resizeDepthStencil(size, sampleCount);
537         if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
538             return false;
539     }
540 
541     return true;
542 }
543 
resizeDepthStencil(const IntSize & size,int sampleCount)544 void DrawingBuffer::resizeDepthStencil(const IntSize& size, int sampleCount)
545 {
546     if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtensionSupported) {
547         if (!m_depthStencilBuffer)
548             m_depthStencilBuffer = m_context->createRenderbuffer();
549         m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
550         if (multisample())
551             m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, size.width(), size.height());
552         else
553             m_context->renderbufferStorage(GL_RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, size.width(), size.height());
554         m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
555         m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
556     } else {
557         if (m_attributes.depth) {
558             if (!m_depthBuffer)
559                 m_depthBuffer = m_context->createRenderbuffer();
560             m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
561             if (multisample())
562                 m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height());
563             else
564                 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
565             m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
566         }
567         if (m_attributes.stencil) {
568             if (!m_stencilBuffer)
569                 m_stencilBuffer = m_context->createRenderbuffer();
570             m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
571             if (multisample())
572                 m_context->extensions()->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_STENCIL_INDEX8, size.width(), size.height());
573             else
574                 m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height());
575             m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
576         }
577     }
578     m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
579 }
580 
581 
582 
clearFramebuffers(GC3Dbitfield clearMask)583 void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask)
584 {
585     if (!m_context)
586         return;
587 
588     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
589 
590     m_context->clear(clearMask);
591 
592     // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too.
593     if (m_multisampleFBO) {
594         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
595         m_context->clear(GL_COLOR_BUFFER_BIT);
596         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
597     }
598 }
599 
setSize(const IntSize & size)600 void DrawingBuffer::setSize(const IntSize& size) {
601     if (m_size == size)
602         return;
603 
604     s_currentResourceUsePixels += pixelDelta(size);
605     m_size = size;
606 }
607 
pixelDelta(const IntSize & size)608 int DrawingBuffer::pixelDelta(const IntSize& size) {
609     return (max(0, size.width()) * max(0, size.height())) - (max(0, m_size.width()) * max(0, m_size.height()));
610 }
611 
adjustSize(const IntSize & size)612 IntSize DrawingBuffer::adjustSize(const IntSize& size) {
613     IntSize adjustedSize = size;
614 
615     // Clamp if the desired size is greater than the maximum texture size for the device.
616     if (adjustedSize.height() > m_maxTextureSize)
617         adjustedSize.setHeight(m_maxTextureSize);
618 
619     if (adjustedSize.width() > m_maxTextureSize)
620         adjustedSize.setWidth(m_maxTextureSize);
621 
622     // Try progressively smaller sizes until we find a size that fits or reach a scale limit.
623     int scaleAttempts = 0;
624     while ((s_currentResourceUsePixels + pixelDelta(adjustedSize)) > s_maximumResourceUsePixels) {
625         scaleAttempts++;
626         if (scaleAttempts > s_maxScaleAttempts)
627             return IntSize();
628 
629         adjustedSize.scale(s_resourceAdjustedRatio);
630 
631         if (adjustedSize.isEmpty())
632             return IntSize();
633     }
634 
635     return adjustedSize;
636 }
637 
adjustSizeWithContextEviction(const IntSize & size,bool & evictContext)638 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool& evictContext) {
639     IntSize adjustedSize = adjustSize(size);
640     if (!adjustedSize.isEmpty()) {
641         evictContext = false;
642         return adjustedSize; // Buffer fits without evicting a context.
643     }
644 
645     // Speculatively adjust the pixel budget to see if the buffer would fit should the oldest context be evicted.
646     IntSize oldestSize = m_contextEvictionManager->oldestContextSize();
647     int pixelDelta = oldestSize.width() * oldestSize.height();
648 
649     s_currentResourceUsePixels -= pixelDelta;
650     adjustedSize = adjustSize(size);
651     s_currentResourceUsePixels += pixelDelta;
652 
653     evictContext = !adjustedSize.isEmpty();
654     return adjustedSize;
655 }
656 
reset(const IntSize & newSize)657 void DrawingBuffer::reset(const IntSize& newSize)
658 {
659     if (!m_context)
660         return;
661 
662     IntSize adjustedSize;
663     bool evictContext = false;
664     bool isNewContext = m_size.isEmpty();
665     if (s_allowContextEvictionOnCreate && isNewContext)
666         adjustedSize = adjustSizeWithContextEviction(newSize, evictContext);
667     else
668         adjustedSize = adjustSize(newSize);
669 
670     if (adjustedSize.isEmpty())
671         return;
672 
673     if (evictContext)
674         m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL contexts have exceeded the maximum allowed backbuffer area. Oldest context will be lost.");
675 
676     if (adjustedSize != m_size) {
677         do {
678             // resize multisample FBO
679             if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffer(adjustedSize)) {
680                 adjustedSize.scale(s_resourceAdjustedRatio);
681                 continue;
682             }
683             break;
684         } while (!adjustedSize.isEmpty());
685 
686         setSize(adjustedSize);
687 
688         if (adjustedSize.isEmpty())
689             return;
690     }
691 
692     m_context->disable(GL_SCISSOR_TEST);
693     m_context->clearColor(0, 0, 0, 0);
694     m_context->colorMask(true, true, true, true);
695 
696     GC3Dbitfield clearMask = GL_COLOR_BUFFER_BIT;
697     if (m_attributes.depth) {
698         m_context->clearDepth(1.0f);
699         clearMask |= GL_DEPTH_BUFFER_BIT;
700         m_context->depthMask(true);
701     }
702     if (m_attributes.stencil) {
703         m_context->clearStencil(0);
704         clearMask |= GL_STENCIL_BUFFER_BIT;
705         m_context->stencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
706     }
707 
708     clearFramebuffers(clearMask);
709 }
710 
commit(long x,long y,long width,long height)711 void DrawingBuffer::commit(long x, long y, long width, long height)
712 {
713     if (!m_context)
714         return;
715 
716     if (width < 0)
717         width = m_size.width();
718     if (height < 0)
719         height = m_size.height();
720 
721     m_context->makeContextCurrent();
722 
723     if (m_multisampleFBO && !m_contentsChangeCommitted) {
724         m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
725         m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
726 
727         if (m_scissorEnabled)
728             m_context->disable(GL_SCISSOR_TEST);
729 
730         // Use NEAREST, because there is no scale performed during the blit.
731         m_context->extensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
732 
733         if (m_scissorEnabled)
734             m_context->enable(GL_SCISSOR_TEST);
735     }
736 
737     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
738     m_contentsChangeCommitted = true;
739 }
740 
restoreFramebufferBinding()741 void DrawingBuffer::restoreFramebufferBinding()
742 {
743     if (!m_context || !m_framebufferBinding)
744         return;
745 
746     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebufferBinding);
747 }
748 
multisample() const749 bool DrawingBuffer::multisample() const
750 {
751     return m_attributes.antialias && m_multisampleExtensionSupported;
752 }
753 
bind()754 void DrawingBuffer::bind()
755 {
756     if (!m_context)
757         return;
758 
759     m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
760 }
761 
762 } // namespace WebCore
763