1 /*
2  * Copyright 2010 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrBufferAllocPool_DEFINED
9 #define GrBufferAllocPool_DEFINED
10 
11 #include "include/core/SkTypes.h"
12 #include "include/private/GrTypesPriv.h"
13 #include "include/private/SkNoncopyable.h"
14 #include "include/private/SkTArray.h"
15 #include "include/private/SkTDArray.h"
16 #include "src/gpu/GrCpuBuffer.h"
17 #include "src/gpu/GrNonAtomicRef.h"
18 
19 class GrGpu;
20 
21 /**
22  * A pool of geometry buffers tied to a GrGpu.
23  *
24  * The pool allows a client to make space for geometry and then put back excess
25  * space if it over allocated. When a client is ready to draw from the pool
26  * it calls unmap on the pool ensure buffers are ready for drawing. The pool
27  * can be reset after drawing is completed to recycle space.
28  *
29  * At creation time a minimum per-buffer size can be specified. Additionally,
30  * a number of buffers to preallocate can be specified. These will
31  * be allocated at the min size and kept around until the pool is destroyed.
32  */
33 class GrBufferAllocPool : SkNoncopyable {
34 public:
35     static constexpr size_t kDefaultBufferSize = 1 << 15;
36 
37     /**
38      * A cache object that can be shared by multiple GrBufferAllocPool instances. It caches
39      * cpu buffer allocations to avoid reallocating them.
40      */
41     class CpuBufferCache : public GrNonAtomicRef<CpuBufferCache> {
42     public:
43         static sk_sp<CpuBufferCache> Make(int maxBuffersToCache);
44 
45         sk_sp<GrCpuBuffer> makeBuffer(size_t size, bool mustBeInitialized);
46         void releaseAll();
47 
48     private:
49         CpuBufferCache(int maxBuffersToCache);
50 
51         struct Buffer {
52             sk_sp<GrCpuBuffer> fBuffer;
53             bool fCleared = false;
54         };
55         std::unique_ptr<Buffer[]> fBuffers;
56         int fMaxBuffersToCache = 0;
57     };
58 
59     /**
60      * Ensures all buffers are unmapped and have all data written to them.
61      * Call before drawing using buffers from the pool.
62      */
63     void unmap();
64 
65     /**
66      *  Invalidates all the data in the pool, unrefs non-preallocated buffers.
67      */
68     void reset();
69 
70     /**
71      * Frees data from makeSpaces in LIFO order.
72      */
73     void putBack(size_t bytes);
74 
75 protected:
76     /**
77      * Constructor
78      *
79      * @param gpu                   The GrGpu used to create the buffers.
80      * @param bufferType            The type of buffers to create.
81      * @param cpuBufferCache        If non-null a cache for client side array buffers
82      *                              or staging buffers used before data is uploaded to
83      *                              GPU buffer objects.
84      */
85     GrBufferAllocPool(GrGpu* gpu, GrGpuBufferType bufferType, sk_sp<CpuBufferCache> cpuBufferCache);
86 
87     virtual ~GrBufferAllocPool();
88 
89     /**
90      * Returns a block of memory to hold data. A buffer designated to hold the
91      * data is given to the caller. The buffer may or may not be locked. The
92      * returned ptr remains valid until any of the following:
93      *      *makeSpace is called again.
94      *      *unmap is called.
95      *      *reset is called.
96      *      *this object is destroyed.
97      *
98      * Once unmap on the pool is called the data is guaranteed to be in the
99      * buffer at the offset indicated by offset. Until that time it may be
100      * in temporary storage and/or the buffer may be locked.
101      *
102      * @param size         the amount of data to make space for
103      * @param alignment    alignment constraint from start of buffer
104      * @param buffer       returns the buffer that will hold the data.
105      * @param offset       returns the offset into buffer of the data.
106      * @return pointer to where the client should write the data.
107      */
108     void* makeSpace(size_t size, size_t alignment, sk_sp<const GrBuffer>* buffer, size_t* offset);
109 
110     /**
111      * Returns a block of memory to hold data. A buffer designated to hold the
112      * data is given to the caller. The buffer may or may not be locked. The
113      * returned ptr remains valid until any of the following:
114      *      *makeSpace is called again.
115      *      *unmap is called.
116      *      *reset is called.
117      *      *this object is destroyed.
118      *
119      * Once unmap on the pool is called the data is guaranteed to be in the
120      * buffer at the offset indicated by offset. Until that time it may be
121      * in temporary storage and/or the buffer may be locked.
122      *
123      * The caller requests a minimum number of bytes, but the block may be (much)
124      * larger. Assuming that a new block must be allocated, it will be fallbackSize bytes.
125      * The actual block size is returned in actualSize.
126      *
127      * @param minSize        the minimum amount of data to make space for
128      * @param fallbackSize   the amount of data to make space for if a new block is needed
129      * @param alignment      alignment constraint from start of buffer
130      * @param buffer         returns the buffer that will hold the data.
131      * @param offset         returns the offset into buffer of the data.
132      * @param actualSize     returns the capacity of the block
133      * @return pointer to where the client should write the data.
134      */
135     void* makeSpaceAtLeast(size_t minSize,
136                            size_t fallbackSize,
137                            size_t alignment,
138                            sk_sp<const GrBuffer>* buffer,
139                            size_t* offset,
140                            size_t* actualSize);
141 
142     sk_sp<GrBuffer> getBuffer(size_t size);
143 
144 private:
145     struct BufferBlock {
146         size_t fBytesFree;
147         sk_sp<GrBuffer> fBuffer;
148     };
149 
150     bool createBlock(size_t requestSize);
151     void destroyBlock();
152     void deleteBlocks();
153     void flushCpuData(const BufferBlock& block, size_t flushSize);
154     void resetCpuData(size_t newSize);
155 #ifdef SK_DEBUG
156     void validate(bool unusedBlockAllowed = false) const;
157 #endif
158     size_t fBytesInUse = 0;
159 
160     SkTArray<BufferBlock> fBlocks;
161     sk_sp<CpuBufferCache> fCpuBufferCache;
162     sk_sp<GrCpuBuffer> fCpuStagingBuffer;
163     GrGpu* fGpu;
164     GrGpuBufferType fBufferType;
165     void* fBufferPtr = nullptr;
166 };
167 
168 /**
169  * A GrBufferAllocPool of vertex buffers
170  */
171 class GrVertexBufferAllocPool : public GrBufferAllocPool {
172 public:
173     /**
174      * Constructor
175      *
176      * @param gpu                   The GrGpu used to create the vertex buffers.
177      * @param cpuBufferCache        If non-null a cache for client side array buffers
178      *                              or staging buffers used before data is uploaded to
179      *                              GPU buffer objects.
180      */
181     GrVertexBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache);
182 
183     /**
184      * Returns a block of memory to hold vertices. A buffer designated to hold
185      * the vertices given to the caller. The buffer may or may not be locked.
186      * The returned ptr remains valid until any of the following:
187      *      *makeSpace is called again.
188      *      *unmap is called.
189      *      *reset is called.
190      *      *this object is destroyed.
191      *
192      * Once unmap on the pool is called the vertices are guaranteed to be in
193      * the buffer at the offset indicated by startVertex. Until that time they
194      * may be in temporary storage and/or the buffer may be locked.
195      *
196      * @param vertexSize   specifies size of a vertex to allocate space for
197      * @param vertexCount  number of vertices to allocate space for
198      * @param buffer       returns the vertex buffer that will hold the
199      *                     vertices.
200      * @param startVertex  returns the offset into buffer of the first vertex.
201      *                     In units of the size of a vertex from layout param.
202      * @return pointer to first vertex.
203      */
204     void* makeSpace(size_t vertexSize,
205                     int vertexCount,
206                     sk_sp<const GrBuffer>* buffer,
207                     int* startVertex);
208 
209     /**
210      * Returns a block of memory to hold vertices. A buffer designated to hold
211      * the vertices given to the caller. The buffer may or may not be locked.
212      * The returned ptr remains valid until any of the following:
213      *      *makeSpace is called again.
214      *      *unmap is called.
215      *      *reset is called.
216      *      *this object is destroyed.
217      *
218      * Once unmap on the pool is called the vertices are guaranteed to be in
219      * the buffer at the offset indicated by startVertex. Until that time they
220      * may be in temporary storage and/or the buffer may be locked.
221      *
222      * The caller requests a minimum number of vertices, but the block may be (much)
223      * larger. Assuming that a new block must be allocated, it will be sized to hold
224      * fallbackVertexCount vertices. The actual block size (in vertices) is returned in
225      * actualVertexCount.
226      *
227      * @param vertexSize           specifies size of a vertex to allocate space for
228      * @param minVertexCount       minimum number of vertices to allocate space for
229      * @param fallbackVertexCount  number of vertices to allocate space for if a new block is needed
230      * @param buffer               returns the vertex buffer that will hold the vertices.
231      * @param startVertex          returns the offset into buffer of the first vertex.
232      *                             In units of the size of a vertex from layout param.
233      * @param actualVertexCount    returns the capacity of the block (in vertices)
234      * @return pointer to first vertex.
235      */
236     void* makeSpaceAtLeast(size_t vertexSize,
237                            int minVertexCount,
238                            int fallbackVertexCount,
239                            sk_sp<const GrBuffer>* buffer,
240                            int* startVertex,
241                            int* actualVertexCount);
242 
243 private:
244     using INHERITED = GrBufferAllocPool;
245 };
246 
247 /**
248  * A GrBufferAllocPool of index buffers
249  */
250 class GrIndexBufferAllocPool : public GrBufferAllocPool {
251 public:
252     /**
253      * Constructor
254      *
255      * @param gpu                   The GrGpu used to create the index buffers.
256      * @param cpuBufferCache        If non-null a cache for client side array buffers
257      *                              or staging buffers used before data is uploaded to
258      *                              GPU buffer objects.
259      */
260     GrIndexBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache);
261 
262     /**
263      * Returns a block of memory to hold indices. A buffer designated to hold
264      * the indices is given to the caller. The buffer may or may not be locked.
265      * The returned ptr remains valid until any of the following:
266      *      *makeSpace is called again.
267      *      *unmap is called.
268      *      *reset is called.
269      *      *this object is destroyed.
270      *
271      * Once unmap on the pool is called the indices are guaranteed to be in the
272      * buffer at the offset indicated by startIndex. Until that time they may be
273      * in temporary storage and/or the buffer may be locked.
274      *
275      * @param indexCount   number of indices to allocate space for
276      * @param buffer       returns the index buffer that will hold the indices.
277      * @param startIndex   returns the offset into buffer of the first index.
278      * @return pointer to first index.
279      */
280     void* makeSpace(int indexCount, sk_sp<const GrBuffer>* buffer, int* startIndex);
281 
282     /**
283      * Returns a block of memory to hold indices. A buffer designated to hold
284      * the indices is given to the caller. The buffer may or may not be locked.
285      * The returned ptr remains valid until any of the following:
286      *      *makeSpace is called again.
287      *      *unmap is called.
288      *      *reset is called.
289      *      *this object is destroyed.
290      *
291      * Once unmap on the pool is called the indices are guaranteed to be in the
292      * buffer at the offset indicated by startIndex. Until that time they may be
293      * in temporary storage and/or the buffer may be locked.
294      *
295      * The caller requests a minimum number of indices, but the block may be (much)
296      * larger. Assuming that a new block must be allocated, it will be sized to hold
297      * fallbackIndexCount indices. The actual block size (in indices) is returned in
298      * actualIndexCount.
299      *
300      * @param minIndexCount        minimum number of indices to allocate space for
301      * @param fallbackIndexCount   number of indices to allocate space for if a new block is needed
302      * @param buffer               returns the index buffer that will hold the indices.
303      * @param startIndex           returns the offset into buffer of the first index.
304      * @param actualIndexCount     returns the capacity of the block (in indices)
305      * @return pointer to first index.
306      */
307     void* makeSpaceAtLeast(int minIndexCount,
308                            int fallbackIndexCount,
309                            sk_sp<const GrBuffer>* buffer,
310                            int* startIndex,
311                            int* actualIndexCount);
312 
313 private:
314     using INHERITED = GrBufferAllocPool;
315 };
316 
317 class GrDrawIndirectBufferAllocPool : private GrBufferAllocPool {
318 public:
GrDrawIndirectBufferAllocPool(GrGpu * gpu,sk_sp<CpuBufferCache> cpuBufferCache)319     GrDrawIndirectBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache)
320             : GrBufferAllocPool(gpu, GrGpuBufferType::kDrawIndirect, std::move(cpuBufferCache)) {}
321 
makeSpace(int drawCount,sk_sp<const GrBuffer> * buffer,size_t * offset)322     GrDrawIndirectCommand* makeSpace(int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) {
323         return static_cast<GrDrawIndirectCommand*>(this->GrBufferAllocPool::makeSpace(
324                 (size_t)drawCount * sizeof(GrDrawIndirectCommand), 4, buffer, offset));
325     }
326 
makeIndexedSpace(int drawCount,sk_sp<const GrBuffer> * buffer,size_t * offset)327     GrDrawIndexedIndirectCommand* makeIndexedSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
328                                                    size_t* offset) {
329         return static_cast<GrDrawIndexedIndirectCommand*>(this->GrBufferAllocPool::makeSpace(
330                 (size_t)drawCount * sizeof(GrDrawIndexedIndirectCommand), 4, buffer, offset));
331     }
332 
333     using GrBufferAllocPool::unmap;
334     using GrBufferAllocPool::reset;
335 };
336 
337 #endif
338