Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrBufferAllocPool.cpp
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 "GrDrawTargetCaps.h"
12 #include "GrGpu.h"
13 #include "GrIndexBuffer.h"
14 #include "GrTypes.h"
15 #include "GrVertexBuffer.h"
16
17 #include "SkTraceEvent.h"
18
19 #ifdef SK_DEBUG
20     #define VALIDATE validate
21 #else
22     static void VALIDATE(bool = false) {}
23 #endif
24
25 // page size
26 #define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
27
28 #define UNMAP_BUFFER(block)                                                               \
29 do {                                                                                      \
30     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"),                           \
31                          "GrBufferAllocPool Unmapping Buffer",                            \
32                          TRACE_EVENT_SCOPE_THREAD,                                        \
33                          "percent_unwritten",                                             \
34                          (float)((block).fBytesFree) / (block).fBuffer->gpuMemorySize()); \
35     (block).fBuffer->unmap();                                                             \
36 } while (false)
37
38 GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
39                                      BufferType bufferType,
40                                      bool frequentResetHint,
41                                      size_t blockSize,
42                                      int preallocBufferCnt) :
43         fBlocks(SkTMax(8, 2*preallocBufferCnt)) {
44
45     SkASSERT(gpu);
46     fGpu = gpu;
47     fGpu->ref();
48     fGpuIsReffed = true;
49
50     fBufferType = bufferType;
51     fFrequentResetHint = frequentResetHint;
52     fBufferPtr = NULL;
53     fMinBlockSize = SkTMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
54
55     fBytesInUse = 0;
56
57     fPreallocBuffersInUse = 0;
58     fPreallocBufferStartIdx = 0;
59     for (int i = 0; i < preallocBufferCnt; ++i) {
60         GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
61         if (buffer) {
62             *fPreallocBuffers.append() = buffer;
63         }
64     }
65 }
66
67 GrBufferAllocPool::~GrBufferAllocPool() {
68     VALIDATE();
69     if (fBlocks.count()) {
70         GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
71         if (buffer->isMapped()) {
72             UNMAP_BUFFER(fBlocks.back());
73         }
74     }
75     while (!fBlocks.empty()) {
76         destroyBlock();
77     }
78     fPreallocBuffers.unrefAll();
79     releaseGpuRef();
80 }
81
82 void GrBufferAllocPool::releaseGpuRef() {
83     if (fGpuIsReffed) {
84         fGpu->unref();
85         fGpuIsReffed = false;
86     }
87 }
88
89 void GrBufferAllocPool::reset() {
90     VALIDATE();
91     fBytesInUse = 0;
92     if (fBlocks.count()) {
93         GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
94         if (buffer->isMapped()) {
95             UNMAP_BUFFER(fBlocks.back());
96         }
97     }
98     // fPreallocBuffersInUse will be decremented down to zero in the while loop
99     int preallocBuffersInUse = fPreallocBuffersInUse;
100     while (!fBlocks.empty()) {
101         this->destroyBlock();
102     }
103     if (fPreallocBuffers.count()) {
104         // must set this after above loop.
105         fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
106                                    preallocBuffersInUse) %
107                                   fPreallocBuffers.count();
108     }
109     // we may have created a large cpu mirror of a large VB. Reset the size
110     // to match our pre-allocated VBs.
111     fCpuData.reset(fMinBlockSize);
112     SkASSERT(0 == fPreallocBuffersInUse);
113     VALIDATE();
114 }
115
116 void GrBufferAllocPool::unmap() {
117     VALIDATE();
118
119     if (fBufferPtr) {
120         BufferBlock& block = fBlocks.back();
121         if (block.fBuffer->isMapped()) {
122             UNMAP_BUFFER(block);
123         } else {
124             size_t flushSize = block.fBuffer->gpuMemorySize() - block.fBytesFree;
125             this->flushCpuData(fBlocks.back(), flushSize);
126         }
127         fBufferPtr = NULL;
128     }
129     VALIDATE();
130 }
131
132 #ifdef SK_DEBUG
133 void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
134     if (fBufferPtr) {
135         SkASSERT(!fBlocks.empty());
136         if (fBlocks.back().fBuffer->isMapped()) {
137             GrGeometryBuffer* buf = fBlocks.back().fBuffer;
138             SkASSERT(buf->mapPtr() == fBufferPtr);
139         } else {
140             SkASSERT(fCpuData.get() == fBufferPtr);
141         }
142     } else {
143         SkASSERT(fBlocks.empty() || !fBlocks.back().fBuffer->isMapped());
144     }
145     size_t bytesInUse = 0;
146     for (int i = 0; i < fBlocks.count() - 1; ++i) {
147         SkASSERT(!fBlocks[i].fBuffer->isMapped());
148     }
149     for (int i = 0; i < fBlocks.count(); ++i) {
150         size_t bytes = fBlocks[i].fBuffer->gpuMemorySize() - fBlocks[i].fBytesFree;
151         bytesInUse += bytes;
152         SkASSERT(bytes || unusedBlockAllowed);
153     }
154
155     SkASSERT(bytesInUse == fBytesInUse);
156     if (unusedBlockAllowed) {
157         SkASSERT((fBytesInUse && !fBlocks.empty()) ||
158                  (!fBytesInUse && (fBlocks.count() < 2)));
159     } else {
160         SkASSERT((0 == fBytesInUse) == fBlocks.empty());
161     }
162 }
163 #endif
164
165 void* GrBufferAllocPool::makeSpace(size_t size,
166                                    size_t alignment,
167                                    const GrGeometryBuffer** buffer,
168                                    size_t* offset) {
169     VALIDATE();
170
171     SkASSERT(buffer);
172     SkASSERT(offset);
173
174     if (fBufferPtr) {
175         BufferBlock& back = fBlocks.back();
176         size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
177         size_t pad = GrSizeAlignUpPad(usedBytes,
178                                       alignment);
179         if ((size + pad) <= back.fBytesFree) {
180             usedBytes += pad;
181             *offset = usedBytes;
182             *buffer = back.fBuffer;
183             back.fBytesFree -= size + pad;
184             fBytesInUse += size + pad;
185             VALIDATE();
186             return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
187         }
188     }
189
190     // We could honor the space request using by a partial update of the current
191     // VB (if there is room). But we don't currently use draw calls to GL that
192     // allow the driver to know that previously issued draws won't read from
193     // the part of the buffer we update. Also, the GL buffer implementation
194     // may be cheating on the actual buffer size by shrinking the buffer on
195     // updateData() if the amount of data passed is less than the full buffer
196     // size.
197
198     if (!createBlock(size)) {
199         return NULL;
200     }
201     SkASSERT(fBufferPtr);
202
203     *offset = 0;
204     BufferBlock& back = fBlocks.back();
205     *buffer = back.fBuffer;
206     back.fBytesFree -= size;
207     fBytesInUse += size;
208     VALIDATE();
209     return fBufferPtr;
210 }
211
212 int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
213     VALIDATE();
214     if (fBufferPtr) {
215         const BufferBlock& back = fBlocks.back();
216         size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
217         size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
218         return static_cast<int>((back.fBytesFree - pad) / itemSize);
219     } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
220         return static_cast<int>(fMinBlockSize / itemSize);
221     }
222     return 0;
223 }
224
225 int GrBufferAllocPool::preallocatedBuffersRemaining() const {
226     return fPreallocBuffers.count() - fPreallocBuffersInUse;
227 }
228
229 int GrBufferAllocPool::preallocatedBufferCount() const {
230     return fPreallocBuffers.count();
231 }
232
233 void GrBufferAllocPool::putBack(size_t bytes) {
234     VALIDATE();
235
236     // if the putBack unwinds all the preallocated buffers then we will
237     // advance the starting index. As blocks are destroyed fPreallocBuffersInUse
238     // will be decremented. I will reach zero if all blocks using preallocated
239     // buffers are released.
240     int preallocBuffersInUse = fPreallocBuffersInUse;
241
242     while (bytes) {
243         // caller shouldnt try to put back more than they've taken
244         SkASSERT(!fBlocks.empty());
245         BufferBlock& block = fBlocks.back();
246         size_t bytesUsed = block.fBuffer->gpuMemorySize() - block.fBytesFree;
247         if (bytes >= bytesUsed) {
248             bytes -= bytesUsed;
249             fBytesInUse -= bytesUsed;
250             // if we locked a vb to satisfy the make space and we're releasing
251             // beyond it, then unmap it.
252             if (block.fBuffer->isMapped()) {
253                 UNMAP_BUFFER(block);
254             }
255             this->destroyBlock();
256         } else {
257             block.fBytesFree += bytes;
258             fBytesInUse -= bytes;
259             bytes = 0;
260             break;
261         }
262     }
263     if (!fPreallocBuffersInUse && fPreallocBuffers.count()) {
264             fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
265                                        preallocBuffersInUse) %
266                                       fPreallocBuffers.count();
267     }
268     VALIDATE();
269 }
270
271 bool GrBufferAllocPool::createBlock(size_t requestSize) {
272
273     size_t size = SkTMax(requestSize, fMinBlockSize);
274     SkASSERT(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
275
276     VALIDATE();
277
278     BufferBlock& block = fBlocks.push_back();
279
280     if (size == fMinBlockSize &&
281         fPreallocBuffersInUse < fPreallocBuffers.count()) {
282
283         uint32_t nextBuffer = (fPreallocBuffersInUse +
284                                fPreallocBufferStartIdx) %
285                               fPreallocBuffers.count();
286         block.fBuffer = fPreallocBuffers[nextBuffer];
287         block.fBuffer->ref();
288         ++fPreallocBuffersInUse;
289     } else {
290         block.fBuffer = this->createBuffer(size);
291         if (NULL == block.fBuffer) {
292             fBlocks.pop_back();
293             return false;
294         }
295     }
296
297     block.fBytesFree = size;
298     if (fBufferPtr) {
299         SkASSERT(fBlocks.count() > 1);
300         BufferBlock& prev = fBlocks.fromBack(1);
301         if (prev.fBuffer->isMapped()) {
302             UNMAP_BUFFER(prev);
303         } else {
304             this->flushCpuData(prev, prev.fBuffer->gpuMemorySize() - prev.fBytesFree);
305         }
306         fBufferPtr = NULL;
307     }
308
309     SkASSERT(NULL == fBufferPtr);
310
311     // If the buffer is CPU-backed we map it because it is free to do so and saves a copy.
312     // Otherwise when buffer mapping is supported:
313     //      a) If the frequently reset hint is set we only map when the requested size meets a
314     //      threshold (since we don't expect it is likely that we will see more vertex data)
315     //      b) If the hint is not set we map if the buffer size is greater than the threshold.
316     bool attemptMap = block.fBuffer->isCPUBacked();
317     if (!attemptMap && GrDrawTargetCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags()) {
318         if (fFrequentResetHint) {
319             attemptMap = requestSize > GR_GEOM_BUFFER_MAP_THRESHOLD;
320         } else {
321             attemptMap = size > GR_GEOM_BUFFER_MAP_THRESHOLD;
322         }
323     }
324
325     if (attemptMap) {
326         fBufferPtr = block.fBuffer->map();
327     }
328
329     if (NULL == fBufferPtr) {
330         fBufferPtr = fCpuData.reset(size);
331     }
332
333     VALIDATE(true);
334
335     return true;
336 }
337
338 void GrBufferAllocPool::destroyBlock() {
339     SkASSERT(!fBlocks.empty());
340
341     BufferBlock& block = fBlocks.back();
342     if (fPreallocBuffersInUse > 0) {
343         uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
344                                        fPreallocBufferStartIdx +
345                                        (fPreallocBuffers.count() - 1)) %
346                                       fPreallocBuffers.count();
347         if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
348             --fPreallocBuffersInUse;
349         }
350     }
351     SkASSERT(!block.fBuffer->isMapped());
352     block.fBuffer->unref();
353     fBlocks.pop_back();
354     fBufferPtr = NULL;
355 }
356
357 void GrBufferAllocPool::flushCpuData(const BufferBlock& block, size_t flushSize) {
358     GrGeometryBuffer* buffer = block.fBuffer;
359     SkASSERT(buffer);
360     SkASSERT(!buffer->isMapped());
361     SkASSERT(fCpuData.get() == fBufferPtr);
362     SkASSERT(flushSize <= buffer->gpuMemorySize());
363     VALIDATE(true);
364
365     if (GrDrawTargetCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags() &&
366         flushSize > GR_GEOM_BUFFER_MAP_THRESHOLD) {
367         void* data = buffer->map();
368         if (data) {
369             memcpy(data, fBufferPtr, flushSize);
370             UNMAP_BUFFER(block);
371             return;
372         }
373     }
374     buffer->updateData(fBufferPtr, flushSize);
375     VALIDATE(true);
376 }
377
378 GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
379     if (kIndex_BufferType == fBufferType) {
380         return fGpu->createIndexBuffer(size, true);
381     } else {
382         SkASSERT(kVertex_BufferType == fBufferType);
383         return fGpu->createVertexBuffer(size, true);
384     }
385 }
386
387 ////////////////////////////////////////////////////////////////////////////////
388
389 GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
390                                                  bool frequentResetHint,
391                                                  size_t bufferSize,
392                                                  int preallocBufferCnt)
393 : GrBufferAllocPool(gpu,
394                     kVertex_BufferType,
395                     frequentResetHint,
396                     bufferSize,
397                     preallocBufferCnt) {
398 }
399
400 void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
401                                          int vertexCount,
402                                          const GrVertexBuffer** buffer,
403                                          int* startVertex) {
404
405     SkASSERT(vertexCount >= 0);
406     SkASSERT(buffer);
407     SkASSERT(startVertex);
408
409     size_t offset = 0; // assign to suppress warning
410     const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
411     void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
412                                      vertexSize,
413                                      &geomBuffer,
414                                      &offset);
415
416     *buffer = (const GrVertexBuffer*) geomBuffer;
417     SkASSERT(0 == offset % vertexSize);
418     *startVertex = static_cast<int>(offset / vertexSize);
419     return ptr;
420 }
421
422 bool GrVertexBufferAllocPool::appendVertices(size_t vertexSize,
423                                              int vertexCount,
424                                              const void* vertices,
425                                              const GrVertexBuffer** buffer,
426                                              int* startVertex) {
427     void* space = makeSpace(vertexSize, vertexCount, buffer, startVertex);
428     if (space) {
429         memcpy(space,
430                vertices,
431                vertexSize * vertexCount);
432         return true;
433     } else {
434         return false;
435     }
436 }
437
438 int GrVertexBufferAllocPool::preallocatedBufferVertices(size_t vertexSize) const {
439     return static_cast<int>(INHERITED::preallocatedBufferSize() / vertexSize);
440 }
441
442 int GrVertexBufferAllocPool::currentBufferVertices(size_t vertexSize) const {
443     return currentBufferItems(vertexSize);
444 }
445
446 ////////////////////////////////////////////////////////////////////////////////
447
448 GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
449                                                bool frequentResetHint,
450                                                size_t bufferSize,
451                                                int preallocBufferCnt)
452 : GrBufferAllocPool(gpu,
453                     kIndex_BufferType,
454                     frequentResetHint,
455                     bufferSize,
456                     preallocBufferCnt) {
457 }
458
459 void* GrIndexBufferAllocPool::makeSpace(int indexCount,
460                                         const GrIndexBuffer** buffer,
461                                         int* startIndex) {
462
463     SkASSERT(indexCount >= 0);
464     SkASSERT(buffer);
465     SkASSERT(startIndex);
466
467     size_t offset = 0; // assign to suppress warning
468     const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
469     void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
470                                      sizeof(uint16_t),
471                                      &geomBuffer,
472                                      &offset);
473
474     *buffer = (const GrIndexBuffer*) geomBuffer;
475     SkASSERT(0 == offset % sizeof(uint16_t));
476     *startIndex = static_cast<int>(offset / sizeof(uint16_t));
477     return ptr;
478 }
479
480 bool GrIndexBufferAllocPool::appendIndices(int indexCount,
481                                            const void* indices,
482                                            const GrIndexBuffer** buffer,
483                                            int* startIndex) {
484     void* space = makeSpace(indexCount, buffer, startIndex);
485     if (space) {
486         memcpy(space, indices, sizeof(uint16_t) * indexCount);
487         return true;
488     } else {
489         return false;
490     }
491 }
492
493 int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
494     return static_cast<int>(INHERITED::preallocatedBufferSize() / sizeof(uint16_t));
495 }
496
497 int GrIndexBufferAllocPool::currentBufferIndices() const {
498     return currentBufferItems(sizeof(uint16_t));
499 }