| /* |
| * 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 = GrSizeAlignUp(sizeof(Header), kAllocAlign); |
| // We're assuming if we back up from kItemOffset by sizeof(Header) we will still be aligned. |
| GR_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(); |
| } |
| GR_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 |