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) || USE(3D_GRAPHICS) 34 35#include "DrawingBuffer.h" 36 37#include "Extensions3D.h" 38#include "GraphicsContext3D.h" 39 40namespace WebCore { 41 42#if PLATFORM(WIN) || USE(CAIRO) 43DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, const IntSize& size, bool multisampleExtensionSupported, bool packedDepthStencilExtensionSupported, PreserveDrawingBuffer preserveDrawingBuffer, AlphaRequirement alpha) 44 : m_preserveDrawingBuffer(preserveDrawingBuffer) 45 , m_alpha(alpha) 46 , m_scissorEnabled(false) 47 , m_texture2DBinding(0) 48 , m_framebufferBinding(0) 49 , m_activeTextureUnit(GraphicsContext3D::TEXTURE0) 50 , m_context(context) 51 , m_size(-1, -1) 52 , m_multisampleExtensionSupported(multisampleExtensionSupported) 53 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported) 54 , m_fbo(context->createFramebuffer()) 55 , m_colorBuffer(0) 56 , m_frontColorBuffer(0) 57 , m_separateFrontTexture(false) 58 , m_depthStencilBuffer(0) 59 , m_depthBuffer(0) 60 , m_stencilBuffer(0) 61 , m_multisampleFBO(0) 62 , m_multisampleColorBuffer(0) 63{ 64 ASSERT(m_fbo); 65 if (!m_fbo) { 66 clear(); 67 return; 68 } 69 70 // create a texture to render into 71 m_colorBuffer = context->createTexture(); 72 context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer); 73 context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); 74 context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); 75 context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); 76 context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); 77 context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); 78 79 createSecondaryBuffers(); 80 reset(size); 81} 82 83DrawingBuffer::~DrawingBuffer() 84{ 85 clear(); 86} 87#endif 88 89// Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize. 90// When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would 91// exceed the global cap will instead clear the buffer. 92static int s_maximumResourceUsePixels = 0; 93static int s_currentResourceUsePixels = 0; 94static const float s_resourceAdjustedRatio = 0.5; 95 96PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, AlphaRequirement alpha) 97{ 98 Extensions3D* extensions = context->getExtensions(); 99 bool multisampleSupported = extensions->maySupportMultisampling() 100 && extensions->supports("GL_ANGLE_framebuffer_blit") 101 && extensions->supports("GL_ANGLE_framebuffer_multisample") 102 && extensions->supports("GL_OES_rgb8_rgba8"); 103 if (multisampleSupported) { 104 extensions->ensureEnabled("GL_ANGLE_framebuffer_blit"); 105 extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample"); 106 extensions->ensureEnabled("GL_OES_rgb8_rgba8"); 107 } 108 bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil"); 109 if (packedDepthStencilSupported) 110 extensions->ensureEnabled("GL_OES_packed_depth_stencil"); 111 RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, alpha)); 112 return (drawingBuffer->m_context) ? drawingBuffer.release() : 0; 113} 114 115void DrawingBuffer::clear() 116{ 117 if (!m_context) 118 return; 119 120 m_context->makeContextCurrent(); 121 122 if (!m_size.isEmpty()) { 123 s_currentResourceUsePixels -= m_size.width() * m_size.height(); 124 m_size = IntSize(); 125 } 126 127 if (m_colorBuffer) { 128 m_context->deleteTexture(m_colorBuffer); 129 m_colorBuffer = 0; 130 } 131 132 if (m_frontColorBuffer) { 133 m_context->deleteTexture(m_frontColorBuffer); 134 m_frontColorBuffer = 0; 135 } 136 137 if (m_multisampleColorBuffer) { 138 m_context->deleteRenderbuffer(m_multisampleColorBuffer); 139 m_multisampleColorBuffer = 0; 140 } 141 142 if (m_depthStencilBuffer) { 143 m_context->deleteRenderbuffer(m_depthStencilBuffer); 144 m_depthStencilBuffer = 0; 145 } 146 147 if (m_depthBuffer) { 148 m_context->deleteRenderbuffer(m_depthBuffer); 149 m_depthBuffer = 0; 150 } 151 152 if (m_stencilBuffer) { 153 m_context->deleteRenderbuffer(m_stencilBuffer); 154 m_stencilBuffer = 0; 155 } 156 157 if (m_multisampleFBO) { 158 m_context->deleteFramebuffer(m_multisampleFBO); 159 m_multisampleFBO = 0; 160 } 161 162 if (m_fbo) { 163 m_context->deleteFramebuffer(m_fbo); 164 m_fbo = 0; 165 } 166} 167 168void DrawingBuffer::createSecondaryBuffers() 169{ 170 // create a multisample FBO 171 if (multisample()) { 172 m_multisampleFBO = m_context->createFramebuffer(); 173 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); 174 m_multisampleColorBuffer = m_context->createRenderbuffer(); 175 } 176} 177 178void DrawingBuffer::resizeDepthStencil(int sampleCount) 179{ 180 const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes(); 181 if (attributes.depth && attributes.stencil && m_packedDepthStencilExtensionSupported) { 182 if (!m_depthStencilBuffer) 183 m_depthStencilBuffer = m_context->createRenderbuffer(); 184 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer); 185 if (multisample()) 186 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height()); 187 else 188 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height()); 189 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer); 190 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer); 191 } else { 192 if (attributes.depth) { 193 if (!m_depthBuffer) 194 m_depthBuffer = m_context->createRenderbuffer(); 195 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer); 196 if (multisample()) 197 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height()); 198 else 199 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height()); 200 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer); 201 } 202 if (attributes.stencil) { 203 if (!m_stencilBuffer) 204 m_stencilBuffer = m_context->createRenderbuffer(); 205 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer); 206 if (multisample()) 207 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height()); 208 else 209 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height()); 210 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer); 211 } 212 } 213 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); 214} 215 216void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask) 217{ 218 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo); 219 220 m_context->clear(clearMask); 221 222 // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too. 223 if (m_multisampleFBO) { 224 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); 225 m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); 226 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); 227 } 228} 229 230// Only way to ensure that we're not getting a bad framebuffer on some AMD/OSX devices. 231// FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly. 232bool DrawingBuffer::checkBufferIntegrity() 233{ 234 if (!m_multisampleFBO) 235 return true; 236 237 if (m_scissorEnabled) 238 m_context->disable(GraphicsContext3D::SCISSOR_TEST); 239 240 m_context->colorMask(true, true, true, true); 241 242 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); 243 m_context->clearColor(1.0f, 0.0f, 1.0f, 1.0f); 244 m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); 245 246 commit(0, 0, 1, 1); 247 248 unsigned char pixel[4] = {0, 0, 0, 0}; 249 m_context->readPixels(0, 0, 1, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, &pixel); 250 251 if (m_scissorEnabled) 252 m_context->enable(GraphicsContext3D::SCISSOR_TEST); 253 254 return (pixel[0] == 0xFF && pixel[1] == 0x00 && pixel[2] == 0xFF && pixel[3] == 0xFF); 255} 256 257bool DrawingBuffer::reset(const IntSize& newSize) 258{ 259 if (!m_context) 260 return false; 261 262 m_context->makeContextCurrent(); 263 264 int maxTextureSize = 0; 265 m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize); 266 if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) { 267 clear(); 268 return false; 269 } 270 271 int pixelDelta = newSize.width() * newSize.height(); 272 int oldSize = 0; 273 if (!m_size.isEmpty()) { 274 oldSize = m_size.width() * m_size.height(); 275 pixelDelta -= oldSize; 276 } 277 278 IntSize adjustedSize = newSize; 279 if (s_maximumResourceUsePixels) { 280 while ((s_currentResourceUsePixels + pixelDelta) > s_maximumResourceUsePixels) { 281 adjustedSize.scale(s_resourceAdjustedRatio); 282 if (adjustedSize.isEmpty()) { 283 clear(); 284 return false; 285 } 286 pixelDelta = adjustedSize.width() * adjustedSize.height(); 287 pixelDelta -= oldSize; 288 } 289 } 290 291 const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes(); 292 293 if (adjustedSize != m_size) { 294 295 unsigned internalColorFormat, colorFormat, internalRenderbufferFormat; 296 if (attributes.alpha) { 297 internalColorFormat = GraphicsContext3D::RGBA; 298 colorFormat = GraphicsContext3D::RGBA; 299 internalRenderbufferFormat = Extensions3D::RGBA8_OES; 300 } else { 301 internalColorFormat = GraphicsContext3D::RGB; 302 colorFormat = GraphicsContext3D::RGB; 303 internalRenderbufferFormat = Extensions3D::RGB8_OES; 304 } 305 306 do { 307 m_size = adjustedSize; 308 // resize multisample FBO 309 if (multisample()) { 310 int maxSampleCount = 0; 311 312 m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount); 313 int sampleCount = std::min(4, maxSampleCount); 314 315 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); 316 317 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer); 318 m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height()); 319 320 if (m_context->getError() == GraphicsContext3D::OUT_OF_MEMORY) { 321 adjustedSize.scale(s_resourceAdjustedRatio); 322 continue; 323 } 324 325 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer); 326 resizeDepthStencil(sampleCount); 327 if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { 328 adjustedSize.scale(s_resourceAdjustedRatio); 329 continue; 330 } 331 } 332 333 // resize regular FBO 334 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); 335 336 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer); 337 m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0); 338 339 m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0); 340 341 // resize the front color buffer 342 if (m_separateFrontTexture) { 343 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_frontColorBuffer); 344 m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0); 345 } 346 347 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); 348 349 if (!multisample()) 350 resizeDepthStencil(0); 351 if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { 352 adjustedSize.scale(s_resourceAdjustedRatio); 353 continue; 354 } 355 356#if OS(DARWIN) 357 // FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly on OSX. 358 if (!checkBufferIntegrity()) { 359 adjustedSize.scale(s_resourceAdjustedRatio); 360 continue; 361 } 362#endif 363 364 break; 365 366 } while (!adjustedSize.isEmpty()); 367 368 pixelDelta = m_size.width() * m_size.height(); 369 pixelDelta -= oldSize; 370 s_currentResourceUsePixels += pixelDelta; 371 372 if (!newSize.isEmpty() && adjustedSize.isEmpty()) { 373 clear(); 374 return false; 375 } 376 } 377 378 m_context->disable(GraphicsContext3D::SCISSOR_TEST); 379 m_context->clearColor(0, 0, 0, 0); 380 m_context->colorMask(true, true, true, true); 381 382 GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT; 383 if (attributes.depth) { 384 m_context->clearDepth(1.0f); 385 clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT; 386 m_context->depthMask(true); 387 } 388 if (attributes.stencil) { 389 m_context->clearStencil(0); 390 clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT; 391 m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF); 392 } 393 394 clearFramebuffers(clearMask); 395 396 return true; 397} 398 399void DrawingBuffer::commit(long x, long y, long width, long height) 400{ 401 if (!m_context) 402 return; 403 404 if (width < 0) 405 width = m_size.width(); 406 if (height < 0) 407 height = m_size.height(); 408 409 m_context->makeContextCurrent(); 410 411 if (m_multisampleFBO) { 412 m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO); 413 m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo); 414 415 if (m_scissorEnabled) 416 m_context->disable(GraphicsContext3D::SCISSOR_TEST); 417 418 // Use NEAREST, because there is no scale performed during the blit. 419 m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST); 420 421 if (m_scissorEnabled) 422 m_context->enable(GraphicsContext3D::SCISSOR_TEST); 423 } 424 425 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); 426} 427 428void DrawingBuffer::restoreFramebufferBinding() 429{ 430 if (!m_context || !m_framebufferBinding) 431 return; 432 433 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding); 434} 435 436bool DrawingBuffer::multisample() const 437{ 438 return m_context && m_context->getContextAttributes().antialias && m_multisampleExtensionSupported; 439} 440 441void DrawingBuffer::discardResources() 442{ 443 m_colorBuffer = 0; 444 m_frontColorBuffer = 0; 445 m_multisampleColorBuffer = 0; 446 447 m_depthStencilBuffer = 0; 448 m_depthBuffer = 0; 449 450 m_stencilBuffer = 0; 451 452 m_multisampleFBO = 0; 453 m_fbo = 0; 454} 455 456void DrawingBuffer::bind() 457{ 458 if (!m_context) 459 return; 460 461 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo); 462} 463 464} // namespace WebCore 465 466#endif 467