2 * Copyright (c) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(WEBGL)
35 #include "DrawingBuffer.h"
37 #include "Extensions3D.h"
38 #include "GraphicsContext3D.h"
39 #include "ImageData.h"
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;
51 static int s_currentResourceUsePixels = 0;
52 static const float s_resourceAdjustedRatio = 0.5;
54 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, AlphaRequirement alpha)
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");
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;
70 void DrawingBuffer::clear()
75 m_context->makeContextCurrent();
76 if (!m_size.isEmpty()) {
77 s_currentResourceUsePixels -= m_size.width() * m_size.height();
82 m_context->deleteTexture(m_colorBuffer);
86 if (m_frontColorBuffer) {
87 m_context->deleteTexture(m_frontColorBuffer);
88 m_frontColorBuffer = 0;
91 if (m_multisampleColorBuffer) {
92 m_context->deleteRenderbuffer(m_multisampleColorBuffer);
93 m_multisampleColorBuffer = 0;
96 if (m_depthStencilBuffer) {
97 m_context->deleteRenderbuffer(m_depthStencilBuffer);
98 m_depthStencilBuffer = 0;
102 m_context->deleteRenderbuffer(m_depthBuffer);
106 if (m_stencilBuffer) {
107 m_context->deleteRenderbuffer(m_stencilBuffer);
111 if (m_multisampleFBO) {
112 m_context->deleteFramebuffer(m_multisampleFBO);
113 m_multisampleFBO = 0;
117 m_context->deleteFramebuffer(m_fbo);
122 void DrawingBuffer::createSecondaryBuffers()
124 // create a multisample FBO
126 m_multisampleFBO = m_context->createFramebuffer();
127 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
128 m_multisampleColorBuffer = m_context->createRenderbuffer();
132 void DrawingBuffer::resizeDepthStencil(int sampleCount)
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);
140 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
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);
146 if (attributes.depth) {
148 m_depthBuffer = m_context->createRenderbuffer();
149 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
151 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
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);
156 if (attributes.stencil) {
157 if (!m_stencilBuffer)
158 m_stencilBuffer = m_context->createRenderbuffer();
159 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
161 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
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);
167 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
170 void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask)
172 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
174 m_context->clear(clearMask);
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);
184 bool DrawingBuffer::reset(const IntSize& newSize)
189 m_context->makeContextCurrent();
191 int maxTextureSize = 0;
192 m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
193 if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) {
198 int pixelDelta = newSize.width() * newSize.height();
200 if (!m_size.isEmpty()) {
201 oldSize = m_size.width() * m_size.height();
202 pixelDelta -= oldSize;
205 IntSize adjustedSize = newSize;
206 if (s_maximumResourceUsePixels) {
207 while ((s_currentResourceUsePixels + pixelDelta) > s_maximumResourceUsePixels) {
208 adjustedSize.scale(s_resourceAdjustedRatio);
209 if (adjustedSize.isEmpty()) {
213 pixelDelta = adjustedSize.width() * adjustedSize.height();
214 pixelDelta -= oldSize;
218 const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
220 if (adjustedSize != m_size) {
222 unsigned internalColorFormat, colorFormat, internalRenderbufferFormat;
223 if (attributes.alpha) {
224 internalColorFormat = GraphicsContext3D::RGBA;
225 colorFormat = GraphicsContext3D::RGBA;
226 internalRenderbufferFormat = Extensions3D::RGBA8_OES;
228 internalColorFormat = GraphicsContext3D::RGB;
229 colorFormat = GraphicsContext3D::RGB;
230 internalRenderbufferFormat = Extensions3D::RGB8_OES;
235 m_size = adjustedSize;
236 // resize multisample FBO
238 int maxSampleCount = 0;
240 m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
241 int sampleCount = std::min(4, maxSampleCount);
243 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
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);
255 // resize regular FBO
256 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
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);
261 m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
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);
269 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
272 resizeDepthStencil(0);
273 if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) == GraphicsContext3D::FRAMEBUFFER_COMPLETE)
275 adjustedSize.scale(s_resourceAdjustedRatio);
277 } while (!adjustedSize.isEmpty());
279 pixelDelta = m_size.width() * m_size.height();
280 pixelDelta -= oldSize;
281 s_currentResourceUsePixels += pixelDelta;
283 if (!newSize.isEmpty() && adjustedSize.isEmpty()) {
289 m_context->disable(GraphicsContext3D::SCISSOR_TEST);
290 m_context->clearColor(0, 0, 0, 0);
291 m_context->colorMask(true, true, true, true);
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);
299 if (attributes.stencil) {
300 m_context->clearStencil(0);
301 clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
302 m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF);
305 clearFramebuffers(clearMask);
310 void DrawingBuffer::commit(long x, long y, long width, long height)
316 width = m_size.width();
318 height = m_size.height();
320 m_context->makeContextCurrent();
322 if (m_multisampleFBO) {
323 m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
324 m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
326 if (m_scissorEnabled)
327 m_context->disable(GraphicsContext3D::SCISSOR_TEST);
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);
332 if (m_scissorEnabled)
333 m_context->enable(GraphicsContext3D::SCISSOR_TEST);
336 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
339 bool DrawingBuffer::multisample() const
341 return m_context && m_context->getContextAttributes().antialias && m_multisampleExtensionSupported;
344 PassRefPtr<ImageData> DrawingBuffer::paintRenderingResultsToImageData()
346 return m_context->paintRenderingResultsToImageData(this);
349 void DrawingBuffer::discardResources()
352 m_frontColorBuffer = 0;
353 m_multisampleColorBuffer = 0;
355 m_depthStencilBuffer = 0;
360 m_multisampleFBO = 0;
364 void DrawingBuffer::bind()
369 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
372 } // namespace WebCore