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