| OLD | NEW |
| (Empty) |
| 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 "core/platform/graphics/gpu/DrawingBuffer.h" | |
| 34 | |
| 35 #include <algorithm> | |
| 36 #include "core/platform/graphics/Extensions3D.h" | |
| 37 #include "core/platform/graphics/GraphicsLayer.h" | |
| 38 #include "platform/TraceEvent.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 crea
tion and resize. | |
| 50 // When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() ca
lls 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: | |
| 61 ScopedTextureUnit0BindingRestorer(GraphicsContext3D* context, GC3Denum activ
eTextureUnit, Platform3DObject textureUnitZeroId) | |
| 62 : m_context(context) | |
| 63 , m_oldActiveTextureUnit(activeTextureUnit) | |
| 64 , m_oldTextureUnitZeroId(textureUnitZeroId) | |
| 65 { | |
| 66 m_context->activeTexture(GraphicsContext3D::TEXTURE0); | |
| 67 } | |
| 68 ~ScopedTextureUnit0BindingRestorer() | |
| 69 { | |
| 70 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_oldTextureUnitZe
roId); | |
| 71 m_context->activeTexture(m_oldActiveTextureUnit); | |
| 72 } | |
| 73 | |
| 74 private: | |
| 75 GraphicsContext3D* m_context; | |
| 76 GC3Denum m_oldActiveTextureUnit; | |
| 77 Platform3DObject m_oldTextureUnitZeroId; | |
| 78 }; | |
| 79 | |
| 80 PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, cons
t IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManag
er> 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, si
ze, multisampleSupported, packedDepthStencilSupported, preserve, contextEviction
Manager)); | |
| 96 return drawingBuffer.release(); | |
| 97 } | |
| 98 | |
| 99 DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, | |
| 100 const IntSize& size, | |
| 101 bool multisampleExtensionSupported, | |
| 102 bool packedDepthStencilExtensionSupported, | |
| 103 PreserveDrawingBuffer preserve, | |
| 104 PassRefPtr<ContextEvictionManager> contextEvictionM
anager) | |
| 105 : m_preserveDrawingBuffer(preserve) | |
| 106 , m_scissorEnabled(false) | |
| 107 , m_texture2DBinding(0) | |
| 108 , m_framebufferBinding(0) | |
| 109 , m_activeTextureUnit(GraphicsContext3D::TEXTURE0) | |
| 110 , m_context(context) | |
| 111 , m_size(-1, -1) | |
| 112 , m_multisampleExtensionSupported(multisampleExtensionSupported) | |
| 113 , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupporte
d) | |
| 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 | |
| 135 DrawingBuffer::~DrawingBuffer() | |
| 136 { | |
| 137 releaseResources(); | |
| 138 } | |
| 139 | |
| 140 void DrawingBuffer::markContentsChanged() | |
| 141 { | |
| 142 m_contentsChanged = true; | |
| 143 m_contentsChangeCommitted = false; | |
| 144 } | |
| 145 | |
| 146 blink::WebGraphicsContext3D* DrawingBuffer::context() | |
| 147 { | |
| 148 if (!m_context) | |
| 149 return 0; | |
| 150 return m_context->webContext(); | |
| 151 } | |
| 152 | |
| 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_lastColo
rBuffer 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.premultiplied
Alpha; | |
| 169 GraphicsContext3D::AlphaOp op = needPremultiply ? GraphicsContext3D::Alp
haDoPremultiply : 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_activeTextureU
nit, 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 f
ramebuffer binding in the DrawingBuffer and restore | |
| 197 // it after attaching the new back buffer here. | |
| 198 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); | |
| 199 m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, Graphics
Context3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0); | |
| 200 } else { | |
| 201 Extensions3D* extensions = m_context->extensions(); | |
| 202 extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, m_colorBu
ffer, nextFrontColorBuffer->textureId, 0, GraphicsContext3D::RGBA, GraphicsConte
xt3D::UNSIGNED_BYTE); | |
| 203 } | |
| 204 | |
| 205 if (multisample() && !m_framebufferBinding) | |
| 206 bind(); | |
| 207 else | |
| 208 restoreFramebufferBinding(); | |
| 209 | |
| 210 m_contentsChanged = false; | |
| 211 | |
| 212 context()->bindTexture(GraphicsContext3D::TEXTURE_2D, nextFrontColorBuffer->
textureId); | |
| 213 context()->produceTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, nextFrontCo
lorBuffer->mailbox.name); | |
| 214 context()->flush(); | |
| 215 m_context->markLayerComposited(); | |
| 216 | |
| 217 *outMailbox = nextFrontColorBuffer->mailbox; | |
| 218 m_frontColorBuffer = nextFrontColorBuffer->textureId; | |
| 219 return true; | |
| 220 } | |
| 221 | |
| 222 void DrawingBuffer::mailboxReleased(const blink::WebExternalTextureMailbox& mail
box) | |
| 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.nam
e))) { | |
| 227 mailboxInfo->mailbox.syncPoint = mailbox.syncPoint; | |
| 228 m_recycledMailboxes.append(mailboxInfo.release()); | |
| 229 return; | |
| 230 } | |
| 231 } | |
| 232 ASSERT_NOT_REACHED(); | |
| 233 } | |
| 234 | |
| 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(GraphicsContext3D::TEXTURE_2D, mailboxInfo->textureId
); | |
| 249 context()->consumeTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, mailboxInfo
->mailbox.name); | |
| 250 | |
| 251 if (mailboxInfo->size != m_size) { | |
| 252 m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, m_in
ternalColorFormat, m_size.width(), m_size.height(), 0, m_colorFormat, GraphicsCo
ntext3D::UNSIGNED_BYTE); | |
| 253 mailboxInfo->size = m_size; | |
| 254 } | |
| 255 | |
| 256 return mailboxInfo.release(); | |
| 257 } | |
| 258 | |
| 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 | |
| 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 = GraphicsContext3D::RGBA; | |
| 276 m_colorFormat = GraphicsContext3D::RGBA; | |
| 277 m_internalRenderbufferFormat = Extensions3D::RGBA8_OES; | |
| 278 } else { | |
| 279 m_internalColorFormat = GraphicsContext3D::RGB; | |
| 280 m_colorFormat = GraphicsContext3D::RGB; | |
| 281 m_internalRenderbufferFormat = Extensions3D::RGB8_OES; | |
| 282 } | |
| 283 | |
| 284 m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSiz
e); | |
| 285 | |
| 286 m_fbo = m_context->createFramebuffer(); | |
| 287 | |
| 288 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); | |
| 289 m_colorBuffer = createColorTexture(); | |
| 290 m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsCont
ext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0); | |
| 291 createSecondaryBuffers(); | |
| 292 reset(size); | |
| 293 m_lastColorBuffer = createNewMailbox(m_colorBuffer); | |
| 294 } | |
| 295 | |
| 296 unsigned DrawingBuffer::frontColorBuffer() const | |
| 297 { | |
| 298 return m_frontColorBuffer; | |
| 299 } | |
| 300 | |
| 301 bool DrawingBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3D
Object 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->suppor
ts("GL_CHROMIUM_flipy") | |
| 321 || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, leve
l)) | |
| 322 return false; | |
| 323 | |
| 324 bool unpackPremultiplyAlphaNeeded = false; | |
| 325 bool unpackUnpremultiplyAlphaNeeded = false; | |
| 326 if (m_attributes.alpha && m_attributes.premultipliedAlpha && !premultiplyAlp
ha) | |
| 327 unpackUnpremultiplyAlphaNeeded = true; | |
| 328 else if (m_attributes.alpha && !m_attributes.premultipliedAlpha && premultip
lyAlpha) | |
| 329 unpackPremultiplyAlphaNeeded = true; | |
| 330 | |
| 331 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, unpac
kUnpremultiplyAlphaNeeded); | |
| 332 context.pixelStorei(Extensions3D::UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, unpackP
remultiplyAlphaNeeded); | |
| 333 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, flipY); | |
| 334 extensions->copyTextureCHROMIUM(GraphicsContext3D::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 | |
| 339 return true; | |
| 340 } | |
| 341 | |
| 342 Platform3DObject DrawingBuffer::framebuffer() const | |
| 343 { | |
| 344 return m_fbo; | |
| 345 } | |
| 346 | |
| 347 blink::WebLayer* DrawingBuffer::platformLayer() | |
| 348 { | |
| 349 if (!m_context) | |
| 350 return 0; | |
| 351 | |
| 352 if (!m_layer) { | |
| 353 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->crea
teExternalTextureLayer(this)); | |
| 354 | |
| 355 m_layer->setOpaque(!m_attributes.alpha); | |
| 356 m_layer->setBlendBackgroundColor(m_attributes.alpha); | |
| 357 m_layer->setPremultipliedAlpha(m_attributes.premultipliedAlpha); | |
| 358 GraphicsLayer::registerContentsLayer(m_layer->layer()); | |
| 359 } | |
| 360 | |
| 361 return m_layer->layer(); | |
| 362 } | |
| 363 | |
| 364 void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer* imageBuffer) | |
| 365 { | |
| 366 if (!m_context || !m_context->makeContextCurrent() || m_context->extensions(
)->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR) | |
| 367 return; | |
| 368 | |
| 369 Extensions3D* extensions = m_context->extensions(); | |
| 370 | |
| 371 if (!imageBuffer) | |
| 372 return; | |
| 373 Platform3DObject tex = imageBuffer->getBackingTexture(); | |
| 374 if (tex) { | |
| 375 extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, m_frontCo
lorBuffer, | |
| 376 tex, 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE); | |
| 377 return; | |
| 378 } | |
| 379 | |
| 380 // Since the m_frontColorBuffer was produced and sent to the compositor, it
cannot be bound to an fbo. | |
| 381 // We have to make a copy of it here and bind that copy instead. | |
| 382 // FIXME: That's not true any more, provided we don't change texture | |
| 383 // parameters. | |
| 384 unsigned sourceTexture = createColorTexture(m_size); | |
| 385 extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, m_frontColorB
uffer, sourceTexture, 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BY
TE); | |
| 386 | |
| 387 // Since we're using the same context as WebGL, we have to restore any state
we change (in this case, just the framebuffer binding). | |
| 388 // FIXME: The WebGLRenderingContext tracks the current framebuffer binding,
it would be slightly more efficient to use this value | |
| 389 // rather than querying it off of the context. | |
| 390 GC3Dint previousFramebuffer = 0; | |
| 391 m_context->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &previousFram
ebuffer); | |
| 392 | |
| 393 Platform3DObject framebuffer = m_context->createFramebuffer(); | |
| 394 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, framebuffer); | |
| 395 m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsCont
ext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, sourceTexture, 0); | |
| 396 | |
| 397 extensions->paintFramebufferToCanvas(framebuffer, size().width(), size().hei
ght(), !m_attributes.premultipliedAlpha, imageBuffer); | |
| 398 m_context->deleteFramebuffer(framebuffer); | |
| 399 m_context->deleteTexture(sourceTexture); | |
| 400 | |
| 401 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, previousFramebuff
er); | |
| 402 } | |
| 403 | |
| 404 void DrawingBuffer::clearPlatformLayer() | |
| 405 { | |
| 406 if (m_layer) | |
| 407 m_layer->clearTexture(); | |
| 408 | |
| 409 if (m_context) | |
| 410 m_context->flush(); | |
| 411 } | |
| 412 | |
| 413 void DrawingBuffer::releaseResources() | |
| 414 { | |
| 415 if (m_context) { | |
| 416 m_context->makeContextCurrent(); | |
| 417 | |
| 418 clearPlatformLayer(); | |
| 419 | |
| 420 for (size_t i = 0; i < m_textureMailboxes.size(); i++) | |
| 421 m_context->deleteTexture(m_textureMailboxes[i]->textureId); | |
| 422 | |
| 423 if (m_multisampleColorBuffer) | |
| 424 m_context->deleteRenderbuffer(m_multisampleColorBuffer); | |
| 425 | |
| 426 if (m_depthStencilBuffer) | |
| 427 m_context->deleteRenderbuffer(m_depthStencilBuffer); | |
| 428 | |
| 429 if (m_depthBuffer) | |
| 430 m_context->deleteRenderbuffer(m_depthBuffer); | |
| 431 | |
| 432 if (m_stencilBuffer) | |
| 433 m_context->deleteRenderbuffer(m_stencilBuffer); | |
| 434 | |
| 435 if (m_multisampleFBO) | |
| 436 m_context->deleteFramebuffer(m_multisampleFBO); | |
| 437 | |
| 438 if (m_fbo) | |
| 439 m_context->deleteFramebuffer(m_fbo); | |
| 440 | |
| 441 m_context.clear(); | |
| 442 } | |
| 443 | |
| 444 setSize(IntSize()); | |
| 445 | |
| 446 m_colorBuffer = 0; | |
| 447 m_frontColorBuffer = 0; | |
| 448 m_multisampleColorBuffer = 0; | |
| 449 m_depthStencilBuffer = 0; | |
| 450 m_depthBuffer = 0; | |
| 451 m_stencilBuffer = 0; | |
| 452 m_multisampleFBO = 0; | |
| 453 m_fbo = 0; | |
| 454 m_contextEvictionManager.clear(); | |
| 455 | |
| 456 m_lastColorBuffer.clear(); | |
| 457 m_recycledMailboxes.clear(); | |
| 458 m_textureMailboxes.clear(); | |
| 459 | |
| 460 if (m_layer) { | |
| 461 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); | |
| 462 m_layer.clear(); | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 unsigned DrawingBuffer::createColorTexture(const IntSize& size) | |
| 467 { | |
| 468 if (!m_context) | |
| 469 return 0; | |
| 470 | |
| 471 unsigned offscreenColorTexture = m_context->createTexture(); | |
| 472 if (!offscreenColorTexture) | |
| 473 return 0; | |
| 474 | |
| 475 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, offscreenColorTexture)
; | |
| 476 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); | |
| 477 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); | |
| 478 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); | |
| 479 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); | |
| 480 if (!size.isEmpty()) | |
| 481 m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, m_in
ternalColorFormat, size.width(), size.height(), 0, m_colorFormat, GraphicsContex
t3D::UNSIGNED_BYTE); | |
| 482 | |
| 483 return offscreenColorTexture; | |
| 484 } | |
| 485 | |
| 486 void DrawingBuffer::createSecondaryBuffers() | |
| 487 { | |
| 488 // create a multisample FBO | |
| 489 if (multisample()) { | |
| 490 m_multisampleFBO = m_context->createFramebuffer(); | |
| 491 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisample
FBO); | |
| 492 m_multisampleColorBuffer = m_context->createRenderbuffer(); | |
| 493 } | |
| 494 } | |
| 495 | |
| 496 bool DrawingBuffer::resizeFramebuffer(const IntSize& size) | |
| 497 { | |
| 498 // resize regular FBO | |
| 499 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); | |
| 500 | |
| 501 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer); | |
| 502 m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, m_intern
alColorFormat, size.width(), size.height(), 0, m_colorFormat, GraphicsContext3D:
:UNSIGNED_BYTE); | |
| 503 if (m_lastColorBuffer) | |
| 504 m_lastColorBuffer->size = m_size; | |
| 505 | |
| 506 m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsCont
ext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0); | |
| 507 | |
| 508 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); | |
| 509 | |
| 510 if (!multisample()) | |
| 511 resizeDepthStencil(size, 0); | |
| 512 if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != Gra
phicsContext3D::FRAMEBUFFER_COMPLETE) | |
| 513 return false; | |
| 514 | |
| 515 return true; | |
| 516 } | |
| 517 | |
| 518 bool DrawingBuffer::resizeMultisampleFramebuffer(const IntSize& size) | |
| 519 { | |
| 520 if (multisample()) { | |
| 521 int maxSampleCount = 0; | |
| 522 | |
| 523 m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount); | |
| 524 int sampleCount = std::min(4, maxSampleCount); | |
| 525 | |
| 526 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisample
FBO); | |
| 527 | |
| 528 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisamp
leColorBuffer); | |
| 529 m_context->extensions()->renderbufferStorageMultisample(GraphicsContext3
D::RENDERBUFFER, sampleCount, m_internalRenderbufferFormat, size.width(), size.h
eight()); | |
| 530 | |
| 531 if (m_context->getError() == GraphicsContext3D::OUT_OF_MEMORY) | |
| 532 return false; | |
| 533 | |
| 534 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, Graph
icsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleC
olorBuffer); | |
| 535 resizeDepthStencil(size, sampleCount); | |
| 536 if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) !=
GraphicsContext3D::FRAMEBUFFER_COMPLETE) | |
| 537 return false; | |
| 538 } | |
| 539 | |
| 540 return true; | |
| 541 } | |
| 542 | |
| 543 void DrawingBuffer::resizeDepthStencil(const IntSize& size, int sampleCount) | |
| 544 { | |
| 545 if (m_attributes.depth && m_attributes.stencil && m_packedDepthStencilExtens
ionSupported) { | |
| 546 if (!m_depthStencilBuffer) | |
| 547 m_depthStencilBuffer = m_context->createRenderbuffer(); | |
| 548 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthSten
cilBuffer); | |
| 549 if (multisample()) | |
| 550 m_context->extensions()->renderbufferStorageMultisample(GraphicsCont
ext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, size.width(),
size.height()); | |
| 551 else | |
| 552 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Exte
nsions3D::DEPTH24_STENCIL8, size.width(), size.height()); | |
| 553 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, Graph
icsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStenci
lBuffer); | |
| 554 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, Graph
icsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilB
uffer); | |
| 555 } else { | |
| 556 if (m_attributes.depth) { | |
| 557 if (!m_depthBuffer) | |
| 558 m_depthBuffer = m_context->createRenderbuffer(); | |
| 559 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depth
Buffer); | |
| 560 if (multisample()) | |
| 561 m_context->extensions()->renderbufferStorageMultisample(Graphics
Context3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, size
.width(), size.height()); | |
| 562 else | |
| 563 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER,
GraphicsContext3D::DEPTH_COMPONENT16, size.width(), size.height()); | |
| 564 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, G
raphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuff
er); | |
| 565 } | |
| 566 if (m_attributes.stencil) { | |
| 567 if (!m_stencilBuffer) | |
| 568 m_stencilBuffer = m_context->createRenderbuffer(); | |
| 569 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stenc
ilBuffer); | |
| 570 if (multisample()) | |
| 571 m_context->extensions()->renderbufferStorageMultisample(Graphics
Context3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, size.wi
dth(), size.height()); | |
| 572 else | |
| 573 m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER,
GraphicsContext3D::STENCIL_INDEX8, size.width(), size.height()); | |
| 574 m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, G
raphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencil
Buffer); | |
| 575 } | |
| 576 } | |
| 577 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); | |
| 578 } | |
| 579 | |
| 580 | |
| 581 | |
| 582 void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask) | |
| 583 { | |
| 584 if (!m_context) | |
| 585 return; | |
| 586 | |
| 587 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO
? m_multisampleFBO : m_fbo); | |
| 588 | |
| 589 m_context->clear(clearMask); | |
| 590 | |
| 591 // The multisample fbo was just cleared, but we also need to clear the non-m
ultisampled buffer too. | |
| 592 if (m_multisampleFBO) { | |
| 593 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); | |
| 594 m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); | |
| 595 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisample
FBO); | |
| 596 } | |
| 597 } | |
| 598 | |
| 599 void DrawingBuffer::setSize(const IntSize& size) { | |
| 600 if (m_size == size) | |
| 601 return; | |
| 602 | |
| 603 s_currentResourceUsePixels += pixelDelta(size); | |
| 604 m_size = size; | |
| 605 } | |
| 606 | |
| 607 int DrawingBuffer::pixelDelta(const IntSize& size) { | |
| 608 return (max(0, size.width()) * max(0, size.height())) - (max(0, m_size.width
()) * max(0, m_size.height())); | |
| 609 } | |
| 610 | |
| 611 IntSize DrawingBuffer::adjustSize(const IntSize& size) { | |
| 612 IntSize adjustedSize = size; | |
| 613 | |
| 614 // Clamp if the desired size is greater than the maximum texture size for th
e device. | |
| 615 if (adjustedSize.height() > m_maxTextureSize) | |
| 616 adjustedSize.setHeight(m_maxTextureSize); | |
| 617 | |
| 618 if (adjustedSize.width() > m_maxTextureSize) | |
| 619 adjustedSize.setWidth(m_maxTextureSize); | |
| 620 | |
| 621 // Try progressively smaller sizes until we find a size that fits or reach a
scale limit. | |
| 622 int scaleAttempts = 0; | |
| 623 while ((s_currentResourceUsePixels + pixelDelta(adjustedSize)) > s_maximumRe
sourceUsePixels) { | |
| 624 scaleAttempts++; | |
| 625 if (scaleAttempts > s_maxScaleAttempts) | |
| 626 return IntSize(); | |
| 627 | |
| 628 adjustedSize.scale(s_resourceAdjustedRatio); | |
| 629 | |
| 630 if (adjustedSize.isEmpty()) | |
| 631 return IntSize(); | |
| 632 } | |
| 633 | |
| 634 return adjustedSize; | |
| 635 } | |
| 636 | |
| 637 IntSize DrawingBuffer::adjustSizeWithContextEviction(const IntSize& size, bool&
evictContext) { | |
| 638 IntSize adjustedSize = adjustSize(size); | |
| 639 if (!adjustedSize.isEmpty()) { | |
| 640 evictContext = false; | |
| 641 return adjustedSize; // Buffer fits without evicting a context. | |
| 642 } | |
| 643 | |
| 644 // Speculatively adjust the pixel budget to see if the buffer would fit shou
ld the oldest context be evicted. | |
| 645 IntSize oldestSize = m_contextEvictionManager->oldestContextSize(); | |
| 646 int pixelDelta = oldestSize.width() * oldestSize.height(); | |
| 647 | |
| 648 s_currentResourceUsePixels -= pixelDelta; | |
| 649 adjustedSize = adjustSize(size); | |
| 650 s_currentResourceUsePixels += pixelDelta; | |
| 651 | |
| 652 evictContext = !adjustedSize.isEmpty(); | |
| 653 return adjustedSize; | |
| 654 } | |
| 655 | |
| 656 void DrawingBuffer::reset(const IntSize& newSize) | |
| 657 { | |
| 658 if (!m_context) | |
| 659 return; | |
| 660 | |
| 661 IntSize adjustedSize; | |
| 662 bool evictContext = false; | |
| 663 bool isNewContext = m_size.isEmpty(); | |
| 664 if (s_allowContextEvictionOnCreate && isNewContext) | |
| 665 adjustedSize = adjustSizeWithContextEviction(newSize, evictContext); | |
| 666 else | |
| 667 adjustedSize = adjustSize(newSize); | |
| 668 | |
| 669 if (adjustedSize.isEmpty()) | |
| 670 return; | |
| 671 | |
| 672 if (evictContext) | |
| 673 m_contextEvictionManager->forciblyLoseOldestContext("WARNING: WebGL cont
exts have exceeded the maximum allowed backbuffer area. Oldest context will be l
ost."); | |
| 674 | |
| 675 if (adjustedSize != m_size) { | |
| 676 do { | |
| 677 // resize multisample FBO | |
| 678 if (!resizeMultisampleFramebuffer(adjustedSize) || !resizeFramebuffe
r(adjustedSize)) { | |
| 679 adjustedSize.scale(s_resourceAdjustedRatio); | |
| 680 continue; | |
| 681 } | |
| 682 break; | |
| 683 } while (!adjustedSize.isEmpty()); | |
| 684 | |
| 685 setSize(adjustedSize); | |
| 686 | |
| 687 if (adjustedSize.isEmpty()) | |
| 688 return; | |
| 689 } | |
| 690 | |
| 691 m_context->disable(GraphicsContext3D::SCISSOR_TEST); | |
| 692 m_context->clearColor(0, 0, 0, 0); | |
| 693 m_context->colorMask(true, true, true, true); | |
| 694 | |
| 695 GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT; | |
| 696 if (m_attributes.depth) { | |
| 697 m_context->clearDepth(1.0f); | |
| 698 clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT; | |
| 699 m_context->depthMask(true); | |
| 700 } | |
| 701 if (m_attributes.stencil) { | |
| 702 m_context->clearStencil(0); | |
| 703 clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT; | |
| 704 m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF); | |
| 705 } | |
| 706 | |
| 707 clearFramebuffers(clearMask); | |
| 708 } | |
| 709 | |
| 710 void DrawingBuffer::commit(long x, long y, long width, long height) | |
| 711 { | |
| 712 if (!m_context) | |
| 713 return; | |
| 714 | |
| 715 if (width < 0) | |
| 716 width = m_size.width(); | |
| 717 if (height < 0) | |
| 718 height = m_size.height(); | |
| 719 | |
| 720 m_context->makeContextCurrent(); | |
| 721 | |
| 722 if (m_multisampleFBO && !m_contentsChangeCommitted) { | |
| 723 m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisample
FBO); | |
| 724 m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo); | |
| 725 | |
| 726 if (m_scissorEnabled) | |
| 727 m_context->disable(GraphicsContext3D::SCISSOR_TEST); | |
| 728 | |
| 729 // Use NEAREST, because there is no scale performed during the blit. | |
| 730 m_context->extensions()->blitFramebuffer(x, y, width, height, x, y, widt
h, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST); | |
| 731 | |
| 732 if (m_scissorEnabled) | |
| 733 m_context->enable(GraphicsContext3D::SCISSOR_TEST); | |
| 734 } | |
| 735 | |
| 736 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); | |
| 737 m_contentsChangeCommitted = true; | |
| 738 } | |
| 739 | |
| 740 void DrawingBuffer::restoreFramebufferBinding() | |
| 741 { | |
| 742 if (!m_context || !m_framebufferBinding) | |
| 743 return; | |
| 744 | |
| 745 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBind
ing); | |
| 746 } | |
| 747 | |
| 748 bool DrawingBuffer::multisample() const | |
| 749 { | |
| 750 return m_attributes.antialias && m_multisampleExtensionSupported; | |
| 751 } | |
| 752 | |
| 753 void DrawingBuffer::bind() | |
| 754 { | |
| 755 if (!m_context) | |
| 756 return; | |
| 757 | |
| 758 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO
? m_multisampleFBO : m_fbo); | |
| 759 } | |
| 760 | |
| 761 } // namespace WebCore | |
| OLD | NEW |