blob: 0f43c5205c95c1af57e1b7b8090f77987f933f0b [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/weak_ptr.h"
#include "components/viz/common/resources/shared_bitmap.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/ipc/common/mailbox.mojom-blink.h"
#include "third_party/blink/renderer/platform/geometry/int_size.h"
#include "third_party/blink/renderer/platform/graphics/canvas_color_params.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"
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
namespace gfx {
class GpuMemoryBuffer;
} // namespace gfx
namespace viz {
class SingleReleaseCallback;
struct TransferableResource;
} // namespace viz
namespace blink {
class CanvasResourceProvider;
class StaticBitmapImage;
// TODO(danakj): One day the gpu::mojom::Mailbox type should be shared with
// blink directly and we won't need to use gpu::mojom::blink::Mailbox, nor the
// conversion through WTF::Vector.
gpu::mojom::blink::MailboxPtr SharedBitmapIdToGpuMailboxPtr(
const viz::SharedBitmapId& id);
// 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:
virtual ~CanvasResource();
virtual bool IsRecycleable() const = 0;
virtual bool IsAccelerated() const = 0;
virtual bool SupportsAcceleratedCompositing() const = 0;
virtual bool IsValid() const = 0;
virtual bool NeedsReadLockFences() const { return false; }
virtual IntSize Size() const = 0;
virtual const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) = 0;
virtual void Transfer() {}
virtual const gpu::SyncToken GetSyncToken() {
NOTREACHED();
return gpu::SyncToken();
}
bool PrepareTransferableResource(viz::TransferableResource*,
std::unique_ptr<viz::SingleReleaseCallback>*,
MailboxSyncMode);
void WaitSyncToken(const gpu::SyncToken&);
virtual scoped_refptr<CanvasResource> MakeAccelerated(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>) = 0;
virtual scoped_refptr<CanvasResource> MakeUnaccelerated() = 0;
virtual bool OriginClean() const = 0;
virtual void SetOriginClean(bool) = 0;
virtual scoped_refptr<StaticBitmapImage> Bitmap() = 0;
virtual void CopyFromTexture(GLuint source_texture,
GLenum format,
GLenum type) {
NOTREACHED();
}
// Only CanvasResourceProvider and derivatives should call this.
virtual void TakeSkImage(sk_sp<SkImage> image) = 0;
virtual GLuint GetBackingTextureHandleForOverwrite() {
NOTREACHED();
return 0;
}
virtual GLenum TextureTarget() const {
NOTREACHED();
return 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() {
// TODO(khushalsagar): Some implementations respect the don't write again
// policy but some don't. Fix that once shared images replace all
// accelerated use-cases.
Abandon();
}
SkFilterQuality FilterQuality() const { return filter_quality_; }
SkImageInfo CreateSkImageInfo() const;
protected:
CanvasResource(base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
const CanvasColorParams&);
// Called during resource destruction if the resource is destroyed on a thread
// other than where it was created. This implies that no context associated
// cleanup can be done and any resources tied to the context may be leaked. As
// such, a resource must be deleted on the owning thread and this should only
// be called when the owning thread and its associated context was torn down
// before this resource could be deleted.
virtual void Abandon() { TearDown(); }
virtual bool IsOverlayCandidate() const { return false; }
virtual bool HasGpuMailbox() const = 0;
virtual void TearDown() = 0;
gpu::gles2::GLES2Interface* ContextGL() const;
GLenum GLFilter() const;
GrContext* GetGrContext() const;
virtual base::WeakPtr<WebGraphicsContext3DProviderWrapper>
ContextProviderWrapper() const {
NOTREACHED();
return nullptr;
}
bool PrepareAcceleratedTransferableResource(
viz::TransferableResource* out_resource,
MailboxSyncMode);
bool PrepareUnacceleratedTransferableResource(
viz::TransferableResource* out_resource);
const CanvasColorParams& ColorParams() const { return color_params_; }
void OnDestroy();
CanvasResourceProvider* Provider() { return provider_.get(); }
base::WeakPtr<CanvasResourceProvider> WeakProvider() { return provider_; }
private:
// Sync token that was provided when resource was released
gpu::SyncToken sync_token_for_release_;
base::WeakPtr<CanvasResourceProvider> provider_;
SkFilterQuality filter_quality_;
CanvasColorParams color_params_;
blink::PlatformThreadId thread_of_origin_;
#if DCHECK_IS_ON()
bool did_call_on_destroy_ = false;
#endif
};
// Resource type for skia Bitmaps (RAM and texture backed)
class PLATFORM_EXPORT CanvasResourceBitmap final : public CanvasResource {
public:
static scoped_refptr<CanvasResourceBitmap> Create(
scoped_refptr<StaticBitmapImage>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
const CanvasColorParams&);
~CanvasResourceBitmap() override;
// Not recyclable: Skia handles texture recycling internally and bitmaps are
// cheap to allocate.
bool IsRecycleable() const final { return false; }
bool IsAccelerated() const final;
bool SupportsAcceleratedCompositing() const override {
return IsAccelerated();
}
bool IsValid() const final;
IntSize Size() const final;
void Transfer() final;
scoped_refptr<StaticBitmapImage> Bitmap() final;
bool OriginClean() const final;
void SetOriginClean(bool value) final;
scoped_refptr<CanvasResource> MakeAccelerated(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final;
scoped_refptr<CanvasResource> MakeUnaccelerated() final;
void TakeSkImage(sk_sp<SkImage> image) final;
private:
void TearDown() override;
GLenum TextureTarget() const final;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
bool HasGpuMailbox() const override;
const gpu::SyncToken GetSyncToken() override;
CanvasResourceBitmap(scoped_refptr<StaticBitmapImage>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
const CanvasColorParams&);
scoped_refptr<StaticBitmapImage> image_;
};
// Resource type for GpuMemoryBuffers
class PLATFORM_EXPORT CanvasResourceGpuMemoryBuffer final
: public CanvasResource {
public:
static scoped_refptr<CanvasResourceGpuMemoryBuffer> Create(
const IntSize&,
const CanvasColorParams&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
bool is_accelerated);
~CanvasResourceGpuMemoryBuffer() override;
bool IsRecycleable() const final { return IsValid(); }
bool IsAccelerated() const final { return is_accelerated_; }
bool IsValid() const override;
bool SupportsAcceleratedCompositing() const override { return true; }
bool NeedsReadLockFences() const final { return true; }
bool OriginClean() const final { return is_origin_clean_; }
void SetOriginClean(bool value) final { is_origin_clean_ = value; }
scoped_refptr<CanvasResource> MakeAccelerated(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
NOTREACHED();
return nullptr;
}
scoped_refptr<CanvasResource> MakeUnaccelerated() final {
NOTREACHED();
return nullptr;
}
void Abandon() final;
IntSize Size() const final;
void TakeSkImage(sk_sp<SkImage> image) final;
void CopyFromTexture(GLuint source_texture,
GLenum format,
GLenum type) override;
scoped_refptr<StaticBitmapImage> Bitmap() override;
GLuint GetBackingTextureHandleForOverwrite() final;
private:
void TearDown() override;
GLenum TextureTarget() const final;
bool IsOverlayCandidate() const final { return true; }
const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
bool HasGpuMailbox() const override;
const gpu::SyncToken GetSyncToken() override;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
void WillPaint();
void DidPaint();
CanvasResourceGpuMemoryBuffer(
const IntSize&,
const CanvasColorParams&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
bool is_accelerated);
gpu::Mailbox gpu_mailbox_;
gpu::SyncToken sync_token_;
bool mailbox_needs_new_sync_token_ = false;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
void* buffer_base_address_ = nullptr;
sk_sp<SkSurface> surface_;
GLuint texture_id_ = 0;
MailboxSyncMode mailbox_sync_mode_ = kVerifiedSyncToken;
bool is_accelerated_;
bool is_origin_clean_ = true;
// GL_TEXTURE_2D view of |gpu_memory_buffer_| for CopyFromTexture(); only used
// if TextureTarget() is GL_TEXTURE_EXTERNAL_OES.
GLuint texture_2d_id_for_copy_ = 0;
};
// Resource type for SharedBitmaps
class PLATFORM_EXPORT CanvasResourceSharedBitmap final : public CanvasResource {
public:
static scoped_refptr<CanvasResourceSharedBitmap> Create(
const IntSize&,
const CanvasColorParams&,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality);
~CanvasResourceSharedBitmap() override;
bool IsRecycleable() const final { return IsValid(); }
bool IsAccelerated() const final { return false; }
bool IsValid() const final;
bool SupportsAcceleratedCompositing() const final { return false; }
bool NeedsReadLockFences() const final { return false; }
scoped_refptr<CanvasResource> MakeAccelerated(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
NOTREACHED();
return nullptr;
}
scoped_refptr<CanvasResource> MakeUnaccelerated() final {
NOTREACHED();
return nullptr;
}
void Abandon() final;
IntSize Size() const final;
void TakeSkImage(sk_sp<SkImage> image) final;
void CopyFromTexture(GLuint source_texture,
GLenum format,
GLenum type) override {
NOTREACHED();
}
scoped_refptr<StaticBitmapImage> Bitmap() final;
bool OriginClean() const final { return is_origin_clean_; }
void SetOriginClean(bool flag) final { is_origin_clean_ = flag; }
private:
void TearDown() override;
const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
bool HasGpuMailbox() const override;
CanvasResourceSharedBitmap(const IntSize&,
const CanvasColorParams&,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality);
viz::SharedBitmapId shared_bitmap_id_;
base::WritableSharedMemoryMapping shared_mapping_;
IntSize size_;
bool is_origin_clean_ = true;
};
// Resource type for SharedImage
class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource {
public:
static scoped_refptr<CanvasResourceSharedImage> Create(
const IntSize&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
const CanvasColorParams&,
bool is_overlay_candidate,
bool is_origin_top_left,
bool allow_concurrent_read_write_access,
bool is_accelerated);
~CanvasResourceSharedImage() override;
bool IsRecycleable() const final { return true; }
bool IsAccelerated() const final { return is_accelerated_; }
bool SupportsAcceleratedCompositing() const override { return true; }
bool IsValid() const final;
IntSize Size() const final { return size_; }
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; }
scoped_refptr<CanvasResource> MakeAccelerated(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
NOTREACHED();
return nullptr;
}
scoped_refptr<CanvasResource> MakeUnaccelerated() final {
NOTREACHED();
return nullptr;
}
void TakeSkImage(sk_sp<SkImage> image) final { NOTREACHED(); }
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_;
}
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 is_cross_thread() const {
return base::PlatformThread::CurrentId() != owning_thread_id_;
}
bool has_read_access() const {
return owning_thread_data().bitmap_image_read_refs > 0u;
}
bool is_lost() const { return owning_thread_data().is_lost; }
void CopyRenderingResultsToGpuMemoryBuffer(const sk_sp<SkImage>& image);
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;
gpu::Mailbox shared_image_mailbox;
gpu::SyncToken sync_token;
bool needs_gl_filter_reset = true;
size_t bitmap_image_read_refs = 0u;
MailboxSyncMode mailbox_sync_mode = kVerifiedSyncToken;
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);
void TearDown() override;
void Abandon() override;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
GLenum TextureTarget() const final;
bool HasGpuMailbox() const override;
const gpu::SyncToken GetSyncToken() override;
bool IsOverlayCandidate() const final { return is_overlay_candidate_; }
CanvasResourceSharedImage(const IntSize&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality,
const CanvasColorParams&,
bool is_overlay_candidate,
bool is_origin_top_left,
bool allow_concurrent_read_write_access,
bool is_accelerated);
void SetGLFilterIfNeeded();
OwningThreadData& owning_thread_data() {
DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_);
return owning_thread_data_;
}
const OwningThreadData& owning_thread_data() const {
DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_);
return owning_thread_data_;
}
// Can be read on any thread but updated only on the owning thread.
const gpu::Mailbox& mailbox() const {
return owning_thread_data_.shared_image_mailbox;
}
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;
// GMB based software raster path. The resource is written to on the CPU but
// passed using the mailbox to the display compositor for use as an overlay.
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
// Accessed on any thread.
const bool is_overlay_candidate_;
const IntSize size_;
const bool is_origin_top_left_;
const bool is_accelerated_;
const GLenum texture_target_;
const base::PlatformThreadId owning_thread_id_;
const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;
OwningThreadData owning_thread_data_;
};
// Resource type for a given opaque external resource described on construction
// via a Mailbox; this CanvasResource IsAccelerated() by definition.
class PLATFORM_EXPORT ExternalCanvasResource final : public CanvasResource {
public:
static scoped_refptr<ExternalCanvasResource> Create(
const gpu::Mailbox&,
const IntSize&,
GLenum texture_target,
const CanvasColorParams&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality);
~ExternalCanvasResource() override;
bool IsRecycleable() const final { return IsValid(); }
bool IsAccelerated() const final { return true; }
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; }
scoped_refptr<CanvasResource> MakeAccelerated(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
NOTREACHED();
return nullptr;
}
scoped_refptr<CanvasResource> MakeUnaccelerated() final {
NOTREACHED();
return nullptr;
}
void Abandon() final;
IntSize Size() const final { return size_; }
void TakeSkImage(sk_sp<SkImage> image) final;
scoped_refptr<StaticBitmapImage> Bitmap() override;
private:
void TearDown() override;
GLenum TextureTarget() const final { return texture_target_; }
bool IsOverlayCandidate() const final { return true; }
const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
bool HasGpuMailbox() const override;
const gpu::SyncToken GetSyncToken() override;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
const override;
ExternalCanvasResource(const gpu::Mailbox&,
const IntSize&,
GLenum texture_target,
const CanvasColorParams&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
base::WeakPtr<CanvasResourceProvider>,
SkFilterQuality);
const base::WeakPtr<WebGraphicsContext3DProviderWrapper>
context_provider_wrapper_;
const IntSize size_;
const GLenum texture_target_;
gpu::Mailbox mailbox_;
gpu::SyncToken sync_token_;
bool is_origin_clean_ = true;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_