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