aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrMemoryPool.h
blob: 26a763430ff013becafd30c49009630f46690d23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrMemoryPool_DEFINED
#define GrMemoryPool_DEFINED

#include "GrTypes.h"
#ifdef SK_DEBUG
#include "SkTHash.h"
#endif

/**
 * Allocates memory in blocks and parcels out space in the blocks for allocation
 * requests. It is optimized for allocate / release speed over memory
 * efficiency. The interface is designed to be used to implement operator new
 * and delete overrides. All allocations are expected to be released before the
 * pool's destructor is called. Allocations will be 8-byte aligned.
 */
class GrMemoryPool {
public:
    /**
     * Prealloc size is the amount of space to allocate at pool creation
     * time and keep around until pool destruction. The min alloc size is
     * the smallest allowed size of additional allocations. Both sizes are
     * adjusted to ensure that:
     *   1. they are are 8-byte aligned
     *   2. minAllocSize >= kSmallestMinAllocSize
     *   3. preallocSize >= minAllocSize
     *
     * Both sizes is what the pool will end up allocating from the system, and
     * portions of the allocated memory is used for internal bookkeeping.
     */
    GrMemoryPool(size_t preallocSize, size_t minAllocSize);

    ~GrMemoryPool();

    /**
     * Allocates memory. The memory must be freed with release().
     */
    void* allocate(size_t size);

    /**
     * p must have been returned by allocate()
     */
    void release(void* p);

    /**
     * Returns true if there are no unreleased allocations.
     */
    bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; }

    /**
     * Returns the total allocated size of the GrMemoryPool minus any preallocated amount
     */
    size_t size() const { return fSize; }

    /**
     * Returns the preallocated size of the GrMemoryPool
     */
    size_t preallocSize() const { return fHead->fSize; }

    /**
     * Minimum value of minAllocSize constructor argument.
     */
    constexpr static size_t kSmallestMinAllocSize = 1 << 10;

private:
    struct BlockHeader;

    static BlockHeader* CreateBlock(size_t size);

    static void DeleteBlock(BlockHeader* block);

    void validate();

    struct BlockHeader {
#ifdef SK_DEBUG
        uint32_t     fBlockSentinal;  ///< known value to check for bad back pointers to blocks
#endif
        BlockHeader* fNext;      ///< doubly-linked list of blocks.
        BlockHeader* fPrev;
        int          fLiveCount; ///< number of outstanding allocations in the
                                 ///< block.
        intptr_t     fCurrPtr;   ///< ptr to the start of blocks free space.
        intptr_t     fPrevPtr;   ///< ptr to the last allocation made
        size_t       fFreeSize;  ///< amount of free space left in the block.
        size_t       fSize;      ///< total allocated size of the block
    };

    static const uint32_t kAssignedMarker = 0xCDCDCDCD;
    static const uint32_t kFreedMarker    = 0xEFEFEFEF;

    struct AllocHeader {
#ifdef SK_DEBUG
        uint32_t fSentinal;      ///< known value to check for memory stomping (e.g., (CD)*)
        int32_t fID;             ///< ID that can be used to track down leaks by clients.
#endif
        BlockHeader* fHeader;    ///< pointer back to the block header in which an alloc resides
    };

    size_t                            fSize;
    size_t                            fMinAllocSize;
    BlockHeader*                      fHead;
    BlockHeader*                      fTail;
#ifdef SK_DEBUG
    int                               fAllocationCnt;
    int                               fAllocBlockCnt;
    SkTHashSet<int32_t>               fAllocatedIDs;
#endif

protected:
    enum {
        // We assume this alignment is good enough for everybody.
        kAlignment    = 8,
        kHeaderSize   = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment),
        kPerAllocPad  = GR_CT_ALIGN_UP(sizeof(AllocHeader), kAlignment),
    };
};

/**
 * Variant of GrMemoryPool that can only allocate objects of a single type. It is
 * not as flexible as GrMemoryPool, but it has more convenient allocate() method,
 * and more importantly, it guarantees number of objects that are preallocated at
 * construction or when adding a new memory block. I.e.
 *
 * GrMemoryPool pool(3 * sizeof(T), 1000 * sizeof(T));
 * pool.allocate(sizeof(T));
 * pool.allocate(sizeof(T));
 * pool.allocate(sizeof(T));
 *
 * will preallocate 3 * sizeof(T) bytes and use some of those bytes for internal
 * structures. Because of that, last allocate() call will end up allocating a new
 * block of 1000 * sizeof(T) bytes. In contrast,
 *
 * GrObjectMemoryPool<T> pool(3, 1000);
 * pool.allocate();
 * pool.allocate();
 * pool.allocate();
 *
 * guarantees to preallocate enough memory for 3 objects of sizeof(T), so last
 * allocate() will use preallocated memory and won't cause allocation of a new block.
 *
 * Same thing is true for the second (minAlloc) ctor argument: this class guarantees
 * that a newly added block will have enough space for 1000 objects of sizeof(T), while
 * GrMemoryPool does not.
 */
template <class T>
class GrObjectMemoryPool: public GrMemoryPool {
public:
    /**
     * Preallocates memory for preallocCount objects, and sets new block size to be
     * enough to hold minAllocCount objects.
     */
    GrObjectMemoryPool(size_t preallocCount, size_t minAllocCount)
        : GrMemoryPool(CountToSize(preallocCount),
                       CountToSize(SkTMax(minAllocCount, kSmallestMinAllocCount))) {
    }

    /**
     * Allocates memory for an object, but doesn't construct or otherwise initialize it.
     * The memory must be freed with release().
     */
    T* allocate() { return static_cast<T*>(GrMemoryPool::allocate(sizeof(T))); }

private:
    constexpr static size_t kTotalObjectSize =
        kPerAllocPad + GR_CT_ALIGN_UP(sizeof(T), kAlignment);

    constexpr static size_t CountToSize(size_t count) {
        return kHeaderSize + count * kTotalObjectSize;
    }

public:
    /**
     * Minimum value of minAllocCount constructor argument.
     */
    constexpr static size_t kSmallestMinAllocCount =
        (GrMemoryPool::kSmallestMinAllocSize - kHeaderSize + kTotalObjectSize - 1) /
            kTotalObjectSize;
};

template <class T>
constexpr size_t GrObjectMemoryPool<T>::kSmallestMinAllocCount;

#endif