c58c3c6a77a96692d96277447db4a31a0ba3e61a
[WebKit-https.git] / Source / WebCore / 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 #if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(WEBGL)
34
35 #include "DrawingBuffer.h"
36
37 #include "Extensions3D.h"
38 #include "GraphicsContext3D.h"
39 #include "ImageData.h"
40
41 namespace WebCore {
42
43 // Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
44 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
45 // exceed the global cap will instead clear the buffer.
46 #if PLATFORM(CHROMIUM) // Currently, this cap only exists for chromium.
47 static int s_maximumResourceUsePixels = 16 * 1024 * 1024;
48 #elif !PLATFORM(BLACKBERRY)
49 static int s_maximumResourceUsePixels = 0;
50 #endif
51 static int s_currentResourceUsePixels = 0;
52 static const float s_resourceAdjustedRatio = 0.5;
53
54 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, AlphaRequirement alpha)
55 {
56     Extensions3D* extensions = context->getExtensions();
57     bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit") && extensions->supports("GL_ANGLE_framebuffer_multisample") && extensions->supports("GL_OES_rgb8_rgba8");
58     if (multisampleSupported) {
59         extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
60         extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
61         extensions->ensureEnabled("GL_OES_rgb8_rgba8");
62     }
63     bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil");
64     if (packedDepthStencilSupported)
65         extensions->ensureEnabled("GL_OES_packed_depth_stencil");
66     RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, alpha));
67     return (drawingBuffer->m_context) ? drawingBuffer.release() : 0;
68 }
69
70 void DrawingBuffer::clear()
71 {
72     if (!m_context)
73         return;
74
75     m_context->makeContextCurrent();
76     if (!m_size.isEmpty()) {
77         s_currentResourceUsePixels -= m_size.width() * m_size.height();
78         m_size = IntSize();
79     }
80
81     if (m_colorBuffer) {
82         m_context->deleteTexture(m_colorBuffer);
83         m_colorBuffer = 0;
84     }
85
86     if (m_frontColorBuffer) {
87         m_context->deleteTexture(m_frontColorBuffer);
88         m_frontColorBuffer = 0;
89     }
90
91     if (m_multisampleColorBuffer) {
92         m_context->deleteRenderbuffer(m_multisampleColorBuffer);
93         m_multisampleColorBuffer = 0;
94     }
95
96     if (m_depthStencilBuffer) {
97         m_context->deleteRenderbuffer(m_depthStencilBuffer);
98         m_depthStencilBuffer = 0;
99     }
100
101     if (m_depthBuffer) {
102         m_context->deleteRenderbuffer(m_depthBuffer);
103         m_depthBuffer = 0;
104     }
105
106     if (m_stencilBuffer) {
107         m_context->deleteRenderbuffer(m_stencilBuffer);
108         m_stencilBuffer = 0;
109     }
110
111     if (m_multisampleFBO) {
112         m_context->deleteFramebuffer(m_multisampleFBO);
113         m_multisampleFBO = 0;
114     }
115
116     if (m_fbo) {
117         m_context->deleteFramebuffer(m_fbo);
118         m_fbo = 0;
119     }
120 }
121
122 void DrawingBuffer::createSecondaryBuffers()
123 {
124     // create a multisample FBO
125     if (multisample()) {
126         m_multisampleFBO = m_context->createFramebuffer();
127         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
128         m_multisampleColorBuffer = m_context->createRenderbuffer();
129     }
130 }
131
132 void DrawingBuffer::resizeDepthStencil(int sampleCount)
133 {
134     const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
135     if (attributes.depth && attributes.stencil && m_packedDepthStencilExtensionSupported) {
136         if (!m_depthStencilBuffer)
137             m_depthStencilBuffer = m_context->createRenderbuffer();
138         m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
139         if (multisample())
140             m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
141         else
142             m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
143         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
144         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
145     } else {
146         if (attributes.depth) {
147             if (!m_depthBuffer)
148                 m_depthBuffer = m_context->createRenderbuffer();
149             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
150             if (multisample())
151                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
152             else
153                 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
154             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
155         }
156         if (attributes.stencil) {
157             if (!m_stencilBuffer)
158                 m_stencilBuffer = m_context->createRenderbuffer();
159             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
160             if (multisample())
161                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
162             else 
163                 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
164             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
165         }
166     }
167     m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
168 }
169
170 void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask)
171 {
172     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
173
174     m_context->clear(clearMask);
175
176     // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too.
177     if (m_multisampleFBO) {
178         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
179         m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
180         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
181     }
182 }
183
184 bool DrawingBuffer::reset(const IntSize& newSize)
185 {
186     if (!m_context)
187         return false;
188
189     m_context->makeContextCurrent();
190
191     int maxTextureSize = 0;
192     m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
193     if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) {
194         clear();
195         return false;
196     }
197
198     int pixelDelta = newSize.width() * newSize.height();
199     int oldSize = 0;
200     if (!m_size.isEmpty()) {
201         oldSize = m_size.width() * m_size.height();
202         pixelDelta -= oldSize;
203     }
204
205     IntSize adjustedSize = newSize;
206     if (s_maximumResourceUsePixels) {
207         while ((s_currentResourceUsePixels + pixelDelta) > s_maximumResourceUsePixels) {
208             adjustedSize.scale(s_resourceAdjustedRatio);
209             if (adjustedSize.isEmpty()) {
210                 clear();
211                 return false;
212             }
213             pixelDelta = adjustedSize.width() * adjustedSize.height();
214             pixelDelta -= oldSize;
215         }
216      }
217
218     const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
219
220     if (adjustedSize != m_size) {
221
222         unsigned internalColorFormat, colorFormat, internalRenderbufferFormat;
223         if (attributes.alpha) {
224             internalColorFormat = GraphicsContext3D::RGBA;
225             colorFormat = GraphicsContext3D::RGBA;
226             internalRenderbufferFormat = Extensions3D::RGBA8_OES;
227         } else {
228             internalColorFormat = GraphicsContext3D::RGB;
229             colorFormat = GraphicsContext3D::RGB;
230             internalRenderbufferFormat = Extensions3D::RGB8_OES;
231         }
232
233
234         do {
235             m_size = adjustedSize;
236             // resize multisample FBO
237             if (multisample()) {
238                 int maxSampleCount = 0;
239
240                 m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
241                 int sampleCount = std::min(4, maxSampleCount);
242
243                 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
244
245                 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
246                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height());
247                 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
248                 resizeDepthStencil(sampleCount);
249                 if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
250                     adjustedSize.scale(s_resourceAdjustedRatio);
251                     continue;
252                 }
253             }
254
255             // resize regular FBO
256             m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
257
258             m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer);
259             m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0);
260
261             m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
262
263             // resize the front color buffer
264             if (m_separateFrontTexture) {
265                 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_frontColorBuffer);
266                 m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0);
267             }
268
269             m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
270
271             if (!multisample())
272                 resizeDepthStencil(0);
273             if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) == GraphicsContext3D::FRAMEBUFFER_COMPLETE)
274                 break;
275             adjustedSize.scale(s_resourceAdjustedRatio);
276
277         } while (!adjustedSize.isEmpty());
278
279         pixelDelta = m_size.width() * m_size.height();
280         pixelDelta -= oldSize;
281         s_currentResourceUsePixels += pixelDelta;
282
283         if (!newSize.isEmpty() && adjustedSize.isEmpty()) {
284             clear();
285             return false;
286         }
287     }
288
289     m_context->disable(GraphicsContext3D::SCISSOR_TEST);
290     m_context->clearColor(0, 0, 0, 0);
291     m_context->colorMask(true, true, true, true);
292
293     GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT;
294     if (attributes.depth) {
295         m_context->clearDepth(1.0f);
296         clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
297         m_context->depthMask(true);
298     }
299     if (attributes.stencil) {
300         m_context->clearStencil(0);
301         clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
302         m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF);
303     }
304
305     clearFramebuffers(clearMask);
306
307     return true;
308 }
309
310 void DrawingBuffer::commit(long x, long y, long width, long height)
311 {
312     if (!m_context)
313         return;
314
315     if (width < 0)
316         width = m_size.width();
317     if (height < 0)
318         height = m_size.height();
319
320     m_context->makeContextCurrent();
321
322     if (m_multisampleFBO) {
323         m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
324         m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
325
326         if (m_scissorEnabled)
327             m_context->disable(GraphicsContext3D::SCISSOR_TEST);
328
329         // Use NEAREST, because there is no scale performed during the blit.
330         m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST);
331
332         if (m_scissorEnabled)
333             m_context->enable(GraphicsContext3D::SCISSOR_TEST);
334     }
335
336     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
337 }
338
339 void DrawingBuffer::restoreFramebufferBinding()
340 {
341     if (!m_context || !m_framebufferBinding)
342         return;
343
344     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding);
345 }
346
347 bool DrawingBuffer::multisample() const
348 {
349     return m_context && m_context->getContextAttributes().antialias && m_multisampleExtensionSupported;
350 }
351
352 PassRefPtr<ImageData> DrawingBuffer::paintRenderingResultsToImageData()
353 {
354     return m_context->paintRenderingResultsToImageData(this);
355 }
356
357 void DrawingBuffer::discardResources()
358 {
359     m_colorBuffer = 0;
360     m_frontColorBuffer = 0;
361     m_multisampleColorBuffer = 0;
362
363     m_depthStencilBuffer = 0;
364     m_depthBuffer = 0;
365
366     m_stencilBuffer = 0;
367
368     m_multisampleFBO = 0;
369     m_fbo = 0;
370 }
371
372 void DrawingBuffer::bind()
373 {
374     if (!m_context)
375         return;
376
377     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
378 }
379
380 } // namespace WebCore
381
382 #endif