blob: 1f72db49336ddb95626745b3f1aade9b006cea78 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/check_op.h"
#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "components/viz/common/resources/release_callback.h"
#include "components/viz/common/resources/shared_bitmap.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "skia/buildflags.h"
#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/size.h"
class GrBackendTexture;
class SkImage;
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
namespace gfx {
class ColorSpace;
} // namespace gfx
namespace gpu::raster {
class RasterInterface;
} // namespace gpu::raster
namespace blink {
class CanvasResourceProvider;
class StaticBitmapImage;
// Generic resource interface, used for locking (RAII) and recycling pixel
// buffers of any type.
// Note that this object may be accessed across multiple threads but not
// concurrently. The caller is responsible to call Transfer on the object before
// using it on a different thread.
class PLATFORM_EXPORT CanvasResource
: public WTF::ThreadSafeRefCounted<CanvasResource> {
public:
using ReleaseCallback = base::OnceCallback<void(
scoped_refptr<blink::CanvasResource>&& canvas_resource,
const gpu::SyncToken& sync_token,
bool is_lost)>;
using LastUnrefCallback = base::OnceCallback<void(
scoped_refptr<blink::CanvasResource> canvas_resource)>;
virtual ~CanvasResource();
// Non-virtual override of ThreadSafeRefCounted::Release
void Release();
// Set a callback that will be invoked as the last outstanding reference to
// this CanvasResource goes out of scope. This provides a last chance hook
// to intercept a canvas before it get destroyed. For resources that need to
// be destroyed on their thread of origin, this hook can be used to return
// resources to their creators.
void SetLastUnrefCallback(LastUnrefCallback callback) {
last_unref_callback_ = std::move(callback);
}
bool HasLastUnrefCallback() { return !!last_unref_callback_; }
// We perform a lazy copy on write if the canvas content needs to be updated
// while its current resource is in use. In order to avoid re-allocating
// resources, its preferable to reuse a resource if its no longer in use.
// This API indicates whether a resource can be recycled. This method does
// not however check whether the resource is still in use (e.g. has
// outstanding references).
virtual bool IsRecycleable() const = 0;
// Returns true if the resource can be used with accelerated compositing.
virtual bool SupportsAcceleratedCompositing() const = 0;
// Transfers ownership of the resource's vix::ReleaseCallback. This is useful
// prior to transferring a resource to another thread, to retain the release
// callback on the current thread since the callback may not be thread safe.
// Even if the callback is never executed on another thread, simply transiting
// through another thread is dangerous because garbage collection races may
// make it impossible to return the resource to its thread of origin for
// destruction; in which case the callback (and its bound arguments) may be
// destroyed on the wrong thread.
virtual viz::ReleaseCallback TakeVizReleaseCallback() {
return viz::ReleaseCallback();
}
virtual void SetVizReleaseCallback(viz::ReleaseCallback cb) {
CHECK(cb.is_null());
}
// Returns true if the resource is still usable. It maybe not be valid in the
// case of a context loss or if we fail to initialize the memory backing for
// the resource.
virtual bool IsValid() const = 0;
// When a resource is returned by the display compositor, a sync token is
// provided to indicate when the compositor's commands using the resource are
// executed on the GPU thread.
// However in some cases we need to ensure that the commands using the
// resource have finished executing on the GPU itself. This API indicates
// whether this is required. The primary use-case for this is GMBs rendered to
// on the CPU but composited on the GPU. Its important for the GPU reads to be
// finished before updating the resource on the CPU.
virtual bool NeedsReadLockFences() const { return false; }
// The bounds for this resource.
virtual gfx::Size Size() const = 0;
// Whether this is origin top-left or bottom-left image.
virtual bool IsOriginTopLeft() const { return true; }
// Whether this resource uses ClientSharedImage.
// TODO(crbug.com/351275962): Remove this method once
// CanvasResourceSharedBitmap holds ClientSharedImage and
// ExternalCanvasResource either holds ClientSharedImage or is removed.
virtual bool UsesClientSharedImage() { return false; }
// The ClientSharedImage containing information on the SharedImage (if any)
// attached to the resource.
// NOTE: Valid to call only if UsesClientSharedImage() is true.
virtual scoped_refptr<gpu::ClientSharedImage> GetClientSharedImage() {
NOTREACHED_NORETURN();
}
// A CanvasResource is not thread-safe and does not allow concurrent usage
// from multiple threads. But it maybe used from any thread. It remains bound
// to the current thread until Transfer is called. Note that while the
// resource maybe used for reads on any thread, it can be written to only on
// the thread where it was created.
virtual void Transfer() {}
// Returns the sync token to indicate when all writes to the current resource
// are finished on the GPU thread. Note that in some subclasses the token is
// not guaranteed to be verified at the time of calling this method. Passing
// true for `needs_verified_token` ensures that the returned token will be
// verified.
virtual const gpu::SyncToken GetSyncToken(bool needs_verified_token) {
NOTREACHED_IN_MIGRATION();
return gpu::SyncToken();
}
// Provides a TransferableResource representation of this resource to share it
// with the compositor.
bool PrepareTransferableResource(viz::TransferableResource*,
ReleaseCallback*,
bool needs_verified_synctoken);
// Issues a wait for this sync token on the context used by this resource for
// rendering.
void WaitSyncToken(const gpu::SyncToken&);
virtual bool OriginClean() const = 0;
virtual void SetOriginClean(bool) = 0;
// Provides a StaticBitmapImage wrapping this resource. Commonly used for
// snapshots not used in compositing (for instance to draw to another canvas).
virtual scoped_refptr<StaticBitmapImage> Bitmap() = 0;
// Copies the contents of |image| to the resource's backing memory. Only
// CanvasResourceProvider and derivatives should call this.
virtual void TakeSkImage(sk_sp<SkImage> image) = 0;
// Called when the resource is marked lost. Losing a resource does not mean
// that the backing memory has been destroyed, since the resource itself keeps
// a ref on that memory.
// It means that the consumer (commonly the compositor) can not provide a sync
// token for the resource to be safely recycled and its the GL state may be
// inconsistent with when the resource was given to the compositor. So it
// should not be recycled for writing again but can be safely read from.
virtual void NotifyResourceLost() = 0;
void SetFilterQuality(cc::PaintFlags::FilterQuality filter) {
filter_quality_ = filter;
}
// The filter quality to use when the resource is drawn by the compositor.
cc::PaintFlags::FilterQuality FilterQuality() const {
return filter_quality_;
}
// Returns the texture format used by this resource.
viz::SharedImageFormat GetSharedImageFormat() const;
SkImageInfo CreateSkImageInfo() const;
bool is_cross_thread() const {
return base::PlatformThread::CurrentRef() != owning_thread_ref_;
}
virtual bool HasDetailedMemoryDumpProvider() const { return false; }
protected:
CanvasResource(base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality,
const SkColorInfo&);
// Returns true if the resource is backed by memory such that it can be used
// for direct scanout by the display.
virtual bool IsOverlayCandidate() const { return false; }
gpu::InterfaceBase* InterfaceBase() const;
gpu::gles2::GLES2Interface* ContextGL() const;
gpu::raster::RasterInterface* RasterInterface() const;
gpu::webgpu::WebGPUInterface* WebGPUInterface() const;
gfx::ColorSpace GetColorSpace() const;
virtual base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper() const {
NOTREACHED_IN_MIGRATION();
return nullptr;
}
// Prepares GPU TransferableResource from the resource's ClientSharedImage.
// Invoked if the resource is accelerated and UsesClientSharedImage() returns
// true.
bool PrepareAcceleratedTransferableResourceFromClientSI(
viz::TransferableResource* out_resource,
bool needs_verified_synctoken);
// Prepares GPU TransferableResource for resources for which
// SupportsAcceleratedCompositing() is true but UsesClientSharedImage()
// returns false. Subclasses that match these conditions *must* override this
// method.
virtual bool PrepareAcceleratedTransferableResourceWithoutClientSI(
viz::TransferableResource* out_resource) {
NOTREACHED();
}
// Prepares software TransferableResource if supported (by default it is not).
// Subclasses that return false for SupportsAcceleratedCompositing() must
// override this method to implement support.
// NOTE: Will be called only if SupportsAcceleratedCompositing() is false.
virtual bool PrepareUnacceleratedTransferableResource(
viz::TransferableResource* out_resource) {
NOTREACHED_IN_MIGRATION();
return false;
}
const SkColorInfo& GetSkColorInfo() const { return info_; }
CanvasResourceProvider* Provider() { return provider_.get(); }
base::WeakPtr<CanvasResourceProvider> WeakProvider() { return provider_; }
const base::PlatformThreadRef owning_thread_ref_;
const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
private:
base::WeakPtr<CanvasResourceProvider> provider_;
SkColorInfo info_;
cc::PaintFlags::FilterQuality filter_quality_;
LastUnrefCallback last_unref_callback_;
};
// Resource type for SharedBitmaps
class PLATFORM_EXPORT CanvasResourceSharedBitmap final : public CanvasResource {
public:
static scoped_refptr<CanvasResourceSharedBitmap> Create(
const SkImageInfo&,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality);
~CanvasResourceSharedBitmap() override;
bool IsRecycleable() const final { return IsValid(); }
bool IsValid() const final;
bool SupportsAcceleratedCompositing() const final { return false; }
bool PrepareUnacceleratedTransferableResource(
viz::TransferableResource* out_resource) final;
bool NeedsReadLockFences() const final { return false; }
gfx::Size Size() const final;
void TakeSkImage(sk_sp<SkImage> image) final;
scoped_refptr<StaticBitmapImage> Bitmap() final;
bool OriginClean() const final { return is_origin_clean_; }
void SetOriginClean(bool flag) final { is_origin_clean_ = flag; }
void NotifyResourceLost() override;
private:
CanvasResourceSharedBitmap(const SkImageInfo&,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality);
viz::SharedBitmapId shared_bitmap_id_;
base::WritableSharedMemoryMapping shared_mapping_;
gfx::Size size_;
bool is_origin_clean_ = true;
};
// Resource type for SharedImage
class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource {
public:
static scoped_refptr<CanvasResourceSharedImage> Create(
const SkImageInfo&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality,
bool is_origin_top_left,
bool is_accelerated,
uint32_t shared_image_usage_flags);
~CanvasResourceSharedImage() override;
bool IsRecycleable() const final { return true; }
bool SupportsAcceleratedCompositing() const override { return true; }
bool IsValid() const final;
gfx::Size Size() const final { return size_; }
bool IsOriginTopLeft() const final { return is_origin_top_left_; }
scoped_refptr<StaticBitmapImage> Bitmap() final;
void Transfer() final;
bool OriginClean() const final { return is_origin_clean_; }
void SetOriginClean(bool value) final { is_origin_clean_ = value; }
void TakeSkImage(sk_sp<SkImage> image) final { NOTREACHED_IN_MIGRATION(); }
void NotifyResourceLost() final;
bool NeedsReadLockFences() const final {
// If the resource is not accelerated, it will be written to on the CPU. We
// need read lock fences to ensure that all reads on the GPU are done when
// the resource is returned by the display compositor.
return !is_accelerated_;
}
void BeginReadAccess();
void EndReadAccess();
void BeginWriteAccess();
void EndWriteAccess();
GrBackendTexture CreateGrTexture() const;
GLuint GetTextureIdForReadAccess() const {
return owning_thread_data().texture_id_for_read_access;
}
GLuint GetTextureIdForWriteAccess() const {
return owning_thread_data().texture_id_for_write_access;
}
void WillDraw();
bool HasReadAccess() const {
return owning_thread_data().bitmap_image_read_refs > 0u;
}
bool IsLost() const { return owning_thread_data().is_lost; }
void CopyRenderingResultsToGpuMemoryBuffer(const sk_sp<SkImage>& image);
bool UsesClientSharedImage() override { return true; }
scoped_refptr<gpu::ClientSharedImage> GetClientSharedImage() override;
const scoped_refptr<gpu::ClientSharedImage>& GetClientSharedImage() const;
void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
const std::string& parent_path,
size_t bytes_per_pixel) const;
// Whether this type of CanvasResource can provide detailed memory data. If
// true, then the CanvasResourceProvider will not report data, to avoid
// double-countintg.
bool HasDetailedMemoryDumpProvider() const override { return true; }
private:
// These members are either only accessed on the owning thread, or are only
// updated on the owning thread and then are read on a different thread.
// We ensure to correctly update their state in Transfer, which is called
// before a resource is used on a different thread.
struct OwningThreadData {
bool mailbox_needs_new_sync_token = true;
scoped_refptr<gpu::ClientSharedImage> client_shared_image;
gpu::SyncToken sync_token;
size_t bitmap_image_read_refs = 0u;
bool is_lost = false;
// We need to create 2 representations if canvas is operating in single
// buffered mode to allow concurrent scopes for read and write access,
// because the Begin/EndSharedImageAccessDirectCHROMIUM APIs allow only one
// active access mode for a representation.
// In non single buffered mode, the 2 texture ids are the same.
GLuint texture_id_for_read_access = 0u;
GLuint texture_id_for_write_access = 0u;
};
static void OnBitmapImageDestroyed(
scoped_refptr<CanvasResourceSharedImage> resource,
bool has_read_ref_on_texture,
const gpu::SyncToken& sync_token,
bool is_lost);
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
const gpu::SyncToken GetSyncToken(bool needs_verified_token) override;
bool IsOverlayCandidate() const final { return is_overlay_candidate_; }
CanvasResourceSharedImage(const SkImageInfo&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality,
bool is_origin_top_left,
bool is_accelerated,
uint32_t shared_image_usage_flags);
OwningThreadData& owning_thread_data() {
DCHECK(!is_cross_thread());
return owning_thread_data_;
}
const OwningThreadData& owning_thread_data() const {
DCHECK(!is_cross_thread());
return owning_thread_data_;
}
// Can be read on any thread.
bool mailbox_needs_new_sync_token() const {
return owning_thread_data_.mailbox_needs_new_sync_token;
}
const gpu::SyncToken& sync_token() const {
return owning_thread_data_.sync_token;
}
// This should only be de-referenced on the owning thread but may be copied
// on a different thread.
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
// This can be accessed on any thread, irrespective of whether there are
// active readers or not.
bool is_origin_clean_ = true;
// Accessed on any thread.
const gfx::Size size_;
const bool is_origin_top_left_;
const bool is_accelerated_;
const bool is_overlay_candidate_;
const bool supports_display_compositing_;
const bool use_oop_rasterization_;
OwningThreadData owning_thread_data_;
};
// Resource type for a given opaque external resource described on construction
// via a TransferableResource. This CanvasResource should only be used with
// context that support GL.
class PLATFORM_EXPORT ExternalCanvasResource final : public CanvasResource {
public:
static scoped_refptr<ExternalCanvasResource> Create(
const viz::TransferableResource& transferable_resource,
viz::ReleaseCallback release_callback,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality,
bool is_origin_top_left);
~ExternalCanvasResource() override;
bool IsRecycleable() const final { return IsValid(); }
bool IsValid() const override;
bool SupportsAcceleratedCompositing() const override { return true; }
bool NeedsReadLockFences() const final { return false; }
bool OriginClean() const final { return is_origin_clean_; }
void SetOriginClean(bool value) final { is_origin_clean_ = value; }
gfx::Size Size() const final { return transferable_resource_.size; }
bool IsOriginTopLeft() const final { return is_origin_top_left_; }
void TakeSkImage(sk_sp<SkImage> image) final;
void NotifyResourceLost() override { resource_is_lost_ = true; }
scoped_refptr<StaticBitmapImage> Bitmap() override;
viz::ReleaseCallback TakeVizReleaseCallback() override {
return std::move(release_callback_);
}
void SetVizReleaseCallback(viz::ReleaseCallback cb) override {
release_callback_ = std::move(cb);
}
private:
bool IsOverlayCandidate() const final {
return transferable_resource_.is_overlay_candidate;
}
bool HasGpuMailbox() const;
const gpu::SyncToken GetSyncToken(bool needs_verified_token) override;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
bool PrepareAcceleratedTransferableResourceWithoutClientSI(
viz::TransferableResource* out_resource) override;
void GenOrFlushSyncToken();
ExternalCanvasResource(const viz::TransferableResource& transferable_resource,
viz::ReleaseCallback out_callback,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality,
bool is_origin_top_left);
const base::WeakPtr<WebGraphicsContext3DProviderWrapper>
context_provider_wrapper_;
viz::TransferableResource transferable_resource_;
viz::ReleaseCallback release_callback_;
const bool is_origin_top_left_;
bool is_origin_clean_ = true;
bool resource_is_lost_ = false;
};
class PLATFORM_EXPORT CanvasResourceSwapChain final : public CanvasResource {
public:
static scoped_refptr<CanvasResourceSwapChain> Create(
const SkImageInfo&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality);
~CanvasResourceSwapChain() override;
bool IsRecycleable() const final { return IsValid(); }
bool IsValid() const override;
bool SupportsAcceleratedCompositing() const override { return true; }
bool NeedsReadLockFences() const final { return false; }
bool OriginClean() const final { return is_origin_clean_; }
void SetOriginClean(bool value) final { is_origin_clean_ = value; }
gfx::Size Size() const final { return size_; }
void TakeSkImage(sk_sp<SkImage> image) final;
void NotifyResourceLost() override {
// Used for single buffering mode which doesn't need to care about sync
// token synchronization.
}
scoped_refptr<StaticBitmapImage> Bitmap() override;
GLuint GetBackBufferTextureId() const { return back_buffer_texture_id_; }
scoped_refptr<gpu::ClientSharedImage> GetBackBufferClientSharedImage() {
CHECK(back_buffer_shared_image_);
return back_buffer_shared_image_;
}
void PresentSwapChain();
bool UsesClientSharedImage() override { return true; }
scoped_refptr<gpu::ClientSharedImage> GetClientSharedImage() override;
private:
bool IsOverlayCandidate() const final { return true; }
bool HasGpuMailbox() const;
const gpu::SyncToken GetSyncToken(bool needs_verified_token) override;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
CanvasResourceSwapChain(const SkImageInfo&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
cc::PaintFlags::FilterQuality);
const base::WeakPtr<WebGraphicsContext3DProviderWrapper>
context_provider_wrapper_;
const gfx::Size size_;
scoped_refptr<gpu::ClientSharedImage> front_buffer_shared_image_;
scoped_refptr<gpu::ClientSharedImage> back_buffer_shared_image_;
GLuint back_buffer_texture_id_ = 0u;
gpu::SyncToken sync_token_;
const bool use_oop_rasterization_;
bool is_origin_clean_ = true;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_