|  | /* | 
|  | * 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 "include/gpu/GrTypes.h" | 
|  | #include "include/private/SkTLogic.h" | 
|  | #include "src/core/SkArenaAlloc.h" | 
|  |  | 
|  | /** | 
|  | * Records a list of items with a common base type, optional associated data, and | 
|  | * permanent memory addresses. It supports forward iteration. | 
|  | * | 
|  | * This class allocates space for the stored items and associated data in a SkArenaAlloc. | 
|  | * There is an overhead of 1 pointer for each stored item. | 
|  | * | 
|  | * 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. It is assumed that the items are | 
|  | *                trivially destructable or that TBase has a virtual destructor as ~TBase() | 
|  | *                is called to destroy the items. | 
|  | */ | 
|  | template <typename TBase> class GrTRecorder { | 
|  | private: | 
|  | template <bool IsConst> class IterImpl; | 
|  |  | 
|  | public: | 
|  | using iterator = IterImpl<false>; | 
|  | using const_iterator = IterImpl<true>; | 
|  |  | 
|  | /** | 
|  | * Create a recorder. | 
|  | * | 
|  | * @param initialSizeInBytes  The amount of memory reserved by the recorder initially, | 
|  | and after calls to reset(). | 
|  | */ | 
|  | explicit GrTRecorder(size_t initialSizeInBytes) : fArena(initialSizeInBytes) {} | 
|  | GrTRecorder(const GrTRecorder&) = delete; | 
|  | GrTRecorder& operator=(const GrTRecorder&) = delete; | 
|  |  | 
|  | ~GrTRecorder() { this->reset(); } | 
|  |  | 
|  | bool empty() { return !SkToBool(fTail); } | 
|  |  | 
|  | /** The last item. Must not be empty. */ | 
|  | TBase& back() { | 
|  | SkASSERT(!this->empty()); | 
|  | return *fTail->get(); | 
|  | } | 
|  |  | 
|  | /** Forward mutable iteration */ | 
|  | iterator begin() { return iterator(fHead); } | 
|  | iterator end() { return iterator(nullptr); } | 
|  |  | 
|  | /** Forward const iteration */ | 
|  | const_iterator begin() const { return const_iterator(fHead); } | 
|  | const_iterator end() const { return const_iterator(nullptr); } | 
|  |  | 
|  | /** Destruct all items in the list and reset to empty. Frees memory allocated from arena. */ | 
|  | void reset(); | 
|  |  | 
|  | /** | 
|  | * Emplace a new TItem (which derives from TBase) in the recorder. This requires equivalence | 
|  | * between reinterpret_cast<TBase*> and static_cast<TBase*> when operating on TItem*. | 
|  | * Multiple inheritance may make this not true. It is runtime asserted. | 
|  | */ | 
|  | template <typename TItem, typename... Args> TItem& emplace(Args&&... args) { | 
|  | return this->emplaceWithData<TItem, Args...>(0, std::forward<Args>(args)...); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Emplace a new TItem (which derives from TBase) in the recorder with extra data space. The | 
|  | * extra data immediately follows the stored item with no extra alignment. E.g., | 
|  | *      void* extraData = &recorder->emplaceWithData<Subclass>(dataSize, ...) + 1; | 
|  | * | 
|  | * This requires equivalence between reinterpret_cast<TBase*> and static_cast<TBase*> when | 
|  | * operating on TItem*. Multiple inheritance may make this not true. It is runtime asserted. | 
|  | */ | 
|  | template <typename TItem, typename... Args> | 
|  | SK_WHEN((std::is_base_of<TBase, TItem>::value), TItem&) | 
|  | emplaceWithData(size_t extraDataSize, Args... args); | 
|  |  | 
|  | private: | 
|  | struct Header { | 
|  | Header* fNext = nullptr; | 
|  | // We always store the T immediately after the header (and ensure proper alignment). See | 
|  | // emplaceWithData() implementation. | 
|  | TBase* get() const { return reinterpret_cast<TBase*>(const_cast<Header*>(this) + 1); } | 
|  | }; | 
|  |  | 
|  | SkArenaAlloc fArena; | 
|  | Header* fHead = nullptr; | 
|  | Header* fTail = nullptr; | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | template <typename TBase> | 
|  | template <typename TItem, typename... Args> | 
|  | inline SK_WHEN((std::is_base_of<TBase, TItem>::value), TItem&) | 
|  | GrTRecorder<TBase>::emplaceWithData(size_t extraDataSize, Args... args) { | 
|  | static constexpr size_t kTAlign = alignof(TItem); | 
|  | static constexpr size_t kHeaderAlign = alignof(Header); | 
|  | static constexpr size_t kAllocAlign = kTAlign > kHeaderAlign ? kTAlign : kHeaderAlign; | 
|  | static constexpr size_t kTItemOffset = GrAlignTo(sizeof(Header), kAllocAlign); | 
|  | // We're assuming if we back up from kItemOffset by sizeof(Header) we will still be aligned. | 
|  | static_assert(sizeof(Header) % alignof(Header) == 0); | 
|  | const size_t totalSize = kTItemOffset + sizeof(TItem) + extraDataSize; | 
|  | auto alloc = reinterpret_cast<char*>(fArena.makeBytesAlignedTo(totalSize, kAllocAlign)); | 
|  | Header* header = new (alloc + kTItemOffset - sizeof(Header)) Header(); | 
|  | if (fTail) { | 
|  | fTail->fNext = header; | 
|  | } | 
|  | fTail = header; | 
|  | if (!fHead) { | 
|  | fHead = header; | 
|  | } | 
|  | auto* item = new (alloc + kTItemOffset) TItem(std::forward<Args>(args)...); | 
|  | // We require that we can reinterpret_cast between TBase* and TItem*. Could not figure out how | 
|  | // to statically assert this. See proposal for std::is_initial_base_of here: | 
|  | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0466r0.pdf | 
|  | SkASSERT(reinterpret_cast<uintptr_t>(item) == | 
|  | reinterpret_cast<uintptr_t>(static_cast<TBase*>(item))); | 
|  | return *item; | 
|  | } | 
|  |  | 
|  | template <typename TBase> inline void GrTRecorder<TBase>::reset() { | 
|  | for (auto& i : *this) { | 
|  | i.~TBase(); | 
|  | } | 
|  | static_assert(std::is_trivially_destructible<Header>::value); | 
|  | fHead = fTail = nullptr; | 
|  | fArena.reset(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Iterates through a recorder front-to-back, const or not. | 
|  | */ | 
|  | template <typename TBase> template <bool IsConst> class GrTRecorder<TBase>::IterImpl { | 
|  | private: | 
|  | using T = typename std::conditional<IsConst, const TBase, TBase>::type; | 
|  |  | 
|  | public: | 
|  | IterImpl() = default; | 
|  |  | 
|  | IterImpl operator++() { | 
|  | fCurr = fCurr->fNext; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | IterImpl operator++(int) { | 
|  | auto old = fCurr; | 
|  | fCurr = fCurr->fNext; | 
|  | return {old}; | 
|  | } | 
|  |  | 
|  | T& operator*() const { return *fCurr->get(); } | 
|  | T* operator->() const { return fCurr->get(); } | 
|  |  | 
|  | bool operator==(const IterImpl& that) const { return fCurr == that.fCurr; } | 
|  | bool operator!=(const IterImpl& that) const { return !(*this == that); } | 
|  |  | 
|  | private: | 
|  | IterImpl(Header* curr) : fCurr(curr) {} | 
|  | Header* fCurr = nullptr; | 
|  |  | 
|  | friend class GrTRecorder<TBase>; // To construct from Header. | 
|  | }; | 
|  |  | 
|  | #endif |