1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h"
6 
7 #include "base/memory/read_only_shared_memory_region.h"
8 #include "cc/layers/texture_layer.h"
9 #include "cc/resources/cross_thread_shared_bitmap.h"
10 #include "components/viz/common/resources/bitmap_allocation.h"
11 #include "components/viz/common/resources/shared_bitmap.h"
12 #include "components/viz/common/resources/transferable_resource.h"
13 #include "gpu/command_buffer/client/gles2_interface.h"
14 #include "gpu/command_buffer/client/shared_image_interface.h"
15 #include "gpu/command_buffer/common/shared_image_usage.h"
16 #include "third_party/blink/public/platform/platform.h"
17 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
18 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
19 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
20 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
21 #include "third_party/blink/renderer/platform/graphics/color_behavior.h"
22 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
23 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
24 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
25 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
26 #include "third_party/blink/renderer/platform/wtf/functional.h"
27 #include "ui/gfx/geometry/size.h"
28 
29 namespace blink {
30 namespace {
31 
MakeAccelerated(const scoped_refptr<StaticBitmapImage> & source,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper)32 scoped_refptr<StaticBitmapImage> MakeAccelerated(
33     const scoped_refptr<StaticBitmapImage>& source,
34     base::WeakPtr<WebGraphicsContext3DProviderWrapper>
35         context_provider_wrapper) {
36   if (source->IsTextureBacked())
37     return source;
38 
39   auto paint_image = source->PaintImageForCurrentFrame();
40   auto provider = CanvasResourceProvider::CreateSharedImageProvider(
41       source->Size(), context_provider_wrapper, kLow_SkFilterQuality,
42       CanvasColorParams(paint_image.GetSkImage()->imageInfo()),
43       source->IsOriginTopLeft(), CanvasResourceProvider::RasterMode::kGPU,
44       gpu::SHARED_IMAGE_USAGE_DISPLAY);
45   if (!provider || !provider->IsAccelerated())
46     return nullptr;
47 
48   provider->Canvas()->drawImage(paint_image, 0, 0, nullptr);
49   return provider->Snapshot();
50 }
51 
52 }  // namespace
53 
ImageLayerBridge(OpacityMode opacity_mode)54 ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode)
55     : opacity_mode_(opacity_mode) {
56   layer_ = cc::TextureLayer::CreateForMailbox(this);
57   layer_->SetIsDrawable(true);
58   layer_->SetHitTestable(true);
59   layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
60   if (opacity_mode_ == kOpaque) {
61     layer_->SetContentsOpaque(true);
62     layer_->SetBlendBackgroundColor(false);
63   }
64 }
65 
~ImageLayerBridge()66 ImageLayerBridge::~ImageLayerBridge() {
67   if (!disposed_)
68     Dispose();
69 }
70 
SetImage(scoped_refptr<StaticBitmapImage> image)71 void ImageLayerBridge::SetImage(scoped_refptr<StaticBitmapImage> image) {
72   if (disposed_)
73     return;
74   // There could be the case that the current SkImage (encapsulated in the image
75   // parameter of the function) is null, that means that something went wrong
76   // during the creation of the image and we should not try and setImage with it
77   if (image && !image->PaintImageForCurrentFrame().GetSkImage())
78     return;
79 
80   image_ = std::move(image);
81   if (image_) {
82     if (opacity_mode_ == kNonOpaque) {
83       layer_->SetContentsOpaque(image_->CurrentFrameKnownToBeOpaque());
84       layer_->SetBlendBackgroundColor(!image_->CurrentFrameKnownToBeOpaque());
85     }
86     if (opacity_mode_ == kOpaque) {
87       // If we in opaque mode but image might have transparency we need to
88       // ensure its opacity is not used.
89       layer_->SetForceTextureToOpaque(!image_->CurrentFrameKnownToBeOpaque());
90     }
91     if (!has_presented_since_last_set_image_ && image_->IsTextureBacked()) {
92       // If the layer bridge is not presenting, the GrContext may not be getting
93       // flushed regularly.  The flush is normally triggered inside the
94       // m_image->EnsureMailbox() call of
95       // ImageLayerBridge::PrepareTransferableResource. To prevent a potential
96       // memory leak we must flush the GrContext here.
97       image_->PaintImageForCurrentFrame().GetSkImage()->getBackendTexture(
98           true);  // GrContext flush.
99     }
100   }
101   has_presented_since_last_set_image_ = false;
102 }
103 
SetUV(const FloatPoint & left_top,const FloatPoint & right_bottom)104 void ImageLayerBridge::SetUV(const FloatPoint& left_top,
105                              const FloatPoint& right_bottom) {
106   if (disposed_)
107     return;
108 
109   layer_->SetUV(left_top, right_bottom);
110 }
111 
Dispose()112 void ImageLayerBridge::Dispose() {
113   if (layer_) {
114     layer_->ClearClient();
115     layer_ = nullptr;
116   }
117   image_ = nullptr;
118   disposed_ = true;
119 }
120 
PrepareTransferableResource(cc::SharedBitmapIdRegistrar * bitmap_registrar,viz::TransferableResource * out_resource,std::unique_ptr<viz::SingleReleaseCallback> * out_release_callback)121 bool ImageLayerBridge::PrepareTransferableResource(
122     cc::SharedBitmapIdRegistrar* bitmap_registrar,
123     viz::TransferableResource* out_resource,
124     std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
125   if (disposed_)
126     return false;
127 
128   if (!image_)
129     return false;
130 
131   if (has_presented_since_last_set_image_)
132     return false;
133 
134   has_presented_since_last_set_image_ = true;
135 
136   bool gpu_compositing = SharedGpuContext::IsGpuCompositingEnabled();
137   bool gpu_image = image_->IsTextureBacked();
138 
139   // Expect software images for software compositing.
140   if (!gpu_compositing && gpu_image)
141     return false;
142 
143   // If the texture comes from a software image then it does not need to be
144   // flipped.
145   layer_->SetFlipped(gpu_image);
146 
147   if (gpu_compositing) {
148     scoped_refptr<StaticBitmapImage> image_for_compositor =
149         MakeAccelerated(image_, SharedGpuContext::ContextProviderWrapper());
150     if (!image_for_compositor || !image_for_compositor->ContextProvider())
151       return false;
152 
153     const gfx::Size size(image_for_compositor->width(),
154                          image_for_compositor->height());
155     uint32_t filter =
156         filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
157     auto mailbox_holder = image_for_compositor->GetMailboxHolder();
158     auto* sii = image_for_compositor->ContextProvider()->SharedImageInterface();
159     bool is_overlay_candidate = sii->UsageForMailbox(mailbox_holder.mailbox) &
160                                 gpu::SHARED_IMAGE_USAGE_SCANOUT;
161 
162     *out_resource = viz::TransferableResource::MakeGL(
163         mailbox_holder.mailbox, filter, mailbox_holder.texture_target,
164         mailbox_holder.sync_token, size, is_overlay_candidate);
165     auto func =
166         WTF::Bind(&ImageLayerBridge::ResourceReleasedGpu,
167                   WrapWeakPersistent(this), std::move(image_for_compositor));
168     *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
169   } else {
170     // Readback if needed and retain the readback in image_ to prevent future
171     // readbacks
172     image_ = image_->MakeUnaccelerated();
173     if (!image_)
174       return false;
175 
176     sk_sp<SkImage> sk_image = image_->PaintImageForCurrentFrame().GetSkImage();
177     if (!sk_image)
178       return false;
179 
180     const gfx::Size size(image_->width(), image_->height());
181     viz::ResourceFormat resource_format = viz::RGBA_8888;
182     if (sk_image->colorType() == SkColorType::kRGBA_F16_SkColorType)
183       resource_format = viz::RGBA_F16;
184     RegisteredBitmap registered =
185         CreateOrRecycleBitmap(size, resource_format, bitmap_registrar);
186 
187     SkImageInfo dst_info =
188         SkImageInfo::Make(size.width(), size.height(), sk_image->colorType(),
189                           kPremul_SkAlphaType, sk_image->refColorSpace());
190     void* pixels = registered.bitmap->memory();
191 
192     // Copy from SkImage into SharedMemory owned by |registered|.
193     if (!sk_image->readPixels(dst_info, pixels, dst_info.minRowBytes(), 0, 0))
194       return false;
195 
196     *out_resource = viz::TransferableResource::MakeSoftware(
197         registered.bitmap->id(), size, resource_format);
198     if (RuntimeEnabledFeatures::CanvasColorManagementEnabled()) {
199       out_resource->color_space = sk_image->colorSpace()
200                                       ? gfx::ColorSpace(*sk_image->colorSpace())
201                                       : gfx::ColorSpace::CreateSRGB();
202     }
203     auto func = WTF::Bind(&ImageLayerBridge::ResourceReleasedSoftware,
204                           WrapWeakPersistent(this), std::move(registered));
205     *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
206   }
207 
208   return true;
209 }
210 
CreateOrRecycleBitmap(const gfx::Size & size,viz::ResourceFormat format,cc::SharedBitmapIdRegistrar * bitmap_registrar)211 ImageLayerBridge::RegisteredBitmap ImageLayerBridge::CreateOrRecycleBitmap(
212     const gfx::Size& size,
213     viz::ResourceFormat format,
214     cc::SharedBitmapIdRegistrar* bitmap_registrar) {
215   auto* it = std::remove_if(
216       recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
217       [&size, &format](const RegisteredBitmap& registered) {
218         unsigned src_bytes_per_pixel =
219             (registered.bitmap->format() == viz::RGBA_8888) ? 4 : 8;
220         unsigned target_bytes_per_pixel = (format == viz::RGBA_8888) ? 4 : 8;
221         return (registered.bitmap->size().GetArea() * src_bytes_per_pixel !=
222                 size.GetArea() * target_bytes_per_pixel);
223       });
224   recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin());
225 
226   if (!recycled_bitmaps_.IsEmpty()) {
227     RegisteredBitmap registered = std::move(recycled_bitmaps_.back());
228     recycled_bitmaps_.pop_back();
229     DCHECK(registered.bitmap->size() == size);
230     return registered;
231   }
232 
233   // There are no bitmaps to recycle so allocate a new one.
234   viz::SharedBitmapId id = viz::SharedBitmap::GenerateId();
235   base::MappedReadOnlyRegion shm =
236       viz::bitmap_allocation::AllocateSharedBitmap(size, format);
237 
238   RegisteredBitmap registered;
239   registered.bitmap = base::MakeRefCounted<cc::CrossThreadSharedBitmap>(
240       id, std::move(shm), size, format);
241   registered.registration =
242       bitmap_registrar->RegisterSharedBitmapId(id, registered.bitmap);
243 
244   return registered;
245 }
246 
ResourceReleasedGpu(scoped_refptr<StaticBitmapImage> image,const gpu::SyncToken & token,bool lost_resource)247 void ImageLayerBridge::ResourceReleasedGpu(
248     scoped_refptr<StaticBitmapImage> image,
249     const gpu::SyncToken& token,
250     bool lost_resource) {
251   if (image && image->IsValid()) {
252     DCHECK(image->IsTextureBacked());
253     if (token.HasData() && image->ContextProvider() &&
254         image->ContextProvider()->InterfaceBase()) {
255       image->ContextProvider()->InterfaceBase()->WaitSyncTokenCHROMIUM(
256           token.GetConstData());
257     }
258   }
259   // let 'image' go out of scope to release gpu resources.
260 }
261 
ResourceReleasedSoftware(RegisteredBitmap registered,const gpu::SyncToken & sync_token,bool lost_resource)262 void ImageLayerBridge::ResourceReleasedSoftware(
263     RegisteredBitmap registered,
264     const gpu::SyncToken& sync_token,
265     bool lost_resource) {
266   DCHECK(!sync_token.HasData());  // No sync tokens for software resources.
267   if (!disposed_ && !lost_resource)
268     recycled_bitmaps_.push_back(std::move(registered));
269 }
270 
CcLayer() const271 cc::Layer* ImageLayerBridge::CcLayer() const {
272   return layer_.get();
273 }
274 
275 ImageLayerBridge::RegisteredBitmap::RegisteredBitmap() = default;
276 ImageLayerBridge::RegisteredBitmap::RegisteredBitmap(RegisteredBitmap&& other) =
277     default;
278 ImageLayerBridge::RegisteredBitmap& ImageLayerBridge::RegisteredBitmap::
279 operator=(RegisteredBitmap&& other) = default;
280 
281 }  // namespace blink
282