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 
     10 #ifdef SK_DEBUG
     11     #define VALIDATE this->validate()
     12 #else
     13     #define VALIDATE
     14 #endif
     15 
     16 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
     17     SkDEBUGCODE(fAllocationCnt = 0);
     18     SkDEBUGCODE(fAllocBlockCnt = 0);
     19 
     20     minAllocSize = SkTMax<size_t>(minAllocSize, 1 << 10);
     21     fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment),
     22     fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment);
     23     fPreallocSize = SkTMax(fPreallocSize, fMinAllocSize);
     24     fSize = 0;
     25 
     26     fHead = CreateBlock(fPreallocSize);
     27     fTail = fHead;
     28     fHead->fNext = nullptr;
     29     fHead->fPrev = nullptr;
     30     VALIDATE;
     31 };
     32 
     33 GrMemoryPool::~GrMemoryPool() {
     34     VALIDATE;
     35     SkASSERT(0 == fAllocationCnt);
     36     SkASSERT(fHead == fTail);
     37     SkASSERT(0 == fHead->fLiveCount);
     38     DeleteBlock(fHead);
     39 };
     40 
     41 void* GrMemoryPool::allocate(size_t size) {
     42     VALIDATE;
     43     size += kPerAllocPad;
     44     size = GrSizeAlignUp(size, kAlignment);
     45     if (fTail->fFreeSize < size) {
     46         size_t blockSize = size;
     47         blockSize = SkTMax<size_t>(blockSize, fMinAllocSize);
     48         BlockHeader* block = CreateBlock(blockSize);
     49 
     50         block->fPrev = fTail;
     51         block->fNext = nullptr;
     52         SkASSERT(nullptr == fTail->fNext);
     53         fTail->fNext = block;
     54         fTail = block;
     55         fSize += block->fSize;
     56         SkDEBUGCODE(++fAllocBlockCnt);
     57     }
     58     SkASSERT(kAssignedMarker == fTail->fBlockSentinal);
     59     SkASSERT(fTail->fFreeSize >= size);
     60     intptr_t ptr = fTail->fCurrPtr;
     61     // We stash a pointer to the block header, just before the allocated space,
     62     // so that we can decrement the live count on delete in constant time.
     63     AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
     64     SkDEBUGCODE(allocData->fSentinal = kAssignedMarker);
     65     allocData->fHeader = fTail;
     66     ptr += kPerAllocPad;
     67     fTail->fPrevPtr = fTail->fCurrPtr;
     68     fTail->fCurrPtr += size;
     69     fTail->fFreeSize -= size;
     70     fTail->fLiveCount += 1;
     71 
     72     SkDEBUGCODE(++fAllocationCnt);
     73     VALIDATE;
     74     return reinterpret_cast<void*>(ptr);
     75 }
     76 
     77 void GrMemoryPool::release(void* p) {
     78     VALIDATE;
     79     intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
     80     AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
     81     SkASSERT(kAssignedMarker == allocData->fSentinal);
     82     SkDEBUGCODE(allocData->fSentinal = kFreedMarker);
     83     BlockHeader* block = allocData->fHeader;
     84     SkASSERT(kAssignedMarker == block->fBlockSentinal);
     85     if (1 == block->fLiveCount) {
     86         // the head block is special, it is reset rather than deleted
     87         if (fHead == block) {
     88             fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
     89             fHead->fLiveCount = 0;
     90             fHead->fFreeSize = fPreallocSize;
     91         } else {
     92             BlockHeader* prev = block->fPrev;
     93             BlockHeader* next = block->fNext;
     94             SkASSERT(prev);
     95             prev->fNext = next;
     96             if (next) {
     97                 next->fPrev = prev;
     98             } else {
     99                 SkASSERT(fTail == block);
    100                 fTail = prev;
    101             }
    102             fSize -= block->fSize;
    103             DeleteBlock(block);
    104             SkDEBUGCODE(fAllocBlockCnt--);
    105         }
    106     } else {
    107         --block->fLiveCount;
    108         // Trivial reclaim: if we're releasing the most recent allocation, reuse it
    109         if (block->fPrevPtr == ptr) {
    110             block->fFreeSize += (block->fCurrPtr - block->fPrevPtr);
    111             block->fCurrPtr = block->fPrevPtr;
    112         }
    113     }
    114     SkDEBUGCODE(--fAllocationCnt);
    115     VALIDATE;
    116 }
    117 
    118 GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) {
    119     size_t paddedSize = size + kHeaderSize;
    120     BlockHeader* block =
    121         reinterpret_cast<BlockHeader*>(sk_malloc_throw(paddedSize));
    122     // we assume malloc gives us aligned memory
    123     SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment));
    124     SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker);
    125     block->fLiveCount = 0;
    126     block->fFreeSize = size;
    127     block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
    128     block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t.
    129     block->fSize = paddedSize;
    130     return block;
    131 }
    132 
    133 void GrMemoryPool::DeleteBlock(BlockHeader* block) {
    134     SkASSERT(kAssignedMarker == block->fBlockSentinal);
    135     SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW
    136     sk_free(block);
    137 }
    138 
    139 void GrMemoryPool::validate() {
    140 #ifdef SK_DEBUG
    141     BlockHeader* block = fHead;
    142     BlockHeader* prev = nullptr;
    143     SkASSERT(block);
    144     int allocCount = 0;
    145     do {
    146         SkASSERT(kAssignedMarker == block->fBlockSentinal);
    147         allocCount += block->fLiveCount;
    148         SkASSERT(prev == block->fPrev);
    149         if (prev) {
    150             SkASSERT(prev->fNext == block);
    151         }
    152 
    153         intptr_t b = reinterpret_cast<intptr_t>(block);
    154         size_t ptrOffset = block->fCurrPtr - b;
    155         size_t totalSize = ptrOffset + block->fFreeSize;
    156         size_t userSize = totalSize - kHeaderSize;
    157         intptr_t userStart = b + kHeaderSize;
    158 
    159         SkASSERT(!(b % kAlignment));
    160         SkASSERT(!(totalSize % kAlignment));
    161         SkASSERT(!(userSize % kAlignment));
    162         SkASSERT(!(block->fCurrPtr % kAlignment));
    163         if (fHead != block) {
    164             SkASSERT(block->fLiveCount);
    165             SkASSERT(userSize >= fMinAllocSize);
    166         } else {
    167             SkASSERT(userSize == fPreallocSize);
    168         }
    169         if (!block->fLiveCount) {
    170             SkASSERT(ptrOffset ==  kHeaderSize);
    171             SkASSERT(userStart == block->fCurrPtr);
    172         } else {
    173             AllocHeader* allocData = reinterpret_cast<AllocHeader*>(userStart);
    174             SkASSERT(allocData->fSentinal == kAssignedMarker ||
    175                      allocData->fSentinal == kFreedMarker);
    176             SkASSERT(block == allocData->fHeader);
    177         }
    178 
    179         prev = block;
    180     } while ((block = block->fNext));
    181     SkASSERT(allocCount == fAllocationCnt);
    182     SkASSERT(prev == fTail);
    183     SkASSERT(fAllocBlockCnt != 0 || fSize == 0);
    184 #endif
    185 }
    186