[graphite] Add Resource class to be base class for cached objects.

This updates all current resources that are tracked on the CommandBuffer
to use the new Resource class.

Bug: skia:12754
Change-Id: If96f47972c3b8acc43a9926cafa82c2860e01371
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/490276
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Greg Daniel 2022-01-06 15:00:05 -05:00 committed by SkCQ
parent 048545d249
commit 2ac624f6e0
20 changed files with 288 additions and 51 deletions

View File

@ -8,12 +8,12 @@
#ifndef skgpu_Buffer_DEFINED
#define skgpu_Buffer_DEFINED
#include "experimental/graphite/src/Resource.h"
#include "experimental/graphite/src/ResourceTypes.h"
#include "include/core/SkRefCnt.h"
namespace skgpu {
class Buffer : public SkRefCnt {
class Buffer : public Resource {
public:
size_t size() const { return fSize; }
@ -23,8 +23,8 @@ public:
bool isMapped() const { return fMapPtr; }
protected:
Buffer(size_t size, BufferType type, PrioritizeGpuReads prioritizeGpuReads)
: fSize(size), fType(type), fPrioritizeGpuReads(prioritizeGpuReads) {}
Buffer(const Gpu* gpu, size_t size, BufferType type, PrioritizeGpuReads prioritizeGpuReads)
: Resource(gpu), fSize(size), fType(type), fPrioritizeGpuReads(prioritizeGpuReads) {}
void* fMapPtr = nullptr;

View File

@ -18,12 +18,20 @@ namespace skgpu {
CommandBuffer::CommandBuffer() {}
CommandBuffer::~CommandBuffer() {
this->releaseResources();
}
void CommandBuffer::releaseResources() {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
fTrackedResources.reset();
}
void CommandBuffer::trackResource(sk_sp<Resource> resource) {
fTrackedResources.push_back(std::move(resource));
}
bool CommandBuffer::beginRenderPass(const RenderPassDesc& renderPassDesc,
sk_sp<Texture> colorTexture,
sk_sp<Texture> resolveTexture,

View File

@ -22,6 +22,7 @@ namespace skgpu {
class Buffer;
class Gpu;
class GraphicsPipeline;
class Resource;
class Texture;
class TextureProxy;
@ -57,17 +58,13 @@ struct RenderPassDesc {
class CommandBuffer : public SkRefCnt, private DrawDispatcher {
public:
~CommandBuffer() override {
this->releaseResources();
}
~CommandBuffer() override;
#ifdef SK_DEBUG
bool hasWork() { return fHasWork; }
#endif
void trackResource(sk_sp<SkRefCnt> resource) {
fTrackedResources.push_back(std::move(resource));
}
void trackResource(sk_sp<Resource> resource);
bool beginRenderPass(const RenderPassDesc&,
sk_sp<Texture> colorTexture,
@ -186,7 +183,7 @@ private:
#endif
inline static constexpr int kInitialTrackedResourcesCount = 32;
SkSTArray<kInitialTrackedResourcesCount, sk_sp<SkRefCnt>> fTrackedResources;
SkSTArray<kInitialTrackedResourcesCount, sk_sp<Resource>> fTrackedResources;
};
} // namespace skgpu

View File

@ -9,7 +9,7 @@
namespace skgpu {
GraphicsPipeline::GraphicsPipeline() {
GraphicsPipeline::GraphicsPipeline(const Gpu* gpu) : Resource(gpu) {
}
GraphicsPipeline::~GraphicsPipeline() {

View File

@ -8,7 +8,7 @@
#ifndef skgpu_GraphicsPipeline_DEFINED
#define skgpu_GraphicsPipeline_DEFINED
#include "include/core/SkRefCnt.h"
#include "experimental/graphite/src/Resource.h"
namespace skgpu {
@ -22,12 +22,12 @@ namespace skgpu {
* A GraphicsPipeline is created from the combination of a GraphicsPipelineDesc (representing draw
* specific configuration) and a RenderPassDesc (representing the target of the draw).
*/
class GraphicsPipeline : public SkRefCnt {
class GraphicsPipeline : public Resource {
public:
~GraphicsPipeline() override;
protected:
GraphicsPipeline();
GraphicsPipeline(const Gpu*);
private:
};

View File

@ -0,0 +1,35 @@
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "experimental/graphite/src/Resource.h"
namespace skgpu {
Resource::Resource(const Gpu* gpu) : fGpu(gpu) {}
Resource::~Resource() {
// The cache should have released or destroyed this resource.
SkASSERT(this->wasDestroyed());
}
void Resource::notifyARefCntIsZero(LastRemovedRef removedRef) const {
// TODO: Eventually we'll go through the cache to release the resource, but for now we just do
// this immediately.
SkASSERT(removedRef == LastRemovedRef::kUsageRef);
Resource* mutableThis = const_cast<Resource*>(this);
mutableThis->freeGpuData();
}
void Resource::freeGpuData() {
SkASSERT(fGpu);
this->onFreeGpuData();
fGpu = nullptr;
delete this;
}
} // namespace skgpu

View File

@ -0,0 +1,144 @@
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skgpu_Resource_DEFINED
#define skgpu_Resource_DEFINED
#include "include/private/SkNoncopyable.h"
#include <atomic>
namespace skgpu {
class Gpu;
/**
* Base class for Resource. Provides the hooks for resources to interact with the cache.
* Separated out as a base class to isolate the ref-cnting behavior and provide friendship without
* exposing all of Resource.
*
* AFTER the ref count reaches zero DERIVED::notifyARefCntIsZero() will be called.
*/
template <typename DERIVED> class ResourceRef : public SkNoncopyable {
public:
// Adds a usage ref to the resource. Named ref so we can easily manage usage refs with sk_sp.
void ref() const {
// Only the cache should be able to add the first usage ref to a resource.
SkASSERT(this->hasUsageRef());
// No barrier required.
(void)fUsageRefCnt.fetch_add(+1, std::memory_order_relaxed);
}
// This enum is used to notify the ResourceCache which type of ref just dropped to zero.
enum class LastRemovedRef {
kUsageRef,
kCommandBufferRef,
};
// Removes a usage ref from the resource
void unref() const {
SkASSERT(this->hasUsageRef());
// A release here acts in place of all releases we "should" have been doing in ref().
if (1 == fUsageRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
this->notifyARefIsZero(LastRemovedRef::kUsageRef);
}
}
// Adds a command buffer ref to the resource
void refCommandBuffer() const {
// No barrier required.
(void)fCommandBufferRefCnt.fetch_add(+1, std::memory_order_relaxed);
}
// Removes a command buffer ref from the resource
void unrefCommandBuffer() const {
SkASSERT(this->hasCommandBufferRef());
// A release here acts in place of all releases we "should" have been doing in ref().
if (1 == fCommandBufferRefCnt.fetch_add(-1, std::memory_order_acq_rel)) {
this->notifyARefIsZero(LastRemovedRef::kCommandBufferUsage);
}
}
protected:
ResourceRef() : fUsageRefCnt(1), fCommandBufferRefCnt(0) {}
bool hasUsageRef() const {
if (0 == fUsageRefCnt.load(std::memory_order_acquire)) {
// The acquire barrier is only really needed if we return true. It
// prevents code conditioned on the result of hasUsageRef() from running until previous
// owners are all totally done calling unref().
return false;
}
return true;
}
bool hasCommandBufferRef() const {
if (0 == fCommandBufferRefCnt.load(std::memory_order_acquire)) {
// The acquire barrier is only really needed if we return true. It
// prevents code conditioned on the result of hasCommandBufferRef() from running
// until previous owners are all totally done calling unrefCommandBuffer().
return false;
}
return true;
}
// Privileged method that allows going from ref count = 0 to ref count = 1.
void addInitialUsageRef() const {
SkASSERT(!this->hasUsageRef());
// No barrier required.
(void)fUsageRefCnt.fetch_add(+1, std::memory_order_relaxed);
}
private:
void notifyARefIsZero(LastRemovedRef removedRef) const {
static_cast<const DERIVED*>(this)->notifyARefCntIsZero(removedRef);
}
mutable std::atomic<int32_t> fUsageRefCnt;
mutable std::atomic<int32_t> fCommandBufferRefCnt;
};
/**
* Base class for objects that can be kept in the ResourceCache.
*/
class Resource : public ResourceRef<Resource> {
public:
/**
* Tests whether a object has been abandoned or released. All objects will be in this state
* after their creating Context is destroyed or abandoned.
*
* @return true if the object has been released or abandoned,
* false otherwise.
*/
bool wasDestroyed() const { return fGpu == nullptr; }
protected:
Resource(const Gpu*);
virtual ~Resource();
/** Overridden to free GPU resources in the backend API. */
virtual void onFreeGpuData() = 0;
private:
friend class ResourceRef<Resource>; // to access notifyARefCntIsZero.
void notifyARefCntIsZero(LastRemovedRef removedRef) const;
/**
* Frees the object in the underlying 3D API.
*/
void freeGpuData();
// This is not ref'ed but abandon() or release() will be called before the Gpu object is
// destroyed. Those calls set will this to nullptr.
const Gpu* fGpu;
};
} // namespace skgpu
#endif // skgpu_Resource_DEFINED

View File

@ -9,8 +9,8 @@
namespace skgpu {
Texture::Texture(SkISize dimensions, const TextureInfo& info, Ownership ownership)
: fDimensions(dimensions), fInfo(info), fOwnership(ownership) {}
Texture::Texture(const Gpu* gpu, SkISize dimensions, const TextureInfo& info, Ownership ownership)
: Resource(gpu), fDimensions(dimensions), fInfo(info), fOwnership(ownership) {}
Texture::~Texture() {}

View File

@ -9,13 +9,13 @@
#define skgpu_Texture_DEFINED
#include "experimental/graphite/include/TextureInfo.h"
#include "experimental/graphite/src/Resource.h"
#include "experimental/graphite/src/ResourceTypes.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
namespace skgpu {
class Texture : public SkRefCnt {
class Texture : public Resource {
public:
~Texture() override;
@ -26,7 +26,7 @@ public:
const TextureInfo& textureInfo() const { return fInfo; }
protected:
Texture(SkISize dimensions, const TextureInfo& info, Ownership);
Texture(const Gpu*, SkISize dimensions, const TextureInfo& info, Ownership);
Ownership ownership() const { return fOwnership; }

View File

@ -8,6 +8,7 @@
#ifndef skgpu_MtlBlitCommandEncoder_DEFINED
#define skgpu_MtlBlitCommandEncoder_DEFINED
#include "experimental/graphite/src/Resource.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/ports/SkCFObject.h"
@ -19,13 +20,14 @@ namespace skgpu::mtl {
/**
* Wraps a MTLBlitCommandEncoder object
*/
class BlitCommandEncoder : public SkRefCnt {
class BlitCommandEncoder : public Resource {
public:
static sk_sp<BlitCommandEncoder> Make(id<MTLCommandBuffer> commandBuffer) {
static sk_sp<BlitCommandEncoder> Make(const skgpu::Gpu* gpu,
id<MTLCommandBuffer> commandBuffer) {
// Adding a retain here to keep our own ref separate from the autorelease pool
sk_cfp<id<MTLBlitCommandEncoder>> encoder =
sk_ret_cfp<id<MTLBlitCommandEncoder>>([commandBuffer blitCommandEncoder]);
return sk_sp<BlitCommandEncoder>(new BlitCommandEncoder(std::move(encoder)));
return sk_sp<BlitCommandEncoder>(new BlitCommandEncoder(gpu, std::move(encoder)));
}
void pushDebugGroup(NSString* string) {
@ -61,8 +63,12 @@ public:
}
private:
BlitCommandEncoder(sk_cfp<id<MTLBlitCommandEncoder>> encoder)
: fCommandEncoder(std::move(encoder)) {}
BlitCommandEncoder(const skgpu::Gpu* gpu, sk_cfp<id<MTLBlitCommandEncoder>> encoder)
: Resource(gpu), fCommandEncoder(std::move(encoder)) {}
void onFreeGpuData() override {
fCommandEncoder.reset();
}
sk_cfp<id<MTLBlitCommandEncoder>> fCommandEncoder;
};

View File

@ -8,9 +8,9 @@
#ifndef skgpu_MtlBuffer_DEFINED
#define skgpu_MtlBuffer_DEFINED
#include "experimental/graphite/src/Buffer.h"
#include "experimental/graphite/include/mtl/MtlTypes.h"
#include "experimental/graphite/src/Buffer.h"
#include "include/core/SkRefCnt.h"
#import <Metal/Metal.h>
@ -25,11 +25,13 @@ public:
id<MTLBuffer> mtlBuffer() const { return fBuffer.get(); }
private:
Buffer(size_t size, BufferType type, PrioritizeGpuReads, sk_cfp<id<MTLBuffer>>);
Buffer(const Gpu*, size_t size, BufferType type, PrioritizeGpuReads, sk_cfp<id<MTLBuffer>>);
void onMap() override;
void onUnmap() override;
void onFreeGpuData() override;
sk_cfp<id<MTLBuffer>> fBuffer;
};

View File

@ -55,14 +55,15 @@ sk_sp<Buffer> Buffer::Make(const Gpu* gpu,
(*buffer).label = kBufferTypeNames[(int)type];
#endif
return sk_sp<Buffer>(new Buffer(size, type, prioritizeGpuReads, std::move(buffer)));
return sk_sp<Buffer>(new Buffer(gpu, size, type, prioritizeGpuReads, std::move(buffer)));
}
Buffer::Buffer(size_t size,
Buffer::Buffer(const Gpu* gpu,
size_t size,
BufferType type,
PrioritizeGpuReads prioritizeGpuReads,
sk_cfp<id<MTLBuffer>> buffer)
: skgpu::Buffer(size, type, prioritizeGpuReads)
: skgpu::Buffer(gpu, size, type, prioritizeGpuReads)
, fBuffer(std::move(buffer)) {}
void Buffer::onMap() {
@ -87,5 +88,9 @@ void Buffer::onUnmap() {
fMapPtr = nullptr;
}
void Buffer::onFreeGpuData() {
fBuffer.reset();
}
} // namespace skgpu::mtl

View File

@ -163,7 +163,8 @@ bool CommandBuffer::onBeginRenderPass(const RenderPassDesc& renderPassDesc,
SkASSERT(!depthStencilInfo.fTextureInfo.isValid());
}
fActiveRenderCommandEncoder = RenderCommandEncoder::Make(fCommandBuffer.get(),
fActiveRenderCommandEncoder = RenderCommandEncoder::Make(fGpu,
fCommandBuffer.get(),
descriptor.get());
this->trackResource(fActiveRenderCommandEncoder);
@ -188,7 +189,7 @@ BlitCommandEncoder* CommandBuffer::getBlitCommandEncoder() {
}
#endif
fActiveBlitCommandEncoder = BlitCommandEncoder::Make(fCommandBuffer.get());
fActiveBlitCommandEncoder = BlitCommandEncoder::Make(fGpu, fCommandBuffer.get());
if (!fActiveBlitCommandEncoder) {
return nullptr;

View File

@ -9,7 +9,7 @@
#define skgpu_MtlGraphicsPipeline_DEFINED
#include "experimental/graphite/src/GraphicsPipeline.h"
#include "include/core/SkRefCnt.h"
#include "include/ports/SkCFObject.h"
#include <memory>
@ -40,17 +40,21 @@ public:
size_t instanceStride() const { return fInstanceStride; }
private:
GraphicsPipeline(sk_cfp<id<MTLRenderPipelineState>> pso,
GraphicsPipeline(const skgpu::Gpu* gpu,
sk_cfp<id<MTLRenderPipelineState>> pso,
id<MTLDepthStencilState> dss,
uint32_t refValue,
size_t vertexStride,
size_t instanceStride)
: fPipelineState(std::move(pso))
: skgpu::GraphicsPipeline(gpu)
, fPipelineState(std::move(pso))
, fDepthStencilState(dss)
, fStencilReferenceValue(refValue)
, fVertexStride(vertexStride)
, fInstanceStride(instanceStride) {}
void onFreeGpuData() override;
sk_cfp<id<MTLRenderPipelineState>> fPipelineState;
id<MTLDepthStencilState> fDepthStencilState;
uint32_t fStencilReferenceValue;

View File

@ -411,11 +411,16 @@ sk_sp<GraphicsPipeline> GraphicsPipeline::Make(const Gpu* gpu,
id<MTLDepthStencilState> dss = resourceProvider->findOrCreateCompatibleDepthStencilState(
depthStencilSettings);
return sk_sp<GraphicsPipeline>(new GraphicsPipeline(std::move(pso),
return sk_sp<GraphicsPipeline>(new GraphicsPipeline(gpu,
std::move(pso),
dss,
depthStencilSettings.fStencilReferenceValue,
desc.renderStep()->vertexStride(),
desc.renderStep()->instanceStride()));
}
void GraphicsPipeline::onFreeGpuData() {
fPipelineState.reset();
}
} // namespace skgpu::mtl

View File

@ -8,6 +8,7 @@
#ifndef skgpu_MtlRenderCommandEncoder_DEFINED
#define skgpu_MtlRenderCommandEncoder_DEFINED
#include "experimental/graphite/src/Resource.h"
#include "include/core/SkRefCnt.h"
#include "include/ports/SkCFObject.h"
@ -18,14 +19,15 @@ namespace skgpu::mtl {
/**
* Wraps a MTLRenderCommandEncoder object and associated tracked state
*/
class RenderCommandEncoder : public SkRefCnt {
class RenderCommandEncoder : public Resource {
public:
static sk_sp<RenderCommandEncoder> Make(id<MTLCommandBuffer> commandBuffer,
static sk_sp<RenderCommandEncoder> Make(const Gpu* gpu,
id<MTLCommandBuffer> commandBuffer,
MTLRenderPassDescriptor* descriptor) {
// Adding a retain here to keep our own ref separate from the autorelease pool
sk_cfp<id<MTLRenderCommandEncoder>> encoder =
sk_ret_cfp([commandBuffer renderCommandEncoderWithDescriptor:descriptor]);
return sk_sp<RenderCommandEncoder>(new RenderCommandEncoder(std::move(encoder)));
return sk_sp<RenderCommandEncoder>(new RenderCommandEncoder(gpu, std::move(encoder)));
}
void setLabel(NSString* label) {
@ -240,8 +242,12 @@ public:
}
private:
RenderCommandEncoder(sk_cfp<id<MTLRenderCommandEncoder>> encoder)
: fCommandEncoder(std::move(encoder)) {}
RenderCommandEncoder(const Gpu* gpu, sk_cfp<id<MTLRenderCommandEncoder>> encoder)
: Resource(gpu), fCommandEncoder(std::move(encoder)) {}
void onFreeGpuData() override {
fCommandEncoder.reset();
}
sk_cfp<id<MTLRenderCommandEncoder>> fCommandEncoder;

View File

@ -47,7 +47,10 @@ sk_sp<skgpu::Texture> ResourceProvider::createWrappedTexture(const BackendTextur
return nullptr;
}
sk_cfp<id<MTLTexture>> mtlTexture = sk_ret_cfp((id<MTLTexture>)mtlHandleTexture);
return Texture::MakeWrapped(texture.dimensions(), texture.info(), std::move(mtlTexture));
return Texture::MakeWrapped(this->mtlGpu(),
texture.dimensions(),
texture.info(),
std::move(mtlTexture));
}
sk_sp<skgpu::Buffer> ResourceProvider::createBuffer(size_t size,

View File

@ -9,6 +9,7 @@
#define skgpu_MtlTexture_DEFINED
#include "experimental/graphite/src/Texture.h"
#include "include/core/SkRefCnt.h"
#import <Metal/Metal.h>
@ -22,11 +23,12 @@ public:
SkISize dimensions,
const skgpu::TextureInfo&);
static sk_sp<Texture> Make(const Gpu* gpu,
static sk_sp<Texture> Make(const Gpu*,
SkISize dimensions,
const skgpu::TextureInfo&);
static sk_sp<Texture> MakeWrapped(SkISize dimensions,
static sk_sp<Texture> MakeWrapped(const Gpu*,
SkISize dimensions,
const skgpu::TextureInfo&,
sk_cfp<id<MTLTexture>>);
@ -35,11 +37,14 @@ public:
id<MTLTexture> mtlTexture() const { return fTexture.get(); }
private:
Texture(SkISize dimensions,
Texture(const Gpu* gpu,
SkISize dimensions,
const skgpu::TextureInfo& info,
sk_cfp<id<MTLTexture>>,
Ownership);
void onFreeGpuData() override;
sk_cfp<id<MTLTexture>> fTexture;
};

View File

@ -77,11 +77,12 @@ sk_cfp<id<MTLTexture>> Texture::MakeMtlTexture(const Gpu* gpu,
return texture;
}
Texture::Texture(SkISize dimensions,
Texture::Texture(const Gpu* gpu,
SkISize dimensions,
const skgpu::TextureInfo& info,
sk_cfp<id<MTLTexture>> texture,
Ownership ownership)
: skgpu::Texture(dimensions, info, ownership)
: skgpu::Texture(gpu, dimensions, info, ownership)
, fTexture(std::move(texture)) {}
sk_sp<Texture> Texture::Make(const Gpu* gpu,
@ -91,13 +92,26 @@ sk_sp<Texture> Texture::Make(const Gpu* gpu,
if (!texture) {
return nullptr;
}
return sk_sp<Texture>(new Texture(dimensions, info, std::move(texture), Ownership::kOwned));
return sk_sp<Texture>(new Texture(gpu,
dimensions,
info,
std::move(texture),
Ownership::kOwned));
}
sk_sp<Texture> Texture::MakeWrapped(SkISize dimensions,
sk_sp<Texture> Texture::MakeWrapped(const Gpu* gpu,
SkISize dimensions,
const skgpu::TextureInfo& info,
sk_cfp<id<MTLTexture>> texture) {
return sk_sp<Texture>(new Texture(dimensions, info, std::move(texture), Ownership::kWrapped));
return sk_sp<Texture>(new Texture(gpu,
dimensions,
info,
std::move(texture),
Ownership::kWrapped));
}
void Texture::onFreeGpuData() {
fTexture.reset();
}
} // namespace skgpu::mtl

View File

@ -62,6 +62,8 @@ skia_graphite_sources = [
"$_src/RenderPassTask.cpp",
"$_src/RenderPassTask.h",
"$_src/Renderer.h",
"$_src/Resource.cpp",
"$_src/Resource.h",
"$_src/ResourceProvider.cpp",
"$_src/ResourceProvider.h",
"$_src/ResourceTypes.h",