diff options
Diffstat (limited to 'src/gpu/GrTRecorder.h')
-rw-r--r-- | src/gpu/GrTRecorder.h | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/src/gpu/GrTRecorder.h b/src/gpu/GrTRecorder.h new file mode 100644 index 0000000000..c8f7644f4f --- /dev/null +++ b/src/gpu/GrTRecorder.h @@ -0,0 +1,251 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTRecorder_DEFINED +#define GrTRecorder_DEFINED + +#include "SkTemplates.h" +#include "SkTypes.h" + +template<typename TBase, typename TAlign> class GrTRecorder; +template<typename TItem> struct GrTRecorderAllocWrapper; + +/** + * Records a list of items with a common base type, optional associated data, and + * permanent memory addresses. + * + * This class preallocates its own chunks of memory for hosting objects, so new items can + * be created without excessive calls to malloc(). + * + * To create a new item and append it to the back of the list, use the following macros: + * + * GrNEW_APPEND_TO_RECORDER(recorder, SubclassName, (args)) + * GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassName, (args), sizeOfData) + * + * Upon reset or delete, the items are destructed in the same order they were received, + * not reverse (stack) order. + * + * @param TBase Common base type of items in the list. If TBase is not a class with a + * virtual destructor, the client is responsible for invoking any necessary + * destructors. + * + * For now, any subclass used in the list must have the same start address + * as TBase (or in other words, the types must be convertible via + * reinterpret_cast<>). Classes with multiple inheritance (or any subclass + * on an obscure compiler) may not be compatible. This is runtime asserted + * in debug builds. + * + * @param TAlign A type whose size is the desired memory alignment for object allocations. + * This should be the largest known alignment requirement for all objects + * that may be stored in the list. + */ +template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable { +public: + class Iter; + + /** + * Create a recorder. + * + * @param initialSizeInBytes The amount of memory reserved by the recorder initially, + and after calls to reset(). + */ + GrTRecorder(int initialSizeInBytes) + : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes))), + fTailBlock(fHeadBlock), + fLastItem(NULL) {} + + ~GrTRecorder() { + this->reset(); + sk_free(fHeadBlock); + } + + bool empty() { return !fLastItem; } + + TBase& back() { + SkASSERT(!this->empty()); + return *fLastItem; + } + + /** + * Destruct all items in the list and reset to empty. + */ + void reset(); + + /** + * Retrieve the extra data associated with an item that was allocated using + * GrNEW_APPEND_WITH_DATA_TO_RECORDER(). + * + * @param item The item whose data to retrieve. The pointer must be of the same type + * that was allocated initally; it can't be a pointer to a base class. + * + * @return The item's associated data. + */ + template<typename TItem> static const void* GetDataForItem(const TItem* item) { + const TAlign* ptr = reinterpret_cast<const TAlign*>(item); + return &ptr[length_of<TItem>::kValue]; + } + template<typename TItem> static void* GetDataForItem(TItem* item) { + TAlign* ptr = reinterpret_cast<TAlign*>(item); + return &ptr[length_of<TItem>::kValue]; + } + +private: + template<typename TItem> struct length_of { + enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) }; + }; + static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); } + + struct Header { + int fTotalLength; + }; + template<typename TItem> TItem* alloc_back(int dataLength); + + struct MemBlock { + static MemBlock* Alloc(int length) { + void* ptr = sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::kValue + length)); + return SkNEW_PLACEMENT_ARGS(ptr, MemBlock, (length)); + } + TAlign& operator [](int i) { + return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue + i]; + } + ~MemBlock() { sk_free(fNext); } + + const int fLength; + int fBack; + MemBlock* fNext; + + private: + MemBlock(int length) : fLength(length), fBack(0), fNext(NULL) {} + }; + MemBlock* const fHeadBlock; + MemBlock* fTailBlock; + + TBase* fLastItem; + + template<typename TItem> friend struct GrTRecorderAllocWrapper; + + template <typename UBase, typename UAlign, typename UAlloc> + friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&, + const GrTRecorderAllocWrapper<UAlloc>&); + + friend class Iter; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template<typename TBase, typename TAlign> +template<typename TItem> +TItem* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) { + const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue + dataLength; + + if (fTailBlock->fBack + totalLength > fTailBlock->fLength) { + SkASSERT(!fTailBlock->fNext); + fTailBlock->fNext = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength)); + fTailBlock = fTailBlock->fNext; + } + + Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]); + TItem* rawPtr = reinterpret_cast<TItem*>( + &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kValue]); + + header->fTotalLength = totalLength; + fLastItem = rawPtr; + fTailBlock->fBack += totalLength; + + // FIXME: We currently require that the base and subclass share the same start address. + // This is not required by the C++ spec, and is likely to not be true in the case of + // multiple inheritance or a base class that doesn't have virtual methods (when the + // subclass does). It would be ideal to find a more robust solution that comes at no + // extra cost to performance or code generality. + SkDEBUGCODE(void* baseAddr = fLastItem; + void* subclassAddr = rawPtr); + SkASSERT(baseAddr == subclassAddr); + + return rawPtr; +} + +template<typename TBase, typename TAlign> +class GrTRecorder<TBase, TAlign>::Iter { +public: + Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fItem(NULL) {} + + bool next() { + if (fPosition >= fBlock->fBack) { + SkASSERT(fPosition == fBlock->fBack); + if (!fBlock->fNext) { + return false; + } + SkASSERT(0 != fBlock->fNext->fBack); + fBlock = fBlock->fNext; + fPosition = 0; + } + + Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]); + fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]); + fPosition += header->fTotalLength; + return true; + } + + TBase* get() const { + SkASSERT(fItem); + return fItem; + } + + TBase* operator->() const { return this->get(); } + +private: + MemBlock* fBlock; + int fPosition; + TBase* fItem; +}; + +template<typename TBase, typename TAlign> +void GrTRecorder<TBase, TAlign>::reset() { + Iter iter(*this); + while (iter.next()) { + iter->~TBase(); + } + fHeadBlock->fBack = 0; + sk_free(fHeadBlock->fNext); + fHeadBlock->fNext = NULL; + fTailBlock = fHeadBlock; + fLastItem = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +template<typename TItem> struct GrTRecorderAllocWrapper { + GrTRecorderAllocWrapper() : fDataLength(0) {} + + template <typename TBase, typename TAlign> + GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData) + : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {} + + const int fDataLength; +}; + +template <typename TBase, typename TAlign, typename TItem> +void* operator new(size_t size, GrTRecorder<TBase, TAlign>& recorder, + const GrTRecorderAllocWrapper<TItem>& wrapper) { + SkASSERT(size == sizeof(TItem)); + return recorder.template alloc_back<TItem>(wrapper.fDataLength); +} + +template <typename TBase, typename TAlign, typename TItem> +void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocWrapper<TItem>&) { + // We only provide an operator delete to work around compiler warnings that can come + // up for an unmatched operator new when compiling with exceptions. + SK_CRASH(); +} + +#define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \ + (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args) + +#define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_data) \ + (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data)) type_name args) + +#endif |