|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "platform/graphics/gpu/ImageLayerBridge.h" | 
|  |  | 
|  | #include "components/viz/common/quads/shared_bitmap.h" | 
|  | #include "components/viz/common/resources/transferable_resource.h" | 
|  | #include "gpu/command_buffer/client/gles2_interface.h" | 
|  | #include "platform/graphics/AcceleratedStaticBitmapImage.h" | 
|  | #include "platform/graphics/ColorBehavior.h" | 
|  | #include "platform/graphics/GraphicsLayer.h" | 
|  | #include "platform/graphics/gpu/SharedGpuContext.h" | 
|  | #include "public/platform/Platform.h" | 
|  | #include "public/platform/WebCompositorSupport.h" | 
|  | #include "public/platform/WebExternalTextureLayer.h" | 
|  | #include "public/platform/WebGraphicsContext3DProvider.h" | 
|  |  | 
|  | namespace blink { | 
|  |  | 
|  | ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode) | 
|  | : opacity_mode_(opacity_mode) { | 
|  | layer_ = Platform::Current()->CompositorSupport()->CreateExternalTextureLayer( | 
|  | this); | 
|  | GraphicsLayer::RegisterContentsLayer(layer_->Layer()); | 
|  | layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality); | 
|  | if (opacity_mode_ == kOpaque) { | 
|  | layer_->SetOpaque(true); | 
|  | layer_->SetBlendBackgroundColor(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | ImageLayerBridge::~ImageLayerBridge() { | 
|  | if (!disposed_) | 
|  | Dispose(); | 
|  | } | 
|  |  | 
|  | void ImageLayerBridge::SetImage(scoped_refptr<StaticBitmapImage> image) { | 
|  | image_ = std::move(image); | 
|  | if (image_) { | 
|  | if (opacity_mode_ == kNonOpaque) { | 
|  | layer_->SetOpaque(image_->CurrentFrameKnownToBeOpaque()); | 
|  | layer_->SetBlendBackgroundColor(!image_->CurrentFrameKnownToBeOpaque()); | 
|  | } | 
|  | } | 
|  | if (!has_presented_since_last_set_image_ && image_ && | 
|  | image_->IsTextureBacked()) { | 
|  | // If the layer bridge is not presenting, the GrContext may not be getting | 
|  | // flushed regularly.  The flush is normally triggered inside the | 
|  | // m_image->EnsureMailbox() call of | 
|  | // ImageLayerBridge::PrepareTransferableResource. To prevent a potential | 
|  | // memory leak we must flush the GrContext here. | 
|  | image_->PaintImageForCurrentFrame().GetSkImage()->getTextureHandle( | 
|  | true);  // GrContext flush. | 
|  | } | 
|  | has_presented_since_last_set_image_ = false; | 
|  | } | 
|  |  | 
|  | void ImageLayerBridge::Dispose() { | 
|  | if (layer_) { | 
|  | GraphicsLayer::UnregisterContentsLayer(layer_->Layer()); | 
|  | layer_->ClearTexture(); | 
|  | layer_.reset(); | 
|  | } | 
|  | image_ = nullptr; | 
|  | disposed_ = true; | 
|  | } | 
|  |  | 
|  | bool ImageLayerBridge::PrepareTransferableResource( | 
|  | viz::TransferableResource* out_resource, | 
|  | std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) { | 
|  | if (disposed_) | 
|  | return false; | 
|  |  | 
|  | if (!image_) | 
|  | return false; | 
|  |  | 
|  | if (has_presented_since_last_set_image_) | 
|  | return false; | 
|  |  | 
|  | has_presented_since_last_set_image_ = true; | 
|  |  | 
|  | bool gpu_compositing = SharedGpuContext::IsGpuCompositingEnabled(); | 
|  | bool gpu_image = image_->IsTextureBacked(); | 
|  |  | 
|  | // Expect software images for software compositing. | 
|  | if (!gpu_compositing && gpu_image) | 
|  | return false; | 
|  |  | 
|  | // If the texture comes from a software image then it does not need to be | 
|  | // flipped. | 
|  | layer_->SetFlipped(gpu_image); | 
|  |  | 
|  | scoped_refptr<StaticBitmapImage> image_for_compositor; | 
|  |  | 
|  | // Upload to a texture if the compositor is expecting one. | 
|  | if (gpu_compositing && !image_->IsTextureBacked()) { | 
|  | image_for_compositor = | 
|  | image_->MakeAccelerated(SharedGpuContext::ContextProviderWrapper()); | 
|  | } else if (!gpu_compositing && image_->IsTextureBacked()) { | 
|  | image_for_compositor = image_->MakeUnaccelerated(); | 
|  | } else { | 
|  | image_for_compositor = image_; | 
|  | } | 
|  | DCHECK_EQ(image_for_compositor->IsTextureBacked(), gpu_compositing); | 
|  |  | 
|  | if (gpu_compositing) { | 
|  | uint32_t filter = | 
|  | filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR; | 
|  | image_for_compositor->EnsureMailbox(kUnverifiedSyncToken, filter); | 
|  | *out_resource = viz::TransferableResource::MakeGL( | 
|  | image_for_compositor->GetMailbox(), filter, GL_TEXTURE_2D, | 
|  | image_for_compositor->GetSyncToken()); | 
|  | auto func = | 
|  | WTF::Bind(&ImageLayerBridge::ResourceReleasedGpu, | 
|  | WrapWeakPersistent(this), std::move(image_for_compositor)); | 
|  | *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func)); | 
|  | } else { | 
|  | std::unique_ptr<viz::SharedBitmap> bitmap = | 
|  | CreateOrRecycleBitmap(image_for_compositor->Size()); | 
|  | if (!bitmap) | 
|  | return false; | 
|  |  | 
|  | sk_sp<SkImage> sk_image = | 
|  | image_for_compositor->PaintImageForCurrentFrame().GetSkImage(); | 
|  | if (!sk_image) | 
|  | return false; | 
|  |  | 
|  | SkImageInfo dst_info = | 
|  | SkImageInfo::MakeN32Premul(image_for_compositor->width(), 1); | 
|  | size_t row_bytes = image_for_compositor->width() * 4; | 
|  |  | 
|  | // Copy from SkImage into |bitmap|, while flipping the Y axis. | 
|  | for (int row = 0; row < image_for_compositor->height(); row++) { | 
|  | if (!sk_image->readPixels(dst_info, bitmap->pixels(), row_bytes, 0, 0)) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *out_resource = viz::TransferableResource::MakeSoftware( | 
|  | bitmap->id(), bitmap->sequence_number(), | 
|  | gfx::Size(image_for_compositor->width(), | 
|  | image_for_compositor->height())); | 
|  | auto func = WTF::Bind(&ImageLayerBridge::ResourceReleasedSoftware, | 
|  | WrapWeakPersistent(this), base::Passed(&bitmap), | 
|  | image_for_compositor->Size()); | 
|  | *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func)); | 
|  | } | 
|  |  | 
|  | // TODO(junov): Figure out how to get the color space info. | 
|  | // out_resource->color_space = ...; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<viz::SharedBitmap> ImageLayerBridge::CreateOrRecycleBitmap( | 
|  | const IntSize& size) { | 
|  | auto it = std::remove_if( | 
|  | recycled_bitmaps_.begin(), recycled_bitmaps_.end(), | 
|  | [&size](const RecycledBitmap& bitmap) { return bitmap.size != size; }); | 
|  | recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin()); | 
|  |  | 
|  | if (!recycled_bitmaps_.IsEmpty()) { | 
|  | RecycledBitmap recycled = std::move(recycled_bitmaps_.back()); | 
|  | recycled_bitmaps_.pop_back(); | 
|  | DCHECK(recycled.size == size); | 
|  | return std::move(recycled.bitmap); | 
|  | } | 
|  | return Platform::Current()->AllocateSharedBitmap(size); | 
|  | } | 
|  |  | 
|  | void ImageLayerBridge::ResourceReleasedGpu( | 
|  | scoped_refptr<StaticBitmapImage> image, | 
|  | const gpu::SyncToken& token, | 
|  | bool lost_resource) { | 
|  | if (image && image->IsValid()) { | 
|  | DCHECK(image->IsTextureBacked()); | 
|  | if (token.HasData() && image->ContextProvider() && | 
|  | image->ContextProvider()->ContextGL()) { | 
|  | image->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM( | 
|  | token.GetConstData()); | 
|  | } | 
|  | } | 
|  | // let 'image' go out of scope to release gpu resources. | 
|  | } | 
|  |  | 
|  | void ImageLayerBridge::ResourceReleasedSoftware( | 
|  | std::unique_ptr<viz::SharedBitmap> bitmap, | 
|  | const IntSize& size, | 
|  | const gpu::SyncToken& sync_token, | 
|  | bool lost_resource) { | 
|  | DCHECK(!sync_token.HasData());  // No sync tokens for software resources. | 
|  | if (!disposed_ && !lost_resource) { | 
|  | RecycledBitmap recycled = {std::move(bitmap), size}; | 
|  | recycled_bitmaps_.push_back(std::move(recycled)); | 
|  | } | 
|  | } | 
|  |  | 
|  | WebLayer* ImageLayerBridge::PlatformLayer() const { | 
|  | return layer_->Layer(); | 
|  | } | 
|  |  | 
|  | }  // namespace blink |