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/canvas_resource.h"
6
7 #include <utility>
8
9 #include "base/memory/read_only_shared_memory_region.h"
10 #include "components/viz/common/resources/bitmap_allocation.h"
11 #include "components/viz/common/resources/resource_format_utils.h"
12 #include "components/viz/common/resources/single_release_callback.h"
13 #include "components/viz/common/resources/transferable_resource.h"
14 #include "gpu/GLES2/gl2extchromium.h"
15 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
16 #include "gpu/command_buffer/client/raster_interface.h"
17 #include "gpu/command_buffer/client/shared_image_interface.h"
18 #include "gpu/command_buffer/common/capabilities.h"
19 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
20 #include "gpu/command_buffer/common/shared_image_usage.h"
21 #include "gpu/command_buffer/common/sync_token.h"
22 #include "third_party/blink/public/platform/platform.h"
23 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
24 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
25 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
26 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
27 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
28 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
29 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
30 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
31 #include "third_party/blink/renderer/platform/wtf/functional.h"
32 #include "third_party/skia/include/gpu/GrContext.h"
33 #include "ui/gfx/buffer_format_util.h"
34 #include "ui/gfx/color_space.h"
35
36 namespace blink {
37
38 // TODO(danakj): One day the gpu::mojom::Mailbox type should be shared with
39 // blink directly and we won't need to use gpu::mojom::blink::Mailbox, nor the
40 // conversion through WTF::Vector.
SharedBitmapIdToGpuMailboxPtr(const viz::SharedBitmapId & id)41 gpu::mojom::blink::MailboxPtr SharedBitmapIdToGpuMailboxPtr(
42 const viz::SharedBitmapId& id) {
43 WTF::Vector<int8_t> name(GL_MAILBOX_SIZE_CHROMIUM);
44 for (int i = 0; i < GL_MAILBOX_SIZE_CHROMIUM; ++i)
45 name[i] = id.name[i];
46 return {base::in_place, name};
47 }
48
CanvasResource(base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality,const CanvasColorParams & color_params)49 CanvasResource::CanvasResource(base::WeakPtr<CanvasResourceProvider> provider,
50 SkFilterQuality filter_quality,
51 const CanvasColorParams& color_params)
52 : owning_thread_ref_(base::PlatformThread::CurrentRef()),
53 owning_thread_task_runner_(Thread::Current()->GetTaskRunner()),
54 provider_(std::move(provider)),
55 filter_quality_(filter_quality),
56 color_params_(color_params) {}
57
~CanvasResource()58 CanvasResource::~CanvasResource() {
59 #if DCHECK_IS_ON()
60 DCHECK(did_call_on_destroy_);
61 #endif
62 }
63
OnDestroy()64 void CanvasResource::OnDestroy() {
65 if (is_cross_thread()) {
66 // Destroyed on wrong thread. This can happen when the thread of origin was
67 // torn down, in which case the GPU context owning any underlying resources
68 // no longer exists.
69 Abandon();
70 } else {
71 if (provider_)
72 provider_->OnDestroyResource();
73 TearDown();
74 }
75 #if DCHECK_IS_ON()
76 did_call_on_destroy_ = true;
77 #endif
78 }
79
InterfaceBase() const80 gpu::InterfaceBase* CanvasResource::InterfaceBase() const {
81 if (!ContextProviderWrapper())
82 return nullptr;
83 return ContextProviderWrapper()->ContextProvider()->InterfaceBase();
84 }
85
ContextGL() const86 gpu::gles2::GLES2Interface* CanvasResource::ContextGL() const {
87 if (!ContextProviderWrapper())
88 return nullptr;
89 return ContextProviderWrapper()->ContextProvider()->ContextGL();
90 }
91
RasterInterface() const92 gpu::raster::RasterInterface* CanvasResource::RasterInterface() const {
93 if (!ContextProviderWrapper())
94 return nullptr;
95 return ContextProviderWrapper()->ContextProvider()->RasterInterface();
96 }
97
WaitSyncToken(const gpu::SyncToken & sync_token)98 void CanvasResource::WaitSyncToken(const gpu::SyncToken& sync_token) {
99 if (sync_token.HasData()) {
100 if (auto* interface_base = InterfaceBase())
101 interface_base->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
102 }
103 }
104
ReleaseFrameResources(base::WeakPtr<CanvasResourceProvider> resource_provider,scoped_refptr<CanvasResource> resource,const gpu::SyncToken & sync_token,bool lost_resource)105 static void ReleaseFrameResources(
106 base::WeakPtr<CanvasResourceProvider> resource_provider,
107 scoped_refptr<CanvasResource> resource,
108 const gpu::SyncToken& sync_token,
109 bool lost_resource) {
110 resource->WaitSyncToken(sync_token);
111
112 if (resource_provider)
113 resource_provider->NotifyTexParamsModified(resource.get());
114
115 // TODO(khushalsagar): If multiple readers had access to this resource, losing
116 // it once should make sure subsequent releases don't try to recycle this
117 // resource.
118 if (lost_resource)
119 resource->NotifyResourceLost();
120 if (resource_provider && !lost_resource && resource->IsRecycleable())
121 resource_provider->RecycleResource(std::move(resource));
122 }
123
PrepareTransferableResource(viz::TransferableResource * out_resource,std::unique_ptr<viz::SingleReleaseCallback> * out_callback,MailboxSyncMode sync_mode)124 bool CanvasResource::PrepareTransferableResource(
125 viz::TransferableResource* out_resource,
126 std::unique_ptr<viz::SingleReleaseCallback>* out_callback,
127 MailboxSyncMode sync_mode) {
128 DCHECK(IsValid());
129
130 DCHECK(out_callback);
131 auto func = WTF::Bind(&ReleaseFrameResources, provider_,
132 WTF::Passed(base::WrapRefCounted(this)));
133 *out_callback = viz::SingleReleaseCallback::Create(std::move(func));
134
135 if (!out_resource)
136 return true;
137 if (SupportsAcceleratedCompositing())
138 return PrepareAcceleratedTransferableResource(out_resource, sync_mode);
139 return PrepareUnacceleratedTransferableResource(out_resource);
140 }
141
PrepareAcceleratedTransferableResource(viz::TransferableResource * out_resource,MailboxSyncMode sync_mode)142 bool CanvasResource::PrepareAcceleratedTransferableResource(
143 viz::TransferableResource* out_resource,
144 MailboxSyncMode sync_mode) {
145 TRACE_EVENT0("blink",
146 "CanvasResource::PrepareAcceleratedTransferableResource");
147 // Gpu compositing is a prerequisite for compositing an accelerated resource
148 DCHECK(SharedGpuContext::IsGpuCompositingEnabled());
149 if (!ContextProviderWrapper())
150 return false;
151 const gpu::Mailbox& mailbox = GetOrCreateGpuMailbox(sync_mode);
152 if (mailbox.IsZero())
153 return false;
154
155 *out_resource = viz::TransferableResource::MakeGL(
156 mailbox, GLFilter(), TextureTarget(), GetSyncToken(), gfx::Size(Size()),
157 IsOverlayCandidate());
158
159 out_resource->color_space = color_params_.GetSamplerGfxColorSpace();
160 out_resource->format = color_params_.TransferableResourceFormat();
161 out_resource->read_lock_fences_enabled = NeedsReadLockFences();
162
163 return true;
164 }
165
PrepareUnacceleratedTransferableResource(viz::TransferableResource * out_resource)166 bool CanvasResource::PrepareUnacceleratedTransferableResource(
167 viz::TransferableResource* out_resource) {
168 TRACE_EVENT0("blink",
169 "CanvasResource::PrepareUnacceleratedTransferableResource");
170 const gpu::Mailbox& mailbox = GetOrCreateGpuMailbox(kVerifiedSyncToken);
171 if (mailbox.IsZero())
172 return false;
173
174 // For software compositing, the display compositor assumes an N32 format for
175 // the resource type and completely ignores the format set on the
176 // TransferableResource. Clients are expected to render in N32 format but use
177 // RGBA as the tagged format on resources.
178 *out_resource = viz::TransferableResource::MakeSoftware(
179 mailbox, gfx::Size(Size()), viz::RGBA_8888);
180
181 out_resource->color_space = color_params_.GetSamplerGfxColorSpace();
182
183 return true;
184 }
185
GetGrContext() const186 GrContext* CanvasResource::GetGrContext() const {
187 if (!ContextProviderWrapper())
188 return nullptr;
189 return ContextProviderWrapper()->ContextProvider()->GetGrContext();
190 }
191
CreateSkImageInfo() const192 SkImageInfo CanvasResource::CreateSkImageInfo() const {
193 return SkImageInfo::Make(
194 Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
195 ColorParams().GetSkAlphaType(), ColorParams().GetSkColorSpace());
196 }
197
GLFilter() const198 GLenum CanvasResource::GLFilter() const {
199 return filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
200 }
201
202 // CanvasResourceSharedBitmap
203 //==============================================================================
204
CanvasResourceSharedBitmap(const IntSize & size,const CanvasColorParams & color_params,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality)205 CanvasResourceSharedBitmap::CanvasResourceSharedBitmap(
206 const IntSize& size,
207 const CanvasColorParams& color_params,
208 base::WeakPtr<CanvasResourceProvider> provider,
209 SkFilterQuality filter_quality)
210 : CanvasResource(std::move(provider), filter_quality, color_params),
211 size_(size) {
212 // Software compositing lazily uses RGBA_8888 as the resource format
213 // everywhere but the content is expected to be rendered in N32 format.
214 base::MappedReadOnlyRegion shm = viz::bitmap_allocation::AllocateSharedBitmap(
215 gfx::Size(Size()), viz::RGBA_8888);
216
217 if (!shm.IsValid())
218 return;
219
220 shared_mapping_ = std::move(shm.mapping);
221 shared_bitmap_id_ = viz::SharedBitmap::GenerateId();
222
223 CanvasResourceDispatcher* resource_dispatcher =
224 Provider() ? Provider()->ResourceDispatcher() : nullptr;
225 if (resource_dispatcher) {
226 resource_dispatcher->DidAllocateSharedBitmap(
227 std::move(shm.region),
228 SharedBitmapIdToGpuMailboxPtr(shared_bitmap_id_));
229 }
230 }
231
~CanvasResourceSharedBitmap()232 CanvasResourceSharedBitmap::~CanvasResourceSharedBitmap() {
233 OnDestroy();
234 }
235
IsValid() const236 bool CanvasResourceSharedBitmap::IsValid() const {
237 return shared_mapping_.IsValid();
238 }
239
Size() const240 IntSize CanvasResourceSharedBitmap::Size() const {
241 return size_;
242 }
243
Bitmap()244 scoped_refptr<StaticBitmapImage> CanvasResourceSharedBitmap::Bitmap() {
245 if (!IsValid())
246 return nullptr;
247 // Construct an SkImage that references the shared memory buffer.
248 // The release callback holds a reference to |this| to ensure that the
249 // canvas resource that owns the shared memory stays alive at least until
250 // the SkImage is destroyed.
251 SkImageInfo image_info = SkImageInfo::Make(
252 Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
253 ColorParams().GetSkAlphaType(), ColorParams().GetSkColorSpace());
254 SkPixmap pixmap(image_info, shared_mapping_.memory(),
255 image_info.minRowBytes());
256 this->AddRef();
257 sk_sp<SkImage> sk_image = SkImage::MakeFromRaster(
258 pixmap,
259 [](const void*, SkImage::ReleaseContext resource_to_unref) {
260 static_cast<CanvasResourceSharedBitmap*>(resource_to_unref)->Release();
261 },
262 this);
263 auto image = UnacceleratedStaticBitmapImage::Create(sk_image);
264 image->SetOriginClean(is_origin_clean_);
265 return image;
266 }
267
Create(const IntSize & size,const CanvasColorParams & color_params,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality)268 scoped_refptr<CanvasResourceSharedBitmap> CanvasResourceSharedBitmap::Create(
269 const IntSize& size,
270 const CanvasColorParams& color_params,
271 base::WeakPtr<CanvasResourceProvider> provider,
272 SkFilterQuality filter_quality) {
273 auto resource = AdoptRef(new CanvasResourceSharedBitmap(
274 size, color_params, std::move(provider), filter_quality));
275 return resource->IsValid() ? resource : nullptr;
276 }
277
TearDown()278 void CanvasResourceSharedBitmap::TearDown() {
279 CanvasResourceDispatcher* resource_dispatcher =
280 Provider() ? Provider()->ResourceDispatcher() : nullptr;
281 if (resource_dispatcher && !shared_bitmap_id_.IsZero()) {
282 resource_dispatcher->DidDeleteSharedBitmap(
283 SharedBitmapIdToGpuMailboxPtr(shared_bitmap_id_));
284 }
285 shared_mapping_ = {};
286 }
287
Abandon()288 void CanvasResourceSharedBitmap::Abandon() {
289 shared_mapping_ = {};
290 }
291
NotifyResourceLost()292 void CanvasResourceSharedBitmap::NotifyResourceLost() {
293 // Release our reference to the shared memory mapping since the resource can
294 // no longer be safely recycled and this memory is needed for copy-on-write.
295 shared_mapping_ = {};
296 }
297
GetOrCreateGpuMailbox(MailboxSyncMode sync_mode)298 const gpu::Mailbox& CanvasResourceSharedBitmap::GetOrCreateGpuMailbox(
299 MailboxSyncMode sync_mode) {
300 return shared_bitmap_id_;
301 }
302
HasGpuMailbox() const303 bool CanvasResourceSharedBitmap::HasGpuMailbox() const {
304 return !shared_bitmap_id_.IsZero();
305 }
306
TakeSkImage(sk_sp<SkImage> image)307 void CanvasResourceSharedBitmap::TakeSkImage(sk_sp<SkImage> image) {
308 SkImageInfo image_info = SkImageInfo::Make(
309 Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
310 ColorParams().GetSkAlphaType(),
311 ColorParams().GetSkColorSpaceForSkSurfaces());
312
313 bool read_pixels_successful = image->readPixels(
314 image_info, shared_mapping_.memory(), image_info.minRowBytes(), 0, 0);
315 DCHECK(read_pixels_successful);
316 }
317
318 // CanvasResourceSharedImage
319 //==============================================================================
320
CanvasResourceSharedImage(const IntSize & size,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality,const CanvasColorParams & color_params,bool is_origin_top_left,bool is_accelerated,uint32_t shared_image_usage_flags)321 CanvasResourceSharedImage::CanvasResourceSharedImage(
322 const IntSize& size,
323 base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
324 base::WeakPtr<CanvasResourceProvider> provider,
325 SkFilterQuality filter_quality,
326 const CanvasColorParams& color_params,
327 bool is_origin_top_left,
328 bool is_accelerated,
329 uint32_t shared_image_usage_flags)
330 : CanvasResource(std::move(provider), filter_quality, color_params),
331 context_provider_wrapper_(std::move(context_provider_wrapper)),
332 size_(size),
333 is_origin_top_left_(is_origin_top_left),
334 is_accelerated_(is_accelerated),
335 is_overlay_candidate_(shared_image_usage_flags &
336 gpu::SHARED_IMAGE_USAGE_SCANOUT),
337 texture_target_(
338 is_overlay_candidate_
339 ? gpu::GetBufferTextureTarget(
340 gfx::BufferUsage::SCANOUT,
341 BufferFormat(ColorParams().TransferableResourceFormat()),
342 context_provider_wrapper_->ContextProvider()
343 ->GetCapabilities())
344 : GL_TEXTURE_2D),
345 use_oop_rasterization_(context_provider_wrapper_->ContextProvider()
346 ->GetCapabilities()
347 .supports_oop_raster) {
348 auto* gpu_memory_buffer_manager =
349 Platform::Current()->GetGpuMemoryBufferManager();
350 if (!is_accelerated_) {
351 DCHECK(gpu_memory_buffer_manager);
352 DCHECK(shared_image_usage_flags & gpu::SHARED_IMAGE_USAGE_DISPLAY);
353
354 gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
355 gfx::Size(size), ColorParams().GetBufferFormat(),
356 gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, gpu::kNullSurfaceHandle);
357 if (!gpu_memory_buffer_)
358 return;
359
360 gpu_memory_buffer_->SetColorSpace(color_params.GetStorageGfxColorSpace());
361 }
362
363 auto* shared_image_interface =
364 context_provider_wrapper_->ContextProvider()->SharedImageInterface();
365 DCHECK(shared_image_interface);
366
367 // The GLES2 flag is needed for rendering via GL using a GrContext.
368 if (use_oop_rasterization_) {
369 shared_image_usage_flags = shared_image_usage_flags |
370 gpu::SHARED_IMAGE_USAGE_RASTER |
371 gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
372 } else {
373 shared_image_usage_flags = shared_image_usage_flags |
374 gpu::SHARED_IMAGE_USAGE_GLES2 |
375 gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
376 }
377
378 gpu::Mailbox shared_image_mailbox;
379 if (gpu_memory_buffer_) {
380 shared_image_mailbox = shared_image_interface->CreateSharedImage(
381 gpu_memory_buffer_.get(), gpu_memory_buffer_manager,
382 ColorParams().GetStorageGfxColorSpace(), shared_image_usage_flags);
383 } else {
384 shared_image_mailbox = shared_image_interface->CreateSharedImage(
385 ColorParams().TransferableResourceFormat(), gfx::Size(size),
386 ColorParams().GetStorageGfxColorSpace(), shared_image_usage_flags);
387 }
388
389 // Wait for the mailbox to be ready to be used.
390 WaitSyncToken(shared_image_interface->GenUnverifiedSyncToken());
391
392 auto* raster_interface = RasterInterface();
393 DCHECK(raster_interface);
394 owning_thread_data().shared_image_mailbox = shared_image_mailbox;
395
396 if (use_oop_rasterization_)
397 return;
398
399 owning_thread_data().texture_id_for_read_access =
400 raster_interface->CreateAndConsumeForGpuRaster(shared_image_mailbox);
401
402 // For the non-accelerated case, writes are done on the CPU. So we don't need
403 // a texture for writes.
404 if (!is_accelerated_)
405 return;
406 if (shared_image_usage_flags &
407 gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE) {
408 owning_thread_data().texture_id_for_write_access =
409 raster_interface->CreateAndConsumeForGpuRaster(shared_image_mailbox);
410 } else {
411 owning_thread_data().texture_id_for_write_access =
412 owning_thread_data().texture_id_for_read_access;
413 }
414 }
415
Create(const IntSize & size,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality,const CanvasColorParams & color_params,bool is_origin_top_left,bool is_accelerated,uint32_t shared_image_usage_flags)416 scoped_refptr<CanvasResourceSharedImage> CanvasResourceSharedImage::Create(
417 const IntSize& size,
418 base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
419 base::WeakPtr<CanvasResourceProvider> provider,
420 SkFilterQuality filter_quality,
421 const CanvasColorParams& color_params,
422 bool is_origin_top_left,
423 bool is_accelerated,
424 uint32_t shared_image_usage_flags) {
425 TRACE_EVENT0("blink", "CanvasResourceSharedImage::Create");
426 auto resource = base::AdoptRef(new CanvasResourceSharedImage(
427 size, std::move(context_provider_wrapper), std::move(provider),
428 filter_quality, color_params, is_origin_top_left, is_accelerated,
429 shared_image_usage_flags));
430 return resource->IsValid() ? resource : nullptr;
431 }
432
IsValid() const433 bool CanvasResourceSharedImage::IsValid() const {
434 return !mailbox().IsZero();
435 }
436
~CanvasResourceSharedImage()437 CanvasResourceSharedImage::~CanvasResourceSharedImage() {
438 OnDestroy();
439 }
440
TearDown()441 void CanvasResourceSharedImage::TearDown() {
442 DCHECK(!is_cross_thread());
443
444 // The context deletes all shared images on destruction which means no
445 // cleanup is needed if the context was lost.
446 if (ContextProviderWrapper()) {
447 auto* raster_interface = RasterInterface();
448 auto* shared_image_interface =
449 ContextProviderWrapper()->ContextProvider()->SharedImageInterface();
450 if (raster_interface && shared_image_interface) {
451 gpu::SyncToken shared_image_sync_token;
452 raster_interface->GenUnverifiedSyncTokenCHROMIUM(
453 shared_image_sync_token.GetData());
454 shared_image_interface->DestroySharedImage(shared_image_sync_token,
455 mailbox());
456 }
457 if (raster_interface) {
458 if (owning_thread_data().texture_id_for_read_access) {
459 raster_interface->DeleteGpuRasterTexture(
460 owning_thread_data().texture_id_for_read_access);
461 }
462 if (owning_thread_data().texture_id_for_write_access &&
463 owning_thread_data().texture_id_for_write_access !=
464 owning_thread_data().texture_id_for_read_access) {
465 raster_interface->DeleteGpuRasterTexture(
466 owning_thread_data().texture_id_for_write_access);
467 }
468 }
469 }
470
471 owning_thread_data().texture_id_for_read_access = 0u;
472 owning_thread_data().texture_id_for_write_access = 0u;
473 }
474
Abandon()475 void CanvasResourceSharedImage::Abandon() {
476 // Called when the owning thread has been torn down which will destroy the
477 // context on which the shared image was created so no cleanup is necessary.
478 }
479
WillDraw()480 void CanvasResourceSharedImage::WillDraw() {
481 DCHECK(!is_cross_thread())
482 << "Write access is only allowed on the owning thread";
483
484 // Sync token for software mode is generated from SharedImageInterface each
485 // time the GMB is updated.
486 if (!is_accelerated_)
487 return;
488
489 owning_thread_data().mailbox_needs_new_sync_token = true;
490 }
491
492 // static
OnBitmapImageDestroyed(scoped_refptr<CanvasResourceSharedImage> resource,bool has_read_ref_on_texture,const gpu::SyncToken & sync_token,bool is_lost)493 void CanvasResourceSharedImage::OnBitmapImageDestroyed(
494 scoped_refptr<CanvasResourceSharedImage> resource,
495 bool has_read_ref_on_texture,
496 const gpu::SyncToken& sync_token,
497 bool is_lost) {
498 DCHECK(!resource->is_cross_thread());
499
500 if (has_read_ref_on_texture) {
501 DCHECK(!resource->use_oop_rasterization_);
502 DCHECK_GT(resource->owning_thread_data().bitmap_image_read_refs, 0u);
503
504 resource->owning_thread_data().bitmap_image_read_refs--;
505 if (resource->owning_thread_data().bitmap_image_read_refs == 0u &&
506 resource->RasterInterface()) {
507 resource->RasterInterface()->EndSharedImageAccessDirectCHROMIUM(
508 resource->owning_thread_data().texture_id_for_read_access);
509 }
510 }
511
512 auto weak_provider = resource->WeakProvider();
513 ReleaseFrameResources(std::move(weak_provider), std::move(resource),
514 sync_token, is_lost);
515 }
516
Transfer()517 void CanvasResourceSharedImage::Transfer() {
518 if (is_cross_thread() || !ContextProviderWrapper())
519 return;
520
521 // TODO(khushalsagar): This is for consistency with MailboxTextureHolder
522 // transfer path. Its unclear why the verification can not be deferred until
523 // the resource needs to be transferred cross-process.
524 owning_thread_data().mailbox_sync_mode = kVerifiedSyncToken;
525 GetSyncToken();
526 }
527
Bitmap()528 scoped_refptr<StaticBitmapImage> CanvasResourceSharedImage::Bitmap() {
529 TRACE_EVENT0("blink", "CanvasResourceSharedImage::Bitmap");
530
531 SkImageInfo image_info = CreateSkImageInfo();
532 if (!is_accelerated_) {
533 if (!gpu_memory_buffer_->Map())
534 return nullptr;
535
536 SkPixmap pixmap(CreateSkImageInfo(), gpu_memory_buffer_->memory(0),
537 gpu_memory_buffer_->stride(0));
538 auto sk_image = SkImage::MakeRasterCopy(pixmap);
539 gpu_memory_buffer_->Unmap();
540 return sk_image ? UnacceleratedStaticBitmapImage::Create(sk_image)
541 : nullptr;
542 }
543
544 // In order to avoid creating multiple representations for this shared image
545 // on the same context, the AcceleratedStaticBitmapImage uses the texture id
546 // of the resource here. We keep a count of pending shared image releases to
547 // correctly scope the read lock for this texture.
548 // If this resource is accessed across threads, or the
549 // AcceleratedStaticBitmapImage is accessed on a different thread after being
550 // created here, the image will create a new representation from the mailbox
551 // rather than referring to the shared image's texture ID if it was provided
552 // below.
553 const bool has_read_ref_on_texture =
554 !is_cross_thread() && !use_oop_rasterization_;
555 GLuint texture_id_for_image = 0u;
556 if (has_read_ref_on_texture) {
557 texture_id_for_image = owning_thread_data().texture_id_for_read_access;
558 owning_thread_data().bitmap_image_read_refs++;
559 if (owning_thread_data().bitmap_image_read_refs == 1u &&
560 RasterInterface()) {
561 RasterInterface()->BeginSharedImageAccessDirectCHROMIUM(
562 texture_id_for_image, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
563 }
564 }
565
566 // The |release_callback| keeps a ref on this resource to ensure the backing
567 // shared image is kept alive until the lifetime of the image.
568 // Note that the code in CanvasResourceProvider::RecycleResource also uses the
569 // ref-count on the resource as a proxy for a read lock to allow recycling the
570 // resource once all refs have been released.
571 auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
572 &OnBitmapImageDestroyed, scoped_refptr<CanvasResourceSharedImage>(this),
573 has_read_ref_on_texture));
574
575 scoped_refptr<StaticBitmapImage> image;
576
577 // If its cross thread, then the sync token was already verified. If not, then
578 // we don't need one. The image lazily generates a token if needed.
579 gpu::SyncToken token = is_cross_thread() ? sync_token() : gpu::SyncToken();
580 image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
581 mailbox(), token, texture_id_for_image, image_info, texture_target_,
582 is_origin_top_left_, context_provider_wrapper_, owning_thread_ref_,
583 owning_thread_task_runner_, std::move(release_callback));
584
585 DCHECK(image);
586 return image;
587 }
588
CopyRenderingResultsToGpuMemoryBuffer(const sk_sp<SkImage> & image)589 void CanvasResourceSharedImage::CopyRenderingResultsToGpuMemoryBuffer(
590 const sk_sp<SkImage>& image) {
591 DCHECK(!is_cross_thread());
592
593 if (!ContextProviderWrapper() || !gpu_memory_buffer_->Map())
594 return;
595
596 auto surface = SkSurface::MakeRasterDirect(CreateSkImageInfo(),
597 gpu_memory_buffer_->memory(0),
598 gpu_memory_buffer_->stride(0));
599 surface->getCanvas()->drawImage(image, 0, 0);
600 auto* sii =
601 ContextProviderWrapper()->ContextProvider()->SharedImageInterface();
602 gpu_memory_buffer_->Unmap();
603 sii->UpdateSharedImage(gpu::SyncToken(), mailbox());
604 owning_thread_data().sync_token = sii->GenUnverifiedSyncToken();
605 }
606
GetOrCreateGpuMailbox(MailboxSyncMode sync_mode)607 const gpu::Mailbox& CanvasResourceSharedImage::GetOrCreateGpuMailbox(
608 MailboxSyncMode sync_mode) {
609 if (!is_cross_thread()) {
610 owning_thread_data().mailbox_sync_mode = sync_mode;
611 }
612 return mailbox();
613 }
614
HasGpuMailbox() const615 bool CanvasResourceSharedImage::HasGpuMailbox() const {
616 return !mailbox().IsZero();
617 }
618
GetSyncToken()619 const gpu::SyncToken CanvasResourceSharedImage::GetSyncToken() {
620 if (is_cross_thread()) {
621 // Sync token should be generated at Transfer time, which must always be
622 // called before cross-thread usage. And since we don't allow writes on
623 // another thread, the sync token generated at Transfer time shouldn't
624 // have been invalidated.
625 DCHECK(!mailbox_needs_new_sync_token());
626 DCHECK(sync_token().verified_flush());
627
628 return sync_token();
629 }
630
631 if (mailbox_needs_new_sync_token()) {
632 auto* raster_interface = RasterInterface();
633 DCHECK(raster_interface); // caller should already have early exited if
634 // !raster_interface.
635 raster_interface->GenUnverifiedSyncTokenCHROMIUM(
636 owning_thread_data().sync_token.GetData());
637 owning_thread_data().mailbox_needs_new_sync_token = false;
638 }
639
640 if (owning_thread_data().mailbox_sync_mode == kVerifiedSyncToken &&
641 !owning_thread_data().sync_token.verified_flush()) {
642 int8_t* token_data = owning_thread_data().sync_token.GetData();
643 auto* raster_interface = RasterInterface();
644 raster_interface->ShallowFlushCHROMIUM();
645 raster_interface->VerifySyncTokensCHROMIUM(&token_data, 1);
646 owning_thread_data().sync_token.SetVerifyFlush();
647 }
648
649 return sync_token();
650 }
651
NotifyResourceLost()652 void CanvasResourceSharedImage::NotifyResourceLost() {
653 owning_thread_data().is_lost = true;
654
655 if (WeakProvider())
656 Provider()->NotifyTexParamsModified(this);
657 }
658
659 base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper() const660 CanvasResourceSharedImage::ContextProviderWrapper() const {
661 DCHECK(!is_cross_thread());
662 return context_provider_wrapper_;
663 }
664
665 // ExternalCanvasResource
666 //==============================================================================
Create(const gpu::Mailbox & mailbox,const IntSize & size,GLenum texture_target,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality,bool is_origin_top_left)667 scoped_refptr<ExternalCanvasResource> ExternalCanvasResource::Create(
668 const gpu::Mailbox& mailbox,
669 const IntSize& size,
670 GLenum texture_target,
671 const CanvasColorParams& color_params,
672 base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
673 base::WeakPtr<CanvasResourceProvider> provider,
674 SkFilterQuality filter_quality,
675 bool is_origin_top_left) {
676 TRACE_EVENT0("blink", "ExternalCanvasResource::Create");
677 auto resource = AdoptRef(new ExternalCanvasResource(
678 mailbox, size, texture_target, color_params,
679 std::move(context_provider_wrapper), std::move(provider), filter_quality,
680 is_origin_top_left));
681 return resource->IsValid() ? resource : nullptr;
682 }
683
~ExternalCanvasResource()684 ExternalCanvasResource::~ExternalCanvasResource() {
685 OnDestroy();
686 }
687
IsValid() const688 bool ExternalCanvasResource::IsValid() const {
689 return context_provider_wrapper_ && !mailbox_.IsZero();
690 }
691
Abandon()692 void ExternalCanvasResource::Abandon() {
693 // We don't need to destroy the shared image mailbox since we don't own it.
694 }
695
TakeSkImage(sk_sp<SkImage> image)696 void ExternalCanvasResource::TakeSkImage(sk_sp<SkImage> image) {
697 NOTREACHED();
698 }
699
Bitmap()700 scoped_refptr<StaticBitmapImage> ExternalCanvasResource::Bitmap() {
701 TRACE_EVENT0("blink", "ExternalCanvasResource::Bitmap");
702 if (!IsValid())
703 return nullptr;
704
705 // The |release_callback| keeps a ref on this resource to ensure the backing
706 // shared image is kept alive until the lifetime of the image.
707 auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
708 [](scoped_refptr<ExternalCanvasResource> resource,
709 const gpu::SyncToken& sync_token, bool is_lost) {
710 // Do nothing but hold onto the refptr.
711 },
712 base::RetainedRef(this)));
713
714 return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
715 mailbox_, GetSyncToken(), /*shared_image_texture_id=*/0u,
716 CreateSkImageInfo(), texture_target_, is_origin_top_left_,
717 context_provider_wrapper_, owning_thread_ref_, owning_thread_task_runner_,
718 std::move(release_callback));
719 }
720
TearDown()721 void ExternalCanvasResource::TearDown() {
722 Abandon();
723 }
724
GetOrCreateGpuMailbox(MailboxSyncMode sync_mode)725 const gpu::Mailbox& ExternalCanvasResource::GetOrCreateGpuMailbox(
726 MailboxSyncMode sync_mode) {
727 TRACE_EVENT0("blink", "ExternalCanvasResource::GetOrCreateGpuMailbox");
728 DCHECK_EQ(sync_mode, kVerifiedSyncToken);
729 return mailbox_;
730 }
731
HasGpuMailbox() const732 bool ExternalCanvasResource::HasGpuMailbox() const {
733 return !mailbox_.IsZero();
734 }
735
GetSyncToken()736 const gpu::SyncToken ExternalCanvasResource::GetSyncToken() {
737 TRACE_EVENT0("blink", "ExternalCanvasResource::GetSyncToken");
738 if (!sync_token_.HasData()) {
739 auto* gl = ContextGL();
740 if (gl)
741 gl->GenSyncTokenCHROMIUM(sync_token_.GetData());
742 }
743 return sync_token_;
744 }
745
746 base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper() const747 ExternalCanvasResource::ContextProviderWrapper() const {
748 return context_provider_wrapper_;
749 }
750
ExternalCanvasResource(const gpu::Mailbox & mailbox,const IntSize & size,GLenum texture_target,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality,bool is_origin_top_left)751 ExternalCanvasResource::ExternalCanvasResource(
752 const gpu::Mailbox& mailbox,
753 const IntSize& size,
754 GLenum texture_target,
755 const CanvasColorParams& color_params,
756 base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
757 base::WeakPtr<CanvasResourceProvider> provider,
758 SkFilterQuality filter_quality,
759 bool is_origin_top_left)
760 : CanvasResource(std::move(provider), filter_quality, color_params),
761 context_provider_wrapper_(std::move(context_provider_wrapper)),
762 size_(size),
763 mailbox_(mailbox),
764 texture_target_(texture_target),
765 is_origin_top_left_(is_origin_top_left) {}
766
767 // CanvasResourceSwapChain
768 //==============================================================================
Create(const IntSize & size,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality)769 scoped_refptr<CanvasResourceSwapChain> CanvasResourceSwapChain::Create(
770 const IntSize& size,
771 const CanvasColorParams& color_params,
772 base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
773 base::WeakPtr<CanvasResourceProvider> provider,
774 SkFilterQuality filter_quality) {
775 TRACE_EVENT0("blink", "CanvasResourceSwapChain::Create");
776 auto resource = AdoptRef(new CanvasResourceSwapChain(
777 size, color_params, std::move(context_provider_wrapper),
778 std::move(provider), filter_quality));
779 return resource->IsValid() ? resource : nullptr;
780 }
781
~CanvasResourceSwapChain()782 CanvasResourceSwapChain::~CanvasResourceSwapChain() {
783 OnDestroy();
784 }
785
IsValid() const786 bool CanvasResourceSwapChain::IsValid() const {
787 return context_provider_wrapper_ && HasGpuMailbox();
788 }
789
TakeSkImage(sk_sp<SkImage> image)790 void CanvasResourceSwapChain::TakeSkImage(sk_sp<SkImage> image) {
791 NOTREACHED();
792 }
793
Bitmap()794 scoped_refptr<StaticBitmapImage> CanvasResourceSwapChain::Bitmap() {
795 SkImageInfo image_info = SkImageInfo::Make(
796 Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
797 ColorParams().GetSkAlphaType(), ColorParams().GetSkColorSpace());
798
799 // It's safe to share the front buffer texture id if we're on the same thread
800 // since the |release_callback| ensures this resource will be alive.
801 GLuint shared_texture_id = 0u;
802 if (!is_cross_thread())
803 shared_texture_id = front_buffer_texture_id_;
804
805 // The |release_callback| keeps a ref on this resource to ensure the backing
806 // shared image is kept alive until the lifetime of the image.
807 auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
808 [](scoped_refptr<CanvasResourceSwapChain>, const gpu::SyncToken&, bool) {
809 // Do nothing but hold onto the refptr.
810 },
811 base::RetainedRef(this)));
812
813 return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
814 front_buffer_mailbox_, sync_token_, shared_texture_id, image_info,
815 GL_TEXTURE_2D, true /*is_origin_top_left*/, context_provider_wrapper_,
816 owning_thread_ref_, owning_thread_task_runner_,
817 std::move(release_callback));
818 }
819
Abandon()820 void CanvasResourceSwapChain::Abandon() {
821 // Called when the owning thread has been torn down which will destroy the
822 // context on which the shared image was created so no cleanup is necessary.
823 }
824
TearDown()825 void CanvasResourceSwapChain::TearDown() {
826 // The context deletes all shared images on destruction which means no
827 // cleanup is needed if the context was lost.
828 if (!context_provider_wrapper_)
829 return;
830
831 auto* raster_interface =
832 context_provider_wrapper_->ContextProvider()->RasterInterface();
833 DCHECK(raster_interface);
834 raster_interface->EndSharedImageAccessDirectCHROMIUM(
835 front_buffer_texture_id_);
836 raster_interface->DeleteGpuRasterTexture(front_buffer_texture_id_);
837 raster_interface->EndSharedImageAccessDirectCHROMIUM(back_buffer_texture_id_);
838 raster_interface->DeleteGpuRasterTexture(back_buffer_texture_id_);
839 // No synchronization is needed here because the GL SharedImageRepresentation
840 // will keep the backing alive on the service until the textures are deleted.
841 auto* sii =
842 context_provider_wrapper_->ContextProvider()->SharedImageInterface();
843 DCHECK(sii);
844 sii->DestroySharedImage(gpu::SyncToken(), front_buffer_mailbox_);
845 sii->DestroySharedImage(gpu::SyncToken(), back_buffer_mailbox_);
846 }
847
GetOrCreateGpuMailbox(MailboxSyncMode sync_mode)848 const gpu::Mailbox& CanvasResourceSwapChain::GetOrCreateGpuMailbox(
849 MailboxSyncMode sync_mode) {
850 DCHECK_EQ(sync_mode, kVerifiedSyncToken);
851 return front_buffer_mailbox_;
852 }
853
HasGpuMailbox() const854 bool CanvasResourceSwapChain::HasGpuMailbox() const {
855 return !front_buffer_mailbox_.IsZero();
856 }
857
GetSyncToken()858 const gpu::SyncToken CanvasResourceSwapChain::GetSyncToken() {
859 DCHECK(sync_token_.verified_flush());
860 return sync_token_;
861 }
862
PresentSwapChain()863 void CanvasResourceSwapChain::PresentSwapChain() {
864 DCHECK(!is_cross_thread());
865 DCHECK(context_provider_wrapper_);
866 TRACE_EVENT0("blink", "CanvasResourceSwapChain::PresentSwapChain");
867
868 auto* raster_interface =
869 context_provider_wrapper_->ContextProvider()->RasterInterface();
870 DCHECK(raster_interface);
871
872 auto* sii =
873 context_provider_wrapper_->ContextProvider()->SharedImageInterface();
874 DCHECK(sii);
875
876 // Synchronize presentation and rendering.
877 raster_interface->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData());
878 sii->PresentSwapChain(sync_token_, back_buffer_mailbox_);
879 // This only gets called via the CanvasResourceDispatcher export path so a
880 // verified sync token will be needed ultimately.
881 sync_token_ = sii->GenVerifiedSyncToken();
882 raster_interface->WaitSyncTokenCHROMIUM(sync_token_.GetData());
883
884 // PresentSwapChain() flips the front and back buffers, but the mailboxes
885 // still refer to the current front and back buffer after present. So the
886 // front buffer contains the content we just rendered, and it needs to be
887 // copied into the back buffer to support a retained mode like canvas expects.
888 // The wait sync token ensure that the present executes before we do the copy.
889 raster_interface->CopySubTexture(front_buffer_mailbox_, back_buffer_mailbox_,
890 GL_TEXTURE_2D, 0, 0, 0, 0, size_.Width(),
891 size_.Height(), false /* unpack_flip_y */,
892 false /* unpack_premultiply_alpha */);
893 }
894
895 base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper() const896 CanvasResourceSwapChain::ContextProviderWrapper() const {
897 return context_provider_wrapper_;
898 }
899
CanvasResourceSwapChain(const IntSize & size,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceProvider> provider,SkFilterQuality filter_quality)900 CanvasResourceSwapChain::CanvasResourceSwapChain(
901 const IntSize& size,
902 const CanvasColorParams& color_params,
903 base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
904 base::WeakPtr<CanvasResourceProvider> provider,
905 SkFilterQuality filter_quality)
906 : CanvasResource(std::move(provider), filter_quality, color_params),
907 context_provider_wrapper_(std::move(context_provider_wrapper)),
908 size_(size) {
909 if (!context_provider_wrapper_)
910 return;
911
912 uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY |
913 gpu::SHARED_IMAGE_USAGE_GLES2 |
914 gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
915 gpu::SHARED_IMAGE_USAGE_SCANOUT;
916
917 auto* sii =
918 context_provider_wrapper_->ContextProvider()->SharedImageInterface();
919 DCHECK(sii);
920 gpu::SharedImageInterface::SwapChainMailboxes mailboxes =
921 sii->CreateSwapChain(ColorParams().TransferableResourceFormat(),
922 gfx::Size(size),
923 ColorParams().GetStorageGfxColorSpace(), usage);
924 back_buffer_mailbox_ = mailboxes.back_buffer;
925 front_buffer_mailbox_ = mailboxes.front_buffer;
926 sync_token_ = sii->GenVerifiedSyncToken();
927
928 // Wait for the mailboxes to be ready to be used.
929 auto* raster_interface =
930 context_provider_wrapper_->ContextProvider()->RasterInterface();
931 DCHECK(raster_interface);
932 raster_interface->WaitSyncTokenCHROMIUM(sync_token_.GetData());
933
934 front_buffer_texture_id_ =
935 raster_interface->CreateAndConsumeForGpuRaster(front_buffer_mailbox_);
936 raster_interface->BeginSharedImageAccessDirectCHROMIUM(
937 front_buffer_texture_id_, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
938
939 back_buffer_texture_id_ =
940 raster_interface->CreateAndConsumeForGpuRaster(back_buffer_mailbox_);
941 raster_interface->BeginSharedImageAccessDirectCHROMIUM(
942 back_buffer_texture_id_, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
943 }
944
945 } // namespace blink
946