Implement GrTAllocator in terms of GrBlockAllocator

Previously, the GrAllocator header defined three types: GrAllocator,
GrTAllocator, and GrSTAllocator. GrAllocator was generic, and would
manage the blocks of memory (fixed size to hold N * sizeof(item)). It
stored an SkTArray of pointers to each block. GrTAllocator wrapped
the GrAllocator to add template support for a specific T. GrSTAllocator
extended GrTAllocator to include a template arg for inline storage.

In this CL, GrAllocator is no longer defined, and its memory functionality
is replaced with the use of GrBlockAllocator. For the most part, the
implementation of GrTAllocator on top of GrBlockAllocator remains the
same, although there is explicit array to the block pointers so indexing
is slightly different. GrSTAllocator is also removed entirely, so that
GrTAllocator's template includes initial storage size.

The iteration over the allocated items
is updated to wrap GrBlockAllocator's block iterator, and then iterate
by index within each block. Documentation on GrAllocator already recommended
using the iterator instead of random access, and these changes further
reinforce it. It turns out that many of the use cases of GrAllocator
were written to use random access, so many of the files outside of
GrAllocator.h have just been updated to use the new for-range iteration
instead of a random access for loop.


Change-Id: I28b0bc277c323fd7035d4a8442ae67f058b2b64c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/272058
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Chris Dalton <csmartdalton@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index c655537..433a713 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -35,7 +35,6 @@
   "$_src/gpu/GrAHardwareBufferImageGenerator.h",
   "$_src/gpu/GrAHardwareBufferUtils.cpp",
   "$_src/gpu/GrAHardwareBufferUtils.h",
-  "$_src/gpu/GrAllocator.h",
   "$_src/gpu/GrBackendSurface.cpp",
   "$_src/gpu/GrBackendTextureImageGenerator.cpp",
   "$_src/gpu/GrBackendTextureImageGenerator.h",
@@ -209,6 +208,7 @@
   "$_src/gpu/GrSwizzle.h",
   "$_src/gpu/GrSWMaskHelper.cpp",
   "$_src/gpu/GrSWMaskHelper.h",
+  "$_src/gpu/GrTAllocator.h",
   "$_src/gpu/GrTracing.h",
   "$_src/gpu/GrTransferFromRenderTask.cpp",
   "$_src/gpu/GrTransferFromRenderTask.h",
diff --git a/gn/tests.gni b/gn/tests.gni
index b0332d0..b3a1632 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -96,7 +96,6 @@
   "$_tests/GpuDrawPathTest.cpp",
   "$_tests/GpuRectanizerTest.cpp",
   "$_tests/GrAHardwareBufferTest.cpp",
-  "$_tests/GrAllocatorTest.cpp",
   "$_tests/GrBlockAllocatorTest.cpp",
   "$_tests/GrCCPRTest.cpp",
   "$_tests/GrContextAbandonTest.cpp",
@@ -112,6 +111,7 @@
   "$_tests/GrQuadCropTest.cpp",
   "$_tests/GrShapeTest.cpp",
   "$_tests/GrSurfaceTest.cpp",
+  "$_tests/GrTAllocatorTest.cpp",
   "$_tests/GrTRecorderTest.cpp",
   "$_tests/GrTestingBackendTextureUploadTest.cpp",
   "$_tests/GrTextureMipMapInvalidationTest.cpp",
diff --git a/src/gpu/GrAllocator.h b/src/gpu/GrAllocator.h
deleted file mode 100644
index b2945e8..0000000
--- a/src/gpu/GrAllocator.h
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrAllocator_DEFINED
-#define GrAllocator_DEFINED
-
-#include "include/core/SkTypes.h"
-#include "include/gpu/GrConfig.h"
-#include "include/gpu/GrTypes.h"
-#include "include/private/SkNoncopyable.h"
-#include "include/private/SkTArray.h"
-#include <new>
-
-class GrAllocator : SkNoncopyable {
-public:
-    ~GrAllocator() { this->reset(); }
-
-    /**
-     * Create an allocator
-     *
-     * @param   itemSize        the size of each item to allocate
-     * @param   itemsPerBlock   the number of items to allocate at once
-     * @param   initialBlock    optional memory to use for the first block.
-     *                          Must be at least itemSize*itemsPerBlock sized.
-     *                          Caller is responsible for freeing this memory.
-     */
-    GrAllocator(size_t itemSize, int itemsPerBlock, void* initialBlock)
-        : fItemSize(itemSize)
-        , fItemsPerBlock(itemsPerBlock)
-        , fOwnFirstBlock(nullptr == initialBlock)
-        , fCount(0)
-        , fInsertionIndexInBlock(0) {
-        SkASSERT(itemsPerBlock > 0);
-        fBlockSize = fItemSize * fItemsPerBlock;
-        if (fOwnFirstBlock) {
-            // This force us to allocate a new block on push_back().
-            fInsertionIndexInBlock = fItemsPerBlock;
-        } else {
-            fBlocks.push_back() = initialBlock;
-            fInsertionIndexInBlock = 0;
-        }
-    }
-
-    /**
-     * Adds an item and returns pointer to it.
-     *
-     * @return pointer to the added item.
-     */
-    void* push_back() {
-        // we always have at least one block
-        if (fItemsPerBlock == fInsertionIndexInBlock) {
-            fBlocks.push_back() = sk_malloc_throw(fBlockSize);
-            fInsertionIndexInBlock = 0;
-        }
-        void* ret = (char*)fBlocks.back() + fItemSize * fInsertionIndexInBlock;
-        ++fCount;
-        ++fInsertionIndexInBlock;
-        return ret;
-    }
-
-    /**
-     * Remove the last item, only call if count() != 0
-     */
-    void pop_back() {
-        SkASSERT(fCount);
-        SkASSERT(fInsertionIndexInBlock > 0);
-        --fInsertionIndexInBlock;
-        --fCount;
-        if (0 == fInsertionIndexInBlock) {
-            // Never delete the first block
-            if (fBlocks.count() > 1) {
-                sk_free(fBlocks.back());
-                fBlocks.pop_back();
-                fInsertionIndexInBlock = fItemsPerBlock;
-            }
-        }
-    }
-
-    /**
-     * Removes all added items.
-     */
-    void reset() {
-        int firstBlockToFree = fOwnFirstBlock ? 0 : 1;
-        for (int i = firstBlockToFree; i < fBlocks.count(); ++i) {
-            sk_free(fBlocks[i]);
-        }
-        if (fOwnFirstBlock) {
-            fBlocks.reset();
-            // This force us to allocate a new block on push_back().
-            fInsertionIndexInBlock = fItemsPerBlock;
-        } else {
-            fBlocks.pop_back_n(fBlocks.count() - 1);
-            fInsertionIndexInBlock = 0;
-        }
-        fCount = 0;
-    }
-
-    /**
-     * Returns the item count.
-     */
-    int count() const {
-        return fCount;
-    }
-
-    /**
-     * Is the count 0?
-     */
-    bool empty() const { return 0 == fCount; }
-
-    /**
-     * Access first item, only call if count() != 0
-     */
-    void* front() {
-        SkASSERT(fCount);
-        SkASSERT(fInsertionIndexInBlock > 0);
-        return (char*)(fBlocks.front());
-    }
-
-    /**
-     * Access first item, only call if count() != 0
-     */
-    const void* front() const {
-        SkASSERT(fCount);
-        SkASSERT(fInsertionIndexInBlock > 0);
-        return (const char*)(fBlocks.front());
-    }
-
-    /**
-     * Access last item, only call if count() != 0
-     */
-    void* back() {
-        SkASSERT(fCount);
-        SkASSERT(fInsertionIndexInBlock > 0);
-        return (char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
-    }
-
-    /**
-     * Access last item, only call if count() != 0
-     */
-    const void* back() const {
-        SkASSERT(fCount);
-        SkASSERT(fInsertionIndexInBlock > 0);
-        return (const char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
-    }
-
-    /**
-     * Iterates through the allocator. This is faster than using operator[] when walking linearly
-     * through the allocator.
-     */
-    class Iter {
-    public:
-        /**
-         * Initializes the iterator. next() must be called before get().
-         */
-        Iter(const GrAllocator* allocator)
-            : fAllocator(allocator)
-            , fBlockIndex(-1)
-            , fIndexInBlock(allocator->fItemsPerBlock - 1)
-            , fItemIndex(-1) {}
-
-        /**
-         * Advances the iterator. Iteration is finished when next() returns false.
-         */
-        bool next() {
-            ++fIndexInBlock;
-            ++fItemIndex;
-            if (fIndexInBlock == fAllocator->fItemsPerBlock) {
-                ++fBlockIndex;
-                fIndexInBlock = 0;
-            }
-            return fItemIndex < fAllocator->fCount;
-        }
-
-        /**
-         * Gets the current iterator value. Call next() at least once before calling. Don't call
-         * after next() returns false.
-         */
-        void* get() const {
-            SkASSERT(fItemIndex >= 0 && fItemIndex < fAllocator->fCount);
-            return (char*) fAllocator->fBlocks[fBlockIndex] + fIndexInBlock * fAllocator->fItemSize;
-        }
-
-    private:
-        const GrAllocator* fAllocator;
-        int                fBlockIndex;
-        int                fIndexInBlock;
-        int                fItemIndex;
-    };
-
-    /**
-     * Access item by index.
-     */
-    void* operator[] (int i) {
-        SkASSERT(i >= 0 && i < fCount);
-        return (char*)fBlocks[i / fItemsPerBlock] +
-               fItemSize * (i % fItemsPerBlock);
-    }
-
-    /**
-     * Access item by index.
-     */
-    const void* operator[] (int i) const {
-        SkASSERT(i >= 0 && i < fCount);
-        return (const char*)fBlocks[i / fItemsPerBlock] +
-               fItemSize * (i % fItemsPerBlock);
-    }
-
-protected:
-    /**
-     * Set first block of memory to write into.  Must be called before any other methods.
-     * This requires that you have passed nullptr in the constructor.
-     *
-     * @param   initialBlock    optional memory to use for the first block.
-     *                          Must be at least itemSize*itemsPerBlock sized.
-     *                          Caller is responsible for freeing this memory.
-     */
-    void setInitialBlock(void* initialBlock) {
-        SkASSERT(0 == fCount);
-        SkASSERT(0 == fBlocks.count());
-        SkASSERT(fItemsPerBlock == fInsertionIndexInBlock);
-        fOwnFirstBlock = false;
-        fBlocks.push_back() = initialBlock;
-        fInsertionIndexInBlock = 0;
-    }
-
-    // For access to above function.
-    template <typename T> friend class GrTAllocator;
-
-private:
-    static const int NUM_INIT_BLOCK_PTRS = 8;
-
-    SkSTArray<NUM_INIT_BLOCK_PTRS, void*, true>   fBlocks;
-    size_t                                        fBlockSize;
-    size_t                                        fItemSize;
-    int                                           fItemsPerBlock;
-    bool                                          fOwnFirstBlock;
-    int                                           fCount;
-    int                                           fInsertionIndexInBlock;
-
-    typedef SkNoncopyable INHERITED;
-};
-
-template <typename T> class GrTAllocator;
-template <typename T> void* operator new(size_t, GrTAllocator<T>*);
-
-template <typename T> class GrTAllocator : SkNoncopyable {
-public:
-    virtual ~GrTAllocator() { this->reset(); }
-
-    /**
-     * Create an allocator
-     *
-     * @param   itemsPerBlock   the number of items to allocate at once
-     */
-    explicit GrTAllocator(int itemsPerBlock)
-        : fAllocator(sizeof(T), itemsPerBlock, nullptr) {}
-
-    /**
-     * Adds an item and returns it.
-     *
-     * @return the added item.
-     */
-    T& push_back() {
-        void* item = fAllocator.push_back();
-        SkASSERT(item);
-        new (item) T;
-        return *(T*)item;
-    }
-
-    T& push_back(const T& t) {
-        void* item = fAllocator.push_back();
-        SkASSERT(item);
-        new (item) T(t);
-        return *(T*)item;
-    }
-
-    template <typename... Args> T& emplace_back(Args&&... args) {
-        void* item = fAllocator.push_back();
-        SkASSERT(item);
-        new (item) T(std::forward<Args>(args)...);
-        return *(T*)item;
-    }
-
-    /**
-     * Remove the last item, only call if count() != 0
-     */
-    void pop_back() {
-        this->back().~T();
-        fAllocator.pop_back();
-    }
-
-    /**
-     * Removes all added items.
-     */
-    void reset() {
-        int c = fAllocator.count();
-        for (int i = 0; i < c; ++i) {
-            ((T*)fAllocator[i])->~T();
-        }
-        fAllocator.reset();
-    }
-
-    /**
-     * Returns the item count.
-     */
-    int count() const {
-        return fAllocator.count();
-    }
-
-    /**
-     * Is the count 0?
-     */
-    bool empty() const { return fAllocator.empty(); }
-
-    /**
-     * Access first item, only call if count() != 0
-     */
-    T& front() {
-        return *(T*)fAllocator.front();
-    }
-
-    /**
-     * Access first item, only call if count() != 0
-     */
-    const T& front() const {
-        return *(T*)fAllocator.front();
-    }
-
-    /**
-     * Access last item, only call if count() != 0
-     */
-    T& back() {
-        return *(T*)fAllocator.back();
-    }
-
-    /**
-     * Access last item, only call if count() != 0
-     */
-    const T& back() const {
-        return *(const T*)fAllocator.back();
-    }
-
-    /**
-     * Iterates through the allocator. This is faster than using operator[] when walking linearly
-     * through the allocator.
-     */
-    class Iter {
-    public:
-        /**
-         * Initializes the iterator. next() must be called before get() or ops * and ->.
-         */
-        Iter(const GrTAllocator* allocator) : fImpl(&allocator->fAllocator) {}
-
-        /**
-         * Advances the iterator. Iteration is finished when next() returns false.
-         */
-        bool next() { return fImpl.next(); }
-
-        /**
-         * Gets the current iterator value. Call next() at least once before calling. Don't call
-         * after next() returns false.
-         */
-        T* get() const { return (T*) fImpl.get(); }
-
-        /**
-         * Convenience operators. Same rules for calling apply as get().
-         */
-        T& operator*() const { return *this->get(); }
-        T* operator->() const { return this->get(); }
-
-    private:
-        GrAllocator::Iter fImpl;
-    };
-
-    /**
-     * Access item by index.
-     */
-    T& operator[] (int i) {
-        return *(T*)(fAllocator[i]);
-    }
-
-    /**
-     * Access item by index.
-     */
-    const T& operator[] (int i) const {
-        return *(const T*)(fAllocator[i]);
-    }
-
-protected:
-    /*
-     * Set first block of memory to write into.  Must be called before any other methods.
-     *
-     * @param   initialBlock    optional memory to use for the first block.
-     *                          Must be at least size(T)*itemsPerBlock sized.
-     *                          Caller is responsible for freeing this memory.
-     */
-    void setInitialBlock(void* initialBlock) {
-        fAllocator.setInitialBlock(initialBlock);
-    }
-
-private:
-    friend void* operator new<T>(size_t, GrTAllocator*);
-
-    GrAllocator fAllocator;
-    typedef SkNoncopyable INHERITED;
-};
-
-template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> {
-private:
-    typedef GrTAllocator<T> INHERITED;
-
-public:
-    GrSTAllocator() : INHERITED(N) {
-        this->setInitialBlock(fStorage.get());
-    }
-
-private:
-    SkAlignedSTStorage<N, T> fStorage;
-};
-
-template <typename T> void* operator new(size_t size, GrTAllocator<T>* allocator) {
-    return allocator->fAllocator.push_back();
-}
-
-// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
-// to match the op new silences warnings about missing op delete when a constructor throws an
-// exception.
-template <typename T> void operator delete(void*, GrTAllocator<T>*) {
-    SK_ABORT("Invalid Operation");
-}
-
-#define GrNEW_APPEND_TO_ALLOCATOR(allocator_ptr, type_name, args) \
-    new (allocator_ptr) type_name args
-
-#endif
diff --git a/src/gpu/GrBlockAllocator.h b/src/gpu/GrBlockAllocator.h
index e34d594..fae9dab 100644
--- a/src/gpu/GrBlockAllocator.h
+++ b/src/gpu/GrBlockAllocator.h
@@ -77,6 +77,13 @@
         template <size_t Align = 1, size_t Padding = 0>
         int avail() const { return std::max(0, fSize - this->cursor<Align, Padding>()); }
 
+        // Return the aligned offset of the first allocation, assuming it was made with the
+        // specified Align, and Padding. The returned offset does not mean a valid allocation
+        // starts at that offset, this is a utility function for classes built on top to manage
+        // indexing into a block effectively.
+        template <size_t Align = 1, size_t Padding = 0>
+        int firstAlignedOffset() const { return this->alignedOffset<Align, Padding>(kDataStart); }
+
         // Convert an offset into this block's storage into a usable pointer.
         void* ptr(int offset) {
             SkASSERT(offset >= kDataStart && offset < fSize);
@@ -120,7 +127,10 @@
 
         // Get fCursor, but aligned such that ptr(rval) satisfies Align.
         template <size_t Align, size_t Padding>
-        int cursor() const;
+        int cursor() const { return this->alignedOffset<Align, Padding>(fCursor); }
+
+        template <size_t Align, size_t Padding>
+        int alignedOffset(int offset) const;
 
         SkDEBUGCODE(int fSentinel;) // known value to check for bad back pointers to blocks
 
@@ -149,6 +159,24 @@
     void operator delete(void* p) { ::operator delete(p); }
 
     /**
+     * Helper to calculate the minimum number of bytes needed for heap block size, under the
+     * assumption that Align will be the requested alignment of the first call to allocate().
+     * Ex. To store N instances of T in a heap block, the 'blockIncrementBytes' should be set to
+     *   BlockOverhead<alignof(T)>() + N * sizeof(T) when making the GrBlockAllocator.
+     */
+    template<size_t Align = 1, size_t Padding = 0>
+    static constexpr size_t BlockOverhead();
+
+    /**
+     * Helper to calculate the minimum number of bytes needed for a preallocation, under the
+     * assumption that Align will be the requested alignment of the first call to allocate().
+     * Ex. To preallocate a GrSBlockAllocator to hold N instances of T, its arge should be
+     *   Overhead<alignof(T)>() + N * sizeof(T)
+     */
+    template<size_t Align = 1, size_t Padding = 0>
+    static constexpr size_t Overhead();
+
+    /**
      * Return the total number of bytes of the allocator, including its instance overhead, per-block
      * overhead and space used for allocations.
      */
@@ -219,6 +247,9 @@
     const Block* currentBlock() const { return fTail; }
     Block* currentBlock() { return fTail; }
 
+    const Block* headBlock() const { return &fHead; }
+    Block* headBlock() { return &fHead; }
+
     /**
      * Return the block that owns the allocated 'ptr'. Assuming that earlier, an allocation was
      * returned as {b, start, alignedOffset, end}, and 'p = b->ptr(alignedOffset)', then a call
@@ -295,7 +326,8 @@
 
     // Calculates the size of a new Block required to store a kMaxAllocationSize request for the
     // given alignment and padding bytes. Also represents maximum valid fCursor value in a Block.
-    static constexpr size_t MaxBlockSize(size_t align, size_t padding);
+    template<size_t Align, size_t Padding>
+    static constexpr size_t MaxBlockSize();
 
     static constexpr int BaseHeadBlockSize() {
         return sizeof(GrBlockAllocator) - offsetof(GrBlockAllocator, fHead);
@@ -369,25 +401,35 @@
     alignas(GrBlockAllocator) char fStorage[N];
 };
 
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Template and inline implementations
 
-constexpr size_t GrBlockAllocator::MaxBlockSize(size_t align, size_t padding) {
+template<size_t Align, size_t Padding>
+constexpr size_t GrBlockAllocator::BlockOverhead() {
+    return std::max(sizeof(Block), GrAlignTo(kDataStart + Padding, Align));
+}
+
+template<size_t Align, size_t Padding>
+constexpr size_t GrBlockAllocator::Overhead() {
+    return std::max(sizeof(GrBlockAllocator),
+                    offsetof(GrBlockAllocator, fHead) + BlockOverhead<Align, Padding>());
+}
+
+template<size_t Align, size_t Padding>
+constexpr size_t GrBlockAllocator::MaxBlockSize() {
     // Without loss of generality, assumes 'align' will be the largest encountered alignment for the
     // allocator (if it's not, the largest align will be encountered by the compiler and pass/fail
     // the same set of static asserts).
-    return GrAlignTo(kDataStart + padding, align) + kMaxAllocationSize;
+    return BlockOverhead<Align, Padding>() + kMaxAllocationSize;
 }
 
 template <size_t Align, size_t Padding>
 GrBlockAllocator::ByteRange GrBlockAllocator::allocate(size_t size) {
     // Amount of extra space for a new block to make sure the allocation can succeed.
-    static constexpr int kBlockOverhead =
-            std::max(sizeof(Block), GrAlignTo(kDataStart + Padding, Align));
+    static constexpr int kBlockOverhead = (int) BlockOverhead<Align, Padding>();
 
     // Ensures 'offset' and 'end' calculations will be valid
-    static_assert((kMaxAllocationSize + GrAlignTo(MaxBlockSize(Align, Padding), Align))
+    static_assert((kMaxAllocationSize + GrAlignTo(MaxBlockSize<Align, Padding>(), Align))
                         <= (size_t) std::numeric_limits<int32_t>::max());
     // Ensures size + blockOverhead + addBlock's alignment operations will be valid
     static_assert(kMaxAllocationSize + kBlockOverhead + ((1 << 12) - 1) // 4K align for large blocks
@@ -401,7 +443,7 @@
     int offset = fTail->cursor<Align, Padding>();
     int end = offset + iSize;
     if (end > fTail->fSize) {
-        this->addBlock(iSize + kBlockOverhead, MaxBlockSize(Align, Padding));
+        this->addBlock(iSize + kBlockOverhead, MaxBlockSize<Align, Padding>());
         offset = fTail->cursor<Align, Padding>();
         end = offset + iSize;
     }
@@ -440,20 +482,20 @@
 }
 
 template <size_t Align, size_t Padding>
-int GrBlockAllocator::Block::cursor() const {
+int GrBlockAllocator::Block::alignedOffset(int offset) const {
     static_assert(SkIsPow2(Align));
     // Aligning adds (Padding + Align - 1) as an intermediate step, so ensure that can't overflow
-    static_assert(MaxBlockSize(Align, Padding) + Padding + Align - 1
+    static_assert(MaxBlockSize<Align, Padding>() + Padding + Align - 1
                         <= (size_t) std::numeric_limits<int32_t>::max());
 
     if /* constexpr */ (Align <= alignof(std::max_align_t)) {
         // Same as GrAlignTo, but operates on ints instead of size_t
-        return (fCursor + Padding + Align - 1) & ~(Align - 1);
+        return (offset + Padding + Align - 1) & ~(Align - 1);
     } else {
         // Must take into account that 'this' may be starting at a pointer that doesn't satisfy the
         // larger alignment request, so must align the entire pointer, not just offset
         uintptr_t blockPtr = reinterpret_cast<uintptr_t>(this);
-        uintptr_t alignedPtr = (blockPtr + fCursor + Padding + Align - 1) & ~(Align - 1);
+        uintptr_t alignedPtr = (blockPtr + offset + Padding + Align - 1) & ~(Align - 1);
         SkASSERT(alignedPtr - blockPtr <= (uintptr_t) std::numeric_limits<int32_t>::max());
         return (int) (alignedPtr - blockPtr);
     }
diff --git a/src/gpu/GrDynamicAtlas.h b/src/gpu/GrDynamicAtlas.h
index b37cbc1..609dd5b 100644
--- a/src/gpu/GrDynamicAtlas.h
+++ b/src/gpu/GrDynamicAtlas.h
@@ -8,7 +8,6 @@
 #ifndef GrDynamicAtlas_DEFINED
 #define GrDynamicAtlas_DEFINED
 
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrTextureProxy.h"
 
 class GrOnFlushResourceProvider;
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 3a96c26..3df4f65 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -12,7 +12,6 @@
 #include "include/core/SkSurface.h"
 #include "include/gpu/GrTypes.h"
 #include "include/private/SkTArray.h"
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrOpsRenderPass.h"
 #include "src/gpu/GrSamplePatternDictionary.h"
diff --git a/src/gpu/GrTAllocator.h b/src/gpu/GrTAllocator.h
new file mode 100644
index 0000000..580f5d3
--- /dev/null
+++ b/src/gpu/GrTAllocator.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTAllocator_DEFINED
+#define GrTAllocator_DEFINED
+
+#include "src/gpu/GrBlockAllocator.h"
+
+template <typename T, int StartingItems = 1>
+class GrTAllocator {
+public:
+    /**
+     * Create an allocator that defaults to using StartingItems as heap increment.
+     */
+    GrTAllocator()
+            : fTotalCount(0)
+            , fAllocator(GrBlockAllocator::GrowthPolicy::kFixed) {}
+
+    /**
+     * Create an allocator
+     *
+     * @param   itemsPerBlock   the number of items to allocate at once
+     */
+    explicit GrTAllocator(int itemsPerBlock)
+            : fTotalCount(0)
+            , fAllocator(GrBlockAllocator::GrowthPolicy::kFixed,
+                         GrBlockAllocator::BlockOverhead<alignof(T)>() + sizeof(T)*itemsPerBlock) {}
+
+    ~GrTAllocator() { this->reset(); }
+
+    /**
+     * Adds an item and returns it.
+     *
+     * @return the added item.
+     */
+    T& push_back() {
+        T* item = new (this->pushItem()) T;
+        return *item;
+    }
+
+    T& push_back(const T& t) {
+        T* item = new (this->pushItem()) T(t);
+        return *item;
+    }
+
+    template <typename... Args>
+    T& emplace_back(Args&&... args) {
+        T* item = new (this->pushItem()) T(std::forward<Args>(args)...);
+        return *item;
+    }
+
+    /**
+     * Remove the last item, only call if count() != 0
+     */
+    void pop_back() {
+        SkASSERT(fTotalCount > 0);
+
+        GrBlockAllocator::Block* block = fAllocator->currentBlock();
+        int newCount = block->metadata() - 1;
+
+        // Run dtor for the popped item
+        int releaseIndex;
+        GetItemAndOffset(block, newCount, &releaseIndex)->~T();
+
+        if (newCount == 0) {
+            fAllocator->releaseBlock(block);
+        } else {
+            // Since this always follows LIFO, the block should always be able to release the memory
+            SkAssertResult(block->release(releaseIndex, releaseIndex + sizeof(T)));
+            block->setMetadata(newCount);
+        }
+
+        fTotalCount--;
+    }
+
+    /**
+     * Removes all added items.
+     */
+    void reset() {
+        // Invoke destructors in reverse order
+        for (const auto* b : fAllocator->rblocks()) {
+            int c = b->metadata();
+            for (int i = c - 1; i >= 0; i--) {
+                GetItem(b, i)->~T();
+            }
+        }
+
+        fAllocator->reset();
+        fTotalCount = 0;
+    }
+
+    /**
+     * Returns the item count.
+     */
+    int count() const {
+#ifdef SK_DEBUG
+        // Confirm total count matches sum of block counts
+        int count = 0;
+        int blockCount = 0;
+        for (const auto* b :fAllocator->blocks()) {
+            count += b->metadata();
+            blockCount++;
+        }
+        SkASSERT(count == fTotalCount);
+#endif
+        return fTotalCount;
+    }
+
+    /**
+     * Is the count 0?
+     */
+    bool empty() const { return this->count() == 0; }
+
+    /**
+     * Access first item, only call if count() != 0
+     */
+    T& front() {
+        // This assumes that the head block actually have room to store the first item.
+        static_assert(StartingItems >= 1);
+        SkASSERT(fTotalCount > 0);
+        return *(GetItem(fAllocator->headBlock(), 0));
+    }
+
+    /**
+     * Access first item, only call if count() != 0
+     */
+    const T& front() const {
+        SkASSERT(fTotalCount > 0);
+        return *(GetItem(fAllocator->headBlock(), 0));
+    }
+
+    /**
+     * Access last item, only call if count() != 0
+     */
+    T& back() {
+        SkASSERT(fTotalCount > 0);
+        int blockCount = fAllocator->currentBlock()->metadata();
+        return *(GetItem(fAllocator->currentBlock(), blockCount - 1));
+    }
+
+    /**
+     * Access last item, only call if count() != 0
+     */
+    const T& back() const {
+        SkASSERT(fTotalCount > 0);
+        int blockCount = fAllocator->currentBlock()->metadata();
+        return *(GetItem(fAllocator->currentBlock(), blockCount - 1));
+    }
+
+    template<bool Const>
+    class Iterator {
+        using BlockIter = typename GrBlockAllocator::BlockIter<true, Const>;
+        using C = typename std::conditional<Const, const T, T>::type;
+        using AllocatorT = typename std::conditional<Const, const GrTAllocator, GrTAllocator>::type;
+    public:
+        Iterator(AllocatorT* allocator) : fBlockIter(allocator->fAllocator.allocator()) {}
+
+        class Item {
+        public:
+            bool operator!=(const Item& other) const {
+                if (other.fBlock != fBlock) {
+                    // Treat an empty head block the same as the end block
+                    bool blockEmpty = !(*fBlock) || (*fBlock)->metadata() == 0;
+                    bool otherEmpty = !(*other.fBlock) || (*other.fBlock)->metadata() == 0;
+                    return !blockEmpty || !otherEmpty;
+                } else {
+                    // Same block, so differentiate by index into it (unless it's the "end" block
+                    // in which case ignore index).
+                    return SkToBool(*fBlock) && other.fIndex != fIndex;
+                }
+            }
+
+            C& operator*() const {
+                C* item = const_cast<C*>(static_cast<const C*>((*fBlock)->ptr(fIndex)));
+                SkDEBUGCODE(int offset;)
+                SkASSERT(item == GetItemAndOffset(*fBlock, fItem, &offset) && offset == fIndex);
+                return *item;
+            }
+
+            Item& operator++() {
+                const auto* block = *fBlock;
+                fItem++;
+                fIndex += sizeof(T);
+                if (fItem >= block->metadata()) {
+                    ++fBlock;
+                    fItem = 0;
+                    fIndex = StartingIndex(fBlock);
+                }
+                return *this;
+            }
+
+        private:
+            friend Iterator;
+            using BlockItem = typename BlockIter::Item;
+
+            Item(BlockItem block) : fBlock(block), fItem(0), fIndex(StartingIndex(block)) {}
+
+            static int StartingIndex(const BlockItem& block) {
+                return *block ? (*block)->template firstAlignedOffset<alignof(T)>() : 0;
+            }
+
+            BlockItem fBlock;
+            int       fItem;
+            int       fIndex;
+        };
+
+        Item begin() const { return Item(fBlockIter.begin()); }
+        Item end() const { return Item(fBlockIter.end()); }
+
+    private:
+        const BlockIter fBlockIter;
+    };
+
+    using Iter = Iterator<false>;
+    using CIter = Iterator<true>;
+
+    /**
+     * Prefer using a for-range loop when iterating over all allocated items, vs. index access.
+     */
+    Iter items() { return Iter(this); }
+    CIter items() const { return CIter(this); }
+
+    /**
+     * Access item by index. Not an operator[] since it should not be considered constant time.
+     */
+    T& item(int i) {
+        // Iterate over blocks until we find the one that contains i.
+        SkASSERT(i >= 0 && i < fTotalCount);
+        for (const auto* b : fAllocator->blocks()) {
+            int blockCount = b->metadata();
+            if (i < blockCount) {
+                return *GetItem(b, i);
+            } else {
+                i -= blockCount;
+            }
+        }
+        SkUNREACHABLE;
+    }
+    const T& item(int i) const {
+        return const_cast<GrTAllocator<T>*>(this)->item(i);
+    }
+
+private:
+    static constexpr size_t StartingSize =
+            GrBlockAllocator::Overhead<alignof(T)>() + StartingItems * sizeof(T);
+
+    static T* GetItemAndOffset(const GrBlockAllocator::Block* block, int index, int* offset) {
+        SkASSERT(index >= 0 && index < block->metadata());
+        *offset = block->firstAlignedOffset<alignof(T)>() + index * sizeof(T);
+        return const_cast<T*>(static_cast<const T*>(block->ptr(*offset)));
+    }
+
+    static T* GetItem(const GrBlockAllocator::Block* block, int index) {
+        int offset;
+        return GetItemAndOffset(block, index, &offset);
+    }
+
+    void* pushItem() {
+        // 'template' required because fAllocator is a template, calling a template member
+        auto br = fAllocator->template allocate<alignof(T)>(sizeof(T));
+        br.fBlock->setMetadata(br.fBlock->metadata() + 1);
+        fTotalCount++;
+        return br.fBlock->ptr(br.fAlignedOffset);
+    }
+
+    // Each Block in the allocator tracks their count of items, but it's convenient to store
+    // the sum of their counts as well.
+    int fTotalCount;
+
+    // N represents the number of items, whereas GrSBlockAllocator takes total bytes, so must
+    // account for the block allocator's size too.
+    GrSBlockAllocator<StartingSize> fAllocator;
+};
+
+#endif
diff --git a/src/gpu/ccpr/GrCCAtlas.h b/src/gpu/ccpr/GrCCAtlas.h
index 9a1b9f5..f37910d 100644
--- a/src/gpu/ccpr/GrCCAtlas.h
+++ b/src/gpu/ccpr/GrCCAtlas.h
@@ -9,7 +9,7 @@
 #define GrCCAtlas_DEFINED
 
 #include "src/gpu/GrDynamicAtlas.h"
-
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/ccpr/GrCCPathProcessor.h"
 
 class GrCCCachedAtlas;
@@ -106,6 +106,7 @@
 class GrCCAtlasStack {
 public:
     using CoverageType = GrCCAtlas::CoverageType;
+    using CCAtlasAllocator = GrTAllocator<GrCCAtlas, 4>;
 
     GrCCAtlasStack(CoverageType coverageType, const GrCCAtlas::Specs& specs, const GrCaps* caps)
             : fCoverageType(coverageType), fSpecs(specs), fCaps(caps) {}
@@ -116,14 +117,8 @@
     GrCCAtlas& front() { SkASSERT(!this->empty()); return fAtlases.front(); }
     GrCCAtlas& current() { SkASSERT(!this->empty()); return fAtlases.back(); }
 
-    class Iter {
-    public:
-        Iter(GrCCAtlasStack& stack) : fImpl(&stack.fAtlases) {}
-        bool next() { return fImpl.next(); }
-        GrCCAtlas* operator->() const { return fImpl.get(); }
-    private:
-        typename GrTAllocator<GrCCAtlas>::Iter fImpl;
-    };
+    CCAtlasAllocator::Iter atlases() { return fAtlases.items(); }
+    CCAtlasAllocator::CIter atlases() const { return fAtlases.items(); }
 
     // Adds a rect to the current atlas and returns the offset from device space to atlas space.
     // Call current() to get the atlas it was added to.
@@ -138,7 +133,7 @@
     const CoverageType fCoverageType;
     const GrCCAtlas::Specs fSpecs;
     const GrCaps* const fCaps;
-    GrSTAllocator<4, GrCCAtlas> fAtlases;
+    CCAtlasAllocator fAtlases;
 };
 
 inline void GrCCAtlas::Specs::accountForSpace(int width, int height) {
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index 54fbceb..7fd5d5c 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -534,18 +534,18 @@
     // Draw the copies from coverage count or msaa atlas(es) into 8-bit cached atlas(es).
     int copyRangeIdx = 0;
     int baseCopyInstance = 0;
-    for (GrCCAtlasStack::Iter atlas(fCopyAtlasStack); atlas.next();) {
-        int endCopyRange = atlas->getFillBatchID();
+    for (GrCCAtlas& atlas : fCopyAtlasStack.atlases()) {
+        int endCopyRange = atlas.getFillBatchID();
         SkASSERT(endCopyRange > copyRangeIdx);
 
-        auto rtc = atlas->instantiate(onFlushRP);
+        auto rtc = atlas.instantiate(onFlushRP);
         for (; copyRangeIdx < endCopyRange; ++copyRangeIdx) {
             const CopyPathRange& copyRange = fCopyPathRanges[copyRangeIdx];
             int endCopyInstance = baseCopyInstance + copyRange.fCount;
             if (rtc) {
                 auto op = CopyAtlasOp::Make(
                         rtc->surfPriv().getContext(), sk_ref_sp(this), copyRange.fSrcProxy,
-                        baseCopyInstance, endCopyInstance, atlas->drawBounds());
+                        baseCopyInstance, endCopyInstance, atlas.drawBounds());
                 rtc->addDrawOp(GrNoClip(), std::move(op));
             }
             baseCopyInstance = endCopyInstance;
@@ -557,33 +557,33 @@
 
     // Render the coverage count atlas(es).
     int baseStencilResolveInstance = 0;
-    for (GrCCAtlasStack::Iter atlas(fRenderedAtlasStack); atlas.next();) {
+    for (GrCCAtlas& atlas : fRenderedAtlasStack.atlases()) {
         // Copies will be finished by the time we get to rendering new atlases. See if we can
         // recycle any previous invalidated atlas textures instead of creating new ones.
         sk_sp<GrTexture> backingTexture;
         for (sk_sp<GrTexture>& texture : fRecyclableAtlasTextures) {
-            if (texture && atlas->currentHeight() == texture->height() &&
-                    atlas->currentWidth() == texture->width()) {
+            if (texture && atlas.currentHeight() == texture->height() &&
+                    atlas.currentWidth() == texture->width()) {
                 backingTexture = skstd::exchange(texture, nullptr);
                 break;
             }
         }
 
-        if (auto rtc = atlas->instantiate(onFlushRP, std::move(backingTexture))) {
+        if (auto rtc = atlas.instantiate(onFlushRP, std::move(backingTexture))) {
             std::unique_ptr<GrDrawOp> op;
             if (CoverageType::kA8_Multisample == fRenderedAtlasStack.coverageType()) {
                 op = GrStencilAtlasOp::Make(
-                        rtc->surfPriv().getContext(), sk_ref_sp(this), atlas->getFillBatchID(),
-                        atlas->getStrokeBatchID(), baseStencilResolveInstance,
-                        atlas->getEndStencilResolveInstance(), atlas->drawBounds());
+                        rtc->surfPriv().getContext(), sk_ref_sp(this), atlas.getFillBatchID(),
+                        atlas.getStrokeBatchID(), baseStencilResolveInstance,
+                        atlas.getEndStencilResolveInstance(), atlas.drawBounds());
             } else if (onFlushRP->caps()->shaderCaps()->geometryShaderSupport()) {
                 op = RenderAtlasOp<GrGSCoverageProcessor>::Make(
-                        rtc->surfPriv().getContext(), sk_ref_sp(this), atlas->getFillBatchID(),
-                        atlas->getStrokeBatchID(), atlas->drawBounds());
+                        rtc->surfPriv().getContext(), sk_ref_sp(this), atlas.getFillBatchID(),
+                        atlas.getStrokeBatchID(), atlas.drawBounds());
             } else {
                 op = RenderAtlasOp<GrVSCoverageProcessor>::Make(
-                        rtc->surfPriv().getContext(), sk_ref_sp(this), atlas->getFillBatchID(),
-                        atlas->getStrokeBatchID(), atlas->drawBounds());
+                        rtc->surfPriv().getContext(), sk_ref_sp(this), atlas.getFillBatchID(),
+                        atlas.getStrokeBatchID(), atlas.drawBounds());
             }
             rtc->addDrawOp(GrNoClip(), std::move(op));
             if (rtc->asSurfaceProxy()->requiresManualMSAAResolve()) {
@@ -592,8 +592,8 @@
             }
         }
 
-        SkASSERT(atlas->getEndStencilResolveInstance() >= baseStencilResolveInstance);
-        baseStencilResolveInstance = atlas->getEndStencilResolveInstance();
+        SkASSERT(atlas.getEndStencilResolveInstance() >= baseStencilResolveInstance);
+        baseStencilResolveInstance = atlas.getEndStencilResolveInstance();
     }
     SkASSERT(GrCCAtlas::CoverageType::kA8_Multisample != this->renderedPathCoverageType() ||
              baseStencilResolveInstance == fEndStencilResolveInstance);
diff --git a/src/gpu/ccpr/GrCCStroker.h b/src/gpu/ccpr/GrCCStroker.h
index 157f8d6..f830bf1 100644
--- a/src/gpu/ccpr/GrCCStroker.h
+++ b/src/gpu/ccpr/GrCCStroker.h
@@ -10,7 +10,7 @@
 
 #include "include/private/GrTypesPriv.h"
 #include "include/private/SkNx.h"
-#include "src/gpu/GrAllocator.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/ccpr/GrCCStrokeGeometry.h"
 
 class GrGpuBuffer;
@@ -63,6 +63,7 @@
     static constexpr BatchID kEmptyBatchID = -1;
     using Verb = GrCCStrokeGeometry::Verb;
     using InstanceTallies = GrCCStrokeGeometry::InstanceTallies;
+    using InstanceTalliesAllocator = GrTAllocator<InstanceTallies, 128>;
 
     // Every kBeginPath verb has a corresponding PathInfo entry.
     struct PathInfo {
@@ -74,7 +75,7 @@
     // Defines a sub-batch of stroke instances that have a scissor test and the same scissor rect.
     // Start indices are deduced by looking at the previous ScissorSubBatch.
     struct ScissorSubBatch {
-        ScissorSubBatch(GrTAllocator<InstanceTallies>* alloc, const InstanceTallies& startIndices,
+        ScissorSubBatch(InstanceTalliesAllocator* alloc, const InstanceTallies& startIndices,
                         const SkIRect& scissor)
                 : fEndInstances(&alloc->emplace_back(startIndices)), fScissor(scissor) {}
         InstanceTallies* fEndInstances;
@@ -84,7 +85,7 @@
     // Defines a batch of stroke instances that can be drawn with drawStrokes(). Start indices are
     // deduced by looking at the previous Batch in the list.
     struct Batch {
-        Batch(GrTAllocator<InstanceTallies>* alloc, const InstanceTallies& startNonScissorIndices,
+        Batch(InstanceTalliesAllocator* alloc, const InstanceTallies& startNonScissorIndices,
               int startScissorSubBatch)
                 : fNonScissorEndInstances(&alloc->emplace_back(startNonScissorIndices))
                 , fEndScissorSubBatch(startScissorSubBatch) {}
@@ -113,7 +114,7 @@
     bool fHasOpenBatch = false;
 
     const InstanceTallies fZeroTallies = InstanceTallies();
-    GrSTAllocator<128, InstanceTallies> fTalliesAllocator;
+    InstanceTalliesAllocator fTalliesAllocator;
     const InstanceTallies* fInstanceCounts[kNumScissorModes] = {&fZeroTallies, &fZeroTallies};
 
     sk_sp<GrGpuBuffer> fInstanceBuffer;
diff --git a/src/gpu/dawn/GrDawnProgramDataManager.cpp b/src/gpu/dawn/GrDawnProgramDataManager.cpp
index 8da9995..7349d3c 100644
--- a/src/gpu/dawn/GrDawnProgramDataManager.cpp
+++ b/src/gpu/dawn/GrDawnProgramDataManager.cpp
@@ -15,18 +15,18 @@
     , fUniformsDirty(false) {
     fUniformData.reset(uniformBufferSize);
     memset(fUniformData.get(), 0, fUniformBufferSize);
-    int count = uniforms.count();
-    fUniforms.push_back_n(count);
+    fUniforms.push_back_n(uniforms.count());
     // We must add uniforms in same order is the UniformInfoArray so that UniformHandles already
     // owned by other objects will still match up here.
-    for (int i = 0; i < count; i++) {
+    int i = 0;
+    for (const auto& uniformInfo : uniforms.items()) {
         Uniform& uniform = fUniforms[i];
-        const GrDawnUniformHandler::UniformInfo uniformInfo = uniforms[i];
         SkDEBUGCODE(
             uniform.fArrayCount = uniformInfo.fVar.getArrayCount();
             uniform.fType = uniformInfo.fVar.getType();
         )
         uniform.fOffset = uniformInfo.fUBOOffset;
+        ++i;
     }
 }
 
diff --git a/src/gpu/dawn/GrDawnUniformHandler.cpp b/src/gpu/dawn/GrDawnUniformHandler.cpp
index 005a91c..1a13b94 100644
--- a/src/gpu/dawn/GrDawnUniformHandler.cpp
+++ b/src/gpu/dawn/GrDawnUniformHandler.cpp
@@ -17,11 +17,11 @@
 }
 
 const GrShaderVar& GrDawnUniformHandler::getUniformVariable(UniformHandle u) const {
-    return fUniforms[u.toIndex()].fVar;
+    return fUniforms.item(u.toIndex()).fVar;
 }
 
 const char* GrDawnUniformHandler::getUniformCStr(UniformHandle u) const {
-    return fUniforms[u.toIndex()].fVar.getName().c_str();
+    return fUniforms.item(u.toIndex()).fVar.getName().c_str();
 }
 
 // FIXME: this code was ripped from GrVkUniformHandler; should be refactored.
@@ -280,18 +280,20 @@
 }
 
 void GrDawnUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        if (fSamplers[i].fVisibility & visibility) {
-            fSamplers[i].fVar.appendDecl(fProgramBuilder->shaderCaps(), out);
+    auto textures = fTextures.items().begin();
+    for (const UniformInfo& sampler : fSamplers.items()) {
+        if (sampler.fVisibility & visibility) {
+            sampler.fVar.appendDecl(fProgramBuilder->shaderCaps(), out);
             out->append(";\n");
-            fTextures[i].fVar.appendDecl(fProgramBuilder->shaderCaps(), out);
+            (*textures).fVar.appendDecl(fProgramBuilder->shaderCaps(), out);
             out->append(";\n");
         }
+        ++textures;
     }
     SkString uniformsString;
-    for (int i = 0; i < fUniforms.count(); ++i) {
-        if (fUniforms[i].fVisibility & visibility) {
-            fUniforms[i].fVar.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString);
+    for (const UniformInfo& uniform : fUniforms.items()) {
+        if (uniform.fVisibility & visibility) {
+            uniform.fVar.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString);
             uniformsString.append(";\n");
         }
     }
diff --git a/src/gpu/dawn/GrDawnUniformHandler.h b/src/gpu/dawn/GrDawnUniformHandler.h
index feb44bb..3ef22bd 100644
--- a/src/gpu/dawn/GrDawnUniformHandler.h
+++ b/src/gpu/dawn/GrDawnUniformHandler.h
@@ -8,7 +8,7 @@
 #ifndef GrDawnUniformHandler_DEFINED
 #define GrDawnUniformHandler_DEFINED
 
-#include "src/gpu/GrAllocator.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
 
 class GrDawnGpu;
diff --git a/src/gpu/dawn/GrDawnVaryingHandler.cpp b/src/gpu/dawn/GrDawnVaryingHandler.cpp
index b3885d2..a09fd95 100644
--- a/src/gpu/dawn/GrDawnVaryingHandler.cpp
+++ b/src/gpu/dawn/GrDawnVaryingHandler.cpp
@@ -80,8 +80,7 @@
 
 static void finalize_helper(GrDawnVaryingHandler::VarArray& vars) {
     int locationIndex = 0;
-    for (int i = 0; i < vars.count(); ++i) {
-        GrShaderVar& var = vars[i];
+    for (GrShaderVar& var : vars.items()) {
         SkString location;
         location.appendf("location = %d", locationIndex);
         var.addLayoutQualifier(location.c_str());
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index fd11185..e8f0884 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -5,12 +5,12 @@
  * found in the LICENSE file.
  */
 
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrCoordTransform.h"
 #include "src/gpu/GrPathProcessor.h"
 #include "src/gpu/GrPipeline.h"
 #include "src/gpu/GrProcessor.h"
 #include "src/gpu/GrProgramInfo.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/GrXferProcessor.h"
 #include "src/gpu/gl/GrGLBuffer.h"
diff --git a/src/gpu/gl/GrGLProgramDataManager.cpp b/src/gpu/gl/GrGLProgramDataManager.cpp
index 15d9d36..3b53b79 100644
--- a/src/gpu/gl/GrGLProgramDataManager.cpp
+++ b/src/gpu/gl/GrGLProgramDataManager.cpp
@@ -17,13 +17,12 @@
 GrGLProgramDataManager::GrGLProgramDataManager(GrGLGpu* gpu, GrGLuint programID,
                                                const UniformInfoArray& uniforms,
                                                const VaryingInfoArray& pathProcVaryings)
-    : fGpu(gpu)
-    , fProgramID(programID) {
-    int count = uniforms.count();
-    fUniforms.push_back_n(count);
-    for (int i = 0; i < count; i++) {
-        Uniform& uniform = fUniforms[i];
-        const UniformInfo& builderUniform = uniforms[i];
+        : fGpu(gpu)
+        , fProgramID(programID) {
+    fUniforms.push_back_n(uniforms.count());
+    int i = 0;
+    for (const UniformInfo& builderUniform : uniforms.items()) {
+        Uniform& uniform = fUniforms[i++];
         SkASSERT(GrShaderVar::kNonArray == builderUniform.fVariable.getArrayCount() ||
                  builderUniform.fVariable.getArrayCount() > 0);
         SkDEBUGCODE(
@@ -34,12 +33,11 @@
     }
 
     // NVPR programs have separable varyings
-    count = pathProcVaryings.count();
-    fPathProcVaryings.push_back_n(count);
-    for (int i = 0; i < count; i++) {
+    fPathProcVaryings.push_back_n(pathProcVaryings.count());
+    i = 0;
+    for (const VaryingInfo& builderPathProcVarying : pathProcVaryings.items()) {
         SkASSERT(fGpu->glCaps().shaderCaps()->pathRenderingSupport());
-        PathProcVarying& pathProcVarying = fPathProcVaryings[i];
-        const VaryingInfo& builderPathProcVarying = pathProcVaryings[i];
+        PathProcVarying& pathProcVarying = fPathProcVaryings[i++];
         SkASSERT(GrShaderVar::kNonArray == builderPathProcVarying.fVariable.getArrayCount() ||
                  builderPathProcVarying.fVariable.getArrayCount() > 0);
         SkDEBUGCODE(
@@ -52,12 +50,13 @@
 
 void GrGLProgramDataManager::setSamplerUniforms(const UniformInfoArray& samplers,
                                                 int startUnit) const {
-    for (int i = 0; i < samplers.count(); ++i) {
-        const UniformInfo& sampler = samplers[i];
+    int i = 0;
+    for (const UniformInfo& sampler : samplers.items()) {
         SkASSERT(sampler.fVisibility);
         if (kUnusedUniform != sampler.fLocation) {
             GR_GL_CALL(fGpu->glInterface(), Uniform1i(sampler.fLocation, i + startUnit));
         }
+        ++i;
     }
 }
 
diff --git a/src/gpu/gl/GrGLProgramDataManager.h b/src/gpu/gl/GrGLProgramDataManager.h
index 3a67fe0..43dee9d 100644
--- a/src/gpu/gl/GrGLProgramDataManager.h
+++ b/src/gpu/gl/GrGLProgramDataManager.h
@@ -9,8 +9,8 @@
 #define GrGLProgramDataManager_DEFINED
 
 #include "include/gpu/gl/GrGLTypes.h"
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrShaderVar.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
 
 #include "include/private/SkTArray.h"
diff --git a/src/gpu/gl/GrGLUniformHandler.cpp b/src/gpu/gl/GrGLUniformHandler.cpp
index 33a1649..e9836af 100644
--- a/src/gpu/gl/GrGLUniformHandler.cpp
+++ b/src/gpu/gl/GrGLUniformHandler.cpp
@@ -84,15 +84,15 @@
 }
 
 void GrGLUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
-    for (int i = 0; i < fUniforms.count(); ++i) {
-        if (fUniforms[i].fVisibility & visibility) {
-            fUniforms[i].fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
+    for (const UniformInfo& uniform : fUniforms.items()) {
+        if (uniform.fVisibility & visibility) {
+            uniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
             out->append(";");
         }
     }
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        if (fSamplers[i].fVisibility & visibility) {
-            fSamplers[i].fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
+    for (const UniformInfo& sampler : fSamplers.items()) {
+        if (sampler.fVisibility & visibility) {
+            sampler.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
             out->append(";\n");
         }
     }
@@ -101,29 +101,30 @@
 void GrGLUniformHandler::bindUniformLocations(GrGLuint programID, const GrGLCaps& caps) {
     if (caps.bindUniformLocationSupport()) {
         int currUniform = 0;
-        for (int i = 0; i < fUniforms.count(); ++i, ++currUniform) {
-            GL_CALL(BindUniformLocation(programID, currUniform, fUniforms[i].fVariable.c_str()));
-            fUniforms[i].fLocation = currUniform;
+        for (UniformInfo& uniform : fUniforms.items()) {
+            GL_CALL(BindUniformLocation(programID, currUniform, uniform.fVariable.c_str()));
+            uniform.fLocation = currUniform;
+            ++currUniform;
         }
-        for (int i = 0; i < fSamplers.count(); ++i, ++currUniform) {
-            GL_CALL(BindUniformLocation(programID, currUniform, fSamplers[i].fVariable.c_str()));
-            fSamplers[i].fLocation = currUniform;
+        for (UniformInfo& sampler : fSamplers.items()) {
+            GL_CALL(BindUniformLocation(programID, currUniform, sampler.fVariable.c_str()));
+            sampler.fLocation = currUniform;
+            ++currUniform;
         }
     }
 }
 
 void GrGLUniformHandler::getUniformLocations(GrGLuint programID, const GrGLCaps& caps, bool force) {
     if (!caps.bindUniformLocationSupport() || force) {
-        int count = fUniforms.count();
-        for (int i = 0; i < count; ++i) {
+        for (UniformInfo& uniform : fUniforms.items()) {
             GrGLint location;
-            GL_CALL_RET(location, GetUniformLocation(programID, fUniforms[i].fVariable.c_str()));
-            fUniforms[i].fLocation = location;
+            GL_CALL_RET(location, GetUniformLocation(programID, uniform.fVariable.c_str()));
+            uniform.fLocation = location;
         }
-        for (int i = 0; i < fSamplers.count(); ++i) {
+        for (UniformInfo& sampler : fSamplers.items()) {
             GrGLint location;
-            GL_CALL_RET(location, GetUniformLocation(programID, fSamplers[i].fVariable.c_str()));
-            fSamplers[i].fLocation = location;
+            GL_CALL_RET(location, GetUniformLocation(programID, sampler.fVariable.c_str()));
+            sampler.fLocation = location;
         }
     }
 }
diff --git a/src/gpu/gl/GrGLUniformHandler.h b/src/gpu/gl/GrGLUniformHandler.h
index 5ac235f..d8f5b33 100644
--- a/src/gpu/gl/GrGLUniformHandler.h
+++ b/src/gpu/gl/GrGLUniformHandler.h
@@ -19,7 +19,7 @@
     static const int kUniformsPerBlock = 8;
 
     const GrShaderVar& getUniformVariable(UniformHandle u) const override {
-        return fUniforms[u.toIndex()].fVariable;
+        return fUniforms.item(u.toIndex()).fVariable;
     }
 
     const char* getUniformCStr(UniformHandle u) const override {
@@ -42,7 +42,7 @@
                              const char* name, const GrShaderCaps*) override;
 
     const char* samplerVariable(SamplerHandle handle) const override {
-        return fSamplers[handle.toIndex()].fVariable.c_str();
+        return fSamplers.item(handle.toIndex()).fVariable.c_str();
     }
 
     GrSwizzle samplerSwizzle(SamplerHandle handle) const override {
diff --git a/src/gpu/gl/GrGLVaryingHandler.cpp b/src/gpu/gl/GrGLVaryingHandler.cpp
index 76ab128..8a957c5 100644
--- a/src/gpu/gl/GrGLVaryingHandler.cpp
+++ b/src/gpu/gl/GrGLVaryingHandler.cpp
@@ -28,7 +28,9 @@
 
 void GrGLVaryingHandler::onFinalize() {
     SkASSERT(fPathProcVaryingInfos.empty() || fPathProcVaryingInfos.count() == fFragInputs.count());
-    for (int i = 0; i < fPathProcVaryingInfos.count(); ++i) {
-        fPathProcVaryingInfos[i].fVariable = fFragInputs[i];
+    VarArray::Iter::Item fragInputIter = fFragInputs.items().begin();
+    for (auto& varyingInfo : fPathProcVaryingInfos.items()) {
+        varyingInfo.fVariable = *fragInputIter;
+        ++fragInputIter;
     }
 }
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 7fdb712..6c2ba75 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -472,11 +472,11 @@
         !fGpu->glPathRendering()->shouldBindFragmentInputs()) {
         return;
     }
-    int count = fVaryingHandler.fPathProcVaryingInfos.count();
-    for (int i = 0; i < count; ++i) {
-        GL_CALL(BindFragmentInputLocation(programID, i,
-                                       fVaryingHandler.fPathProcVaryingInfos[i].fVariable.c_str()));
-        fVaryingHandler.fPathProcVaryingInfos[i].fLocation = i;
+    int i = 0;
+    for (auto& varying : fVaryingHandler.fPathProcVaryingInfos.items()) {
+        GL_CALL(BindFragmentInputLocation(programID, i, varying.fVariable.c_str()));
+        varying.fLocation = i;
+        ++i;
     }
 }
 
@@ -523,14 +523,13 @@
         fGpu->glPathRendering()->shouldBindFragmentInputs()) {
         return;
     }
-    int count = fVaryingHandler.fPathProcVaryingInfos.count();
-    for (int i = 0; i < count; ++i) {
+    for (auto& varying : fVaryingHandler.fPathProcVaryingInfos.items()) {
         GrGLint location;
         GL_CALL_RET(location, GetProgramResourceLocation(
                                        programID,
                                        GR_GL_FRAGMENT_INPUT,
-                                       fVaryingHandler.fPathProcVaryingInfos[i].fVariable.c_str()));
-        fVaryingHandler.fPathProcVaryingInfos[i].fLocation = location;
+                                       varying.fVariable.c_str()));
+        varying.fLocation = location;
     }
 }
 
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.h b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
index 12bebc3..554f57c 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
@@ -10,8 +10,8 @@
 
 #include "include/core/SkTypes.h"
 #include "include/gpu/GrContextOptions.h"
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrGpu.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/gl/GrGLContext.h"
 #include "src/sksl/SkSLGLSLCodeGenerator.h"
 
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
index 3692e0d..8702a11 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
@@ -209,7 +209,7 @@
         const char* fbFetchColorName = "sk_LastFragColor";
         if (shaderCaps->fbFetchNeedsCustomOutput()) {
             this->enableCustomOutput();
-            fOutputs[fCustomColorOutputIndex].setTypeModifier(GrShaderVar::kInOut_TypeModifier);
+            fCustomColorOutput->setTypeModifier(GrShaderVar::kInOut_TypeModifier);
             fbFetchColorName = DeclaredColorOutputName();
             // Set the dstColor to an intermediate variable so we don't override it with the output
             this->codeAppendf("half4 %s = %s;", kDstColorName, fbFetchColorName);
@@ -238,11 +238,10 @@
 }
 
 void GrGLSLFragmentShaderBuilder::enableCustomOutput() {
-    if (!fHasCustomColorOutput) {
-        fHasCustomColorOutput = true;
-        fCustomColorOutputIndex = fOutputs.count();
-        fOutputs.push_back().set(kHalf4_GrSLType, DeclaredColorOutputName(),
-                                 GrShaderVar::kOut_TypeModifier);
+    if (!fCustomColorOutput) {
+        fCustomColorOutput = &fOutputs.push_back();
+        fCustomColorOutput->set(kHalf4_GrSLType, DeclaredColorOutputName(),
+                                GrShaderVar::kOut_TypeModifier);
         fProgramBuilder->finalizeFragmentOutputColor(fOutputs.back());
     }
 }
@@ -267,12 +266,12 @@
 }
 
 const char* GrGLSLFragmentShaderBuilder::getPrimaryColorOutputName() const {
-    return fHasCustomColorOutput ? DeclaredColorOutputName() : "sk_FragColor";
+    return this->hasCustomColorOutput() ? DeclaredColorOutputName() : "sk_FragColor";
 }
 
 bool GrGLSLFragmentShaderBuilder::primaryColorOutputIsInOut() const {
-    return fHasCustomColorOutput &&
-           fOutputs[fCustomColorOutputIndex].getTypeModifier() == GrShaderVar::kInOut_TypeModifier;
+    return fCustomColorOutput &&
+           fCustomColorOutput->getTypeModifier() == GrShaderVar::kInOut_TypeModifier;
 }
 
 void GrGLSLFragmentBuilder::declAppendf(const char* fmt, ...) {
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
index 6d5bfe8..51ba50c 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
@@ -46,7 +46,10 @@
 class GrGLSLFPFragmentBuilder : virtual public GrGLSLFragmentBuilder {
 public:
     /** Appease the compiler; the derived class initializes GrGLSLFragmentBuilder. */
-    GrGLSLFPFragmentBuilder() : GrGLSLFragmentBuilder(nullptr) {}
+    GrGLSLFPFragmentBuilder() : GrGLSLFragmentBuilder(nullptr) {
+        // Suppress unused warning error
+        (void) fDummyPadding;
+    }
 
     /**
      * Returns the variable name that holds the array of sample offsets from pixel center to each
@@ -104,6 +107,15 @@
     virtual const SkString& getMangleString() const = 0;
 
     virtual void forceHighPrecision() = 0;
+
+private:
+    // WARNING: LIke GrRenderTargetProxy, changes to this can cause issues in ASAN. This is caused
+    // by GrGLSLProgramBuilder's GrTAllocators requiring 16 byte alignment, but since
+    // GrGLSLFragmentShaderBuilder has a virtual diamond hierarchy, ASAN requires all this pointers
+    // to start aligned, even though clang is already correctly offsetting the individual fields
+    // that require the larger alignment. In the current world, this extra padding is sufficient to
+    // correctly initialize GrGLSLXPFragmentBuilder second.
+    char fDummyPadding[4];
 };
 
 GR_MAKE_BITFIELD_CLASS_OPS(GrGLSLFPFragmentBuilder::ScopeFlags);
@@ -153,7 +165,7 @@
     void forceHighPrecision() override { fForceHighPrecision = true; }
 
     // GrGLSLXPFragmentBuilder interface.
-    bool hasCustomColorOutput() const override { return fHasCustomColorOutput; }
+    bool hasCustomColorOutput() const override { return SkToBool(fCustomColorOutput); }
     bool hasSecondaryOutput() const override { return fHasSecondaryOutput; }
     const char* dstColor() override;
     void enableAdvancedBlendEquationIfNeeded(GrBlendEquation) override;
@@ -210,9 +222,9 @@
      */
     SkString fMangleString;
 
+    GrShaderVar* fCustomColorOutput = nullptr;
+
     bool fSetupFragPosition = false;
-    bool fHasCustomColorOutput = false;
-    int fCustomColorOutputIndex = -1;
     bool fHasSecondaryOutput = false;
     bool fHasModifiedSampleMask = false;
     bool fForceHighPrecision = false;
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index b0dd0c5..42ac1eb 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -235,8 +235,8 @@
 }
 
 void GrGLSLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const {
-    for (int i = 0; i < vars.count(); ++i) {
-        vars[i].appendDecl(fProgramBuilder->shaderCaps(), out);
+    for (const auto& v : vars.items()) {
+        v.appendDecl(fProgramBuilder->shaderCaps(), out);
         out->append(";\n");
     }
 }
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index ee0a494..9705450 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -9,8 +9,8 @@
 #define GrGLSLShaderBuilder_DEFINED
 
 #include "include/private/SkTDArray.h"
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrShaderVar.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
 #include "src/sksl/SkSLString.h"
 
diff --git a/src/gpu/glsl/GrGLSLVarying.cpp b/src/gpu/glsl/GrGLSLVarying.cpp
index ce086a9..2dc36dd 100644
--- a/src/gpu/glsl/GrGLSLVarying.cpp
+++ b/src/gpu/glsl/GrGLSLVarying.cpp
@@ -76,8 +76,7 @@
 
 void GrGLSLVaryingHandler::addAttribute(const GrShaderVar& var) {
     SkASSERT(GrShaderVar::kIn_TypeModifier == var.getTypeModifier());
-    for (int j = 0; j < fVertexInputs.count(); ++j) {
-        const GrShaderVar& attr = fVertexInputs[j];
+    for (const GrShaderVar& attr : fVertexInputs.items()) {
         // if attribute already added, don't add it again
         if (attr.getName().equals(var.getName())) {
             return;
@@ -103,8 +102,7 @@
 }
 
 void GrGLSLVaryingHandler::finalize() {
-    for (int i = 0; i < fVaryings.count(); ++i) {
-        const VaryingInfo& v = this->fVaryings[i];
+    for (const VaryingInfo& v : fVaryings.items()) {
         const char* modifier = v.fIsFlat ? "flat" : fDefaultInterpolationModifier;
         if (v.fVisibility & kVertex_GrShaderFlag) {
             fVertexOutputs.push_back().set(v.fType, v.fVsOut, GrShaderVar::kOut_TypeModifier,
@@ -129,8 +127,8 @@
 }
 
 void GrGLSLVaryingHandler::appendDecls(const VarArray& vars, SkString* out) const {
-    for (int i = 0; i < vars.count(); ++i) {
-        vars[i].appendDecl(fProgramBuilder->shaderCaps(), out);
+    for (const GrShaderVar& varying : vars.items()) {
+        varying.appendDecl(fProgramBuilder->shaderCaps(), out);
         out->append(";");
     }
 }
diff --git a/src/gpu/glsl/GrGLSLVarying.h b/src/gpu/glsl/GrGLSLVarying.h
index 3294e72..0f316f6 100644
--- a/src/gpu/glsl/GrGLSLVarying.h
+++ b/src/gpu/glsl/GrGLSLVarying.h
@@ -9,9 +9,9 @@
 #define GrGLSLVarying_DEFINED
 
 #include "include/private/GrTypesPriv.h"
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrShaderVar.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
 
 class GrGLSLProgramBuilder;
diff --git a/src/gpu/mtl/GrMtlPipelineStateDataManager.mm b/src/gpu/mtl/GrMtlPipelineStateDataManager.mm
index b30dfdb..b0d2a1a 100644
--- a/src/gpu/mtl/GrMtlPipelineStateDataManager.mm
+++ b/src/gpu/mtl/GrMtlPipelineStateDataManager.mm
@@ -19,13 +19,12 @@
         : fUniformSize(uniformSize)
         , fUniformsDirty(false) {
     fUniformData.reset(uniformSize);
-    int count = uniforms.count();
-    fUniforms.push_back_n(count);
+    fUniforms.push_back_n(uniforms.count());
     // We must add uniforms in same order is the UniformInfoArray so that UniformHandles already
     // owned by other objects will still match up here.
-    for (int i = 0; i < count; i++) {
+    int i = 0;
+    for (const auto& uniformInfo : uniforms.items()) {
         Uniform& uniform = fUniforms[i];
-        const GrMtlUniformHandler::UniformInfo uniformInfo = uniforms[i];
         SkASSERT(GrShaderVar::kNonArray == uniformInfo.fVariable.getArrayCount() ||
                  uniformInfo.fVariable.getArrayCount() > 0);
         SkDEBUGCODE(
@@ -33,6 +32,7 @@
             uniform.fType = uniformInfo.fVariable.getType();
         )
         uniform.fOffset = uniformInfo.fUBOffset;
+        ++i;
     }
 }
 
diff --git a/src/gpu/mtl/GrMtlUniformHandler.h b/src/gpu/mtl/GrMtlUniformHandler.h
index 0ee41bc..1b1b34e 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.h
+++ b/src/gpu/mtl/GrMtlUniformHandler.h
@@ -8,8 +8,8 @@
 #ifndef GrMtlUniformHandler_DEFINED
 #define GrMtlUniformHandler_DEFINED
 
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrShaderVar.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
 
 // TODO: this class is basically copy and pasted from GrVkUniformHandler so that we can have
@@ -34,7 +34,7 @@
     typedef GrTAllocator<UniformInfo> UniformInfoArray;
 
     const GrShaderVar& getUniformVariable(UniformHandle u) const override {
-        return fUniforms[u.toIndex()].fVariable;
+        return fUniforms.item(u.toIndex()).fVariable;
     }
 
     const char* getUniformCStr(UniformHandle u) const override {
@@ -65,19 +65,19 @@
 
     int numSamplers() const { return fSamplers.count(); }
     const char* samplerVariable(SamplerHandle handle) const override {
-        return fSamplers[handle.toIndex()].fVariable.c_str();
+        return fSamplers.item(handle.toIndex()).fVariable.c_str();
     }
     GrSwizzle samplerSwizzle(SamplerHandle handle) const override {
         return fSamplerSwizzles[handle.toIndex()];
     }
     uint32_t samplerVisibility(SamplerHandle handle) const {
-        return fSamplers[handle.toIndex()].fVisibility;
+        return fSamplers.item(handle.toIndex()).fVisibility;
     }
 
     void appendUniformDecls(GrShaderFlags, SkString*) const override;
 
     const UniformInfo& getUniformInfo(UniformHandle u) const {
-        return fUniforms[u.toIndex()];
+        return fUniforms.item(u.toIndex());
     }
 
     UniformInfoArray    fUniforms;
diff --git a/src/gpu/mtl/GrMtlUniformHandler.mm b/src/gpu/mtl/GrMtlUniformHandler.mm
index e1f394b..415da17 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.mm
+++ b/src/gpu/mtl/GrMtlUniformHandler.mm
@@ -268,8 +268,7 @@
 }
 
 void GrMtlUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        const UniformInfo& sampler = fSamplers[i];
+    for (const UniformInfo& sampler : fSamplers.items()) {
         SkASSERT(sampler.fVariable.getType() == kTexture2DSampler_GrSLType);
         if (visibility == sampler.fVisibility) {
             sampler.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
@@ -279,8 +278,7 @@
 
 #ifdef SK_DEBUG
     bool firstOffsetCheck = false;
-    for (int i = 0; i < fUniforms.count(); ++i) {
-        const UniformInfo& localUniform = fUniforms[i];
+    for (const UniformInfo& localUniform : fUniforms.items()) {
         if (!firstOffsetCheck) {
             // Check to make sure we are starting our offset at 0 so the offset qualifier we
             // set on each variable in the uniform block is valid.
@@ -291,8 +289,7 @@
 #endif
 
     SkString uniformsString;
-    for (int i = 0; i < fUniforms.count(); ++i) {
-        const UniformInfo& localUniform = fUniforms[i];
+    for (const UniformInfo& localUniform : fUniforms.items()) {
         if (visibility & localUniform.fVisibility) {
             if (GrSLTypeIsFloatType(localUniform.fVariable.getType())) {
                 localUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString);
diff --git a/src/gpu/mtl/GrMtlVaryingHandler.mm b/src/gpu/mtl/GrMtlVaryingHandler.mm
index c01e987..4a72045 100644
--- a/src/gpu/mtl/GrMtlVaryingHandler.mm
+++ b/src/gpu/mtl/GrMtlVaryingHandler.mm
@@ -12,10 +12,9 @@
 #endif
 
 static void finalize_helper(GrMtlVaryingHandler::VarArray& vars) {
-    int locationIndex;
+    int locationIndex = 0;
     int componentCount = 0;
-    for (locationIndex = 0; locationIndex < vars.count(); locationIndex++) {
-        GrShaderVar& var = vars[locationIndex];
+    for (GrShaderVar& var : vars.items()) {
         // Metal only allows scalars (including bool and char) and vectors as varyings
         SkASSERT(GrSLTypeVecLength(var.getType()) != -1);
         componentCount += GrSLTypeVecLength(var.getType());
@@ -23,6 +22,7 @@
         SkString location;
         location.appendf("location = %d", locationIndex);
         var.addLayoutQualifier(location.c_str());
+        ++locationIndex;
     }
     // The max number of inputs is 60 for iOS and 32 for macOS. The max number of components is 60
     // for iOS and 128 for macOS. To be conservative, we are going to assert that we have less than
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 7e8e862..e98295a 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -49,11 +49,10 @@
     fUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, uniformSize));
 
     fNumSamplers = samplers.count();
-
-    for (int i = 0; i < fNumSamplers; ++i) {
+    for (const auto& sampler : samplers.items()) {
         // We store the immutable samplers here and take ownership of the ref from the
         // GrVkUnformHandler.
-        fImmutableSamplers.push_back(samplers[i].fImmutableSampler);
+        fImmutableSamplers.push_back(sampler.fImmutableSampler);
     }
 }
 
diff --git a/src/gpu/vk/GrVkPipelineStateDataManager.cpp b/src/gpu/vk/GrVkPipelineStateDataManager.cpp
index 6d9393c..bd0c8d4 100644
--- a/src/gpu/vk/GrVkPipelineStateDataManager.cpp
+++ b/src/gpu/vk/GrVkPipelineStateDataManager.cpp
@@ -15,13 +15,12 @@
     : fUniformSize(uniformSize)
     , fUniformsDirty(false) {
     fUniformData.reset(uniformSize);
-    int count = uniforms.count();
-    fUniforms.push_back_n(count);
+    fUniforms.push_back_n(uniforms.count());
     // We must add uniforms in same order is the UniformInfoArray so that UniformHandles already
     // owned by other objects will still match up here.
-    for (int i = 0; i < count; i++) {
+    int i = 0;
+    for (const auto& uniformInfo : uniforms.items()) {
         Uniform& uniform = fUniforms[i];
-        const GrVkUniformHandler::UniformInfo uniformInfo = uniforms[i];
         SkASSERT(GrShaderVar::kNonArray == uniformInfo.fVariable.getArrayCount() ||
                  uniformInfo.fVariable.getArrayCount() > 0);
         SkDEBUGCODE(
@@ -30,6 +29,7 @@
         )
 
         uniform.fOffset = uniformInfo.fUBOffset;
+        ++i;
     }
 }
 
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index 2b0e05c..adfa80f 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -203,10 +203,10 @@
 }
 
 GrVkUniformHandler::~GrVkUniformHandler() {
-    for (decltype(fSamplers)::Iter iter(&fSamplers); iter.next();) {
-        if (iter->fImmutableSampler) {
-            iter->fImmutableSampler->unref();
-            iter->fImmutableSampler = nullptr;
+    for (UniformInfo& sampler : fSamplers.items()) {
+        if (sampler.fImmutableSampler) {
+            sampler.fImmutableSampler->unref();
+            sampler.fImmutableSampler = nullptr;
         }
     }
 }
@@ -290,8 +290,7 @@
 }
 
 void GrVkUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        const UniformInfo& sampler = fSamplers[i];
+    for (const UniformInfo& sampler : fSamplers.items()) {
         SkASSERT(sampler.fVariable.getType() == kTexture2DSampler_GrSLType ||
                  sampler.fVariable.getType() == kTextureExternalSampler_GrSLType);
         if (visibility == sampler.fVisibility) {
@@ -302,8 +301,7 @@
 
 #ifdef SK_DEBUG
     bool firstOffsetCheck = false;
-    for (int i = 0; i < fUniforms.count(); ++i) {
-        const UniformInfo& localUniform = fUniforms[i];
+    for (const UniformInfo& localUniform : fUniforms.items()) {
         if (!firstOffsetCheck) {
             // Check to make sure we are starting our offset at 0 so the offset qualifier we
             // set on each variable in the uniform block is valid.
@@ -314,8 +312,7 @@
 #endif
 
     SkString uniformsString;
-    for (int i = 0; i < fUniforms.count(); ++i) {
-        const UniformInfo& localUniform = fUniforms[i];
+    for (const UniformInfo& localUniform : fUniforms.items()) {
         if (visibility & localUniform.fVisibility) {
             if (GrSLTypeIsFloatType(localUniform.fVariable.getType())) {
                 localUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString);
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index c974c06..1b8cf1f 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -9,9 +9,9 @@
 #define GrVkUniformHandler_DEFINED
 
 #include "include/gpu/vk/GrVkTypes.h"
-#include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrSamplerState.h"
 #include "src/gpu/GrShaderVar.h"
+#include "src/gpu/GrTAllocator.h"
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
 #include "src/gpu/vk/GrVkSampler.h"
 
@@ -46,7 +46,7 @@
     ~GrVkUniformHandler() override;
 
     const GrShaderVar& getUniformVariable(UniformHandle u) const override {
-        return fUniforms[u.toIndex()].fVariable;
+        return fUniforms.item(u.toIndex()).fVariable;
     }
 
     const char* getUniformCStr(UniformHandle u) const override {
@@ -81,23 +81,23 @@
 
     int numSamplers() const { return fSamplers.count(); }
     const char* samplerVariable(SamplerHandle handle) const override {
-        return fSamplers[handle.toIndex()].fVariable.c_str();
+        return fSamplers.item(handle.toIndex()).fVariable.c_str();
     }
     GrSwizzle samplerSwizzle(SamplerHandle handle) const override {
         return fSamplerSwizzles[handle.toIndex()];
     }
     uint32_t samplerVisibility(SamplerHandle handle) const {
-        return fSamplers[handle.toIndex()].fVisibility;
+        return fSamplers.item(handle.toIndex()).fVisibility;
     }
 
     const GrVkSampler* immutableSampler(UniformHandle u) const {
-        return fSamplers[u.toIndex()].fImmutableSampler;
+        return fSamplers.item(u.toIndex()).fImmutableSampler;
     }
 
     void appendUniformDecls(GrShaderFlags, SkString*) const override;
 
     const UniformInfo& getUniformInfo(UniformHandle u) const {
-        return fUniforms[u.toIndex()];
+        return fUniforms.item(u.toIndex());
     }
 
 
diff --git a/src/gpu/vk/GrVkVaryingHandler.cpp b/src/gpu/vk/GrVkVaryingHandler.cpp
index 5fe2f45..2cecbee 100644
--- a/src/gpu/vk/GrVkVaryingHandler.cpp
+++ b/src/gpu/vk/GrVkVaryingHandler.cpp
@@ -78,8 +78,7 @@
 
 static void finalize_helper(GrVkVaryingHandler::VarArray& vars) {
     int locationIndex = 0;
-    for (int i = 0; i < vars.count(); ++i) {
-        GrShaderVar& var = vars[i];
+    for (GrShaderVar& var : vars.items()) {
         SkString location;
         location.appendf("location = %d", locationIndex);
         var.addLayoutQualifier(location.c_str());
diff --git a/tests/GrAllocatorTest.cpp b/tests/GrAllocatorTest.cpp
deleted file mode 100644
index 68b2c42..0000000
--- a/tests/GrAllocatorTest.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/GrAllocator.h"
-#include "tests/Test.h"
-
-namespace {
-struct C {
-    C() : fID(-1) { ++gInstCnt; }
-    C(int id) : fID(id) { ++gInstCnt; }
-    ~C() { --gInstCnt; }
-    int fID;
-
-    static int gInstCnt;
-};
-
-int C::gInstCnt = 0;
-}
-
-static void check_allocator_helper(GrTAllocator<C>* allocator, int cnt, int popCnt,
-                                   skiatest::Reporter* reporter);
-
-// Adds cnt items to the allocator, tests the cnts and iterators, pops popCnt items and checks
-// again. Finally it resets the allocator and checks again.
-static void check_allocator(GrTAllocator<C>* allocator, int cnt, int popCnt,
-                            skiatest::Reporter* reporter) {
-    SkASSERT(allocator);
-    SkASSERT(allocator->empty());
-    for (int i = 0; i < cnt; ++i) {
-        // Try both variations of push_back().
-        if (i % 1) {
-            allocator->push_back(C(i));
-        } else {
-            allocator->push_back() = C(i);
-        }
-    }
-    check_allocator_helper(allocator, cnt, popCnt, reporter);
-    allocator->reset();
-    check_allocator_helper(allocator, 0, 0, reporter);
-}
-
-// Checks that the allocator has the correct count, etc and that the element IDs are correct.
-// Then pops popCnt items and checks again.
-static void check_allocator_helper(GrTAllocator<C>* allocator, int cnt, int popCnt,
-                                   skiatest::Reporter* reporter) {
-    REPORTER_ASSERT(reporter, (0 == cnt) == allocator->empty());
-    REPORTER_ASSERT(reporter, cnt == allocator->count());
-    REPORTER_ASSERT(reporter, cnt == C::gInstCnt);
-
-    GrTAllocator<C>::Iter iter(allocator);
-    for (int i = 0; i < cnt; ++i) {
-        REPORTER_ASSERT(reporter, iter.next() && i == iter.get()->fID);
-    }
-    REPORTER_ASSERT(reporter, !iter.next());
-    if (cnt > 0) {
-        REPORTER_ASSERT(reporter, cnt-1 == allocator->back().fID);
-    }
-
-    if (popCnt > 0) {
-        for (int i = 0; i < popCnt; ++i) {
-            allocator->pop_back();
-        }
-        check_allocator_helper(allocator, cnt - popCnt, 0, reporter);
-    }
-}
-
-DEF_TEST(GrAllocator, reporter) {
-
-    // Test combinations of allocators with and without stack storage and with different block
-    // sizes.
-    SkTArray<GrTAllocator<C>*> allocators;
-    GrTAllocator<C> a1(1);
-    allocators.push_back(&a1);
-    GrTAllocator<C> a2(2);
-    allocators.push_back(&a2);
-    GrTAllocator<C> a5(5);
-    allocators.push_back(&a5);
-
-    GrSTAllocator<1, C> sa1;
-    allocators.push_back(&a1);
-    GrSTAllocator<3, C> sa3;
-    allocators.push_back(&sa3);
-    GrSTAllocator<4, C> sa4;
-    allocators.push_back(&sa4);
-
-    for (int i = 0; i < allocators.count(); ++i) {
-        check_allocator(allocators[i], 0, 0, reporter);
-        check_allocator(allocators[i], 1, 1, reporter);
-        check_allocator(allocators[i], 2, 2, reporter);
-        check_allocator(allocators[i], 10, 1, reporter);
-        check_allocator(allocators[i], 10, 5, reporter);
-        check_allocator(allocators[i], 10, 10, reporter);
-        check_allocator(allocators[i], 100, 10, reporter);
-    }
-}
diff --git a/tests/GrTAllocatorTest.cpp b/tests/GrTAllocatorTest.cpp
new file mode 100644
index 0000000..714d30b
--- /dev/null
+++ b/tests/GrTAllocatorTest.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/GrTAllocator.h"
+#include "tests/Test.h"
+
+namespace {
+struct C {
+    C() : fID(-1) { ++gInstCnt; }
+    C(int id) : fID(id) { ++gInstCnt; }
+    ~C() { --gInstCnt; }
+    int fID;
+
+    static int gInstCnt;
+};
+
+int C::gInstCnt = 0;
+}
+
+// Checks that the allocator has the correct count, etc and that the element IDs are correct.
+// Then pops popCnt items and checks again.
+template<int N>
+static void check_allocator_helper(GrTAllocator<C, N>* allocator, int cnt, int popCnt,
+                                   skiatest::Reporter* reporter) {
+    REPORTER_ASSERT(reporter, (0 == cnt) == allocator->empty());
+    REPORTER_ASSERT(reporter, cnt == allocator->count());
+    REPORTER_ASSERT(reporter, cnt == C::gInstCnt);
+
+    int i = 0;
+    for (const C& c : allocator->items()) {
+        REPORTER_ASSERT(reporter, i == c.fID);
+        REPORTER_ASSERT(reporter, allocator->item(i).fID == i);
+        ++i;
+    }
+    REPORTER_ASSERT(reporter, i == cnt);
+
+    if (cnt > 0) {
+        REPORTER_ASSERT(reporter, cnt-1 == allocator->back().fID);
+    }
+
+    if (popCnt > 0) {
+        for (int i = 0; i < popCnt; ++i) {
+            allocator->pop_back();
+        }
+        check_allocator_helper(allocator, cnt - popCnt, 0, reporter);
+    }
+}
+
+// Adds cnt items to the allocator, tests the cnts and iterators, pops popCnt items and checks
+// again. Finally it resets the allocator and checks again.
+template<int N>
+static void check_allocator(GrTAllocator<C, N>* allocator, int cnt, int popCnt,
+                            skiatest::Reporter* reporter) {
+    SkASSERT(allocator);
+    SkASSERT(allocator->empty());
+    for (int i = 0; i < cnt; ++i) {
+        // Try both variations of push_back().
+        if (i % 1) {
+            allocator->push_back(C(i));
+        } else {
+            allocator->push_back() = C(i);
+        }
+    }
+    check_allocator_helper(allocator, cnt, popCnt, reporter);
+    allocator->reset();
+    check_allocator_helper(allocator, 0, 0, reporter);
+}
+
+template<int N>
+static void run_allocator_test(GrTAllocator<C, N>* allocator, skiatest::Reporter* reporter) {
+    check_allocator(allocator, 0, 0, reporter);
+    check_allocator(allocator, 1, 1, reporter);
+    check_allocator(allocator, 2, 2, reporter);
+    check_allocator(allocator, 10, 1, reporter);
+    check_allocator(allocator, 10, 5, reporter);
+    check_allocator(allocator, 10, 10, reporter);
+    check_allocator(allocator, 100, 10, reporter);
+}
+
+DEF_TEST(GrTAllocator, reporter) {
+    // Test combinations of allocators with and without stack storage and with different block
+    // sizes.
+    GrTAllocator<C> a1(1);
+    run_allocator_test(&a1, reporter);
+
+    GrTAllocator<C> a2(2);
+    run_allocator_test(&a2, reporter);
+
+    GrTAllocator<C> a5(5);
+    run_allocator_test(&a5, reporter);
+
+    GrTAllocator<C, 1> sa1;
+    run_allocator_test(&sa1, reporter);
+
+    GrTAllocator<C, 3> sa3;
+    run_allocator_test(&sa3, reporter);
+
+    GrTAllocator<C, 4> sa4;
+    run_allocator_test(&sa4, reporter);
+}
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index d73c9fa..681f94f 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -150,7 +150,7 @@
     bool onIsEqual(const GrFragmentProcessor&) const override { return false; }
     const TextureSampler& onTextureSampler(int i) const override { return fSamplers[i]; }
 
-    GrTAllocator<TextureSampler> fSamplers;
+    SkSTArray<4, TextureSampler> fSamplers;
     typedef GrFragmentProcessor INHERITED;
 };
 }