| Index: src/gpu/GrTRecorder.h
|
| diff --git a/src/gpu/GrTRecorder.h b/src/gpu/GrTRecorder.h
|
| index ab559e4a5962b5abb5f59ec981e21c131fd90b39..bddf1978d801479552267ca829786ff7dfddc4ce 100644
|
| --- a/src/gpu/GrTRecorder.h
|
| +++ b/src/gpu/GrTRecorder.h
|
| @@ -54,7 +54,7 @@ public:
|
| and after calls to reset().
|
| */
|
| GrTRecorder(int initialSizeInBytes)
|
| - : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes))),
|
| + : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes), NULL)),
|
| fTailBlock(fHeadBlock),
|
| fLastItem(NULL) {}
|
|
|
| @@ -71,6 +71,12 @@ public:
|
| }
|
|
|
| /**
|
| + * Removes and destroys the last block added to the recorder. It may not be called when the
|
| + * recorder is empty.
|
| + */
|
| + void pop_back();
|
| +
|
| + /**
|
| * Destruct all items in the list and reset to empty.
|
| */
|
| void reset();
|
| @@ -100,35 +106,49 @@ private:
|
| static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); }
|
|
|
| struct Header {
|
| - int fTotalLength;
|
| + int fTotalLength; // The length of an entry including header, item, and data in TAligns.
|
| + int fPrevLength; // Same but for the previous entry. Used for iterating backwards.
|
| };
|
| template<typename TItem> TItem* alloc_back(int dataLength);
|
|
|
| struct MemBlock : SkNoncopyable {
|
| - static MemBlock* Alloc(int length) {
|
| + /** Allocates a new block and appends it to prev if not NULL. The length param is in units
|
| + of TAlign. */
|
| + static MemBlock* Alloc(int length, MemBlock* prev) {
|
| MemBlock* block = reinterpret_cast<MemBlock*>(
|
| sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::kValue + length)));
|
| block->fLength = length;
|
| block->fBack = 0;
|
| block->fNext = NULL;
|
| + block->fPrev = prev;
|
| + if (prev) {
|
| + SkASSERT(NULL == prev->fNext);
|
| + prev->fNext = block;
|
| + }
|
| return block;
|
| }
|
|
|
| + // Frees from this block forward. Also adjusts prev block's next ptr.
|
| static void Free(MemBlock* block) {
|
| - if (!block) {
|
| - return;
|
| + if (block && block->fPrev) {
|
| + SkASSERT(block->fPrev->fNext == block);
|
| + block->fPrev->fNext = NULL;
|
| + }
|
| + while (block) {
|
| + MemBlock* next = block->fNext;
|
| + sk_free(block);
|
| + block = next;
|
| }
|
| - Free(block->fNext);
|
| - sk_free(block);
|
| }
|
|
|
| TAlign& operator [](int i) {
|
| return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue + i];
|
| }
|
|
|
| - int fLength;
|
| - int fBack;
|
| + int fLength; // Length in units of TAlign of the block.
|
| + int fBack; // Offset, in TAligns, to unused portion of the memory block.
|
| MemBlock* fNext;
|
| + MemBlock* fPrev;
|
| };
|
| MemBlock* const fHeadBlock;
|
| MemBlock* fTailBlock;
|
| @@ -147,15 +167,54 @@ private:
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| template<typename TBase, typename TAlign>
|
| +void GrTRecorder<TBase, TAlign>::pop_back() {
|
| + SkASSERT(fLastItem);
|
| + Header* header = reinterpret_cast<Header*>(
|
| + reinterpret_cast<TAlign*>(fLastItem) - length_of<Header>::kValue);
|
| + fTailBlock->fBack -= header->fTotalLength;
|
| + fLastItem->~TBase();
|
| +
|
| + int lastItemLength = header->fPrevLength;
|
| +
|
| + if (!header->fPrevLength) {
|
| + // We popped the first entry in the recorder.
|
| + SkASSERT(0 == fTailBlock->fBack);
|
| + fLastItem = NULL;
|
| + return;
|
| + }
|
| + if (!fTailBlock->fBack) {
|
| + // We popped the last entry in a block that isn't the head block. Move back a block but
|
| + // don't free it since we'll probably grow into it shortly.
|
| + fTailBlock = fTailBlock->fPrev;
|
| + SkASSERT(fTailBlock);
|
| + }
|
| + fLastItem = reinterpret_cast<TBase*>(
|
| + &(*fTailBlock)[fTailBlock->fBack - lastItemLength + length_of<Header>::kValue]);
|
| +}
|
| +
|
| +template<typename TBase, typename TAlign>
|
| template<typename TItem>
|
| TItem* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) {
|
| + // Find the header of the previous entry and get its length. We need to store that in the new
|
| + // header for backwards iteration (pop_back()).
|
| + int prevLength = 0;
|
| + if (fLastItem) {
|
| + Header* lastHeader = reinterpret_cast<Header*>(
|
| + reinterpret_cast<TAlign*>(fLastItem) - length_of<Header>::kValue);
|
| + prevLength = lastHeader->fTotalLength;
|
| + }
|
| +
|
| const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue + dataLength;
|
|
|
| + // Check if there is room in the current block and if not walk to next (allocating if
|
| + // necessary). Note that pop_back() and reset() can leave the recorder in a state where it
|
| + // has preallocated blocks hanging off the tail that are currently unused.
|
| while (fTailBlock->fBack + totalLength > fTailBlock->fLength) {
|
| if (!fTailBlock->fNext) {
|
| - fTailBlock->fNext = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength));
|
| + fTailBlock = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength), fTailBlock);
|
| + } else {
|
| + fTailBlock = fTailBlock->fNext;
|
| }
|
| - fTailBlock = fTailBlock->fNext;
|
| SkASSERT(0 == fTailBlock->fBack);
|
| }
|
|
|
| @@ -164,6 +223,7 @@ TItem* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) {
|
| &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kValue]);
|
|
|
| header->fTotalLength = totalLength;
|
| + header->fPrevLength = prevLength;
|
| fLastItem = rawPtr;
|
| fTailBlock->fBack += totalLength;
|
|
|
| @@ -225,7 +285,6 @@ void GrTRecorder<TBase, TAlign>::reset() {
|
| // everything else.
|
| if (fTailBlock->fBack <= fTailBlock->fLength / 2) {
|
| MemBlock::Free(fTailBlock->fNext);
|
| - fTailBlock->fNext = NULL;
|
| } else if (fTailBlock->fNext) {
|
| MemBlock::Free(fTailBlock->fNext->fNext);
|
| fTailBlock->fNext->fNext = NULL;
|
|
|