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