Add recycled gpu resource ref counting and use it in GrVkCommandBuffer.

Bug: skia:11136
Change-Id: I31ed877be89bfa34f1fe258d90dfe73ace8618cf
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/351198
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Greg Daniel 2021-01-12 10:45:38 -05:00 committed by Skia Commit-Bot
parent 187d8117cb
commit c8e16bbe32
3 changed files with 108 additions and 108 deletions

View File

@ -10,115 +10,113 @@
#include "include/core/SkRefCnt.h"
/** Check if the argument is non-null, and if so, call obj->addCommandBufferUsage() and return obj.
*/
template <typename T> static inline T* GrSafeRefCBUsage(T* obj) {
if (obj) {
obj->addCommandBufferUsage();
// We have to use auto for the function pointers here because if the actual functions live on the
// base class of T we need the function here to be a pointer to a function of the base class and not
// a function on T. Thus we can't have something like void(T::*Ref)() since we may need T or we may
// need some base class of T.
template <typename T, auto Ref, auto Unref> class gr_sp {
private:
static inline T* SafeRef(T* obj) {
if (obj) {
(obj->*Ref)();
}
return obj;
}
return obj;
}
/** Check if the argument is non-null, and if so, call obj->removeCommandBufferUsage()
*/
template <typename T> static inline void GrSafeUnrefCBUsage(T* obj) {
if (obj) {
obj->removeCommandBufferUsage();
static inline void SafeUnref(T* obj) {
if (obj) {
(obj->*Unref)();
}
}
}
/**
* Shared pointer class to wrap classes that support a addCommandBufferUsage() and
* removeCommandBufferUsage() interface.
*
* This class supports copying, moving, and assigning an sk_sp into it. In general these commands do
* not modify the sk_sp at all but just call addCommandBufferUsage() on the underlying object.
*
* This class is designed to be used by GrGpuResources that need to track when they are in use on
* gpu (usually via a command buffer) separately from tracking if there are any current logical
* usages in Ganesh. This allows for a scratch GrGpuResource to be reused for new draw calls even
* if it is in use on the GPU.
*/
template <typename T> class gr_cb {
public:
using element_type = T;
constexpr gr_cb() : fPtr(nullptr) {}
constexpr gr_cb(std::nullptr_t) : fPtr(nullptr) {}
constexpr gr_sp() : fPtr(nullptr) {}
constexpr gr_sp(std::nullptr_t) : fPtr(nullptr) {}
/**
* Shares the underlying object by calling addCommandBufferUsage(), so that both the argument
* and the newly created gr_cb both have a reference to it.
* Shares the underlying object by calling Ref(), so that both the argument and the newly
* created gr_sp both have a reference to it.
*/
gr_cb(const gr_cb<T>& that) : fPtr(GrSafeRefCBUsage(that.get())) {}
gr_sp(const gr_sp<T, Ref, Unref>& that) : fPtr(SafeRef(that.get())) {}
template <typename U,
typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type>
gr_sp(const gr_sp<U, Ref, Unref>& that) : fPtr(SafeRef(that.get())) {}
gr_sp(const sk_sp<T>& that) : fPtr(SafeRef(that.get())) {}
gr_cb(const sk_sp<T>& that) : fPtr(GrSafeRefCBUsage(that.get())) {}
/**
* Move the underlying object from the argument to the newly created gr_cb. Afterwards only
* the new gr_cb will have a reference to the object, and the argument will point to null.
* No call to addCommandBufferUsage() or removeCommandBufferUsage() will be made.
* Move the underlying object from the argument to the newly created gr_sp. Afterwards only the
* new gr_sp will have a reference to the object, and the argument will point to null.
* No call to Ref() or Unref() will be made.
*/
gr_cb(gr_cb<T>&& that) : fPtr(that.release()) {}
gr_sp(gr_sp<T, Ref, Unref>&& that) : fPtr(that.release()) {}
/**
* Copies the underlying object pointer from the argument to the gr_cb. It will then call
* addCommandBufferUsage() on the new object.
* Copies the underlying object pointer from the argument to the gr_sp. It will then call
* Ref() on the new object.
*/
gr_cb(sk_sp<T>&& that) : fPtr(GrSafeRefCBUsage(that.get())) {}
gr_sp(sk_sp<T>&& that) : fPtr(SafeRef(that.get())) {}
/**
* Calls removeCommandBufferUsage() on the underlying object pointer.
* Adopt the bare pointer into the newly created gr_sp.
* No call to Ref() or Unref() will be made.
*/
~gr_cb() {
GrSafeUnrefCBUsage(fPtr);
explicit gr_sp(T* obj) : fPtr(obj) {}
/**
* Calls Unref() on the underlying object pointer.
*/
~gr_sp() {
SafeUnref(fPtr);
SkDEBUGCODE(fPtr = nullptr);
}
gr_cb<T>& operator=(std::nullptr_t) {
gr_sp& operator=(std::nullptr_t) {
this->reset();
return *this;
}
/**
* Shares the underlying object referenced by the argument by calling addCommandBufferUsage() on
* it. If this gr_cb previously had a reference to an object (i.e. not null) it will call
* removeCommandBufferUsage() on thatobject.
* Shares the underlying object referenced by the argument by calling Ref() on it. If this gr_sp
* previously had a reference to an object (i.e. not null) it will call Unref() on that object.
*/
gr_cb<T>& operator=(const gr_cb<T>& that) {
gr_sp& operator=(const gr_sp<T, Ref, Unref>& that) {
if (this != &that) {
this->reset(GrSafeRefCBUsage(that.get()));
this->reset(SafeRef(that.get()));
}
return *this;
}
/**
* Copies the underlying object pointer from the argument to the gr_cb. If the gr_cb previously
* held a reference to another object, removeCommandBufferUsage() will be called on that object.
* It will then call addCommandBufferUsage() on the new object.
* Copies the underlying object pointer from the argument to the gr_sp. If the gr_sp previously
* held a reference to another object, Unref() will be called on that object. It will then call
* Ref() on the new object.
*/
gr_cb<T>& operator=(const sk_sp<T>& that) {
this->reset(GrSafeRefCBUsage(that.get()));
gr_sp& operator=(const sk_sp<T>& that) {
this->reset(SafeRef(that.get()));
return *this;
}
/**
* Move the underlying object from the argument to the gr_cb. If the gr_cb previously held
* a reference to another object, removeCommandBufferUsage() will be called on that object. No
* call to addCommandBufferUsage() will be made.
* Move the underlying object from the argument to the gr_sp. If the gr_sp previously held
* a reference to another object, Unref() will be called on that object. No call to Ref() will
* be made.
*/
gr_cb<T>& operator=(gr_cb<T>&& that) {
gr_sp& operator=(gr_sp<T, Ref, Unref>&& that) {
this->reset(that.release());
return *this;
}
/**
* Copies the underlying object pointer from the argument to the gr_cb. If the gr_cb previously
* held a reference to another object, removeCommandBufferUsage() will be called on that object.
* It will then call addCommandBufferUsage() on the new object.
* Copies the underlying object pointer from the argument to the gr_sp. If the gr_sp previously
* held a reference to another object, Unref() will be called on that object. It will then call
* Ref() on the new object.
*/
gr_cb<T>& operator=(sk_sp<T>&& that) {
this->reset(GrSafeRefCBUsage(that.get()));
gr_sp& operator=(sk_sp<T>&& that) {
this->reset(SafeRef(that.get()));
return *this;
}
@ -134,19 +132,19 @@ public:
private:
/**
* Adopt the new bare pointer, and call removeCommandBufferUsage() on any previously held object
* (if not null). No call to addCommandBufferUsage() will be made.
* Adopt the new bare pointer, and call Unref() on any previously held object (if not null).
* No call to Ref() will be made.
*/
void reset(T* ptr = nullptr) {
T* oldPtr = fPtr;
fPtr = ptr;
GrSafeUnrefCBUsage(oldPtr);
SafeUnref(oldPtr);
}
/**
* Return the bare pointer, and set the internal object pointer to nullptr.
* The caller must assume ownership of the object, and manage its reference count directly.
* No call to removeCommandBufferUsage() will be made.
* No call to Unref() will be made.
*/
T* SK_WARN_UNUSED_RESULT release() {
T* ptr = fPtr;
@ -157,24 +155,39 @@ private:
T* fPtr;
};
template <typename T, typename U> inline bool operator==(const gr_cb<T>& a, const gr_cb<U>& b) {
return a.get() == b.get();
}
template <typename T> inline bool operator==(const gr_cb<T>& a, std::nullptr_t) /*noexcept*/ {
return !a;
}
template <typename T> inline bool operator==(std::nullptr_t, const gr_cb<T>& b) /*noexcept*/ {
return !b;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T, typename U> inline bool operator!=(const gr_cb<T>& a, const gr_cb<U>& b) {
return a.get() != b.get();
}
template <typename T> inline bool operator!=(const gr_cb<T>& a, std::nullptr_t) /*noexcept*/ {
return static_cast<bool>(a);
}
template <typename T> inline bool operator!=(std::nullptr_t, const gr_cb<T>& b) /*noexcept*/ {
return static_cast<bool>(b);
}
/**
* Shared pointer class to wrap classes that support a addCommandBufferUsage() and
* removeCommandBufferUsage() interface.
*
* This class supports copying, moving, and assigning an sk_sp into it. In general these commands do
* not modify the sk_sp at all but just call addCommandBufferUsage() on the underlying object.
*
* This class is designed to be used by GrGpuResources that need to track when they are in use on
* gpu (usually via a command buffer) separately from tracking if there are any current logical
* usages in Ganesh. This allows for a scratch GrGpuResource to be reused for new draw calls even
* if it is in use on the GPU.
*/
template <typename T> using gr_cb =
gr_sp<T, &T::addCommandBufferUsage, &T::removeCommandBufferUsage>;
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This class mimics sk_sp but instead of calling unref it calls recycle instead.
*/
template<typename T> using gr_rp = gr_sp<T, &T::ref, &T::recycle>;
/**
* Returns a gr_rp wrapping the provided ptr AND calls ref on it (if not null).
*
* This is different than the semantics of the constructor for gr_rp, which just wraps the ptr,
* effectively "adopting" it.
*/
template <typename T> gr_rp<T> gr_ref_rp(T* obj) { return gr_rp<T>(SkSafeRef(obj)); }
template <typename T> gr_rp<T> gr_ref_rp(const T* obj) {
return gr_rp<T>(const_cast<T*>(SkSafeRef(obj)));
}
#endif

View File

@ -64,16 +64,8 @@ void GrVkCommandBuffer::releaseResources() {
fTrackedResources.reset();
for (int i = 0; i < fTrackedRecycledResources.count(); ++i) {
fTrackedRecycledResources[i]->notifyFinishedWithWorkOnGpu();
fTrackedRecycledResources[i]->recycle();
}
if (++fNumResets > kNumRewindResetsBeforeFullReset) {
fTrackedRecycledResources.reset();
fTrackedRecycledResources.setReserve(kInitialTrackedResourcesCount);
fNumResets = 0;
} else {
fTrackedRecycledResources.rewind();
}
fTrackedRecycledResources.reset();
fTrackedGpuBuffers.reset();
fTrackedGpuSurfaces.reset();

View File

@ -114,15 +114,18 @@ public:
}
void addResource(const GrManagedResource* resource) {
this->addResource(sk_ref_sp(resource));
SkASSERT(resource);
}
// Add ref-counted resource that will be tracked and released when this command buffer finishes
// execution. When it is released, it will signal that the resource can be recycled for reuse.
void addRecycledResource(const GrRecycledResource* resource) {
resource->ref();
void addRecycledResource(gr_rp<const GrRecycledResource> resource) {
SkASSERT(resource);
resource->notifyQueuedForWorkOnGpu();
fTrackedRecycledResources.append(1, &resource);
fTrackedRecycledResources.push_back(std::move(resource));
}
void addRecycledResource(const GrRecycledResource* resource) {
this->addRecycledResource(gr_ref_rp<const GrRecycledResource>(resource));
}
void addGrBuffer(sk_sp<const GrBuffer> buffer) {
@ -143,7 +146,6 @@ protected:
GrVkCommandBuffer(VkCommandBuffer cmdBuffer, bool isWrapped = false)
: fCmdBuffer(cmdBuffer)
, fIsWrapped(isWrapped) {
fTrackedRecycledResources.setReserve(kInitialTrackedResourcesCount);
this->invalidateState();
}
@ -157,9 +159,9 @@ private:
static constexpr int kInitialTrackedResourcesCount = 32;
protected:
SkSTArray<kInitialTrackedResourcesCount, sk_sp<const GrManagedResource>> fTrackedResources;
SkTDArray<const GrRecycledResource*> fTrackedRecycledResources;
template <typename T> using TrackedResourceArray = SkSTArray<kInitialTrackedResourcesCount, T>;
TrackedResourceArray<sk_sp<const GrManagedResource>> fTrackedResources;
TrackedResourceArray<gr_rp<const GrRecycledResource>> fTrackedRecycledResources;
SkSTArray<16, sk_sp<const GrBuffer>> fTrackedGpuBuffers;
SkSTArray<16, gr_cb<const GrSurface>> fTrackedGpuSurfaces;
@ -183,13 +185,6 @@ protected:
VkBuffer fBoundInputBuffers[kMaxInputBuffers];
VkBuffer fBoundIndexBuffer;
// When resetting the command buffer, we remove the tracked resources from their arrays, and
// we prefer to not free all the memory every time so usually we just rewind. However, to avoid
// all arrays growing to the max size, after so many resets we'll do a full reset of the tracked
// resource arrays.
static const int kNumRewindResetsBeforeFullReset = 8;
int fNumResets = 0;
// Cached values used for dynamic state updates
VkViewport fCachedViewport;
VkRect2D fCachedScissor;