1
2 /*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10 #include "GrBufferAllocPool.h"
11 #include "GrCaps.h"
12 #include "GrContext.h"
13 #include "GrGpu.h"
14 #include "GrIndexBuffer.h"
15 #include "GrResourceProvider.h"
16 #include "GrTypes.h"
17 #include "GrVertexBuffer.h"
18
19 #include "SkTraceEvent.h"
20
21 #ifdef SK_DEBUG
22 #define VALIDATE validate
23 #else
VALIDATE(bool=false)24 static void VALIDATE(bool = false) {}
25 #endif
26
27 static const size_t MIN_VERTEX_BUFFER_SIZE = 1 << 15;
28 static const size_t MIN_INDEX_BUFFER_SIZE = 1 << 12;
29
30 // page size
31 #define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 15)
32
33 #define UNMAP_BUFFER(block) \
34 do { \
35 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), \
36 "GrBufferAllocPool Unmapping Buffer", \
37 TRACE_EVENT_SCOPE_THREAD, \
38 "percent_unwritten", \
39 (float)((block).fBytesFree) / (block).fBuffer->gpuMemorySize()); \
40 (block).fBuffer->unmap(); \
41 } while (false)
42
GrBufferAllocPool(GrGpu * gpu,BufferType bufferType,size_t blockSize)43 GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
44 BufferType bufferType,
45 size_t blockSize)
46 : fBlocks(8) {
47
48 fGpu = SkRef(gpu);
49 fCpuData = nullptr;
50 fBufferType = bufferType;
51 fBufferPtr = nullptr;
52 fMinBlockSize = SkTMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
53
54 fBytesInUse = 0;
55
56 fGeometryBufferMapThreshold = gpu->caps()->geometryBufferMapThreshold();
57 }
58
deleteBlocks()59 void GrBufferAllocPool::deleteBlocks() {
60 if (fBlocks.count()) {
61 GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
62 if (buffer->isMapped()) {
63 UNMAP_BUFFER(fBlocks.back());
64 }
65 }
66 while (!fBlocks.empty()) {
67 this->destroyBlock();
68 }
69 SkASSERT(!fBufferPtr);
70 }
71
~GrBufferAllocPool()72 GrBufferAllocPool::~GrBufferAllocPool() {
73 VALIDATE();
74 this->deleteBlocks();
75 sk_free(fCpuData);
76 fGpu->unref();
77 }
78
reset()79 void GrBufferAllocPool::reset() {
80 VALIDATE();
81 fBytesInUse = 0;
82 this->deleteBlocks();
83
84 // we may have created a large cpu mirror of a large VB. Reset the size to match our minimum.
85 this->resetCpuData(fMinBlockSize);
86
87 VALIDATE();
88 }
89
unmap()90 void GrBufferAllocPool::unmap() {
91 VALIDATE();
92
93 if (fBufferPtr) {
94 BufferBlock& block = fBlocks.back();
95 if (block.fBuffer->isMapped()) {
96 UNMAP_BUFFER(block);
97 } else {
98 size_t flushSize = block.fBuffer->gpuMemorySize() - block.fBytesFree;
99 this->flushCpuData(fBlocks.back(), flushSize);
100 }
101 fBufferPtr = nullptr;
102 }
103 VALIDATE();
104 }
105
106 #ifdef SK_DEBUG
validate(bool unusedBlockAllowed) const107 void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
108 bool wasDestroyed = false;
109 if (fBufferPtr) {
110 SkASSERT(!fBlocks.empty());
111 if (fBlocks.back().fBuffer->isMapped()) {
112 GrGeometryBuffer* buf = fBlocks.back().fBuffer;
113 SkASSERT(buf->mapPtr() == fBufferPtr);
114 } else {
115 SkASSERT(fCpuData == fBufferPtr);
116 }
117 } else {
118 SkASSERT(fBlocks.empty() || !fBlocks.back().fBuffer->isMapped());
119 }
120 size_t bytesInUse = 0;
121 for (int i = 0; i < fBlocks.count() - 1; ++i) {
122 SkASSERT(!fBlocks[i].fBuffer->isMapped());
123 }
124 for (int i = 0; !wasDestroyed && i < fBlocks.count(); ++i) {
125 if (fBlocks[i].fBuffer->wasDestroyed()) {
126 wasDestroyed = true;
127 } else {
128 size_t bytes = fBlocks[i].fBuffer->gpuMemorySize() - fBlocks[i].fBytesFree;
129 bytesInUse += bytes;
130 SkASSERT(bytes || unusedBlockAllowed);
131 }
132 }
133
134 if (!wasDestroyed) {
135 SkASSERT(bytesInUse == fBytesInUse);
136 if (unusedBlockAllowed) {
137 SkASSERT((fBytesInUse && !fBlocks.empty()) ||
138 (!fBytesInUse && (fBlocks.count() < 2)));
139 } else {
140 SkASSERT((0 == fBytesInUse) == fBlocks.empty());
141 }
142 }
143 }
144 #endif
145
makeSpace(size_t size,size_t alignment,const GrGeometryBuffer ** buffer,size_t * offset)146 void* GrBufferAllocPool::makeSpace(size_t size,
147 size_t alignment,
148 const GrGeometryBuffer** buffer,
149 size_t* offset) {
150 VALIDATE();
151
152 SkASSERT(buffer);
153 SkASSERT(offset);
154
155 if (fBufferPtr) {
156 BufferBlock& back = fBlocks.back();
157 size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
158 size_t pad = GrSizeAlignUpPad(usedBytes, alignment);
159 if ((size + pad) <= back.fBytesFree) {
160 memset((void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes), 0, pad);
161 usedBytes += pad;
162 *offset = usedBytes;
163 *buffer = back.fBuffer;
164 back.fBytesFree -= size + pad;
165 fBytesInUse += size + pad;
166 VALIDATE();
167 return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
168 }
169 }
170
171 // We could honor the space request using by a partial update of the current
172 // VB (if there is room). But we don't currently use draw calls to GL that
173 // allow the driver to know that previously issued draws won't read from
174 // the part of the buffer we update. Also, the GL buffer implementation
175 // may be cheating on the actual buffer size by shrinking the buffer on
176 // updateData() if the amount of data passed is less than the full buffer
177 // size.
178
179 if (!this->createBlock(size)) {
180 return nullptr;
181 }
182 SkASSERT(fBufferPtr);
183
184 *offset = 0;
185 BufferBlock& back = fBlocks.back();
186 *buffer = back.fBuffer;
187 back.fBytesFree -= size;
188 fBytesInUse += size;
189 VALIDATE();
190 return fBufferPtr;
191 }
192
putBack(size_t bytes)193 void GrBufferAllocPool::putBack(size_t bytes) {
194 VALIDATE();
195
196 while (bytes) {
197 // caller shouldn't try to put back more than they've taken
198 SkASSERT(!fBlocks.empty());
199 BufferBlock& block = fBlocks.back();
200 size_t bytesUsed = block.fBuffer->gpuMemorySize() - block.fBytesFree;
201 if (bytes >= bytesUsed) {
202 bytes -= bytesUsed;
203 fBytesInUse -= bytesUsed;
204 // if we locked a vb to satisfy the make space and we're releasing
205 // beyond it, then unmap it.
206 if (block.fBuffer->isMapped()) {
207 UNMAP_BUFFER(block);
208 }
209 this->destroyBlock();
210 } else {
211 block.fBytesFree += bytes;
212 fBytesInUse -= bytes;
213 bytes = 0;
214 break;
215 }
216 }
217
218 VALIDATE();
219 }
220
createBlock(size_t requestSize)221 bool GrBufferAllocPool::createBlock(size_t requestSize) {
222
223 size_t size = SkTMax(requestSize, fMinBlockSize);
224 SkASSERT(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
225
226 VALIDATE();
227
228 BufferBlock& block = fBlocks.push_back();
229
230 block.fBuffer = this->getBuffer(size);
231 if (!block.fBuffer) {
232 fBlocks.pop_back();
233 return false;
234 }
235
236 block.fBytesFree = block.fBuffer->gpuMemorySize();
237 if (fBufferPtr) {
238 SkASSERT(fBlocks.count() > 1);
239 BufferBlock& prev = fBlocks.fromBack(1);
240 if (prev.fBuffer->isMapped()) {
241 UNMAP_BUFFER(prev);
242 } else {
243 this->flushCpuData(prev, prev.fBuffer->gpuMemorySize() - prev.fBytesFree);
244 }
245 fBufferPtr = nullptr;
246 }
247
248 SkASSERT(!fBufferPtr);
249
250 // If the buffer is CPU-backed we map it because it is free to do so and saves a copy.
251 // Otherwise when buffer mapping is supported we map if the buffer size is greater than the
252 // threshold.
253 bool attemptMap = block.fBuffer->isCPUBacked();
254 if (!attemptMap && GrCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags()) {
255 attemptMap = size > fGeometryBufferMapThreshold;
256 }
257
258 if (attemptMap) {
259 fBufferPtr = block.fBuffer->map();
260 }
261
262 if (!fBufferPtr) {
263 fBufferPtr = this->resetCpuData(block.fBytesFree);
264 }
265
266 VALIDATE(true);
267
268 return true;
269 }
270
destroyBlock()271 void GrBufferAllocPool::destroyBlock() {
272 SkASSERT(!fBlocks.empty());
273
274 BufferBlock& block = fBlocks.back();
275
276 SkASSERT(!block.fBuffer->isMapped());
277 block.fBuffer->unref();
278 fBlocks.pop_back();
279 fBufferPtr = nullptr;
280 }
281
resetCpuData(size_t newSize)282 void* GrBufferAllocPool::resetCpuData(size_t newSize) {
283 sk_free(fCpuData);
284 if (newSize) {
285 if (fGpu->caps()->mustClearUploadedBufferData()) {
286 fCpuData = sk_calloc(newSize);
287 } else {
288 fCpuData = sk_malloc_throw(newSize);
289 }
290 } else {
291 fCpuData = nullptr;
292 }
293 return fCpuData;
294 }
295
296
flushCpuData(const BufferBlock & block,size_t flushSize)297 void GrBufferAllocPool::flushCpuData(const BufferBlock& block, size_t flushSize) {
298 GrGeometryBuffer* buffer = block.fBuffer;
299 SkASSERT(buffer);
300 SkASSERT(!buffer->isMapped());
301 SkASSERT(fCpuData == fBufferPtr);
302 SkASSERT(flushSize <= buffer->gpuMemorySize());
303 VALIDATE(true);
304
305 if (GrCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags() &&
306 flushSize > fGeometryBufferMapThreshold) {
307 void* data = buffer->map();
308 if (data) {
309 memcpy(data, fBufferPtr, flushSize);
310 UNMAP_BUFFER(block);
311 return;
312 }
313 }
314 buffer->updateData(fBufferPtr, flushSize);
315 VALIDATE(true);
316 }
317
getBuffer(size_t size)318 GrGeometryBuffer* GrBufferAllocPool::getBuffer(size_t size) {
319
320 GrResourceProvider* rp = fGpu->getContext()->resourceProvider();
321
322 static const GrResourceProvider::BufferUsage kUsage = GrResourceProvider::kDynamic_BufferUsage;
323 // Shouldn't have to use this flag (https://bug.skia.org/4156)
324 static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag;
325 if (kIndex_BufferType == fBufferType) {
326 return rp->createIndexBuffer(size, kUsage, kFlags);
327 } else {
328 SkASSERT(kVertex_BufferType == fBufferType);
329 return rp->createVertexBuffer(size, kUsage, kFlags);
330 }
331 }
332
333 ////////////////////////////////////////////////////////////////////////////////
334
GrVertexBufferAllocPool(GrGpu * gpu)335 GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu)
336 : GrBufferAllocPool(gpu, kVertex_BufferType, MIN_VERTEX_BUFFER_SIZE) {
337 }
338
makeSpace(size_t vertexSize,int vertexCount,const GrVertexBuffer ** buffer,int * startVertex)339 void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
340 int vertexCount,
341 const GrVertexBuffer** buffer,
342 int* startVertex) {
343
344 SkASSERT(vertexCount >= 0);
345 SkASSERT(buffer);
346 SkASSERT(startVertex);
347
348 size_t offset = 0; // assign to suppress warning
349 const GrGeometryBuffer* geomBuffer = nullptr; // assign to suppress warning
350 void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
351 vertexSize,
352 &geomBuffer,
353 &offset);
354
355 *buffer = (const GrVertexBuffer*) geomBuffer;
356 SkASSERT(0 == offset % vertexSize);
357 *startVertex = static_cast<int>(offset / vertexSize);
358 return ptr;
359 }
360
361 ////////////////////////////////////////////////////////////////////////////////
362
GrIndexBufferAllocPool(GrGpu * gpu)363 GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu)
364 : GrBufferAllocPool(gpu, kIndex_BufferType, MIN_INDEX_BUFFER_SIZE) {
365 }
366
makeSpace(int indexCount,const GrIndexBuffer ** buffer,int * startIndex)367 void* GrIndexBufferAllocPool::makeSpace(int indexCount,
368 const GrIndexBuffer** buffer,
369 int* startIndex) {
370
371 SkASSERT(indexCount >= 0);
372 SkASSERT(buffer);
373 SkASSERT(startIndex);
374
375 size_t offset = 0; // assign to suppress warning
376 const GrGeometryBuffer* geomBuffer = nullptr; // assign to suppress warning
377 void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
378 sizeof(uint16_t),
379 &geomBuffer,
380 &offset);
381
382 *buffer = (const GrIndexBuffer*) geomBuffer;
383 SkASSERT(0 == offset % sizeof(uint16_t));
384 *startIndex = static_cast<int>(offset / sizeof(uint16_t));
385 return ptr;
386 }
387