blob: 575e68d5c0e41a648b6d547cc4abda1f2594d1e2 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_COMPOUND_IMAGE_BACKING_H_
#define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_COMPOUND_IMAGE_BACKING_H_
#include <vector>
#include "base/containers/enum_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/command_buffer/service/shared_image/shared_memory_image_backing.h"
#include "gpu/command_buffer/service/shared_memory_region_wrapper.h"
#include "gpu/gpu_gles2_export.h"
#include "gpu/ipc/common/surface_handle.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
namespace gpu {
class D3DImageBackingFactoryTest;
class SharedImageBackingFactory;
class SharedImageCopyManager;
class SharedImageFactory;
// TODO(kylechar): Merge with OzoneImageBacking::AccessStream enum.
enum class SharedImageAccessStream {
kSkia,
kOverlay,
kGL,
kDawn,
kDawnBuffer,
kMemory,
kVaapi,
kWebNNTensor,
kVulkan
};
GPU_GLES2_EXPORT std::ostream& operator<<(
std::ostream& os,
SharedImageAccessStream access_stream);
// Used to represent what access streams a backing can be used for.
using AccessStreamSet = base::EnumSet<SharedImageAccessStream,
SharedImageAccessStream::kSkia,
SharedImageAccessStream::kVulkan>;
// A CompoundImageBacking is a specialized container that manages one or more
// underlying SharedImageBacking instances of different types. It serves as a
// bridge to allow a single SharedImage to be backed by multiple memory types
// (e.g., CPU memory and GPU memory OR multiple GPU memory) and provides the
// necessary interoperability (interop) to synchronize data between them as
// usage requirements change.
//
// ----------------------------------
// Core Architecture and Interop
// ----------------------------------
//
// Initial Creation: It creates one or more backings during initial setup based
// on the provided SharedImageUsageSet.
//
// Dynamic GPU Allocation & Data Sync: If a client requests a new usage that
// the current backings cannot satisfy, CompoundImageBacking can create a new
// GPU backing at runtime. Upon creation, the latest data from the existing
// backings is efficiently and automatically copied to this new backing to
// ensure continuity.
//
// Automated Interop: The container manages the lifecycle and data consistency
// between its members. If a client writes to one backing (e.g., CPU) and later
// requires a different representation (e.g., GPU), the CompoundImageBacking
// handles the synchronization logic internally.
//
// Dynamic Management: To optimize memory, backings can be deleted dynamically
// based on usage and memory pressure, provided that at least one backing
// remains active at all times.
//
// (Note: Dynamic allocation/de-allocations are currently disabled by default
// but is a core architectural feature).
//
// ---------------------------------------
// Critical Constraints and Assumptions
// ---------------------------------------
//
// Shared Memory Limit: A CompoundImageBacking can never have more than one
// SharedMemoryImageBacking.
//
// Mappable Backing Placement: Any type of mappable backing (including
// SharedMemoryImageBacking) must be created during the initial setup and is
// never allocated dynamically. These are strictly stored as the first element
// (elements_[0]).
// TODO(crbug.com/471036798): Add CHECK to ensure that mappable backings are
// never created dynamically.
//
// Persistence: The container must always maintain at least one backing to
// ensure the SharedImage remains valid during dynamic memory adjustments.
//
// Memory Upload Requirements: When combining a shared memory backing with a
// hardware-based GPU backing:
// 1.The GPU backing must implement UploadFromMemory() and ReadbackToMemory() to
// copy to/from shared memory backing.
// 2.The GPU backing must not have its own separate shared memory segment, as it
// relies on the primary shared memory backing for data transfers.
class GPU_GLES2_EXPORT CompoundImageBacking
: public ClearTrackingSharedImageBacking {
public:
using CreateBackingCallback =
base::OnceCallback<void(std::unique_ptr<SharedImageBacking>&)>;
static bool IsValidSharedMemoryFormat(const gfx::Size& size,
viz::SharedImageFormat format);
// Remove the SCANOUT flag if |kAllowShmOverlays|.
static SharedImageUsageSet GetGpuSharedImageUsage(SharedImageUsageSet usage);
// Creates a backing that contains a shared memory backing and GPU backing
// provided by `shared_image_factory` based on `usage`. Eventually, instead of
// creating a shm+gpu backing, this method will have various strategy to
// allocate different combination of backings based on the `usage`.
static std::unique_ptr<SharedImageBacking> Create(
SharedImageFactory* shared_image_factory,
scoped_refptr<SharedImageCopyManager> copy_manager,
const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle handle,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label);
// Creates a backing that contains a shared memory backing and GPU backing
// provided by `shared_image_factory` based on `usage`. Eventually, instead of
// creating a shm+gpu backing, this method will have various strategy to
// allocate different combination of backings based on the `usage`.
// We additionally pass a |buffer_usage| parameter here in order to create a
// CPU mappable by creating a shared memory handle.
// TODO(crbug.com/40276878): Remove this method once we figure out the mapping
// between SharedImageUsage and BufferUsage and no longer need to use
// BufferUsage.
static std::unique_ptr<SharedImageBacking> Create(
SharedImageFactory* shared_image_factory,
scoped_refptr<SharedImageCopyManager> copy_manager,
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label,
gfx::BufferUsage buffer_usage);
// Wraps a backing in a CompoundImageBacking. This is used to enable
// CompoundImageBacking as the default, where it serves as the sole backing.
// To achieve this, SharedImageFactory creates the standard backing and then
// wraps it using this method.
// TODO(crbug.com/448962784): Once CompoundImageBacking is fully enabled by
// default, it will be directly creating underlying backing itself and
// SharedImageFactory will be refactored to move most of backing creation
// logic inside CompoundImageBacking.
static std::unique_ptr<SharedImageBacking> WrapExternalBacking(
SharedImageFactory* shared_image_factory,
scoped_refptr<SharedImageCopyManager> copy_manager,
std::unique_ptr<SharedImageBacking> backing);
~CompoundImageBacking() override;
// Called by wrapped representations before access. This will update
// the backing that is going to be accessed if most recent pixels are in
// a different backing.
void NotifyBeginAccess(SharedImageBacking* backing,
RepresentationAccessMode mode);
// Called by wrapped representations during EndAccess(). This will update the
// CompoundImageBacking's clear rect with the accessed backing's clear rect it
// the access was a write access.
void NotifyEndAccess(SharedImageBacking* backing,
RepresentationAccessMode mode);
// SharedImageBacking implementation.
void OnContextLost() override;
SharedImageBackingType GetType() const override;
void Update(std::unique_ptr<gfx::GpuFence> in_fence) override;
bool CopyToGpuMemoryBuffer() override;
void CopyToGpuMemoryBufferAsync(
base::OnceCallback<void(bool)> callback) override;
gfx::Rect ClearedRect() const override;
// CompoundImageBacking now supports partial clear for upcoming use
// cases as it evolves. The cleared rect is now tracked on the compound
// backing as well as its underlying backings.
// Some important things to note that is :
// 1. When a CompoundImageBacking is backed by a single gpu backing, clear
// rect of CompoundImageBacking will track and reflect clear rect of the
// underlying backing.
// 2. When CompoundImageBacking contains more than 1 gpu backing, clear rect
// of the CompoundImageBacking will track and reflect clear rect of the
// most recently written backing. Note that when a read is performed from a
// stale backing, the latest backing content as well as its clear rect will
// be copied into it.
// 3. Anytime a copy is performend between backings, the src backing's cleared
// rect will be xfered to the dst backing.
// 4. If there is a shm backing, entire CompoundImageBacking as well all the
// created gpu backings will be marked as cleared always.
void SetClearedRect(const gfx::Rect& cleared_rect) override;
void OnAddSecondaryReference() override;
// CompoundImageBacking is registered as the primary backing while creating a
// SharedImageRepresentationFactoryRef whereas the underlying
// elements/backings it holds are not. Since the MarkForDestruction() method
// in SharedImageRepresentationFactoryRef only runs for primary backing,
// CompoundImageBacking needs to propagate this call to all its elements.
void MarkForDestruction() override;
gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override;
scoped_refptr<gfx::NativePixmap> GetNativePixmap() override;
protected:
// SharedImageBacking implementation.
std::unique_ptr<DawnImageRepresentation> ProduceDawn(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
const wgpu::Device& device,
wgpu::BackendType backend_type,
std::vector<wgpu::TextureFormat> view_formats,
scoped_refptr<SharedContextState> context_state) override;
std::unique_ptr<DawnBufferRepresentation> ProduceDawnBuffer(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
const wgpu::Device& device,
wgpu::BackendType backend_type,
scoped_refptr<SharedContextState> context_state) override;
std::unique_ptr<GLTextureImageRepresentation> ProduceGLTexture(
SharedImageManager* manager,
MemoryTypeTracker* tracker) override;
std::unique_ptr<GLTexturePassthroughImageRepresentation>
ProduceGLTexturePassthrough(SharedImageManager* manager,
MemoryTypeTracker* tracker) override;
std::unique_ptr<SkiaGaneshImageRepresentation> ProduceSkiaGanesh(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
scoped_refptr<SharedContextState> context_state) override;
std::unique_ptr<SkiaGraphiteImageRepresentation> ProduceSkiaGraphite(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
scoped_refptr<SharedContextState> context_state) override;
std::unique_ptr<OverlayImageRepresentation> ProduceOverlay(
SharedImageManager* manager,
MemoryTypeTracker* tracker) override;
std::unique_ptr<WebNNTensorRepresentation> ProduceWebNNTensor(
SharedImageManager* manager,
MemoryTypeTracker* tracker) override;
std::unique_ptr<MemoryImageRepresentation> ProduceMemory(
SharedImageManager* manager,
MemoryTypeTracker* tracker) override;
std::unique_ptr<VideoImageRepresentation> ProduceVideo(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
VideoDevice device) override;
#if BUILDFLAG(ENABLE_VULKAN)
std::unique_ptr<VulkanImageRepresentation> ProduceVulkan(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
gpu::VulkanDeviceQueue* vulkan_device_queue,
gpu::VulkanImplementation& vulkan_impl,
bool needs_detiling) override;
#endif
private:
friend class CompoundImageBackingTest;
friend class D3DImageBackingFactoryTest;
// Holds one element, aka SharedImageBacking and related information, that
// makes up the compound.
struct ElementHolder {
public:
ElementHolder();
ElementHolder(const ElementHolder& other) = delete;
ElementHolder& operator=(const ElementHolder& other) = delete;
ElementHolder(ElementHolder&& other);
ElementHolder& operator=(ElementHolder&& other);
~ElementHolder();
// Will invoke `create_callback` to create backing if
// required.
void CreateBackingIfNecessary();
// Returns the backing. Will call `CreateBackingIfNecessary()`.
SharedImageBacking* GetBacking();
AccessStreamSet access_streams;
uint32_t content_id_ = 0;
CreateBackingCallback create_callback;
std::unique_ptr<SharedImageBacking> backing;
};
// Creates a backing that contains a shared memory backing and GPU backing
// provided by `gpu_backing_factory`.
static std::unique_ptr<SharedImageBacking> CreateSharedMemoryForTesting(
SharedImageBackingFactory* gpu_backing_factory,
scoped_refptr<SharedImageCopyManager> copy_manager,
const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle handle,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label);
// Creates a backing that contains a shared memory backing and GPU backing
// provided by `gpu_backing_factory`. We additionally pass a |buffer_usage|
// parameter here in order to create a CPU mappable by creating a shared
// memory handle.
// TODO(crbug.com/40276878): Remove this method once we figure out the mapping
// between SharedImageUsage and BufferUsage and no longer need to use
// BufferUsage.
static std::unique_ptr<SharedImageBacking> CreateSharedMemoryForTesting(
SharedImageBackingFactory* gpu_backing_factory,
scoped_refptr<SharedImageCopyManager> copy_manager,
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label,
gfx::BufferUsage buffer_usage);
CompoundImageBacking(
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label,
std::unique_ptr<SharedImageBacking> shm_backing,
base::WeakPtr<SharedImageFactory> shared_image_factory,
base::WeakPtr<SharedImageBackingFactory> gpu_backing_factory,
scoped_refptr<SharedImageCopyManager> copy_manager,
std::optional<gfx::BufferUsage> buffer_usage = std::nullopt);
CompoundImageBacking(bool is_thread_safe,
std::optional<gfx::BufferUsage> buffer_usage,
std::unique_ptr<SharedImageBacking> backing,
scoped_refptr<SharedImageCopyManager> copy_manager,
base::WeakPtr<SharedImageFactory> shared_image_factory);
base::trace_event::MemoryAllocatorDump* OnMemoryDump(
const std::string& dump_name,
base::trace_event::MemoryAllocatorDumpGuid client_guid,
base::trace_event::ProcessMemoryDump* pmd,
uint64_t client_tracing_id) override;
// Returns a SkPixmap for shared memory backing.
const std::vector<SkPixmap>& GetSharedMemoryPixmaps();
// Returns the shared memory element used for access stream
// SharedImageAccessStream::kMemory. There can be only 1 shared memory element
// at most.
ElementHolder& GetShmElement();
// Gets the element corresponding to the backing.
ElementHolder* GetElement(const SharedImageBacking* backing);
// Finds the element which has the most recent data/content irrespective of
// the stream. There could be multiple elements which has the most recent
// data. This method finds the first element which has most recent data.
ElementHolder* GetElementWithLatestContent();
// Gets or allocates a backing for a given |stream|.
// If a backing with a given |stream| is present, it will either return the
// backing with the latest content OR will return any supported backing (the
// first one it finds).
// If no backing is found, then it will allocate an appropriate backing which
// can support the |stream|.
SharedImageBacking* GetOrAllocateBacking(SharedImageAccessStream stream);
// Returns the gpu backing from the list of |element_| which has a shm and a
// gpu backing.
SharedImageBacking* GetGpuBacking();
bool HasLatestContent(ElementHolder& element);
// Sets the element used for `stream` as having the latest content. If
// `write_access` is true then only that element has the latest content.
void SetLatestContent(SharedImageAccessStream stream, bool write_access);
// Runs CreateSharedImage() on `factory` and stores the result in `backing`.
// If successful this will update the estimated size of compound backing.
void CreateBackingFromBackingFactory(
base::WeakPtr<SharedImageBackingFactory> factory,
std::string debug_label,
std::unique_ptr<SharedImageBacking>& backing);
void OnCopyToGpuMemoryBufferComplete(bool success);
// This is required for CompoundImageBacking to be able to query an
// appropriate SharedImageBackingFactory dynamically based on clients
// required usage(Produce*) which typically happens after the backing
// creation time. WeakPtr since backings can outlive SharedImageFactory.
// Note that CompoundImageBacking is not thread-safe at this moment and
// we would need to switch WeakPtr to something else if we make it
// thread-safe.
base::WeakPtr<SharedImageFactory> shared_image_factory_;
uint32_t latest_content_id_ = 1;
// Holds all of the "element" backings that make up this compound backing. For
// each there is a backing, set of streams and tracking for latest content.
//
// It's expected that for each access stream there is exactly one element used
// to access it. Note that it's possible the backing for a given access stream
// can't actually support that type of usage, in which case the backing will
// be null or the ProduceX() call will just fail.
// As of now, CompoundImageBacking only has 2 backings,i.e., 1 shm and 1 gpu
// backing. In future, it will evolve into a dynamic CompoundImageBacking
// where it can have any number of gpu backings and at most 1 cpu backing.
std::vector<ElementHolder> elements_;
base::OnceCallback<void(bool)> pending_copy_to_gmb_callback_;
scoped_refptr<SharedImageCopyManager> copy_manager_;
bool has_shm_backing_ = false;
};
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_COMPOUND_IMAGE_BACKING_H_