More comments on gpu_buffer_storage

This gives a basic explanation of the role of storages and views, and provides some details on how to implement a new storage type.

PiperOrigin-RevId: 488812807
This commit is contained in:
Camillo Lugaresi 2022-11-15 18:31:54 -08:00 committed by Copybara-Service
parent 4c874fe4cd
commit 767cc2ee3c

View File

@ -22,13 +22,27 @@ struct types {};
template <class V>
class ViewProvider;
// Interface for a backing storage for GpuBuffer.
// Generic interface for a backing storage for GpuBuffer.
//
// GpuBuffer is an opaque handle to an image. Its contents are handled by
// Storage classes. Application code does not interact with the storages
// directly; to access the data, it asks the GpuBuffer for a View, and in turn
// GpuBuffer looks for a storage that can provide that view.
// This architecture decouples application code from the underlying storage,
// making it possible to use platform-specific optimized storage systems, e.g.
// for zero-copy data sharing between CPU and GPU.
//
// Storage implementations should inherit from GpuBufferStorageImpl. See that
// class for details.
class GpuBufferStorage {
public:
virtual ~GpuBufferStorage() = default;
// Concrete storage types should override the following three accessors.
virtual int width() const = 0;
virtual int height() const = 0;
virtual GpuBufferFormat format() const = 0;
// We can't use dynamic_cast since we want to support building without RTTI.
// The public methods delegate to the type-erased private virtual method.
template <class T>
@ -72,6 +86,8 @@ class GpuBufferStorageRegistry {
return *registry;
}
// Registers a storage type by automatically creating a factory for it.
// This is normally called by GpuBufferImpl.
template <class Storage>
RegistryToken Register() {
return RegisterFactory<Storage>(
@ -82,6 +98,7 @@ class GpuBufferStorageRegistry {
});
}
// Registers a new factory for a storage type.
template <class Storage, class F>
RegistryToken RegisterFactory(F&& factory) {
if constexpr (kDisableRegistration<Storage>) {
@ -90,6 +107,7 @@ class GpuBufferStorageRegistry {
return Register(factory, Storage::GetProviderTypes());
}
// Registers a new converter from storage type StorageFrom to StorageTo.
template <class StorageFrom, class StorageTo, class F>
RegistryToken RegisterConverter(F&& converter) {
if constexpr (kDisableRegistration<StorageTo>) {
@ -162,14 +180,26 @@ struct ForceStaticInstantiation {
#endif // _MSC_VER
};
// Inherit from this class to define a new storage type. The storage type itself
// should be passed as the first template argument (CRTP), followed by one or
// more specializations of ViewProvider.
//
// Concrete storage types should implement the basic accessors from
// GpuBufferStorage, plus the view read/write getters for each ViewProvider they
// implement. This class handles the rest.
//
// Arguments:
// T: storage type
// U...: ViewProvider<SomeView>
// Example:
// class MyStorage : public GpuBufferStorageImpl<
// MyStorage, ViewProvider<GlTextureView>>
template <class T, class... U>
class GpuBufferStorageImpl : public GpuBufferStorage, public U... {
public:
static const std::vector<TypeId>& GetProviderTypes() {
static std::vector<TypeId> kHashes{kTypeId<U>...};
return kHashes;
static std::vector<TypeId> kProviderIds{kTypeId<U>...};
return kProviderIds;
}
// Exposing this as a function allows dependent initializers to call this to
@ -180,10 +210,11 @@ class GpuBufferStorageImpl : public GpuBufferStorage, public U... {
}
private:
virtual const void* down_cast(TypeId to) const override {
// Allows a down_cast to any of the view provider types in U.
const void* down_cast(TypeId to) const final {
return down_cast_impl(to, types<T, U...>{});
}
TypeId storage_type() const override { return kTypeId<T>; }
TypeId storage_type() const final { return kTypeId<T>; }
const void* down_cast_impl(TypeId to, types<>) const { return nullptr; }
template <class V, class... W>