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 "base/logging.h"
6 #include "base/memory/shared_memory_mapping.h"
7 #include "base/memory/weak_ptr.h"
8 #include "components/viz/common/resources/shared_bitmap.h"
9 #include "gpu/command_buffer/client/gles2_interface.h"
10 #include "gpu/command_buffer/common/mailbox.h"
11 #include "gpu/command_buffer/common/sync_token.h"
12 #include "gpu/ipc/common/mailbox.mojom-blink.h"
13 #include "third_party/blink/renderer/platform/geometry/int_size.h"
14 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
15 #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
16 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
17 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
18 
19 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
20 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
21 
22 namespace gfx {
23 
24 class GpuMemoryBuffer;
25 
26 }  // namespace gfx
27 
28 namespace gpu {
29 namespace raster {
30 
31 class RasterInterface;
32 
33 }  // namespace raster
34 }  // namespace gpu
35 
36 namespace viz {
37 
38 class SingleReleaseCallback;
39 struct TransferableResource;
40 
41 }  // namespace viz
42 
43 namespace blink {
44 
45 class CanvasResourceProvider;
46 class StaticBitmapImage;
47 
48 // TODO(danakj): One day the gpu::mojom::Mailbox type should be shared with
49 // blink directly and we won't need to use gpu::mojom::blink::Mailbox, nor the
50 // conversion through WTF::Vector.
51 gpu::mojom::blink::MailboxPtr SharedBitmapIdToGpuMailboxPtr(
52     const viz::SharedBitmapId& id);
53 
54 // Generic resource interface, used for locking (RAII) and recycling pixel
55 // buffers of any type.
56 // Note that this object may be accessed across multiple threads but not
57 // concurrently. The caller is responsible to call Transfer on the object before
58 // using it on a different thread.
59 class PLATFORM_EXPORT CanvasResource
60     : public WTF::ThreadSafeRefCounted<CanvasResource> {
61  public:
62   virtual ~CanvasResource();
63 
64   // We perform a lazy copy on write if the canvas content needs to be updated
65   // while its current resource is in use. In order to avoid re-allocating
66   // resources, its preferable to reuse a resource if its no longer in use.
67   // This API indicates whether a resource can be recycled.
68   virtual bool IsRecycleable() const = 0;
69 
70   // Returns true if rendering to the resource is accelerated.
71   virtual bool IsAccelerated() const = 0;
72 
73   // Returns true if the resource can be used with accelerated compositing. This
74   // is different from IsAccelerated since a resource may be rendered to on the
75   // CPU but can be used with GPU compositing (using GMBs).
76   virtual bool SupportsAcceleratedCompositing() const = 0;
77 
78   // Returns true if the resource is still usable. It maybe not be valid in the
79   // case of a context loss or if we fail to initialize the memory backing for
80   // the resource.
81   virtual bool IsValid() const = 0;
82 
83   // When a resource is returned by the display compositor, a sync token is
84   // provided to indicate when the compositor's commands using the resource are
85   // executed on the GPU thread.
86   // However in some cases we need to ensure that the commands using the
87   // resource have finished executing on the GPU itself. This API indicates
88   // whether this is required. The primary use-case for this is GMBs rendered to
89   // on the CPU but composited on the GPU. Its important for the GPU reads to be
90   // finished before updating the resource on the CPU.
NeedsReadLockFences()91   virtual bool NeedsReadLockFences() const { return false; }
92 
93   // The bounds for this resource.
94   virtual IntSize Size() const = 0;
95 
96   // The mailbox which can be used to reference this resource in GPU commands.
97   // The sync mode indicates how the sync token for the resource should be
98   // prepared.
99   virtual const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) = 0;
100 
101   // A CanvasResource is not thread-safe and does not allow concurrent usage
102   // from multiple threads. But it maybe used from any thread. It remains bound
103   // to the current thread until Transfer is called. Note that while the
104   // resource maybe used for reads on any thread, it can be written to only on
105   // the thread where it was created.
Transfer()106   virtual void Transfer() {}
107 
108   // Returns the sync token to indicate when all writes to the current resource
109   // are finished on the GPU thread.
GetSyncToken()110   virtual const gpu::SyncToken GetSyncToken() {
111     NOTREACHED();
112     return gpu::SyncToken();
113   }
114 
115   // Provides a TransferableResource representation of this resource to share it
116   // with the compositor.
117   bool PrepareTransferableResource(viz::TransferableResource*,
118                                    std::unique_ptr<viz::SingleReleaseCallback>*,
119                                    MailboxSyncMode);
120 
121   // Issues a wait for this sync token on the context used by this resource for
122   // rendering.
123   void WaitSyncToken(const gpu::SyncToken&);
124 
125   virtual bool OriginClean() const = 0;
126   virtual void SetOriginClean(bool) = 0;
127 
128   // Provides a StaticBitmapImage wrapping this resource. Commonly used for
129   // snapshots not used in compositing (for instance to draw to another canvas).
130   virtual scoped_refptr<StaticBitmapImage> Bitmap() = 0;
131 
132   // Copies the contents of |image| to the resource's backing memory. Only
133   // CanvasResourceProvider and derivatives should call this.
134   virtual void TakeSkImage(sk_sp<SkImage> image) = 0;
135 
136   // Called when the resource is marked lost. Losing a resource does not mean
137   // that the backing memory has been destroyed, since the resource itself keeps
138   // a ref on that memory.
139   // It means that the consumer (commonly the compositor) can not provide a sync
140   // token for the resource to be safely recycled and its the GL state may be
141   // inconsistent with when the resource was given to the compositor. So it
142   // should not be recycled for writing again but can be safely read from.
143   virtual void NotifyResourceLost() = 0;
144 
SetFilterQuality(SkFilterQuality filter)145   void SetFilterQuality(SkFilterQuality filter) { filter_quality_ = filter; }
146   // The filter quality to use when the resource is drawn by the compositor.
FilterQuality()147   SkFilterQuality FilterQuality() const { return filter_quality_; }
148 
149   SkImageInfo CreateSkImageInfo() const;
150 
is_cross_thread()151   bool is_cross_thread() const {
152     return base::PlatformThread::CurrentRef() != owning_thread_ref_;
153   }
154 
155  protected:
156   CanvasResource(base::WeakPtr<CanvasResourceProvider>,
157                  SkFilterQuality,
158                  const CanvasColorParams&);
159 
160   // Called during resource destruction if the resource is destroyed on a thread
161   // other than where it was created. This implies that no context associated
162   // cleanup can be done and any resources tied to the context may be leaked. As
163   // such, a resource must be deleted on the owning thread and this should only
164   // be called when the owning thread and its associated context was torn down
165   // before this resource could be deleted.
Abandon()166   virtual void Abandon() { TearDown(); }
167 
168   // Returns true if the resource is backed by memory such that it can be used
169   // for direct scanout by the display.
IsOverlayCandidate()170   virtual bool IsOverlayCandidate() const { return false; }
171 
172   // Returns true if the resource is backed by memory that can be referenced
173   // using a mailbox.
174   virtual bool HasGpuMailbox() const = 0;
175 
176   // Destroys the backing memory and any other references to it kept alive by
177   // this object. This must be called from the same thread where the resource
178   // was created.
179   virtual void TearDown() = 0;
180 
181   gpu::InterfaceBase* InterfaceBase() const;
182   gpu::gles2::GLES2Interface* ContextGL() const;
183   gpu::raster::RasterInterface* RasterInterface() const;
184   GLenum GLFilter() const;
185   GrContext* GetGrContext() const;
186   virtual base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper()187   ContextProviderWrapper() const {
188     NOTREACHED();
189     return nullptr;
190   }
191   bool PrepareAcceleratedTransferableResource(
192       viz::TransferableResource* out_resource,
193       MailboxSyncMode);
194   bool PrepareUnacceleratedTransferableResource(
195       viz::TransferableResource* out_resource);
ColorParams()196   const CanvasColorParams& ColorParams() const { return color_params_; }
197   void OnDestroy();
Provider()198   CanvasResourceProvider* Provider() { return provider_.get(); }
WeakProvider()199   base::WeakPtr<CanvasResourceProvider> WeakProvider() { return provider_; }
200 
201   const base::PlatformThreadRef owning_thread_ref_;
202   const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
203 
204  protected:
205   // Returns the texture target for the resource.
TextureTarget()206   virtual GLenum TextureTarget() const {
207     NOTREACHED();
208     return 0;
209   }
210 
211  private:
212   // Sync token that was provided when resource was released
213   gpu::SyncToken sync_token_for_release_;
214   base::WeakPtr<CanvasResourceProvider> provider_;
215   SkFilterQuality filter_quality_;
216   CanvasColorParams color_params_;
217 #if DCHECK_IS_ON()
218   bool did_call_on_destroy_ = false;
219 #endif
220 };
221 
222 // Resource type for SharedBitmaps
223 class PLATFORM_EXPORT CanvasResourceSharedBitmap final : public CanvasResource {
224  public:
225   static scoped_refptr<CanvasResourceSharedBitmap> Create(
226       const IntSize&,
227       const CanvasColorParams&,
228       base::WeakPtr<CanvasResourceProvider>,
229       SkFilterQuality);
230   ~CanvasResourceSharedBitmap() override;
IsRecycleable()231   bool IsRecycleable() const final { return IsValid(); }
IsAccelerated()232   bool IsAccelerated() const final { return false; }
233   bool IsValid() const final;
SupportsAcceleratedCompositing()234   bool SupportsAcceleratedCompositing() const final { return false; }
NeedsReadLockFences()235   bool NeedsReadLockFences() const final { return false; }
236   void Abandon() final;
237   IntSize Size() const final;
238   void TakeSkImage(sk_sp<SkImage> image) final;
239   scoped_refptr<StaticBitmapImage> Bitmap() final;
OriginClean()240   bool OriginClean() const final { return is_origin_clean_; }
SetOriginClean(bool flag)241   void SetOriginClean(bool flag) final { is_origin_clean_ = flag; }
242   const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
243   void NotifyResourceLost() override;
244 
245  private:
246   void TearDown() override;
247   bool HasGpuMailbox() const override;
248 
249   CanvasResourceSharedBitmap(const IntSize&,
250                              const CanvasColorParams&,
251                              base::WeakPtr<CanvasResourceProvider>,
252                              SkFilterQuality);
253 
254   viz::SharedBitmapId shared_bitmap_id_;
255   base::WritableSharedMemoryMapping shared_mapping_;
256   IntSize size_;
257   bool is_origin_clean_ = true;
258 };
259 
260 // Resource type for SharedImage
261 class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource {
262  public:
263   static scoped_refptr<CanvasResourceSharedImage> Create(
264       const IntSize&,
265       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
266       base::WeakPtr<CanvasResourceProvider>,
267       SkFilterQuality,
268       const CanvasColorParams&,
269       bool is_origin_top_left,
270       bool is_accelerated,
271       uint32_t shared_image_usage_flags);
272   ~CanvasResourceSharedImage() override;
273 
IsRecycleable()274   bool IsRecycleable() const final { return true; }
IsAccelerated()275   bool IsAccelerated() const final { return is_accelerated_; }
SupportsAcceleratedCompositing()276   bool SupportsAcceleratedCompositing() const override { return true; }
277   bool IsValid() const final;
Size()278   IntSize Size() const final { return size_; }
279   scoped_refptr<StaticBitmapImage> Bitmap() final;
280   void Transfer() final;
281 
OriginClean()282   bool OriginClean() const final { return is_origin_clean_; }
SetOriginClean(bool value)283   void SetOriginClean(bool value) final { is_origin_clean_ = value; }
TakeSkImage(sk_sp<SkImage> image)284   void TakeSkImage(sk_sp<SkImage> image) final { NOTREACHED(); }
285   void NotifyResourceLost() final;
NeedsReadLockFences()286   bool NeedsReadLockFences() const final {
287     // If the resource is not accelerated, it will be written to on the CPU. We
288     // need read lock fences to ensure that all reads on the GPU are done when
289     // the resource is returned by the display compositor.
290     return !is_accelerated_;
291   }
292 
GetTextureIdForReadAccess()293   GLuint GetTextureIdForReadAccess() const {
294     return owning_thread_data().texture_id_for_read_access;
295   }
GetTextureIdForWriteAccess()296   GLuint GetTextureIdForWriteAccess() const {
297     return owning_thread_data().texture_id_for_write_access;
298   }
TextureTarget()299   GLenum TextureTarget() const override { return texture_target_; }
300 
301   void WillDraw();
has_read_access()302   bool has_read_access() const {
303     return owning_thread_data().bitmap_image_read_refs > 0u;
304   }
is_lost()305   bool is_lost() const { return owning_thread_data().is_lost; }
306   void CopyRenderingResultsToGpuMemoryBuffer(const sk_sp<SkImage>& image);
307   const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
308 
309  private:
310   // These members are either only accessed on the owning thread, or are only
311   // updated on the owning thread and then are read on a different thread.
312   // We ensure to correctly update their state in Transfer, which is called
313   // before a resource is used on a different thread.
314   struct OwningThreadData {
315     bool mailbox_needs_new_sync_token = true;
316     gpu::Mailbox shared_image_mailbox;
317     gpu::SyncToken sync_token;
318     size_t bitmap_image_read_refs = 0u;
319     MailboxSyncMode mailbox_sync_mode = kVerifiedSyncToken;
320     bool is_lost = false;
321 
322     // We need to create 2 representations if canvas is operating in single
323     // buffered mode to allow concurrent scopes for read and write access,
324     // because the Begin/EndSharedImageAccessDirectCHROMIUM APIs allow only one
325     // active access mode for a representation.
326     // In non single buffered mode, the 2 texture ids are the same.
327     GLuint texture_id_for_read_access = 0u;
328     GLuint texture_id_for_write_access = 0u;
329   };
330 
331   static void OnBitmapImageDestroyed(
332       scoped_refptr<CanvasResourceSharedImage> resource,
333       bool has_read_ref_on_texture,
334       const gpu::SyncToken& sync_token,
335       bool is_lost);
336 
337   void TearDown() override;
338   void Abandon() override;
339   base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
340       const override;
341   bool HasGpuMailbox() const override;
342   const gpu::SyncToken GetSyncToken() override;
IsOverlayCandidate()343   bool IsOverlayCandidate() const final { return is_overlay_candidate_; }
344 
345   CanvasResourceSharedImage(const IntSize&,
346                             base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
347                             base::WeakPtr<CanvasResourceProvider>,
348                             SkFilterQuality,
349                             const CanvasColorParams&,
350                             bool is_origin_top_left,
351                             bool is_accelerated,
352                             uint32_t shared_image_usage_flags);
353 
owning_thread_data()354   OwningThreadData& owning_thread_data() {
355     DCHECK(!is_cross_thread());
356     return owning_thread_data_;
357   }
owning_thread_data()358   const OwningThreadData& owning_thread_data() const {
359     DCHECK(!is_cross_thread());
360     return owning_thread_data_;
361   }
362 
363   // Can be read on any thread but updated only on the owning thread.
mailbox()364   const gpu::Mailbox& mailbox() const {
365     return owning_thread_data_.shared_image_mailbox;
366   }
mailbox_needs_new_sync_token()367   bool mailbox_needs_new_sync_token() const {
368     return owning_thread_data_.mailbox_needs_new_sync_token;
369   }
sync_token()370   const gpu::SyncToken& sync_token() const {
371     return owning_thread_data_.sync_token;
372   }
373 
374   // This should only be de-referenced on the owning thread but may be copied
375   // on a different thread.
376   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
377 
378   // This can be accessed on any thread, irrespective of whether there are
379   // active readers or not.
380   bool is_origin_clean_ = true;
381 
382   // GMB based software raster path. The resource is written to on the CPU but
383   // passed using the mailbox to the display compositor for use as an overlay.
384   std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
385 
386   // Accessed on any thread.
387   const IntSize size_;
388   const bool is_origin_top_left_;
389   const bool is_accelerated_;
390   const bool is_overlay_candidate_;
391   const GLenum texture_target_;
392   const bool use_oop_rasterization_;
393 
394   OwningThreadData owning_thread_data_;
395 };
396 
397 // Resource type for a given opaque external resource described on construction
398 // via a Mailbox; this CanvasResource IsAccelerated() by definition.
399 class PLATFORM_EXPORT ExternalCanvasResource final : public CanvasResource {
400  public:
401   static scoped_refptr<ExternalCanvasResource> Create(
402       const gpu::Mailbox&,
403       const IntSize&,
404       GLenum texture_target,
405       const CanvasColorParams&,
406       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
407       base::WeakPtr<CanvasResourceProvider>,
408       SkFilterQuality,
409       bool is_origin_top_left);
410   ~ExternalCanvasResource() override;
IsRecycleable()411   bool IsRecycleable() const final { return IsValid(); }
IsAccelerated()412   bool IsAccelerated() const final { return true; }
413   bool IsValid() const override;
SupportsAcceleratedCompositing()414   bool SupportsAcceleratedCompositing() const override { return true; }
NeedsReadLockFences()415   bool NeedsReadLockFences() const final { return false; }
OriginClean()416   bool OriginClean() const final { return is_origin_clean_; }
SetOriginClean(bool value)417   void SetOriginClean(bool value) final { is_origin_clean_ = value; }
418   void Abandon() final;
Size()419   IntSize Size() const final { return size_; }
420   void TakeSkImage(sk_sp<SkImage> image) final;
NotifyResourceLost()421   void NotifyResourceLost() override {
422     // Used for single buffering mode which doesn't need to care about sync
423     // token synchronization.
424   }
425 
426   scoped_refptr<StaticBitmapImage> Bitmap() override;
427   const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
428 
429  private:
430   void TearDown() override;
TextureTarget()431   GLenum TextureTarget() const final { return texture_target_; }
IsOverlayCandidate()432   bool IsOverlayCandidate() const final { return true; }
433   bool HasGpuMailbox() const override;
434   const gpu::SyncToken GetSyncToken() override;
435   base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
436       const override;
437 
438   ExternalCanvasResource(const gpu::Mailbox&,
439                          const IntSize&,
440                          GLenum texture_target,
441                          const CanvasColorParams&,
442                          base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
443                          base::WeakPtr<CanvasResourceProvider>,
444                          SkFilterQuality,
445                          bool is_origin_top_left);
446 
447   const base::WeakPtr<WebGraphicsContext3DProviderWrapper>
448       context_provider_wrapper_;
449   const IntSize size_;
450   const gpu::Mailbox mailbox_;
451   const GLenum texture_target_;
452   const bool is_origin_top_left_;
453 
454   gpu::SyncToken sync_token_;
455 
456   bool is_origin_clean_ = true;
457 };
458 
459 class PLATFORM_EXPORT CanvasResourceSwapChain final : public CanvasResource {
460  public:
461   static scoped_refptr<CanvasResourceSwapChain> Create(
462       const IntSize&,
463       const CanvasColorParams&,
464       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
465       base::WeakPtr<CanvasResourceProvider>,
466       SkFilterQuality);
467   ~CanvasResourceSwapChain() override;
IsRecycleable()468   bool IsRecycleable() const final { return IsValid(); }
IsAccelerated()469   bool IsAccelerated() const final { return true; }
470   bool IsValid() const override;
SupportsAcceleratedCompositing()471   bool SupportsAcceleratedCompositing() const override { return true; }
NeedsReadLockFences()472   bool NeedsReadLockFences() const final { return false; }
OriginClean()473   bool OriginClean() const final { return is_origin_clean_; }
SetOriginClean(bool value)474   void SetOriginClean(bool value) final { is_origin_clean_ = value; }
475   void Abandon() final;
Size()476   IntSize Size() const final { return size_; }
477   void TakeSkImage(sk_sp<SkImage> image) final;
NotifyResourceLost()478   void NotifyResourceLost() override {
479     // Used for single buffering mode which doesn't need to care about sync
480     // token synchronization.
481   }
482 
483   scoped_refptr<StaticBitmapImage> Bitmap() override;
484 
TextureTarget()485   GLenum TextureTarget() const final { return GL_TEXTURE_2D; }
GetBackingTextureHandleForOverwrite()486   GLuint GetBackingTextureHandleForOverwrite() {
487     return back_buffer_texture_id_;
488   }
489 
490   void PresentSwapChain();
491   const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
492 
493  private:
494   void TearDown() override;
IsOverlayCandidate()495   bool IsOverlayCandidate() const final { return true; }
496   bool HasGpuMailbox() const override;
497   const gpu::SyncToken GetSyncToken() override;
498   base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
499       const override;
500 
501   CanvasResourceSwapChain(const IntSize&,
502                           const CanvasColorParams&,
503                           base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
504                           base::WeakPtr<CanvasResourceProvider>,
505                           SkFilterQuality);
506 
507   const base::WeakPtr<WebGraphicsContext3DProviderWrapper>
508       context_provider_wrapper_;
509   const IntSize size_;
510   gpu::Mailbox front_buffer_mailbox_;
511   gpu::Mailbox back_buffer_mailbox_;
512   GLuint front_buffer_texture_id_ = 0u;
513   GLuint back_buffer_texture_id_ = 0u;
514   gpu::SyncToken sync_token_;
515 
516   bool is_origin_clean_ = true;
517 };
518 
519 }  // namespace blink
520 
521 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
522