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