Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrMemoryPool.h"
      9 #include "SkMalloc.h"
     10 #ifdef SK_DEBUG
     11 #include "SkAtomics.h"
     12 #endif
     13 
     14 #ifdef SK_DEBUG
     15     #define VALIDATE this->validate()
     16 #else
     17     #define VALIDATE
     18 #endif
     19 
     20 constexpr size_t GrMemoryPool::kSmallestMinAllocSize;
     21 
     22 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
     23     SkDEBUGCODE(fAllocationCnt = 0);
     24     SkDEBUGCODE(fAllocBlockCnt = 0);
     25 
     26     minAllocSize = SkTMax<size_t>(GrSizeAlignUp(minAllocSize, kAlignment), kSmallestMinAllocSize);
     27     preallocSize = SkTMax<size_t>(GrSizeAlignUp(preallocSize, kAlignment), minAllocSize);
     28 
     29     fMinAllocSize = minAllocSize;
     30     fSize = 0;
     31 
     32     fHead = CreateBlock(preallocSize);
     33     fTail = fHead;
     34     fHead->fNext = nullptr;
     35     fHead->fPrev = nullptr;
     36     VALIDATE;
     37 };
     38 
     39 GrMemoryPool::~GrMemoryPool() {
     40     VALIDATE;
     41 #ifdef SK_DEBUG
     42     int i = 0;
     43     int n = fAllocatedIDs.count();
     44     fAllocatedIDs.foreach([&i, n] (int32_t id) {
     45         if (++i == 1) {
     46             SkDebugf("Leaked IDs (in no particular order): %d", id);
     47         } else if (i < 11) {
     48             SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
     49         } else if (i == 11) {
     50             SkDebugf(", ...\n");
     51         }
     52     });
     53 #endif
     54     SkASSERT(0 == fAllocationCnt);
     55     SkASSERT(fHead == fTail);
     56     SkASSERT(0 == fHead->fLiveCount);
     57     DeleteBlock(fHead);
     58 };
     59 
     60 void* GrMemoryPool::allocate(size_t size) {
     61     VALIDATE;
     62     size += kPerAllocPad;
     63     size = GrSizeAlignUp(size, kAlignment);
     64     if (fTail->fFreeSize < size) {
     65         size_t blockSize = size + kHeaderSize;
     66         blockSize = SkTMax<size_t>(blockSize, fMinAllocSize);
     67         BlockHeader* block = CreateBlock(blockSize);
     68 
     69         block->fPrev = fTail;
     70         block->fNext = nullptr;
     71         SkASSERT(nullptr == fTail->fNext);
     72         fTail->fNext = block;
     73         fTail = block;
     74         fSize += block->fSize;
     75         SkDEBUGCODE(++fAllocBlockCnt);
     76     }
     77     SkASSERT(kAssignedMarker == fTail->fBlockSentinal);
     78     SkASSERT(fTail->fFreeSize >= size);
     79     intptr_t ptr = fTail->fCurrPtr;
     80     // We stash a pointer to the block header, just before the allocated space,
     81     // so that we can decrement the live count on delete in constant time.
     82     AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
     83     SkDEBUGCODE(allocData->fSentinal = kAssignedMarker);
     84     SkDEBUGCODE(allocData->fID = []{static int32_t gID; return sk_atomic_inc(&gID) + 1;}());
     85     // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
     86     SkDEBUGCODE(fAllocatedIDs.add(allocData->fID));
     87     allocData->fHeader = fTail;
     88     ptr += kPerAllocPad;
     89     fTail->fPrevPtr = fTail->fCurrPtr;
     90     fTail->fCurrPtr += size;
     91     fTail->fFreeSize -= size;
     92     fTail->fLiveCount += 1;
     93     SkDEBUGCODE(++fAllocationCnt);
     94     VALIDATE;
     95     return reinterpret_cast<void*>(ptr);
     96 }
     97 
     98 void GrMemoryPool::release(void* p) {
     99     VALIDATE;
    100     intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
    101     AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
    102     SkASSERT(kAssignedMarker == allocData->fSentinal);
    103     SkDEBUGCODE(allocData->fSentinal = kFreedMarker);
    104     SkDEBUGCODE(fAllocatedIDs.remove(allocData->fID));
    105     BlockHeader* block = allocData->fHeader;
    106     SkASSERT(kAssignedMarker == block->fBlockSentinal);
    107     if (1 == block->fLiveCount) {
    108         // the head block is special, it is reset rather than deleted
    109         if (fHead == block) {
    110             fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
    111             fHead->fLiveCount = 0;
    112             fHead->fFreeSize = fHead->fSize - kHeaderSize;
    113         } else {
    114             BlockHeader* prev = block->fPrev;
    115             BlockHeader* next = block->fNext;
    116             SkASSERT(prev);
    117             prev->fNext = next;
    118             if (next) {
    119                 next->fPrev = prev;
    120             } else {
    121                 SkASSERT(fTail == block);
    122                 fTail = prev;
    123             }
    124             fSize -= block->fSize;
    125             DeleteBlock(block);
    126             SkDEBUGCODE(fAllocBlockCnt--);
    127         }
    128     } else {
    129         --block->fLiveCount;
    130         // Trivial reclaim: if we're releasing the most recent allocation, reuse it
    131         if (block->fPrevPtr == ptr) {
    132             block->fFreeSize += (block->fCurrPtr - block->fPrevPtr);
    133             block->fCurrPtr = block->fPrevPtr;
    134         }
    135     }
    136     SkDEBUGCODE(--fAllocationCnt);
    137     VALIDATE;
    138 }
    139 
    140 GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t blockSize) {
    141     blockSize = SkTMax<size_t>(blockSize, kHeaderSize);
    142     BlockHeader* block =
    143         reinterpret_cast<BlockHeader*>(sk_malloc_throw(blockSize));
    144     // we assume malloc gives us aligned memory
    145     SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment));
    146     SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker);
    147     block->fLiveCount = 0;
    148     block->fFreeSize = blockSize - kHeaderSize;
    149     block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
    150     block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t.
    151     block->fSize = blockSize;
    152     return block;
    153 }
    154 
    155 void GrMemoryPool::DeleteBlock(BlockHeader* block) {
    156     SkASSERT(kAssignedMarker == block->fBlockSentinal);
    157     SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW
    158     sk_free(block);
    159 }
    160 
    161 void GrMemoryPool::validate() {
    162 #ifdef SK_DEBUG
    163     BlockHeader* block = fHead;
    164     BlockHeader* prev = nullptr;
    165     SkASSERT(block);
    166     int allocCount = 0;
    167     do {
    168         SkASSERT(kAssignedMarker == block->fBlockSentinal);
    169         allocCount += block->fLiveCount;
    170         SkASSERT(prev == block->fPrev);
    171         if (prev) {
    172             SkASSERT(prev->fNext == block);
    173         }
    174 
    175         intptr_t b = reinterpret_cast<intptr_t>(block);
    176         size_t ptrOffset = block->fCurrPtr - b;
    177         size_t totalSize = ptrOffset + block->fFreeSize;
    178         intptr_t userStart = b + kHeaderSize;
    179 
    180         SkASSERT(!(b % kAlignment));
    181         SkASSERT(!(totalSize % kAlignment));
    182         SkASSERT(!(block->fCurrPtr % kAlignment));
    183         if (fHead != block) {
    184             SkASSERT(block->fLiveCount);
    185             SkASSERT(totalSize >= fMinAllocSize);
    186         } else {
    187             SkASSERT(totalSize == block->fSize);
    188         }
    189         if (!block->fLiveCount) {
    190             SkASSERT(ptrOffset ==  kHeaderSize);
    191             SkASSERT(userStart == block->fCurrPtr);
    192         } else {
    193             AllocHeader* allocData = reinterpret_cast<AllocHeader*>(userStart);
    194             SkASSERT(allocData->fSentinal == kAssignedMarker ||
    195                      allocData->fSentinal == kFreedMarker);
    196             SkASSERT(block == allocData->fHeader);
    197         }
    198 
    199         prev = block;
    200     } while ((block = block->fNext));
    201     SkASSERT(allocCount == fAllocationCnt);
    202     SkASSERT(fAllocationCnt == fAllocatedIDs.count());
    203     SkASSERT(prev == fTail);
    204     SkASSERT(fAllocBlockCnt != 0 || fSize == 0);
    205 #endif
    206 }
    207