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