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 
39 namespace WebCore {
40 
create(GraphicsContext3D * context,const IntSize & size)41 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size)
42 {
43     Extensions3D* extensions = context->getExtensions();
44     bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit") && extensions->supports("GL_ANGLE_framebuffer_multisample") && extensions->supports("GL_OES_rgb8_rgba8");
45     if (multisampleSupported) {
46         extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
47         extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
48         extensions->ensureEnabled("GL_OES_rgb8_rgba8");
49     }
50     bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil");
51     if (packedDepthStencilSupported)
52         extensions->ensureEnabled("GL_OES_packed_depth_stencil");
53     RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported));
54     return (drawingBuffer->m_context) ? drawingBuffer.release() : 0;
55 }
56 
clear()57 void DrawingBuffer::clear()
58 {
59     if (!m_context)
60         return;
61 
62     m_context->makeContextCurrent();
63     m_context->deleteTexture(m_colorBuffer);
64     m_colorBuffer = 0;
65 
66     if (m_multisampleColorBuffer) {
67         m_context->deleteRenderbuffer(m_multisampleColorBuffer);
68         m_multisampleColorBuffer = 0;
69     }
70 
71     if (m_depthStencilBuffer) {
72         m_context->deleteRenderbuffer(m_depthStencilBuffer);
73         m_depthStencilBuffer = 0;
74     }
75 
76     if (m_depthBuffer) {
77         m_context->deleteRenderbuffer(m_depthBuffer);
78         m_depthBuffer = 0;
79     }
80 
81     if (m_stencilBuffer) {
82         m_context->deleteRenderbuffer(m_stencilBuffer);
83         m_stencilBuffer = 0;
84     }
85 
86     if (m_multisampleFBO) {
87         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
88         m_context->deleteFramebuffer(m_multisampleFBO);
89         m_multisampleFBO = 0;
90     }
91 
92     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
93     m_context->deleteFramebuffer(m_fbo);
94     m_fbo = 0;
95 }
96 
createSecondaryBuffers()97 void DrawingBuffer::createSecondaryBuffers()
98 {
99     // create a multisample FBO
100     if (multisample()) {
101         m_multisampleFBO = m_context->createFramebuffer();
102         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
103         m_multisampleColorBuffer = m_context->createRenderbuffer();
104     }
105 }
106 
resizeDepthStencil(int sampleCount)107 void DrawingBuffer::resizeDepthStencil(int sampleCount)
108 {
109     const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
110     if (attributes.depth && attributes.stencil && m_packedDepthStencilExtensionSupported) {
111         if (!m_depthStencilBuffer)
112             m_depthStencilBuffer = m_context->createRenderbuffer();
113         m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
114         if (multisample())
115             m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
116         else
117             m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
118         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
119         m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
120     } else {
121         if (attributes.depth) {
122             if (!m_depthBuffer)
123                 m_depthBuffer = m_context->createRenderbuffer();
124             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
125             if (multisample())
126                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
127             else
128                 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
129             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
130         }
131         if (attributes.stencil) {
132             if (!m_stencilBuffer)
133                 m_stencilBuffer = m_context->createRenderbuffer();
134             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
135             if (multisample())
136                 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
137             else
138                 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
139             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
140         }
141     }
142     m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
143 }
144 
clearFramebuffer()145 void DrawingBuffer::clearFramebuffer()
146 {
147     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
148     const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
149     float clearDepth = 0;
150     int clearStencil = 0;
151     unsigned char depthMask = false;
152     unsigned int stencilMask = 0xffffffff;
153     unsigned char isScissorEnabled = false;
154     unsigned long clearMask = GraphicsContext3D::COLOR_BUFFER_BIT;
155     if (attributes.depth) {
156         m_context->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &clearDepth);
157         m_context->clearDepth(1);
158         m_context->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask);
159         m_context->depthMask(true);
160         clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
161     }
162     if (attributes.stencil) {
163         m_context->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &clearStencil);
164         m_context->clearStencil(0);
165         m_context->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<int*>(&stencilMask));
166         m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xffffffff);
167         clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
168     }
169     isScissorEnabled = m_context->isEnabled(GraphicsContext3D::SCISSOR_TEST);
170     m_context->disable(GraphicsContext3D::SCISSOR_TEST);
171 
172     float clearColor[4];
173     m_context->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, clearColor);
174     m_context->clearColor(0, 0, 0, 0);
175     m_context->clear(clearMask);
176     m_context->clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
177 
178     if (attributes.depth) {
179         m_context->clearDepth(clearDepth);
180         m_context->depthMask(depthMask);
181     }
182     if (attributes.stencil) {
183         m_context->clearStencil(clearStencil);
184         m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, stencilMask);
185     }
186     if (isScissorEnabled)
187         m_context->enable(GraphicsContext3D::SCISSOR_TEST);
188     else
189         m_context->disable(GraphicsContext3D::SCISSOR_TEST);
190 }
191 
reset(const IntSize & newSize)192 bool DrawingBuffer::reset(const IntSize& newSize)
193 {
194     if (!m_context)
195         return false;
196 
197     m_context->makeContextCurrent();
198 
199     int maxTextureSize = 0;
200     m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
201     if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) {
202       clear();
203       return false;
204     }
205 
206     const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
207 
208     if (newSize != m_size) {
209         m_size = newSize;
210 
211         unsigned long internalColorFormat, colorFormat, internalRenderbufferFormat;
212         if (attributes.alpha) {
213             internalColorFormat = GraphicsContext3D::RGBA;
214             colorFormat = GraphicsContext3D::RGBA;
215             internalRenderbufferFormat = Extensions3D::RGBA8_OES;
216         } else {
217             internalColorFormat = GraphicsContext3D::RGB;
218             colorFormat = GraphicsContext3D::RGB;
219             internalRenderbufferFormat = Extensions3D::RGB8_OES;
220         }
221 
222 
223         // resize multisample FBO
224         if (multisample()) {
225             int maxSampleCount = 0;
226 
227             m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
228             int sampleCount = std::min(8, maxSampleCount);
229 
230             m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
231 
232             m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
233             m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height());
234             m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
235             resizeDepthStencil(sampleCount);
236             if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
237                 // Cleanup
238                 clear();
239                 return false;
240             }
241         }
242 
243         // resize regular FBO
244         m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
245 
246         m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer);
247 
248         m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE);
249 
250         m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
251         m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
252 
253         if (!multisample())
254             resizeDepthStencil(0);
255         if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
256             // Cleanup
257             clear();
258             return false;
259         }
260     }
261 
262     clearFramebuffer();
263 
264     didReset();
265 
266     return true;
267 }
268 
commit(long x,long y,long width,long height)269 void DrawingBuffer::commit(long x, long y, long width, long height)
270 {
271     if (!m_context)
272         return;
273 
274     if (width < 0)
275         width = m_size.width();
276     if (height < 0)
277         height = m_size.height();
278 
279     m_context->makeContextCurrent();
280 
281     if (m_multisampleFBO) {
282         m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
283         m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
284         m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::LINEAR);
285     }
286 
287     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
288 }
289 
bind()290 void DrawingBuffer::bind()
291 {
292     if (!m_context)
293         return;
294 
295     m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
296     m_context->viewport(0, 0, m_size.width(), m_size.height());
297 }
298 
299 } // namespace WebCore
300 
301 #endif
302