1 // Copyright 2016 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/accelerated_static_bitmap_image.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "components/viz/common/resources/single_release_callback.h"
11 #include "gpu/GLES2/gl2extchromium.h"
12 #include "gpu/command_buffer/client/gles2_interface.h"
13 #include "gpu/command_buffer/client/raster_interface.h"
14 #include "gpu/command_buffer/client/shared_image_interface.h"
15 #include "gpu/command_buffer/common/sync_token.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/canvas_resource_provider.h"
19 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
20 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
21 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
22 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
23 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
24 #include "third_party/skia/include/core/SkImage.h"
25 
26 namespace blink {
27 namespace {
28 
ReleaseCallbackOnContextThread(std::unique_ptr<viz::SingleReleaseCallback> callback,const gpu::SyncToken sync_token)29 void ReleaseCallbackOnContextThread(
30     std::unique_ptr<viz::SingleReleaseCallback> callback,
31     const gpu::SyncToken sync_token) {
32   callback->Run(sync_token, /* is_lost = */ false);
33 }
34 
35 }  // namespace
36 
MailboxRef(const gpu::SyncToken & sync_token,base::PlatformThreadRef context_thread_ref,scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,std::unique_ptr<viz::SingleReleaseCallback> release_callback)37 AcceleratedStaticBitmapImage::MailboxRef::MailboxRef(
38     const gpu::SyncToken& sync_token,
39     base::PlatformThreadRef context_thread_ref,
40     scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
41     std::unique_ptr<viz::SingleReleaseCallback> release_callback)
42     : sync_token_(sync_token),
43       context_thread_ref_(context_thread_ref),
44       context_task_runner_(std::move(context_task_runner)),
45       release_callback_(std::move(release_callback)) {
46   DCHECK(!is_cross_thread() || sync_token_.verified_flush());
47 }
48 
~MailboxRef()49 AcceleratedStaticBitmapImage::MailboxRef::~MailboxRef() {
50   if (context_thread_ref_ == base::PlatformThread::CurrentRef()) {
51     ReleaseCallbackOnContextThread(std::move(release_callback_), sync_token_);
52   } else {
53     context_task_runner_->PostTask(
54         FROM_HERE, base::BindOnce(&ReleaseCallbackOnContextThread,
55                                   std::move(release_callback_), sync_token_));
56   }
57 }
58 
59 const gpu::SyncToken&
GetOrCreateSyncToken(base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper)60 AcceleratedStaticBitmapImage::MailboxRef::GetOrCreateSyncToken(
61     base::WeakPtr<WebGraphicsContext3DProviderWrapper>
62         context_provider_wrapper) {
63   if (!sync_token_.HasData()) {
64     DCHECK(!is_cross_thread());
65     DCHECK(context_provider_wrapper);
66     context_provider_wrapper->ContextProvider()
67         ->InterfaceBase()
68         ->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData());
69   }
70   return sync_token_;
71 }
72 
73 // static
ReleaseTexture(void * ctx)74 void AcceleratedStaticBitmapImage::ReleaseTexture(void* ctx) {
75   auto* release_ctx = static_cast<ReleaseContext*>(ctx);
76   if (release_ctx->context_provider_wrapper) {
77     if (release_ctx->texture_id) {
78       auto* ri = release_ctx->context_provider_wrapper->ContextProvider()
79                      ->RasterInterface();
80       ri->EndSharedImageAccessDirectCHROMIUM(release_ctx->texture_id);
81       ri->DeleteGpuRasterTexture(release_ctx->texture_id);
82     }
83   }
84 
85   delete release_ctx;
86 }
87 
88 // static
89 scoped_refptr<AcceleratedStaticBitmapImage>
CreateFromCanvasMailbox(const gpu::Mailbox & mailbox,const gpu::SyncToken & sync_token,GLuint shared_image_texture_id,const SkImageInfo & sk_image_info,GLenum texture_target,bool is_origin_top_left,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::PlatformThreadRef context_thread_ref,scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,std::unique_ptr<viz::SingleReleaseCallback> release_callback)90 AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
91     const gpu::Mailbox& mailbox,
92     const gpu::SyncToken& sync_token,
93     GLuint shared_image_texture_id,
94     const SkImageInfo& sk_image_info,
95     GLenum texture_target,
96     bool is_origin_top_left,
97     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
98     base::PlatformThreadRef context_thread_ref,
99     scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
100     std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
101   return base::AdoptRef(new AcceleratedStaticBitmapImage(
102       mailbox, sync_token, shared_image_texture_id, sk_image_info,
103       texture_target, is_origin_top_left, kDefaultImageOrientation,
104       std::move(context_provider_wrapper), context_thread_ref,
105       std::move(context_task_runner), std::move(release_callback)));
106 }
107 
AcceleratedStaticBitmapImage(const gpu::Mailbox & mailbox,const gpu::SyncToken & sync_token,GLuint shared_image_texture_id,const SkImageInfo & sk_image_info,GLenum texture_target,bool is_origin_top_left,const ImageOrientation & orientation,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::PlatformThreadRef context_thread_ref,scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,std::unique_ptr<viz::SingleReleaseCallback> release_callback)108 AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
109     const gpu::Mailbox& mailbox,
110     const gpu::SyncToken& sync_token,
111     GLuint shared_image_texture_id,
112     const SkImageInfo& sk_image_info,
113     GLenum texture_target,
114     bool is_origin_top_left,
115     const ImageOrientation& orientation,
116     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
117     base::PlatformThreadRef context_thread_ref,
118     scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
119     std::unique_ptr<viz::SingleReleaseCallback> release_callback)
120     : StaticBitmapImage(orientation),
121       mailbox_(mailbox),
122       sk_image_info_(sk_image_info),
123       texture_target_(texture_target),
124       is_origin_top_left_(is_origin_top_left),
125       context_provider_wrapper_(std::move(context_provider_wrapper)),
126       mailbox_ref_(
127           base::MakeRefCounted<MailboxRef>(sync_token,
128                                            context_thread_ref,
129                                            std::move(context_task_runner),
130                                            std::move(release_callback))),
131       paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
132   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
133   DCHECK(mailbox_.IsSharedImage());
134 
135   if (shared_image_texture_id)
136     InitializeSkImage(shared_image_texture_id);
137 }
138 
~AcceleratedStaticBitmapImage()139 AcceleratedStaticBitmapImage::~AcceleratedStaticBitmapImage() {
140   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
141 }
142 
Size() const143 IntSize AcceleratedStaticBitmapImage::Size() const {
144   return IntSize(sk_image_info_.width(), sk_image_info_.height());
145 }
146 
147 scoped_refptr<StaticBitmapImage>
MakeUnaccelerated()148 AcceleratedStaticBitmapImage::MakeUnaccelerated() {
149   CreateImageFromMailboxIfNeeded();
150   return UnacceleratedStaticBitmapImage::Create(
151       sk_image_->makeNonTextureImage(), orientation_);
152 }
153 
CopyToTexture(gpu::gles2::GLES2Interface * dest_gl,GLenum dest_target,GLuint dest_texture_id,GLint dest_level,bool unpack_premultiply_alpha,bool unpack_flip_y,const IntPoint & dest_point,const IntRect & source_sub_rectangle)154 bool AcceleratedStaticBitmapImage::CopyToTexture(
155     gpu::gles2::GLES2Interface* dest_gl,
156     GLenum dest_target,
157     GLuint dest_texture_id,
158     GLint dest_level,
159     bool unpack_premultiply_alpha,
160     bool unpack_flip_y,
161     const IntPoint& dest_point,
162     const IntRect& source_sub_rectangle) {
163   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
164   if (!IsValid())
165     return false;
166 
167   // This method should only be used for cross-context copying, otherwise it's
168   // wasting overhead.
169   DCHECK(mailbox_ref_->is_cross_thread() ||
170          dest_gl != ContextProvider()->ContextGL());
171   DCHECK(mailbox_.IsSharedImage());
172 
173   // Get a texture id that |destProvider| knows about and copy from it.
174   dest_gl->WaitSyncTokenCHROMIUM(
175       mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper())
176           .GetConstData());
177   GLuint source_texture_id =
178       dest_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox_.name);
179   dest_gl->BeginSharedImageAccessDirectCHROMIUM(
180       source_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
181   dest_gl->CopySubTextureCHROMIUM(
182       source_texture_id, 0, dest_target, dest_texture_id, dest_level,
183       dest_point.X(), dest_point.Y(), source_sub_rectangle.X(),
184       source_sub_rectangle.Y(), source_sub_rectangle.Width(),
185       source_sub_rectangle.Height(), unpack_flip_y ? GL_FALSE : GL_TRUE,
186       GL_FALSE, unpack_premultiply_alpha ? GL_FALSE : GL_TRUE);
187   dest_gl->EndSharedImageAccessDirectCHROMIUM(source_texture_id);
188   dest_gl->DeleteTextures(1, &source_texture_id);
189 
190   // We need to update the texture holder's sync token to ensure that when this
191   // mailbox is recycled or deleted, it is done after the copy operation above.
192   gpu::SyncToken sync_token;
193   dest_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
194   mailbox_ref_->set_sync_token(sync_token);
195 
196   return true;
197 }
198 
PaintImageForCurrentFrame()199 PaintImage AcceleratedStaticBitmapImage::PaintImageForCurrentFrame() {
200   // TODO(ccameron): This function should not ignore |colorBehavior|.
201   // https://crbug.com/672306
202   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
203   if (!IsValid())
204     return PaintImage();
205 
206   CreateImageFromMailboxIfNeeded();
207 
208   return CreatePaintImageBuilder()
209       .set_image(sk_image_, paint_image_content_id_)
210       .set_completion_state(PaintImage::CompletionState::DONE)
211       .TakePaintImage();
212 }
213 
Draw(cc::PaintCanvas * canvas,const cc::PaintFlags & flags,const FloatRect & dst_rect,const FloatRect & src_rect,RespectImageOrientationEnum should_respect_image_orientation,ImageClampingMode image_clamping_mode,ImageDecodingMode decode_mode)214 void AcceleratedStaticBitmapImage::Draw(
215     cc::PaintCanvas* canvas,
216     const cc::PaintFlags& flags,
217     const FloatRect& dst_rect,
218     const FloatRect& src_rect,
219     RespectImageOrientationEnum should_respect_image_orientation,
220     ImageClampingMode image_clamping_mode,
221     ImageDecodingMode decode_mode) {
222   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
223   auto paint_image = PaintImageForCurrentFrame();
224   if (!paint_image)
225     return;
226   auto paint_image_decoding_mode = ToPaintImageDecodingMode(decode_mode);
227   if (paint_image.decoding_mode() != paint_image_decoding_mode) {
228     paint_image = PaintImageBuilder::WithCopy(std::move(paint_image))
229                       .set_decoding_mode(paint_image_decoding_mode)
230                       .TakePaintImage();
231   }
232   StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect,
233                                 image_clamping_mode,
234                                 should_respect_image_orientation, paint_image);
235 }
236 
IsValid() const237 bool AcceleratedStaticBitmapImage::IsValid() const {
238   if (sk_image_ && (!skia_context_provider_wrapper_ ||
239                     !sk_image_->isValid(ContextProvider()->GetGrContext()))) {
240     return false;
241   }
242 
243   if (mailbox_ref_->is_cross_thread()) {
244     // If context is is from another thread, validity cannot be verified. Just
245     // assume valid. Potential problem will be detected later.
246     return true;
247   }
248 
249   return !!context_provider_wrapper_;
250 }
251 
ContextProvider() const252 WebGraphicsContext3DProvider* AcceleratedStaticBitmapImage::ContextProvider()
253     const {
254   auto context = ContextProviderWrapper();
255   return context ? context->ContextProvider() : nullptr;
256 }
257 
258 base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper() const259 AcceleratedStaticBitmapImage::ContextProviderWrapper() const {
260   return sk_image_ ? skia_context_provider_wrapper_ : context_provider_wrapper_;
261 }
262 
CreateImageFromMailboxIfNeeded()263 void AcceleratedStaticBitmapImage::CreateImageFromMailboxIfNeeded() {
264   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
265   if (sk_image_)
266     return;
267   InitializeSkImage(0u);
268 }
269 
InitializeSkImage(GLuint shared_image_texture_id)270 void AcceleratedStaticBitmapImage::InitializeSkImage(
271     GLuint shared_image_texture_id) {
272   DCHECK(!shared_image_texture_id || !mailbox_ref_->is_cross_thread());
273 
274   auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper();
275   if (!context_provider_wrapper)
276     return;
277 
278   gpu::raster::RasterInterface* shared_ri =
279       context_provider_wrapper->ContextProvider()->RasterInterface();
280   GrContext* shared_gr_context =
281       context_provider_wrapper->ContextProvider()->GetGrContext();
282   DCHECK(shared_ri &&
283          shared_gr_context);  // context isValid already checked in callers
284 
285   GLuint shared_context_texture_id = 0u;
286   bool should_delete_texture_on_release = true;
287 
288   if (shared_image_texture_id) {
289     shared_context_texture_id = shared_image_texture_id;
290     should_delete_texture_on_release = false;
291   } else {
292     shared_ri->WaitSyncTokenCHROMIUM(
293         mailbox_ref_->GetOrCreateSyncToken(context_provider_wrapper)
294             .GetConstData());
295     shared_context_texture_id =
296         shared_ri->CreateAndConsumeForGpuRaster(mailbox_);
297     shared_ri->BeginSharedImageAccessDirectCHROMIUM(
298         shared_context_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
299   }
300 
301   GrGLTextureInfo texture_info;
302   texture_info.fTarget = texture_target_;
303   texture_info.fID = shared_context_texture_id;
304   texture_info.fFormat =
305       CanvasColorParams(sk_image_info_).GLSizedInternalFormat();
306   GrBackendTexture backend_texture(sk_image_info_.width(),
307                                    sk_image_info_.height(), GrMipMapped::kNo,
308                                    texture_info);
309 
310   GrSurfaceOrigin origin = IsOriginTopLeft() ? kTopLeft_GrSurfaceOrigin
311                                              : kBottomLeft_GrSurfaceOrigin;
312 
313   auto* release_ctx = new ReleaseContext;
314   release_ctx->mailbox_ref = mailbox_ref_;
315   if (should_delete_texture_on_release)
316     release_ctx->texture_id = shared_context_texture_id;
317   release_ctx->context_provider_wrapper = context_provider_wrapper;
318 
319   sk_image_ = SkImage::MakeFromTexture(
320       shared_gr_context, backend_texture, origin, sk_image_info_.colorType(),
321       sk_image_info_.alphaType(), sk_image_info_.refColorSpace(),
322       &ReleaseTexture, release_ctx);
323   if (!sk_image_)
324     ReleaseTexture(release_ctx);
325   else
326     skia_context_provider_wrapper_ = std::move(context_provider_wrapper);
327 }
328 
EnsureSyncTokenVerified()329 void AcceleratedStaticBitmapImage::EnsureSyncTokenVerified() {
330   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
331 
332   if (mailbox_ref_->verified_flush())
333     return;
334 
335   if (mailbox_ref_->is_cross_thread()) {
336     // Was originally created on another thread. Should already have a sync
337     // token from the original source context, already verified if needed.
338     NOTREACHED() << "Cross-thread SyncToken should already be verified.";
339     return;
340   }
341 
342   if (!ContextProviderWrapper())
343     return;
344 
345   auto sync_token =
346       mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper());
347   int8_t* token_data = sync_token.GetData();
348   ContextProvider()->InterfaceBase()->VerifySyncTokensCHROMIUM(&token_data, 1);
349   sync_token.SetVerifyFlush();
350   mailbox_ref_->set_sync_token(sync_token);
351 }
352 
GetMailboxHolder() const353 gpu::MailboxHolder AcceleratedStaticBitmapImage::GetMailboxHolder() const {
354   if (!IsValid())
355     return gpu::MailboxHolder();
356 
357   return gpu::MailboxHolder(
358       mailbox_, mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper()),
359       texture_target_);
360 }
361 
Transfer()362 void AcceleratedStaticBitmapImage::Transfer() {
363   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
364   EnsureSyncTokenVerified();
365 
366   // SkImage is bound to the current thread so is no longer valid to use
367   // cross-thread.
368   sk_image_.reset();
369 
370   DETACH_FROM_THREAD(thread_checker_);
371 }
372 
CurrentFrameKnownToBeOpaque()373 bool AcceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() {
374   return sk_image_info_.isOpaque();
375 }
376 
377 scoped_refptr<StaticBitmapImage>
ConvertToColorSpace(sk_sp<SkColorSpace> color_space,SkColorType color_type)378 AcceleratedStaticBitmapImage::ConvertToColorSpace(
379     sk_sp<SkColorSpace> color_space,
380     SkColorType color_type) {
381   DCHECK(color_space);
382   DCHECK(color_type == kRGBA_F16_SkColorType ||
383          color_type == kRGBA_8888_SkColorType);
384 
385   if (!ContextProviderWrapper())
386     return nullptr;
387 
388   sk_sp<SkImage> skia_image = PaintImageForCurrentFrame().GetSkImage();
389   if (SkColorSpace::Equals(color_space.get(), skia_image->colorSpace()) &&
390       color_type == skia_image->colorType()) {
391     return this;
392   }
393 
394   auto image_info = skia_image->imageInfo()
395                         .makeColorSpace(color_space)
396                         .makeColorType(color_type);
397 
398   auto usage_flags = ContextProviderWrapper()
399                          ->ContextProvider()
400                          ->SharedImageInterface()
401                          ->UsageForMailbox(mailbox_);
402   auto provider = CanvasResourceProvider::CreateSharedImageProvider(
403       Size(), ContextProviderWrapper(), kLow_SkFilterQuality,
404       CanvasColorParams(image_info), IsOriginTopLeft(),
405       CanvasResourceProvider::RasterMode::kGPU, usage_flags);
406   if (!provider) {
407     return nullptr;
408   }
409 
410   provider->Canvas()->drawImage(PaintImageForCurrentFrame(), 0, 0, nullptr);
411   return provider->Snapshot(orientation_);
412 }
413 
414 }  // namespace blink
415