Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2010, Google Inc. All rights reserved. | 2 * Copyright (c) 2010, Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 | 210 |
| 211 WebGraphicsContext3DProvider* DrawingBuffer::contextProvider() { | 211 WebGraphicsContext3DProvider* DrawingBuffer::contextProvider() { |
| 212 return m_contextProvider.get(); | 212 return m_contextProvider.get(); |
| 213 } | 213 } |
| 214 | 214 |
| 215 void DrawingBuffer::setIsHidden(bool hidden) { | 215 void DrawingBuffer::setIsHidden(bool hidden) { |
| 216 if (m_isHidden == hidden) | 216 if (m_isHidden == hidden) |
| 217 return; | 217 return; |
| 218 m_isHidden = hidden; | 218 m_isHidden = hidden; |
| 219 if (m_isHidden) { | 219 if (m_isHidden) { |
| 220 m_recycledMailboxQueue.clear(); | 220 m_recycledColorBufferQueue.clear(); |
| 221 m_frontColorBuffer = nullptr; | 221 m_frontColorBuffer = nullptr; |
| 222 } | 222 } |
| 223 } | 223 } |
| 224 | 224 |
| 225 void DrawingBuffer::setFilterQuality(SkFilterQuality filterQuality) { | 225 void DrawingBuffer::setFilterQuality(SkFilterQuality filterQuality) { |
| 226 if (m_filterQuality != filterQuality) { | 226 if (m_filterQuality != filterQuality) { |
| 227 m_filterQuality = filterQuality; | 227 m_filterQuality = filterQuality; |
| 228 if (m_layer) | 228 if (m_layer) |
| 229 m_layer->setNearestNeighbor(filterQuality == kNone_SkFilterQuality); | 229 m_layer->setNearestNeighbor(filterQuality == kNone_SkFilterQuality); |
| 230 } | 230 } |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox"); | 297 TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox"); |
| 298 | 298 |
| 299 if (m_newMailboxCallback) | 299 if (m_newMailboxCallback) |
| 300 (*m_newMailboxCallback)(); | 300 (*m_newMailboxCallback)(); |
| 301 | 301 |
| 302 // Resolve the multisampled buffer into m_backColorBuffer texture. | 302 // Resolve the multisampled buffer into m_backColorBuffer texture. |
| 303 if (m_antiAliasingMode != None) | 303 if (m_antiAliasingMode != None) |
| 304 commit(); | 304 commit(); |
| 305 | 305 |
| 306 if (m_softwareRendering && !forceGpuResult) { | 306 if (m_softwareRendering && !forceGpuResult) { |
| 307 std::unique_ptr<cc::SharedBitmap> bitmap = createOrRecycleBitmap(); | 307 return finishPrepareTextureMailboxSoftware(outMailbox, outReleaseCallback); |
| 308 if (!bitmap) | 308 } else { |
| 309 return false; | 309 return finishPrepareTextureMailboxGpu(outMailbox, outReleaseCallback); |
| 310 } | |
| 311 } | |
| 312 | |
| 313 bool DrawingBuffer::finishPrepareTextureMailboxSoftware( | |
| 314 cc::TextureMailbox* outMailbox, | |
| 315 std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback) { | |
| 316 std::unique_ptr<cc::SharedBitmap> bitmap = createOrRecycleBitmap(); | |
| 317 if (!bitmap) | |
| 318 return false; | |
| 319 | |
| 320 // Read the framebuffer into |bitmap|. | |
| 321 { | |
| 310 unsigned char* pixels = bitmap->pixels(); | 322 unsigned char* pixels = bitmap->pixels(); |
| 311 DCHECK(pixels); | 323 DCHECK(pixels); |
| 312 | |
| 313 bool needPremultiply = m_wantAlphaChannel && !m_premultipliedAlpha; | 324 bool needPremultiply = m_wantAlphaChannel && !m_premultipliedAlpha; |
| 314 WebGLImageConversion::AlphaOp op = | 325 WebGLImageConversion::AlphaOp op = |
| 315 needPremultiply ? WebGLImageConversion::AlphaDoPremultiply | 326 needPremultiply ? WebGLImageConversion::AlphaDoPremultiply |
| 316 : WebGLImageConversion::AlphaDoNothing; | 327 : WebGLImageConversion::AlphaDoNothing; |
| 317 readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, | 328 readBackFramebuffer(pixels, size().width(), size().height(), ReadbackSkia, |
| 318 op); | 329 op); |
| 319 | |
| 320 *outMailbox = cc::TextureMailbox(bitmap.get(), m_size); | |
| 321 | |
| 322 // This holds a ref on the DrawingBuffer that will keep it alive until the | |
| 323 // mailbox is released (and while the release callback is running). It also | |
| 324 // owns the SharedBitmap. | |
| 325 auto func = WTF::bind(&DrawingBuffer::softwareMailboxReleased, | |
| 326 RefPtr<DrawingBuffer>(this), | |
| 327 WTF::passed(std::move(bitmap)), m_size); | |
| 328 *outReleaseCallback = cc::SingleReleaseCallback::Create( | |
| 329 convertToBaseCallback(std::move(func))); | |
| 330 return true; | |
| 331 } | 330 } |
| 332 | 331 |
| 332 *outMailbox = cc::TextureMailbox(bitmap.get(), m_size); | |
| 333 | |
| 334 // This holds a ref on the DrawingBuffer that will keep it alive until the | |
| 335 // mailbox is released (and while the release callback is running). It also | |
| 336 // owns the SharedBitmap. | |
| 337 auto func = WTF::bind(&DrawingBuffer::mailboxReleasedSoftware, | |
| 338 RefPtr<DrawingBuffer>(this), | |
| 339 WTF::passed(std::move(bitmap)), m_size); | |
| 340 *outReleaseCallback = | |
| 341 cc::SingleReleaseCallback::Create(convertToBaseCallback(std::move(func))); | |
| 342 return true; | |
| 343 } | |
| 344 | |
| 345 bool DrawingBuffer::finishPrepareTextureMailboxGpu( | |
| 346 cc::TextureMailbox* outMailbox, | |
| 347 std::unique_ptr<cc::SingleReleaseCallback>* outReleaseCallback) { | |
| 333 if (m_webGLVersion > WebGL1) { | 348 if (m_webGLVersion > WebGL1) { |
| 334 m_gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | 349 m_gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 335 } | 350 } |
| 336 | 351 |
| 337 // We must restore the texture binding since creating new textures, | 352 // We must restore the texture binding since creating new textures, |
| 338 // consuming and producing mailboxes changes it. | 353 // consuming and producing mailboxes changes it. |
| 339 ScopedTextureUnit0BindingRestorer restorer(m_gl, m_activeTextureUnit, | 354 ScopedTextureUnit0BindingRestorer restorer(m_gl, m_activeTextureUnit, |
| 340 m_texture2DBinding); | 355 m_texture2DBinding); |
| 341 | 356 |
| 342 // First try to recycle an old buffer. | 357 // Specify the buffer that we will put in the mailbox. |
| 343 RefPtr<ColorBuffer> colorBufferForMailbox = takeRecycledMailbox(); | 358 RefPtr<ColorBuffer> colorBufferForMailbox; |
| 344 | |
| 345 // No buffer available to recycle, create a new one. | |
| 346 if (!colorBufferForMailbox) | |
| 347 colorBufferForMailbox = createTextureAndAllocateMemory(m_size); | |
| 348 | |
| 349 if (m_preserveDrawingBuffer == Discard) { | 359 if (m_preserveDrawingBuffer == Discard) { |
| 350 std::swap(colorBufferForMailbox, m_backColorBuffer); | 360 // If we can discard the backbuffer, send the old backbuffer directly |
| 361 // into the mailbox, and allocate (or recycle) a new backbuffer. | |
| 362 colorBufferForMailbox = m_backColorBuffer; | |
| 363 m_backColorBuffer = createOrRecycleColorBuffer(); | |
| 351 attachColorBufferToReadFramebuffer(); | 364 attachColorBufferToReadFramebuffer(); |
| 352 | 365 |
| 366 // Explicitly specify that m_fbo (which is now bound to the just-allocated | |
| 367 // m_backColorBuffer) is not initialized, to save GPU memory bandwidth for | |
| 368 // tile-based GPU architectures. | |
| 353 if (m_discardFramebufferSupported) { | 369 if (m_discardFramebufferSupported) { |
| 354 // Explicitly discard the framebuffer to save GPU memory bandwidth for | |
| 355 // tile-based GPU arch. | |
| 356 const GLenum attachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, | 370 const GLenum attachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, |
| 357 GL_STENCIL_ATTACHMENT}; | 371 GL_STENCIL_ATTACHMENT}; |
| 358 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo); | 372 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| 359 m_gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments); | 373 m_gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments); |
| 360 } | 374 } |
| 361 } else { | 375 } else { |
| 376 // If we can't discard the backbuffer, create (or recycle) a buffer to put | |
| 377 // in the mailbox, and copy backbuffer's contents there. | |
| 378 colorBufferForMailbox = createOrRecycleColorBuffer(); | |
| 362 m_gl->CopySubTextureCHROMIUM( | 379 m_gl->CopySubTextureCHROMIUM( |
| 363 m_backColorBuffer->textureId, colorBufferForMailbox->textureId, 0, 0, 0, | 380 m_backColorBuffer->textureId, colorBufferForMailbox->textureId, 0, 0, 0, |
| 364 0, m_size.width(), m_size.height(), GL_FALSE, GL_FALSE, GL_FALSE); | 381 0, m_size.width(), m_size.height(), GL_FALSE, GL_FALSE, GL_FALSE); |
| 365 } | 382 } |
| 366 | 383 |
| 384 // Put colorBufferForMailbox into its mailbox, and populate its | |
| 385 // produceSyncToken with that point. | |
| 386 { | |
| 387 m_gl->ProduceTextureDirectCHROMIUM(colorBufferForMailbox->textureId, | |
| 388 colorBufferForMailbox->parameters.target, | |
| 389 colorBufferForMailbox->mailbox.name); | |
| 390 const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM(); | |
| 391 #if OS(MACOSX) | |
| 392 m_gl->DescheduleUntilFinishedCHROMIUM(); | |
| 393 #endif | |
| 394 m_gl->Flush(); | |
| 395 m_gl->GenSyncTokenCHROMIUM( | |
| 396 fenceSync, colorBufferForMailbox->produceSyncToken.GetData()); | |
| 397 } | |
| 398 | |
| 399 // Populate the output mailbox and callback. | |
| 400 { | |
| 401 bool isOverlayCandidate = colorBufferForMailbox->imageId != 0; | |
| 402 bool secureOutputOnly = false; | |
| 403 *outMailbox = cc::TextureMailbox( | |
| 404 colorBufferForMailbox->mailbox, colorBufferForMailbox->produceSyncToken, | |
| 405 colorBufferForMailbox->parameters.target, gfx::Size(m_size), | |
| 406 isOverlayCandidate, secureOutputOnly); | |
| 407 | |
| 408 // This holds a ref on the DrawingBuffer that will keep it alive until the | |
| 409 // mailbox is released (and while the release callback is running). | |
| 410 auto func = WTF::bind(&DrawingBuffer::mailboxReleasedGpu, | |
| 411 RefPtr<DrawingBuffer>(this), colorBufferForMailbox); | |
| 412 *outReleaseCallback = cc::SingleReleaseCallback::Create( | |
| 413 convertToBaseCallback(std::move(func))); | |
| 414 } | |
| 415 | |
| 416 // Point |m_frontColorBuffer| to the buffer that we are now presenting. | |
| 417 m_frontColorBuffer = colorBufferForMailbox; | |
| 418 | |
| 419 // Restore any state that we may have dirtied, and update dirty bits. | |
| 367 restoreFramebufferBindings(); | 420 restoreFramebufferBindings(); |
| 368 restorePixelUnpackBufferBindings(); | 421 restorePixelUnpackBufferBindings(); |
| 369 m_contentsChanged = false; | 422 m_contentsChanged = false; |
| 370 | |
| 371 m_gl->ProduceTextureDirectCHROMIUM(colorBufferForMailbox->textureId, | |
| 372 colorBufferForMailbox->parameters.target, | |
| 373 colorBufferForMailbox->mailbox.name); | |
| 374 const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM(); | |
| 375 #if OS(MACOSX) | |
| 376 m_gl->DescheduleUntilFinishedCHROMIUM(); | |
| 377 #endif | |
| 378 m_gl->Flush(); | |
| 379 gpu::SyncToken syncToken; | |
| 380 m_gl->GenSyncTokenCHROMIUM(fenceSync, syncToken.GetData()); | |
| 381 | |
| 382 bool isOverlayCandidate = colorBufferForMailbox->imageId != 0; | |
| 383 bool secureOutputOnly = false; | |
| 384 *outMailbox = cc::TextureMailbox(colorBufferForMailbox->mailbox, syncToken, | |
| 385 colorBufferForMailbox->parameters.target, | |
| 386 gfx::Size(m_size.width(), m_size.height()), | |
| 387 isOverlayCandidate, secureOutputOnly); | |
| 388 | |
| 389 // This holds a ref on the DrawingBuffer that will keep it alive until the | |
| 390 // mailbox is released (and while the release callback is running). | |
| 391 auto func = WTF::bind(&DrawingBuffer::gpuMailboxReleased, | |
| 392 RefPtr<DrawingBuffer>(this), colorBufferForMailbox); | |
| 393 *outReleaseCallback = | |
| 394 cc::SingleReleaseCallback::Create(convertToBaseCallback(std::move(func))); | |
| 395 | |
| 396 // Point |m_frontColorBuffer| to the buffer that we are presenting, and | |
| 397 // update its sync token. | |
| 398 colorBufferForMailbox->produceSyncToken = syncToken; | |
| 399 m_frontColorBuffer = colorBufferForMailbox; | |
| 400 setBufferClearNeeded(true); | 423 setBufferClearNeeded(true); |
| 401 return true; | 424 return true; |
| 402 } | 425 } |
| 403 | 426 |
| 404 void DrawingBuffer::gpuMailboxReleased(RefPtr<ColorBuffer> colorBuffer, | 427 void DrawingBuffer::mailboxReleasedGpu(RefPtr<ColorBuffer> colorBuffer, |
| 405 const gpu::SyncToken& syncToken, | 428 const gpu::SyncToken& syncToken, |
| 406 bool lostResource) { | 429 bool lostResource) { |
| 407 // Update the SyncToken to ensure that we will wait for it even if we | 430 // Update the SyncToken to ensure that we will wait for it even if we |
| 408 // immediately destroy this buffer. | 431 // immediately destroy this buffer. |
| 409 colorBuffer->receiveSyncToken = syncToken; | 432 colorBuffer->receiveSyncToken = syncToken; |
| 410 | 433 |
| 411 if (m_destructionInProgress || colorBuffer->size != m_size || | 434 if (m_destructionInProgress || colorBuffer->size != m_size || |
| 412 m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lostResource || | 435 m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lostResource || |
| 413 m_isHidden) { | 436 m_isHidden) { |
| 414 return; | 437 return; |
| 415 } | 438 } |
| 416 | 439 |
| 417 // Creation of image backed mailboxes is very expensive, so be less | 440 // Creation of image backed mailboxes is very expensive, so be less |
| 418 // aggressive about pruning them. Pruning is done in FIFO order. | 441 // aggressive about pruning them. Pruning is done in FIFO order. |
| 419 size_t cacheLimit = 1; | 442 size_t cacheLimit = 1; |
| 420 if (shouldUseChromiumImage()) | 443 if (shouldUseChromiumImage()) |
| 421 cacheLimit = 4; | 444 cacheLimit = 4; |
| 422 while (m_recycledMailboxQueue.size() >= cacheLimit) | 445 while (m_recycledColorBufferQueue.size() >= cacheLimit) |
| 423 m_recycledMailboxQueue.takeLast(); | 446 m_recycledColorBufferQueue.takeLast(); |
| 424 | 447 |
| 425 m_recycledMailboxQueue.prepend(colorBuffer); | 448 m_recycledColorBufferQueue.prepend(colorBuffer); |
| 426 } | 449 } |
| 427 | 450 |
| 428 void DrawingBuffer::softwareMailboxReleased( | 451 void DrawingBuffer::mailboxReleasedSoftware( |
| 429 std::unique_ptr<cc::SharedBitmap> bitmap, | 452 std::unique_ptr<cc::SharedBitmap> bitmap, |
| 430 const IntSize& size, | 453 const IntSize& size, |
| 431 const gpu::SyncToken& syncToken, | 454 const gpu::SyncToken& syncToken, |
| 432 bool lostResource) { | 455 bool lostResource) { |
| 433 DCHECK(!syncToken.HasData()); // No sync tokens for software resources. | 456 DCHECK(!syncToken.HasData()); // No sync tokens for software resources. |
| 434 if (m_destructionInProgress || lostResource || m_isHidden || size != m_size) | 457 if (m_destructionInProgress || lostResource || m_isHidden || size != m_size) |
| 435 return; // Just delete the bitmap. | 458 return; // Just delete the bitmap. |
| 436 | 459 |
| 437 RecycledBitmap recycled = {std::move(bitmap), m_size}; | 460 RecycledBitmap recycled = {std::move(bitmap), m_size}; |
| 438 m_recycledBitmaps.append(std::move(recycled)); | 461 m_recycledBitmaps.append(std::move(recycled)); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 // DrawingBuffer's context anyways. | 527 // DrawingBuffer's context anyways. |
| 505 const auto& skImageSyncToken = textureMailbox.sync_token(); | 528 const auto& skImageSyncToken = textureMailbox.sync_token(); |
| 506 | 529 |
| 507 // TODO(xidachen): Create a small pool of recycled textures from | 530 // TODO(xidachen): Create a small pool of recycled textures from |
| 508 // ImageBitmapRenderingContext's transferFromImageBitmap, and try to use them | 531 // ImageBitmapRenderingContext's transferFromImageBitmap, and try to use them |
| 509 // in DrawingBuffer. | 532 // in DrawingBuffer. |
| 510 return AcceleratedStaticBitmapImage::createFromWebGLContextImage( | 533 return AcceleratedStaticBitmapImage::createFromWebGLContextImage( |
| 511 std::move(skImage), skImageMailbox, skImageSyncToken); | 534 std::move(skImage), skImageMailbox, skImageSyncToken); |
| 512 } | 535 } |
| 513 | 536 |
| 514 DrawingBuffer::TextureParameters | 537 DrawingBuffer::ColorBufferParameters |
| 515 DrawingBuffer::chromiumImageTextureParameters() { | 538 DrawingBuffer::gpuMemoryBufferColorBufferParameters() { |
| 516 #if OS(MACOSX) | 539 #if OS(MACOSX) |
| 517 // A CHROMIUM_image backed texture requires a specialized set of parameters | 540 // A CHROMIUM_image backed texture requires a specialized set of parameters |
| 518 // on OSX. | 541 // on OSX. |
| 519 TextureParameters parameters; | 542 ColorBufferParameters parameters; |
| 520 parameters.target = GC3D_TEXTURE_RECTANGLE_ARB; | 543 parameters.target = GC3D_TEXTURE_RECTANGLE_ARB; |
| 521 | 544 |
| 522 if (m_wantAlphaChannel) { | 545 if (m_wantAlphaChannel) { |
| 523 parameters.creationInternalColorFormat = GL_RGBA; | 546 parameters.creationInternalColorFormat = GL_RGBA; |
| 524 parameters.internalColorFormat = GL_RGBA; | 547 parameters.internalColorFormat = GL_RGBA; |
| 525 } else if (contextProvider() | 548 } else if (contextProvider() |
| 526 ->getCapabilities() | 549 ->getCapabilities() |
| 527 .chromium_image_rgb_emulation) { | 550 .chromium_image_rgb_emulation) { |
| 528 parameters.creationInternalColorFormat = GL_RGB; | 551 parameters.creationInternalColorFormat = GL_RGB; |
| 529 parameters.internalColorFormat = GL_RGBA; | 552 parameters.internalColorFormat = GL_RGBA; |
| 530 } else { | 553 } else { |
| 531 GLenum format = | 554 GLenum format = |
| 532 defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB; | 555 defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB; |
| 533 parameters.creationInternalColorFormat = format; | 556 parameters.creationInternalColorFormat = format; |
| 534 parameters.internalColorFormat = format; | 557 parameters.internalColorFormat = format; |
| 535 } | 558 } |
| 536 | 559 |
| 537 // Unused when CHROMIUM_image is being used. | 560 // Unused when CHROMIUM_image is being used. |
| 538 parameters.colorFormat = 0; | 561 parameters.colorFormat = 0; |
| 539 return parameters; | 562 return parameters; |
| 540 #else | 563 #else |
| 541 return defaultTextureParameters(); | 564 return textureColorBufferParameters(); |
| 542 #endif | 565 #endif |
| 543 } | 566 } |
| 544 | 567 |
| 545 DrawingBuffer::TextureParameters DrawingBuffer::defaultTextureParameters() { | 568 DrawingBuffer::ColorBufferParameters |
| 546 TextureParameters parameters; | 569 DrawingBuffer::textureColorBufferParameters() { |
| 570 ColorBufferParameters parameters; | |
| 547 parameters.target = GL_TEXTURE_2D; | 571 parameters.target = GL_TEXTURE_2D; |
| 548 if (m_wantAlphaChannel) { | 572 if (m_wantAlphaChannel) { |
| 549 parameters.internalColorFormat = GL_RGBA; | 573 parameters.internalColorFormat = GL_RGBA; |
| 550 parameters.creationInternalColorFormat = GL_RGBA; | 574 parameters.creationInternalColorFormat = GL_RGBA; |
| 551 parameters.colorFormat = GL_RGBA; | 575 parameters.colorFormat = GL_RGBA; |
| 552 } else if (contextProvider() | 576 } else if (contextProvider() |
| 553 ->getCapabilities() | 577 ->getCapabilities() |
| 554 .emulate_rgb_buffer_with_rgba) { | 578 .emulate_rgb_buffer_with_rgba) { |
| 555 parameters.internalColorFormat = GL_RGBA; | 579 parameters.internalColorFormat = GL_RGBA; |
| 556 parameters.creationInternalColorFormat = GL_RGBA; | 580 parameters.creationInternalColorFormat = GL_RGBA; |
| 557 parameters.colorFormat = GL_RGBA; | 581 parameters.colorFormat = GL_RGBA; |
| 558 } else { | 582 } else { |
| 559 GLenum format = | 583 GLenum format = |
| 560 defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB; | 584 defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB; |
| 561 parameters.creationInternalColorFormat = format; | 585 parameters.creationInternalColorFormat = format; |
| 562 parameters.internalColorFormat = format; | 586 parameters.internalColorFormat = format; |
| 563 parameters.colorFormat = format; | 587 parameters.colorFormat = format; |
| 564 } | 588 } |
| 565 return parameters; | 589 return parameters; |
| 566 } | 590 } |
| 567 | 591 |
| 568 PassRefPtr<DrawingBuffer::ColorBuffer> DrawingBuffer::takeRecycledMailbox() { | 592 PassRefPtr<DrawingBuffer::ColorBuffer> |
| 569 if (m_recycledMailboxQueue.isEmpty()) | 593 DrawingBuffer::createOrRecycleColorBuffer() { |
| 570 return nullptr; | 594 if (!m_recycledColorBufferQueue.isEmpty()) { |
| 571 | 595 RefPtr<ColorBuffer> recycled = m_recycledColorBufferQueue.takeLast(); |
| 572 RefPtr<ColorBuffer> recycled = m_recycledMailboxQueue.takeLast(); | 596 if (recycled->receiveSyncToken.HasData()) |
| 573 DCHECK(recycled->size == m_size); | 597 m_gl->WaitSyncTokenCHROMIUM(recycled->receiveSyncToken.GetData()); |
| 574 if (recycled->receiveSyncToken.HasData()) | 598 DCHECK(recycled->size == m_size); |
| 575 m_gl->WaitSyncTokenCHROMIUM(recycled->receiveSyncToken.GetData()); | 599 return recycled; |
| 576 return recycled; | 600 } |
| 601 return createColorBuffer(m_size); | |
| 577 } | 602 } |
| 578 | 603 |
| 579 DrawingBuffer::ColorBuffer::ColorBuffer(DrawingBuffer* drawingBuffer, | 604 DrawingBuffer::ColorBuffer::ColorBuffer(DrawingBuffer* drawingBuffer, |
| 580 const TextureParameters& parameters, | 605 const ColorBufferParameters& parameters, |
| 581 const IntSize& size) | 606 const IntSize& size, |
| 582 : drawingBuffer(drawingBuffer), parameters(parameters), size(size) { | 607 GLuint textureId, |
| 608 GLuint imageId) | |
| 609 : drawingBuffer(drawingBuffer), | |
| 610 parameters(parameters), | |
| 611 size(size), | |
| 612 textureId(textureId), | |
| 613 imageId(imageId) { | |
| 583 drawingBuffer->contextGL()->GenMailboxCHROMIUM(mailbox.name); | 614 drawingBuffer->contextGL()->GenMailboxCHROMIUM(mailbox.name); |
| 584 } | 615 } |
| 585 | 616 |
| 586 DrawingBuffer::ColorBuffer::~ColorBuffer() { | 617 DrawingBuffer::ColorBuffer::~ColorBuffer() { |
| 587 gpu::gles2::GLES2Interface* gl = drawingBuffer->contextGL(); | 618 gpu::gles2::GLES2Interface* gl = drawingBuffer->contextGL(); |
| 588 if (receiveSyncToken.HasData()) | 619 if (receiveSyncToken.HasData()) |
| 589 gl->WaitSyncTokenCHROMIUM(receiveSyncToken.GetConstData()); | 620 gl->WaitSyncTokenCHROMIUM(receiveSyncToken.GetConstData()); |
| 590 if (imageId) { | 621 if (imageId) { |
| 591 gl->BindTexture(parameters.target, textureId); | 622 gl->BindTexture(parameters.target, textureId); |
| 592 gl->ReleaseTexImage2DCHROMIUM(parameters.target, imageId); | 623 gl->ReleaseTexImage2DCHROMIUM(parameters.target, imageId); |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 744 m_layer->clearTexture(); | 775 m_layer->clearTexture(); |
| 745 | 776 |
| 746 m_gl->Flush(); | 777 m_gl->Flush(); |
| 747 } | 778 } |
| 748 | 779 |
| 749 void DrawingBuffer::beginDestruction() { | 780 void DrawingBuffer::beginDestruction() { |
| 750 ASSERT(!m_destructionInProgress); | 781 ASSERT(!m_destructionInProgress); |
| 751 m_destructionInProgress = true; | 782 m_destructionInProgress = true; |
| 752 | 783 |
| 753 clearPlatformLayer(); | 784 clearPlatformLayer(); |
| 754 m_recycledMailboxQueue.clear(); | 785 m_recycledColorBufferQueue.clear(); |
| 755 | 786 |
| 756 if (m_multisampleFBO) | 787 if (m_multisampleFBO) |
| 757 m_gl->DeleteFramebuffers(1, &m_multisampleFBO); | 788 m_gl->DeleteFramebuffers(1, &m_multisampleFBO); |
| 758 | 789 |
| 759 if (m_fbo) | 790 if (m_fbo) |
| 760 m_gl->DeleteFramebuffers(1, &m_fbo); | 791 m_gl->DeleteFramebuffers(1, &m_fbo); |
| 761 | 792 |
| 762 if (m_multisampleRenderbuffer) | 793 if (m_multisampleRenderbuffer) |
| 763 m_gl->DeleteRenderbuffers(1, &m_multisampleRenderbuffer); | 794 m_gl->DeleteRenderbuffers(1, &m_multisampleRenderbuffer); |
| 764 | 795 |
| 765 if (m_depthStencilBuffer) | 796 if (m_depthStencilBuffer) |
| 766 m_gl->DeleteRenderbuffers(1, &m_depthStencilBuffer); | 797 m_gl->DeleteRenderbuffers(1, &m_depthStencilBuffer); |
| 767 | 798 |
| 768 m_size = IntSize(); | 799 m_size = IntSize(); |
| 769 | 800 |
| 770 m_backColorBuffer = nullptr; | 801 m_backColorBuffer = nullptr; |
| 771 m_frontColorBuffer = nullptr; | 802 m_frontColorBuffer = nullptr; |
| 772 m_multisampleRenderbuffer = 0; | 803 m_multisampleRenderbuffer = 0; |
| 773 m_depthStencilBuffer = 0; | 804 m_depthStencilBuffer = 0; |
| 774 m_multisampleFBO = 0; | 805 m_multisampleFBO = 0; |
| 775 m_fbo = 0; | 806 m_fbo = 0; |
| 776 | 807 |
| 777 if (m_layer) | 808 if (m_layer) |
| 778 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); | 809 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); |
| 779 } | 810 } |
| 780 | 811 |
| 781 GLuint DrawingBuffer::createColorTexture(const TextureParameters& parameters) { | |
| 782 GLuint offscreenColorTexture; | |
| 783 m_gl->GenTextures(1, &offscreenColorTexture); | |
| 784 m_gl->BindTexture(parameters.target, offscreenColorTexture); | |
| 785 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 786 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 787 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| 788 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| 789 return offscreenColorTexture; | |
| 790 } | |
| 791 | |
| 792 bool DrawingBuffer::resizeDefaultFramebuffer(const IntSize& size) { | 812 bool DrawingBuffer::resizeDefaultFramebuffer(const IntSize& size) { |
| 793 // Recreate m_backColorBuffer. | 813 // Recreate m_backColorBuffer. |
| 794 m_backColorBuffer = createTextureAndAllocateMemory(size); | 814 m_backColorBuffer = createColorBuffer(size); |
| 795 | 815 |
| 796 attachColorBufferToReadFramebuffer(); | 816 attachColorBufferToReadFramebuffer(); |
| 797 | 817 |
| 798 if (wantExplicitResolve()) { | 818 if (wantExplicitResolve()) { |
| 799 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); | 819 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); |
| 800 m_gl->BindRenderbuffer(GL_RENDERBUFFER, m_multisampleRenderbuffer); | 820 m_gl->BindRenderbuffer(GL_RENDERBUFFER, m_multisampleRenderbuffer); |
| 801 m_gl->RenderbufferStorageMultisampleCHROMIUM( | 821 m_gl->RenderbufferStorageMultisampleCHROMIUM( |
| 802 GL_RENDERBUFFER, m_sampleCount, getMultisampledRenderbufferFormat(), | 822 GL_RENDERBUFFER, m_sampleCount, getMultisampledRenderbufferFormat(), |
| 803 size.width(), size.height()); | 823 size.width(), size.height()); |
| 804 | 824 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 886 if (!resizeDefaultFramebuffer(adjustedSize)) { | 906 if (!resizeDefaultFramebuffer(adjustedSize)) { |
| 887 adjustedSize.scale(s_resourceAdjustedRatio); | 907 adjustedSize.scale(s_resourceAdjustedRatio); |
| 888 continue; | 908 continue; |
| 889 } | 909 } |
| 890 break; | 910 break; |
| 891 } while (!adjustedSize.isEmpty()); | 911 } while (!adjustedSize.isEmpty()); |
| 892 | 912 |
| 893 m_size = adjustedSize; | 913 m_size = adjustedSize; |
| 894 // Free all mailboxes, because they are now of the wrong size. Only the | 914 // Free all mailboxes, because they are now of the wrong size. Only the |
| 895 // first call in this loop has any effect. | 915 // first call in this loop has any effect. |
| 896 m_recycledMailboxQueue.clear(); | 916 m_recycledColorBufferQueue.clear(); |
| 897 m_recycledBitmaps.clear(); | 917 m_recycledBitmaps.clear(); |
| 898 | 918 |
| 899 if (adjustedSize.isEmpty()) | 919 if (adjustedSize.isEmpty()) |
| 900 return false; | 920 return false; |
| 901 } | 921 } |
| 902 | 922 |
| 903 m_gl->Disable(GL_SCISSOR_TEST); | 923 m_gl->Disable(GL_SCISSOR_TEST); |
| 904 m_gl->ClearColor(0, 0, 0, | 924 m_gl->ClearColor(0, 0, 0, |
| 905 defaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0); | 925 defaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0); |
| 906 m_gl->ColorMask(true, true, true, true); | 926 m_gl->ColorMask(true, true, true, true); |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1083 pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255); | 1103 pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255); |
| 1084 } | 1104 } |
| 1085 } else if (op != WebGLImageConversion::AlphaDoNothing) { | 1105 } else if (op != WebGLImageConversion::AlphaDoNothing) { |
| 1086 ASSERT_NOT_REACHED(); | 1106 ASSERT_NOT_REACHED(); |
| 1087 } | 1107 } |
| 1088 } | 1108 } |
| 1089 | 1109 |
| 1090 void DrawingBuffer::flipVertically(uint8_t* framebuffer, | 1110 void DrawingBuffer::flipVertically(uint8_t* framebuffer, |
| 1091 int width, | 1111 int width, |
| 1092 int height) { | 1112 int height) { |
| 1093 m_scanline.resize(width * 4); | 1113 std::vector<uint8_t> scanline(width * 4); |
| 1094 uint8_t* scanline = &m_scanline[0]; | |
| 1095 unsigned rowBytes = width * 4; | 1114 unsigned rowBytes = width * 4; |
| 1096 unsigned count = height / 2; | 1115 unsigned count = height / 2; |
| 1097 for (unsigned i = 0; i < count; i++) { | 1116 for (unsigned i = 0; i < count; i++) { |
| 1098 uint8_t* rowA = framebuffer + i * rowBytes; | 1117 uint8_t* rowA = framebuffer + i * rowBytes; |
| 1099 uint8_t* rowB = framebuffer + (height - i - 1) * rowBytes; | 1118 uint8_t* rowB = framebuffer + (height - i - 1) * rowBytes; |
| 1100 memcpy(scanline, rowB, rowBytes); | 1119 memcpy(scanline.data(), rowB, rowBytes); |
| 1101 memcpy(rowB, rowA, rowBytes); | 1120 memcpy(rowB, rowA, rowBytes); |
| 1102 memcpy(rowA, scanline, rowBytes); | 1121 memcpy(rowA, scanline.data(), rowBytes); |
| 1103 } | 1122 } |
| 1104 } | 1123 } |
| 1105 | 1124 |
| 1106 void DrawingBuffer::allocateConditionallyImmutableTexture(GLenum target, | 1125 RefPtr<DrawingBuffer::ColorBuffer> DrawingBuffer::createColorBuffer( |
| 1107 GLenum internalformat, | 1126 const IntSize& size) { |
| 1108 GLsizei width, | 1127 // Select the Parameters for the texture object. Allocate the backing |
| 1109 GLsizei height, | 1128 // GpuMemoryBuffer and GLImage, if one is going to be used. |
| 1110 GLint border, | 1129 ColorBufferParameters parameters; |
| 1111 GLenum format, | 1130 GLuint imageId = 0; |
| 1112 GLenum type) { | 1131 if (shouldUseChromiumImage()) { |
| 1113 if (m_storageTextureSupported) { | 1132 parameters = gpuMemoryBufferColorBufferParameters(); |
| 1114 GLenum internalStorageFormat = GL_NONE; | 1133 imageId = m_gl->CreateGpuMemoryBufferImageCHROMIUM( |
| 1115 if (internalformat == GL_RGB) { | 1134 size.width(), size.height(), parameters.creationInternalColorFormat, |
| 1116 internalStorageFormat = GL_RGB8; | 1135 GC3D_SCANOUT_CHROMIUM); |
| 1117 } else if (internalformat == GL_RGBA) { | 1136 } else { |
| 1118 internalStorageFormat = GL_RGBA8; | 1137 parameters = textureColorBufferParameters(); |
| 1119 } else { | |
| 1120 NOTREACHED(); | |
| 1121 } | |
| 1122 m_gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internalStorageFormat, width, | |
| 1123 height); | |
| 1124 return; | |
| 1125 } | 1138 } |
| 1126 m_gl->TexImage2D(target, 0, internalformat, width, height, border, format, | |
| 1127 type, 0); | |
| 1128 } | |
| 1129 | 1139 |
| 1130 void DrawingBuffer::clearChromiumImageAlpha(const ColorBuffer& info) { | 1140 // Allocate the texture for this object. |
| 1131 if (m_wantAlphaChannel) | 1141 GLuint textureId = 0; |
| 1132 return; | 1142 { |
| 1133 if (!contextProvider()->getCapabilities().chromium_image_rgb_emulation) | 1143 m_gl->GenTextures(1, &textureId); |
| 1134 return; | 1144 m_gl->BindTexture(parameters.target, textureId); |
| 1145 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 1146 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 1147 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| 1148 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| 1149 } | |
| 1135 | 1150 |
| 1136 GLuint fbo = 0; | 1151 // If this is GpuMemoryBuffer-backed, then bind the texture to the |
| 1137 m_gl->GenFramebuffers(1, &fbo); | 1152 // GpuMemoryBuffer's GLImage. Otherwise, allocate ordinary texture storage. |
| 1138 m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| 1139 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 1140 info.parameters.target, info.textureId, 0); | |
| 1141 m_gl->ClearColor(0, 0, 0, 1); | |
| 1142 m_gl->ColorMask(false, false, false, true); | |
| 1143 m_gl->Clear(GL_COLOR_BUFFER_BIT); | |
| 1144 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 1145 info.parameters.target, 0, 0); | |
| 1146 m_gl->DeleteFramebuffers(1, &fbo); | |
| 1147 restoreFramebufferBindings(); | |
| 1148 m_gl->ClearColor(m_clearColor[0], m_clearColor[1], m_clearColor[2], | |
| 1149 m_clearColor[3]); | |
| 1150 m_gl->ColorMask(m_colorMask[0], m_colorMask[1], m_colorMask[2], | |
| 1151 m_colorMask[3]); | |
| 1152 } | |
| 1153 | |
| 1154 RefPtr<DrawingBuffer::ColorBuffer> | |
| 1155 DrawingBuffer::createTextureAndAllocateMemory(const IntSize& size) { | |
| 1156 if (!shouldUseChromiumImage()) | |
| 1157 return createDefaultTextureAndAllocateMemory(size); | |
| 1158 | |
| 1159 TextureParameters parameters = chromiumImageTextureParameters(); | |
| 1160 GLuint imageId = m_gl->CreateGpuMemoryBufferImageCHROMIUM( | |
| 1161 size.width(), size.height(), parameters.creationInternalColorFormat, | |
| 1162 GC3D_SCANOUT_CHROMIUM); | |
| 1163 GLuint textureId = createColorTexture(parameters); | |
| 1164 if (imageId) { | 1153 if (imageId) { |
| 1165 m_gl->BindTexImage2DCHROMIUM(parameters.target, imageId); | 1154 m_gl->BindTexImage2DCHROMIUM(parameters.target, imageId); |
| 1155 } else { | |
| 1156 if (m_storageTextureSupported) { | |
| 1157 GLenum internalStorageFormat = GL_NONE; | |
| 1158 if (parameters.creationInternalColorFormat == GL_RGB) { | |
| 1159 internalStorageFormat = GL_RGB8; | |
| 1160 } else if (parameters.creationInternalColorFormat == GL_RGBA) { | |
| 1161 internalStorageFormat = GL_RGBA8; | |
| 1162 } else { | |
| 1163 NOTREACHED(); | |
| 1164 } | |
| 1165 m_gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internalStorageFormat, | |
| 1166 size.width(), size.height()); | |
| 1167 } else { | |
| 1168 m_gl->TexImage2D(parameters.target, 0, | |
| 1169 parameters.creationInternalColorFormat, size.width(), | |
| 1170 size.height(), 0, parameters.colorFormat, | |
| 1171 GL_UNSIGNED_BYTE, 0); | |
| 1172 } | |
| 1166 } | 1173 } |
| 1167 | 1174 |
| 1168 RefPtr<ColorBuffer> info(adoptRef(new ColorBuffer(this, parameters, size))); | 1175 // Clear the alpha channel if this is RGB emulated. |
| 1169 info->textureId = textureId; | 1176 if (contextProvider()->getCapabilities().chromium_image_rgb_emulation && |
| 1170 info->imageId = imageId; | 1177 !m_wantAlphaChannel) { |
|
Ken Russell (switch to Gerrit)
2016/10/07 03:33:53
Should this code path also be gated on shouldUseCh
ccameron
2016/10/07 08:28:23
Yes, good catch. I've conditionalized this on havi
| |
| 1171 clearChromiumImageAlpha(*info); | 1178 GLuint fbo = 0; |
| 1172 return info; | 1179 m_gl->GenFramebuffers(1, &fbo); |
| 1173 } | 1180 m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); |
| 1181 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 1182 parameters.target, textureId, 0); | |
| 1183 m_gl->ClearColor(0, 0, 0, 1); | |
| 1184 m_gl->ColorMask(false, false, false, true); | |
| 1185 m_gl->Clear(GL_COLOR_BUFFER_BIT); | |
| 1186 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 1187 parameters.target, 0, 0); | |
| 1188 m_gl->DeleteFramebuffers(1, &fbo); | |
| 1189 restoreFramebufferBindings(); | |
| 1190 m_gl->ClearColor(m_clearColor[0], m_clearColor[1], m_clearColor[2], | |
| 1191 m_clearColor[3]); | |
| 1192 m_gl->ColorMask(m_colorMask[0], m_colorMask[1], m_colorMask[2], | |
| 1193 m_colorMask[3]); | |
| 1194 } | |
| 1174 | 1195 |
| 1175 RefPtr<DrawingBuffer::ColorBuffer> | 1196 return adoptRef(new ColorBuffer(this, parameters, size, textureId, imageId)); |
| 1176 DrawingBuffer::createDefaultTextureAndAllocateMemory(const IntSize& size) { | |
| 1177 TextureParameters parameters = defaultTextureParameters(); | |
| 1178 RefPtr<ColorBuffer> info(adoptRef(new ColorBuffer(this, parameters, size))); | |
| 1179 info->textureId = createColorTexture(parameters); | |
| 1180 allocateConditionallyImmutableTexture( | |
| 1181 parameters.target, parameters.creationInternalColorFormat, size.width(), | |
| 1182 size.height(), 0, parameters.colorFormat, GL_UNSIGNED_BYTE); | |
| 1183 return info; | |
| 1184 } | 1197 } |
| 1185 | 1198 |
| 1186 void DrawingBuffer::attachColorBufferToReadFramebuffer() { | 1199 void DrawingBuffer::attachColorBufferToReadFramebuffer() { |
| 1187 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo); | 1200 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| 1188 | 1201 |
| 1189 GLenum target = m_backColorBuffer->parameters.target; | 1202 GLenum target = m_backColorBuffer->parameters.target; |
| 1190 GLenum id = m_backColorBuffer->textureId; | 1203 GLenum id = m_backColorBuffer->textureId; |
| 1191 | 1204 |
| 1192 m_gl->BindTexture(target, id); | 1205 m_gl->BindTexture(target, id); |
| 1193 | 1206 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1230 // the public interface for WebGL does not support GL_TEXTURE_RECTANGLE. | 1243 // the public interface for WebGL does not support GL_TEXTURE_RECTANGLE. |
| 1231 m_gl->BindTexture(GL_TEXTURE_2D, m_texture2DBinding); | 1244 m_gl->BindTexture(GL_TEXTURE_2D, m_texture2DBinding); |
| 1232 } | 1245 } |
| 1233 | 1246 |
| 1234 bool DrawingBuffer::shouldUseChromiumImage() { | 1247 bool DrawingBuffer::shouldUseChromiumImage() { |
| 1235 return RuntimeEnabledFeatures::webGLImageChromiumEnabled() && | 1248 return RuntimeEnabledFeatures::webGLImageChromiumEnabled() && |
| 1236 m_chromiumImageUsage == AllowChromiumImage; | 1249 m_chromiumImageUsage == AllowChromiumImage; |
| 1237 } | 1250 } |
| 1238 | 1251 |
| 1239 } // namespace blink | 1252 } // namespace blink |
| OLD | NEW |