blob: 96d17a299a2700afe61087e73d3f2b16020af35c [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrBufferAllocPool_DEFINED
#define GrBufferAllocPool_DEFINED
#include "include/core/SkTypes.h"
#include "include/private/base/SkNoncopyable.h"
#include "include/private/base/SkTArray.h"
#include "include/private/base/SkTDArray.h"
#include "include/private/base/SkTypeTraits.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/gpu/ganesh/GrCpuBuffer.h"
#include "src/gpu/ganesh/GrDrawIndirectCommand.h"
#include "src/gpu/ganesh/GrNonAtomicRef.h"
class GrGpu;
/**
* A pool of geometry buffers tied to a GrGpu.
*
* The pool allows a client to make space for geometry and then put back excess
* space if it over allocated. When a client is ready to draw from the pool
* it calls unmap on the pool ensure buffers are ready for drawing. The pool
* can be reset after drawing is completed to recycle space.
*
* At creation time a minimum per-buffer size can be specified. Additionally,
* a number of buffers to preallocate can be specified. These will
* be allocated at the min size and kept around until the pool is destroyed.
*/
class GrBufferAllocPool : SkNoncopyable {
public:
inline static constexpr size_t kDefaultBufferSize = 1 << 15;
/**
* A cache object that can be shared by multiple GrBufferAllocPool instances. It caches
* cpu buffer allocations to avoid reallocating them.
*/
class CpuBufferCache : public GrNonAtomicRef<CpuBufferCache> {
public:
static sk_sp<CpuBufferCache> Make(int maxBuffersToCache);
sk_sp<GrCpuBuffer> makeBuffer(size_t size, bool mustBeInitialized);
void releaseAll();
private:
CpuBufferCache(int maxBuffersToCache);
struct Buffer {
sk_sp<GrCpuBuffer> fBuffer;
bool fCleared = false;
};
std::unique_ptr<Buffer[]> fBuffers;
int fMaxBuffersToCache = 0;
};
/**
* Ensures all buffers are unmapped and have all data written to them.
* Call before drawing using buffers from the pool.
*/
void unmap();
/**
* Invalidates all the data in the pool, unrefs non-preallocated buffers.
*/
void reset();
/**
* Frees data from makeSpaces in LIFO order.
*/
void putBack(size_t bytes);
protected:
/**
* Constructor
*
* @param gpu The GrGpu used to create the buffers.
* @param bufferType The type of buffers to create.
* @param cpuBufferCache If non-null a cache for client side array buffers
* or staging buffers used before data is uploaded to
* GPU buffer objects.
*/
GrBufferAllocPool(GrGpu* gpu, GrGpuBufferType bufferType, sk_sp<CpuBufferCache> cpuBufferCache);
virtual ~GrBufferAllocPool();
/**
* Returns a block of memory to hold data. A buffer designated to hold the
* data is given to the caller. The buffer may or may not be locked. The
* returned ptr remains valid until any of the following:
* *makeSpace is called again.
* *unmap is called.
* *reset is called.
* *this object is destroyed.
*
* Once unmap on the pool is called the data is guaranteed to be in the
* buffer at the offset indicated by offset. Until that time it may be
* in temporary storage and/or the buffer may be locked.
*
* @param size the amount of data to make space for
* @param alignment alignment constraint from start of buffer
* @param buffer returns the buffer that will hold the data.
* @param offset returns the offset into buffer of the data.
* @return pointer to where the client should write the data.
*/
void* makeSpace(size_t size, size_t alignment, sk_sp<const GrBuffer>* buffer, size_t* offset);
/**
* Returns a block of memory to hold data. A buffer designated to hold the
* data is given to the caller. The buffer may or may not be locked. The
* returned ptr remains valid until any of the following:
* *makeSpace is called again.
* *unmap is called.
* *reset is called.
* *this object is destroyed.
*
* Once unmap on the pool is called the data is guaranteed to be in the
* buffer at the offset indicated by offset. Until that time it may be
* in temporary storage and/or the buffer may be locked.
*
* The caller requests a minimum number of bytes, but the block may be (much)
* larger. Assuming that a new block must be allocated, it will be fallbackSize bytes.
* The actual block size is returned in actualSize.
*
* @param minSize the minimum amount of data to make space for
* @param fallbackSize the amount of data to make space for if a new block is needed
* @param alignment alignment constraint from start of buffer
* @param buffer returns the buffer that will hold the data.
* @param offset returns the offset into buffer of the data.
* @param actualSize returns the capacity of the block
* @return pointer to where the client should write the data.
*/
void* makeSpaceAtLeast(size_t minSize,
size_t fallbackSize,
size_t alignment,
sk_sp<const GrBuffer>* buffer,
size_t* offset,
size_t* actualSize);
sk_sp<GrBuffer> getBuffer(size_t size);
private:
struct BufferBlock {
size_t fBytesFree;
sk_sp<GrBuffer> fBuffer;
static_assert(::sk_is_trivially_relocatable<decltype(fBuffer)>::value);
using sk_is_trivially_relocatable = std::true_type;
};
bool createBlock(size_t requestSize);
void destroyBlock();
void deleteBlocks();
void flushCpuData(const BufferBlock& block, size_t flushSize);
void resetCpuData(size_t newSize);
#ifdef SK_DEBUG
void validate(bool unusedBlockAllowed = false) const;
#endif
size_t fBytesInUse = 0;
skia_private::TArray<BufferBlock> fBlocks;
sk_sp<CpuBufferCache> fCpuBufferCache;
sk_sp<GrCpuBuffer> fCpuStagingBuffer;
GrGpu* fGpu;
GrGpuBufferType fBufferType;
void* fBufferPtr = nullptr;
};
/**
* A GrBufferAllocPool of vertex buffers
*/
class GrVertexBufferAllocPool : public GrBufferAllocPool {
public:
/**
* Constructor
*
* @param gpu The GrGpu used to create the vertex buffers.
* @param cpuBufferCache If non-null a cache for client side array buffers
* or staging buffers used before data is uploaded to
* GPU buffer objects.
*/
GrVertexBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache);
/**
* Returns a block of memory to hold vertices. A buffer designated to hold
* the vertices given to the caller. The buffer may or may not be locked.
* The returned ptr remains valid until any of the following:
* *makeSpace is called again.
* *unmap is called.
* *reset is called.
* *this object is destroyed.
*
* Once unmap on the pool is called the vertices are guaranteed to be in
* the buffer at the offset indicated by startVertex. Until that time they
* may be in temporary storage and/or the buffer may be locked.
*
* @param vertexSize specifies size of a vertex to allocate space for
* @param vertexCount number of vertices to allocate space for
* @param buffer returns the vertex buffer that will hold the
* vertices.
* @param startVertex returns the offset into buffer of the first vertex.
* In units of the size of a vertex from layout param.
* @return pointer to first vertex.
*/
void* makeSpace(size_t vertexSize,
int vertexCount,
sk_sp<const GrBuffer>* buffer,
int* startVertex);
/**
* Returns a block of memory to hold vertices. A buffer designated to hold
* the vertices given to the caller. The buffer may or may not be locked.
* The returned ptr remains valid until any of the following:
* *makeSpace is called again.
* *unmap is called.
* *reset is called.
* *this object is destroyed.
*
* Once unmap on the pool is called the vertices are guaranteed to be in
* the buffer at the offset indicated by startVertex. Until that time they
* may be in temporary storage and/or the buffer may be locked.
*
* The caller requests a minimum number of vertices, but the block may be (much)
* larger. Assuming that a new block must be allocated, it will be sized to hold
* fallbackVertexCount vertices. The actual block size (in vertices) is returned in
* actualVertexCount.
*
* @param vertexSize specifies size of a vertex to allocate space for
* @param minVertexCount minimum number of vertices to allocate space for
* @param fallbackVertexCount number of vertices to allocate space for if a new block is needed
* @param buffer returns the vertex buffer that will hold the vertices.
* @param startVertex returns the offset into buffer of the first vertex.
* In units of the size of a vertex from layout param.
* @param actualVertexCount returns the capacity of the block (in vertices)
* @return pointer to first vertex.
*/
void* makeSpaceAtLeast(size_t vertexSize,
int minVertexCount,
int fallbackVertexCount,
sk_sp<const GrBuffer>* buffer,
int* startVertex,
int* actualVertexCount);
private:
using INHERITED = GrBufferAllocPool;
};
/**
* A GrBufferAllocPool of index buffers
*/
class GrIndexBufferAllocPool : public GrBufferAllocPool {
public:
/**
* Constructor
*
* @param gpu The GrGpu used to create the index buffers.
* @param cpuBufferCache If non-null a cache for client side array buffers
* or staging buffers used before data is uploaded to
* GPU buffer objects.
*/
GrIndexBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache);
/**
* Returns a block of memory to hold indices. A buffer designated to hold
* the indices is given to the caller. The buffer may or may not be locked.
* The returned ptr remains valid until any of the following:
* *makeSpace is called again.
* *unmap is called.
* *reset is called.
* *this object is destroyed.
*
* Once unmap on the pool is called the indices are guaranteed to be in the
* buffer at the offset indicated by startIndex. Until that time they may be
* in temporary storage and/or the buffer may be locked.
*
* @param indexCount number of indices to allocate space for
* @param buffer returns the index buffer that will hold the indices.
* @param startIndex returns the offset into buffer of the first index.
* @return pointer to first index.
*/
void* makeSpace(int indexCount, sk_sp<const GrBuffer>* buffer, int* startIndex);
/**
* Returns a block of memory to hold indices. A buffer designated to hold
* the indices is given to the caller. The buffer may or may not be locked.
* The returned ptr remains valid until any of the following:
* *makeSpace is called again.
* *unmap is called.
* *reset is called.
* *this object is destroyed.
*
* Once unmap on the pool is called the indices are guaranteed to be in the
* buffer at the offset indicated by startIndex. Until that time they may be
* in temporary storage and/or the buffer may be locked.
*
* The caller requests a minimum number of indices, but the block may be (much)
* larger. Assuming that a new block must be allocated, it will be sized to hold
* fallbackIndexCount indices. The actual block size (in indices) is returned in
* actualIndexCount.
*
* @param minIndexCount minimum number of indices to allocate space for
* @param fallbackIndexCount number of indices to allocate space for if a new block is needed
* @param buffer returns the index buffer that will hold the indices.
* @param startIndex returns the offset into buffer of the first index.
* @param actualIndexCount returns the capacity of the block (in indices)
* @return pointer to first index.
*/
void* makeSpaceAtLeast(int minIndexCount,
int fallbackIndexCount,
sk_sp<const GrBuffer>* buffer,
int* startIndex,
int* actualIndexCount);
private:
using INHERITED = GrBufferAllocPool;
};
class GrDrawIndirectBufferAllocPool : private GrBufferAllocPool {
public:
GrDrawIndirectBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache)
: GrBufferAllocPool(gpu, GrGpuBufferType::kDrawIndirect, std::move(cpuBufferCache)) {}
GrDrawIndirectWriter makeSpace(int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) {
return this->GrBufferAllocPool::makeSpace(drawCount * sizeof(GrDrawIndirectCommand), 4,
buffer, offset);
}
void putBack(int drawCount) {
this->GrBufferAllocPool::putBack(drawCount * sizeof(GrDrawIndirectCommand));
}
GrDrawIndexedIndirectWriter makeIndexedSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
size_t* offset) {
return this->GrBufferAllocPool::makeSpace(
drawCount * sizeof(GrDrawIndexedIndirectCommand), 4, buffer, offset);
}
void putBackIndexed(int drawCount) {
this->GrBufferAllocPool::putBack(drawCount * sizeof(GrDrawIndexedIndirectCommand));
}
using GrBufferAllocPool::unmap;
using GrBufferAllocPool::reset;
};
#endif