xref: /aosp_15_r20/external/skia/tests/SkBlockAllocatorTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2020 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkBlockAllocator.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
12*c8dee2aaSAndroid Build Coastguard Worker 
13*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
14*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
15*c8dee2aaSAndroid Build Coastguard Worker #include <new>
16*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker using Block = SkBlockAllocator::Block;
19*c8dee2aaSAndroid Build Coastguard Worker using GrowthPolicy = SkBlockAllocator::GrowthPolicy;
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker class BlockAllocatorTestAccess {
22*c8dee2aaSAndroid Build Coastguard Worker public:
23*c8dee2aaSAndroid Build Coastguard Worker     template<size_t N>
ScratchBlockSize(SkSBlockAllocator<N> & pool)24*c8dee2aaSAndroid Build Coastguard Worker     static size_t ScratchBlockSize(SkSBlockAllocator<N>& pool) {
25*c8dee2aaSAndroid Build Coastguard Worker         return (size_t) pool->scratchBlockSize();
26*c8dee2aaSAndroid Build Coastguard Worker     }
27*c8dee2aaSAndroid Build Coastguard Worker };
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker // Helper functions for modifying the allocator in a controlled manner
30*c8dee2aaSAndroid Build Coastguard Worker template<size_t N>
block_count(const SkSBlockAllocator<N> & pool)31*c8dee2aaSAndroid Build Coastguard Worker static int block_count(const SkSBlockAllocator<N>& pool) {
32*c8dee2aaSAndroid Build Coastguard Worker     int ct = 0;
33*c8dee2aaSAndroid Build Coastguard Worker     for (const Block* b : pool->blocks()) {
34*c8dee2aaSAndroid Build Coastguard Worker         (void) b;
35*c8dee2aaSAndroid Build Coastguard Worker         ct++;
36*c8dee2aaSAndroid Build Coastguard Worker     }
37*c8dee2aaSAndroid Build Coastguard Worker     return ct;
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker template<size_t N>
get_block(SkSBlockAllocator<N> & pool,int blockIndex)41*c8dee2aaSAndroid Build Coastguard Worker static Block* get_block(SkSBlockAllocator<N>& pool, int blockIndex) {
42*c8dee2aaSAndroid Build Coastguard Worker     Block* found = nullptr;
43*c8dee2aaSAndroid Build Coastguard Worker     int i = 0;
44*c8dee2aaSAndroid Build Coastguard Worker     for (Block* b: pool->blocks()) {
45*c8dee2aaSAndroid Build Coastguard Worker         if (i == blockIndex) {
46*c8dee2aaSAndroid Build Coastguard Worker             found = b;
47*c8dee2aaSAndroid Build Coastguard Worker             break;
48*c8dee2aaSAndroid Build Coastguard Worker         }
49*c8dee2aaSAndroid Build Coastguard Worker         i++;
50*c8dee2aaSAndroid Build Coastguard Worker     }
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(found != nullptr);
53*c8dee2aaSAndroid Build Coastguard Worker     return found;
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker 
56*c8dee2aaSAndroid Build Coastguard Worker // SkBlockAllocator holds on to the largest last-released block to reuse for new allocations,
57*c8dee2aaSAndroid Build Coastguard Worker // and this is still counted in its totalSize(). However, it's easier to reason about size - scratch
58*c8dee2aaSAndroid Build Coastguard Worker // in many of these tests.
59*c8dee2aaSAndroid Build Coastguard Worker template<size_t N>
total_size(SkSBlockAllocator<N> & pool)60*c8dee2aaSAndroid Build Coastguard Worker static size_t total_size(SkSBlockAllocator<N>& pool) {
61*c8dee2aaSAndroid Build Coastguard Worker     return pool->totalSize() - BlockAllocatorTestAccess::ScratchBlockSize(pool);
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker 
64*c8dee2aaSAndroid Build Coastguard Worker template<size_t N>
add_block(SkSBlockAllocator<N> & pool)65*c8dee2aaSAndroid Build Coastguard Worker static size_t add_block(SkSBlockAllocator<N>& pool) {
66*c8dee2aaSAndroid Build Coastguard Worker     size_t currentSize = total_size(pool);
67*c8dee2aaSAndroid Build Coastguard Worker     SkBlockAllocator::Block* current = pool->currentBlock();
68*c8dee2aaSAndroid Build Coastguard Worker     while(pool->currentBlock() == current) {
69*c8dee2aaSAndroid Build Coastguard Worker         pool->template allocate<4>(pool->preallocSize() / 2);
70*c8dee2aaSAndroid Build Coastguard Worker     }
71*c8dee2aaSAndroid Build Coastguard Worker     return total_size(pool) - currentSize;
72*c8dee2aaSAndroid Build Coastguard Worker }
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker template<size_t N>
alloc_byte(SkSBlockAllocator<N> & pool)75*c8dee2aaSAndroid Build Coastguard Worker static void* alloc_byte(SkSBlockAllocator<N>& pool) {
76*c8dee2aaSAndroid Build Coastguard Worker     auto br = pool->template allocate<1>(1);
77*c8dee2aaSAndroid Build Coastguard Worker     return br.fBlock->ptr(br.fAlignedOffset);
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorPreallocSize,r)80*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorPreallocSize, r) {
81*c8dee2aaSAndroid Build Coastguard Worker     // Tests stack/member initialization, option #1 described in doc
82*c8dee2aaSAndroid Build Coastguard Worker     SkBlockAllocator stack{GrowthPolicy::kFixed, 2048};
83*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(stack.validate();)
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, stack.preallocSize() == sizeof(SkBlockAllocator));
86*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, stack.preallocUsableSpace() == (size_t) stack.currentBlock()->avail());
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker     // Tests placement new initialization to increase head block size, option #2
89*c8dee2aaSAndroid Build Coastguard Worker     void* mem = operator new(1024);
90*c8dee2aaSAndroid Build Coastguard Worker     SkBlockAllocator* placement = new (mem) SkBlockAllocator(GrowthPolicy::kLinear, 1024,
91*c8dee2aaSAndroid Build Coastguard Worker                                                              1024 - sizeof(SkBlockAllocator));
92*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, placement->preallocSize() == 1024);
93*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, placement->preallocUsableSpace() < 1024 &&
94*c8dee2aaSAndroid Build Coastguard Worker                        placement->preallocUsableSpace() >= (1024 - sizeof(SkBlockAllocator)));
95*c8dee2aaSAndroid Build Coastguard Worker     placement->~SkBlockAllocator();
96*c8dee2aaSAndroid Build Coastguard Worker     operator delete(mem);
97*c8dee2aaSAndroid Build Coastguard Worker 
98*c8dee2aaSAndroid Build Coastguard Worker     // Tests inline increased preallocation, option #3
99*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<2048> inlined{};
100*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(inlined->validate();)
101*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, inlined->preallocSize() == 2048);
102*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, inlined->preallocUsableSpace() < 2048 &&
103*c8dee2aaSAndroid Build Coastguard Worker                        inlined->preallocUsableSpace() >= (2048 - sizeof(SkBlockAllocator)));
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorAlloc,r)106*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorAlloc, r) {
107*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<1024> pool{};
108*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
109*c8dee2aaSAndroid Build Coastguard Worker 
110*c8dee2aaSAndroid Build Coastguard Worker     // Assumes the previous pointer was in the same block
111*c8dee2aaSAndroid Build Coastguard Worker     auto validate_ptr = [&](int align, int size,
112*c8dee2aaSAndroid Build Coastguard Worker                             SkBlockAllocator::ByteRange br,
113*c8dee2aaSAndroid Build Coastguard Worker                             SkBlockAllocator::ByteRange* prevBR) {
114*c8dee2aaSAndroid Build Coastguard Worker         uintptr_t pt = reinterpret_cast<uintptr_t>(br.fBlock->ptr(br.fAlignedOffset));
115*c8dee2aaSAndroid Build Coastguard Worker         // Matches the requested align
116*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, pt % align == 0);
117*c8dee2aaSAndroid Build Coastguard Worker         // And large enough
118*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, br.fEnd - br.fAlignedOffset >= size);
119*c8dee2aaSAndroid Build Coastguard Worker         // And has enough padding for alignment
120*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, br.fAlignedOffset - br.fStart >= 0);
121*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, br.fAlignedOffset - br.fStart <= align - 1);
122*c8dee2aaSAndroid Build Coastguard Worker         // And block of the returned struct is the current block of the allocator
123*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, pool->currentBlock() == br.fBlock);
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker         // And make sure that we're past the required end of the previous allocation
126*c8dee2aaSAndroid Build Coastguard Worker         if (prevBR) {
127*c8dee2aaSAndroid Build Coastguard Worker             uintptr_t prevEnd =
128*c8dee2aaSAndroid Build Coastguard Worker                     reinterpret_cast<uintptr_t>(prevBR->fBlock->ptr(prevBR->fEnd - 1));
129*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, pt > prevEnd);
130*c8dee2aaSAndroid Build Coastguard Worker         }
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker         // And make sure that the entire byte range is safe to write into (excluding the dead space
133*c8dee2aaSAndroid Build Coastguard Worker         // between "start" and "aligned offset," which is just padding and is left poisoned)
134*c8dee2aaSAndroid Build Coastguard Worker         std::memset(br.fBlock->ptr(br.fAlignedOffset), 0xFF, br.fEnd - br.fAlignedOffset);
135*c8dee2aaSAndroid Build Coastguard Worker     };
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker     auto p1 = pool->allocate<1>(14);
138*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(1, 14, p1, nullptr);
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker     auto p2 = pool->allocate<2>(24);
141*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(2, 24, p2, &p1);
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker     auto p4 = pool->allocate<4>(28);
144*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(4, 28, p4, &p2);
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker     auto p8 = pool->allocate<8>(40);
147*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(8, 40, p8, &p4);
148*c8dee2aaSAndroid Build Coastguard Worker 
149*c8dee2aaSAndroid Build Coastguard Worker     auto p16 = pool->allocate<16>(64);
150*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(16, 64, p16, &p8);
151*c8dee2aaSAndroid Build Coastguard Worker 
152*c8dee2aaSAndroid Build Coastguard Worker     auto p32 = pool->allocate<32>(96);
153*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(32, 96, p32, &p16);
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker     // All of these allocations should be in the head block
156*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, total_size(pool) == pool->preallocSize());
157*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker     // Requesting an allocation of avail() should not make a new block
160*c8dee2aaSAndroid Build Coastguard Worker     size_t avail = pool->currentBlock()->avail<4>();
161*c8dee2aaSAndroid Build Coastguard Worker     auto pAvail = pool->allocate<4>(avail);
162*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(4, avail, pAvail, &p32);
163*c8dee2aaSAndroid Build Coastguard Worker 
164*c8dee2aaSAndroid Build Coastguard Worker     // Remaining should be less than the alignment that was requested, and then
165*c8dee2aaSAndroid Build Coastguard Worker     // the next allocation will make a new block
166*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->currentBlock()->avail<4>() < 4);
167*c8dee2aaSAndroid Build Coastguard Worker     auto pNextBlock = pool->allocate<4>(4);
168*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(4, 4, pNextBlock, nullptr);
169*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, total_size(pool) > pool->preallocSize());
170*c8dee2aaSAndroid Build Coastguard Worker 
171*c8dee2aaSAndroid Build Coastguard Worker     // Allocating more than avail() makes an another block
172*c8dee2aaSAndroid Build Coastguard Worker     size_t currentSize = total_size(pool);
173*c8dee2aaSAndroid Build Coastguard Worker     size_t bigRequest = pool->currentBlock()->avail<4>() * 2;
174*c8dee2aaSAndroid Build Coastguard Worker     auto pTooBig = pool->allocate<4>(bigRequest);
175*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(4, bigRequest, pTooBig, nullptr);
176*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, total_size(pool) > currentSize);
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     // Allocating more than the default growth policy (1024 in this case), will fulfill the request
179*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, total_size(pool) - currentSize < 4096);
180*c8dee2aaSAndroid Build Coastguard Worker     currentSize = total_size(pool);
181*c8dee2aaSAndroid Build Coastguard Worker     auto pReallyTooBig = pool->allocate<4>(4096);
182*c8dee2aaSAndroid Build Coastguard Worker     validate_ptr(4, 4096, pReallyTooBig, nullptr);
183*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, total_size(pool) >= currentSize + 4096);
184*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorResize,r)187*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorResize, r) {
188*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<1024> pool{};
189*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     // Fixed resize from 16 to 32
192*c8dee2aaSAndroid Build Coastguard Worker     SkBlockAllocator::ByteRange p = pool->allocate<4>(16);
193*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->avail<4>() > 16);
194*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->resize(p.fStart, p.fEnd, 16));
195*c8dee2aaSAndroid Build Coastguard Worker     p.fEnd += 16;
196*c8dee2aaSAndroid Build Coastguard Worker 
197*c8dee2aaSAndroid Build Coastguard Worker     std::memset(p.fBlock->ptr(p.fAlignedOffset), 0x11, p.fEnd - p.fAlignedOffset);
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker     // Subsequent allocation is 32 bytes ahead of 'p' now, and 'p' cannot be resized further.
200*c8dee2aaSAndroid Build Coastguard Worker     auto pNext = pool->allocate<4>(16);
201*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, reinterpret_cast<uintptr_t>(pNext.fBlock->ptr(pNext.fAlignedOffset)) -
202*c8dee2aaSAndroid Build Coastguard Worker                        reinterpret_cast<uintptr_t>(pNext.fBlock->ptr(p.fAlignedOffset)) == 32);
203*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock == pNext.fBlock);
204*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !p.fBlock->resize(p.fStart, p.fEnd, 48));
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that releasing pNext allows 'p' to be resized, and that it can be resized up to avail
207*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->release(pNext.fStart, pNext.fEnd));
208*c8dee2aaSAndroid Build Coastguard Worker     int fillBlock = p.fBlock->avail<4>();
209*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->resize(p.fStart, p.fEnd, fillBlock));
210*c8dee2aaSAndroid Build Coastguard Worker     p.fEnd += fillBlock;
211*c8dee2aaSAndroid Build Coastguard Worker 
212*c8dee2aaSAndroid Build Coastguard Worker     std::memset(p.fBlock->ptr(p.fAlignedOffset), 0x22, p.fEnd - p.fAlignedOffset);
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that resizing when there's not enough room fails
215*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->avail<4>() < fillBlock);
216*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !p.fBlock->resize(p.fStart, p.fEnd, fillBlock));
217*c8dee2aaSAndroid Build Coastguard Worker 
218*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that we can shrink 'p' back to 32 bytes and then further allocate again
219*c8dee2aaSAndroid Build Coastguard Worker     int shrinkTo32 = p.fStart - p.fEnd + 32;
220*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->resize(p.fStart, p.fEnd, shrinkTo32));
221*c8dee2aaSAndroid Build Coastguard Worker     p.fEnd += shrinkTo32;
222*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fEnd - p.fStart == 32);
223*c8dee2aaSAndroid Build Coastguard Worker 
224*c8dee2aaSAndroid Build Coastguard Worker     std::memset(p.fBlock->ptr(p.fAlignedOffset), 0x33, p.fEnd - p.fAlignedOffset);
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker     pNext = pool->allocate<4>(16);
227*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, reinterpret_cast<uintptr_t>(pNext.fBlock->ptr(pNext.fAlignedOffset)) -
228*c8dee2aaSAndroid Build Coastguard Worker                        reinterpret_cast<uintptr_t>(pNext.fBlock->ptr(p.fAlignedOffset)) == 32);
229*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that we can't shrink past the start of the allocation, but we can shrink it to 0
232*c8dee2aaSAndroid Build Coastguard Worker     int shrinkTo0 = pNext.fStart - pNext.fEnd;
233*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_DEBUG
234*c8dee2aaSAndroid Build Coastguard Worker     // Only test for false on release builds; a negative size should assert on debug builds
235*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !pNext.fBlock->resize(pNext.fStart, pNext.fEnd, shrinkTo0 - 1));
236*c8dee2aaSAndroid Build Coastguard Worker #endif
237*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pNext.fBlock->resize(pNext.fStart, pNext.fEnd, shrinkTo0));
238*c8dee2aaSAndroid Build Coastguard Worker }
239*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorRelease,r)240*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorRelease, r) {
241*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<1024> pool{};
242*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
243*c8dee2aaSAndroid Build Coastguard Worker 
244*c8dee2aaSAndroid Build Coastguard Worker     // Successful allocate and release
245*c8dee2aaSAndroid Build Coastguard Worker     auto p = pool->allocate<8>(32);
246*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->currentBlock()->release(p.fStart, p.fEnd));
247*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the above release actually means the next allocation reuses the same space
248*c8dee2aaSAndroid Build Coastguard Worker     auto p2 = pool->allocate<8>(32);
249*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fStart == p2.fStart);
250*c8dee2aaSAndroid Build Coastguard Worker 
251*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that 'p2' cannot be released if another allocation came after it
252*c8dee2aaSAndroid Build Coastguard Worker     auto p3 = pool->allocate<8>(64);
253*c8dee2aaSAndroid Build Coastguard Worker     (void) p3;
254*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !p2.fBlock->release(p2.fStart, p2.fEnd));
255*c8dee2aaSAndroid Build Coastguard Worker 
256*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that 'p4' can be released if 'p5' is released first, and confirm that 'p2' and 'p3'
257*c8dee2aaSAndroid Build Coastguard Worker     // can be released simultaneously (equivalent to 'p3' then 'p2').
258*c8dee2aaSAndroid Build Coastguard Worker     auto p4 = pool->allocate<8>(16);
259*c8dee2aaSAndroid Build Coastguard Worker     auto p5 = pool->allocate<8>(96);
260*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p5.fBlock->release(p5.fStart, p5.fEnd));
261*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p4.fBlock->release(p4.fStart, p4.fEnd));
262*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p2.fBlock->release(p2.fStart, p3.fEnd));
263*c8dee2aaSAndroid Build Coastguard Worker 
264*c8dee2aaSAndroid Build Coastguard Worker     // And confirm that passing in the wrong size for the allocation fails
265*c8dee2aaSAndroid Build Coastguard Worker     p = pool->allocate<8>(32);
266*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !p.fBlock->release(p.fStart, p.fEnd - 16));
267*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !p.fBlock->release(p.fStart, p.fEnd + 16));
268*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p.fBlock->release(p.fStart, p.fEnd));
269*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
270*c8dee2aaSAndroid Build Coastguard Worker }
271*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorRewind,r)272*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorRewind, r) {
273*c8dee2aaSAndroid Build Coastguard Worker     // Confirm that a bunch of allocations and then releases in stack order fully goes back to the
274*c8dee2aaSAndroid Build Coastguard Worker     // start of the block (i.e. unwinds the entire stack, and not just the last cursor position)
275*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<1024> pool{};
276*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
277*c8dee2aaSAndroid Build Coastguard Worker 
278*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkBlockAllocator::ByteRange> ptrs;
279*c8dee2aaSAndroid Build Coastguard Worker     ptrs.reserve(32); // silence clang-tidy performance warning
280*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < 32; ++i) {
281*c8dee2aaSAndroid Build Coastguard Worker         ptrs.push_back(pool->allocate<4>(16));
282*c8dee2aaSAndroid Build Coastguard Worker     }
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker     // Release everything in reverse order
285*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
286*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 31; i >= 0; --i) {
287*c8dee2aaSAndroid Build Coastguard Worker         auto br = ptrs[i];
288*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, br.fBlock->release(br.fStart, br.fEnd));
289*c8dee2aaSAndroid Build Coastguard Worker     }
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker     // If correct, we've rewound all the way back to the start of the block, so a new allocation
292*c8dee2aaSAndroid Build Coastguard Worker     // will have the same location as ptrs[0]
293*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
294*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->allocate<4>(16).fStart == ptrs[0].fStart);
295*c8dee2aaSAndroid Build Coastguard Worker }
296*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorGrowthPolicy,r)297*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorGrowthPolicy, r) {
298*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kInitSize = 128;
299*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kBlockCount = 5;
300*c8dee2aaSAndroid Build Coastguard Worker     static constexpr size_t kExpectedSizes[SkBlockAllocator::kGrowthPolicyCount][kBlockCount] = {
301*c8dee2aaSAndroid Build Coastguard Worker         // kFixed -> kInitSize per block
302*c8dee2aaSAndroid Build Coastguard Worker         { kInitSize, kInitSize, kInitSize, kInitSize, kInitSize },
303*c8dee2aaSAndroid Build Coastguard Worker         // kLinear -> (block ct + 1) * kInitSize for next block
304*c8dee2aaSAndroid Build Coastguard Worker         { kInitSize, 2 * kInitSize, 3 * kInitSize, 4 * kInitSize, 5 * kInitSize },
305*c8dee2aaSAndroid Build Coastguard Worker         // kFibonacci -> 1, 1, 2, 3, 5 * kInitSize for the blocks
306*c8dee2aaSAndroid Build Coastguard Worker         { kInitSize, kInitSize, 2 * kInitSize, 3 * kInitSize, 5 * kInitSize },
307*c8dee2aaSAndroid Build Coastguard Worker         // kExponential -> 1, 2, 4, 8, 16 * kInitSize for the blocks
308*c8dee2aaSAndroid Build Coastguard Worker         { kInitSize, 2 * kInitSize, 4 * kInitSize, 8 * kInitSize, 16 * kInitSize },
309*c8dee2aaSAndroid Build Coastguard Worker     };
310*c8dee2aaSAndroid Build Coastguard Worker 
311*c8dee2aaSAndroid Build Coastguard Worker     for (int gp = 0; gp < SkBlockAllocator::kGrowthPolicyCount; ++gp) {
312*c8dee2aaSAndroid Build Coastguard Worker         SkSBlockAllocator<kInitSize> pool{(GrowthPolicy) gp};
313*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(pool->validate();)
314*c8dee2aaSAndroid Build Coastguard Worker 
315*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, kExpectedSizes[gp][0] == total_size(pool));
316*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 1; i < kBlockCount; ++i) {
317*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, kExpectedSizes[gp][i] == add_block(pool));
318*c8dee2aaSAndroid Build Coastguard Worker         }
319*c8dee2aaSAndroid Build Coastguard Worker 
320*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(pool->validate();)
321*c8dee2aaSAndroid Build Coastguard Worker     }
322*c8dee2aaSAndroid Build Coastguard Worker }
323*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorReset,r)324*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorReset, r) {
325*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kBlockIncrement = 1024;
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<kBlockIncrement> pool{GrowthPolicy::kLinear};
328*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker     void* firstAlloc = alloc_byte(pool);
331*c8dee2aaSAndroid Build Coastguard Worker 
332*c8dee2aaSAndroid Build Coastguard Worker     // Add several blocks
333*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
334*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
335*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
336*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
337*c8dee2aaSAndroid Build Coastguard Worker 
338*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(pool) == 4); // 3 added plus the implicit head
339*c8dee2aaSAndroid Build Coastguard Worker 
340*c8dee2aaSAndroid Build Coastguard Worker     get_block(pool, 0)->setMetadata(2);
341*c8dee2aaSAndroid Build Coastguard Worker 
342*c8dee2aaSAndroid Build Coastguard Worker     // Reset and confirm that there's only one block, a new allocation matches 'firstAlloc' again,
343*c8dee2aaSAndroid Build Coastguard Worker     // and new blocks are sized based on a reset growth policy.
344*c8dee2aaSAndroid Build Coastguard Worker     pool->reset();
345*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
346*c8dee2aaSAndroid Build Coastguard Worker 
347*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r,block_count(pool) == 1);
348*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->preallocSize() == pool->totalSize());
349*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, get_block(pool, 0)->metadata() == 0);
350*c8dee2aaSAndroid Build Coastguard Worker 
351*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, firstAlloc == alloc_byte(pool));
352*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, 2 * kBlockIncrement == add_block(pool));
353*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, 3 * kBlockIncrement == add_block(pool));
354*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
355*c8dee2aaSAndroid Build Coastguard Worker }
356*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorReleaseBlock,r)357*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorReleaseBlock, r) {
358*c8dee2aaSAndroid Build Coastguard Worker     // This loops over all growth policies to make sure that the incremental releases update the
359*c8dee2aaSAndroid Build Coastguard Worker     // sequence correctly for each policy.
360*c8dee2aaSAndroid Build Coastguard Worker     for (int gp = 0; gp < SkBlockAllocator::kGrowthPolicyCount; ++gp) {
361*c8dee2aaSAndroid Build Coastguard Worker         SkSBlockAllocator<1024> pool{(GrowthPolicy) gp};
362*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(pool->validate();)
363*c8dee2aaSAndroid Build Coastguard Worker 
364*c8dee2aaSAndroid Build Coastguard Worker         void* firstAlloc = alloc_byte(pool);
365*c8dee2aaSAndroid Build Coastguard Worker 
366*c8dee2aaSAndroid Build Coastguard Worker         size_t b1Size = total_size(pool);
367*c8dee2aaSAndroid Build Coastguard Worker         size_t b2Size = add_block(pool);
368*c8dee2aaSAndroid Build Coastguard Worker         size_t b3Size = add_block(pool);
369*c8dee2aaSAndroid Build Coastguard Worker         size_t b4Size = add_block(pool);
370*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(pool->validate();)
371*c8dee2aaSAndroid Build Coastguard Worker 
372*c8dee2aaSAndroid Build Coastguard Worker         get_block(pool, 0)->setMetadata(1);
373*c8dee2aaSAndroid Build Coastguard Worker         get_block(pool, 1)->setMetadata(2);
374*c8dee2aaSAndroid Build Coastguard Worker         get_block(pool, 2)->setMetadata(3);
375*c8dee2aaSAndroid Build Coastguard Worker         get_block(pool, 3)->setMetadata(4);
376*c8dee2aaSAndroid Build Coastguard Worker 
377*c8dee2aaSAndroid Build Coastguard Worker         // Remove the 3 added blocks, but always remove the i = 1 to test intermediate removal (and
378*c8dee2aaSAndroid Build Coastguard Worker         // on the last iteration, will test tail removal).
379*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, total_size(pool) == b1Size + b2Size + b3Size + b4Size);
380*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(get_block(pool, 1));
381*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, block_count(pool) == 3);
382*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, get_block(pool, 1)->metadata() == 3);
383*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, total_size(pool) == b1Size + b3Size + b4Size);
384*c8dee2aaSAndroid Build Coastguard Worker 
385*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(get_block(pool, 1));
386*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, block_count(pool) == 2);
387*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, get_block(pool, 1)->metadata() == 4);
388*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, total_size(pool) == b1Size + b4Size);
389*c8dee2aaSAndroid Build Coastguard Worker 
390*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(get_block(pool, 1));
391*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, block_count(pool) == 1);
392*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, total_size(pool) == b1Size);
393*c8dee2aaSAndroid Build Coastguard Worker 
394*c8dee2aaSAndroid Build Coastguard Worker         // Since we're back to just the head block, if we add a new block, the growth policy should
395*c8dee2aaSAndroid Build Coastguard Worker         // match the original sequence instead of continuing with "b5Size'"
396*c8dee2aaSAndroid Build Coastguard Worker         pool->resetScratchSpace();
397*c8dee2aaSAndroid Build Coastguard Worker         size_t size = add_block(pool);
398*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, size == b2Size);
399*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(get_block(pool, 1));
400*c8dee2aaSAndroid Build Coastguard Worker 
401*c8dee2aaSAndroid Build Coastguard Worker         // Explicitly release the head block and confirm it's reset
402*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(get_block(pool, 0));
403*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, total_size(pool) == pool->preallocSize());
404*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, block_count(pool) == 1);
405*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, firstAlloc == alloc_byte(pool));
406*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, get_block(pool, 0)->metadata() == 0); // metadata reset too
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker         // Confirm that if we have > 1 block, but release the head block we can still access the
409*c8dee2aaSAndroid Build Coastguard Worker         // others
410*c8dee2aaSAndroid Build Coastguard Worker         add_block(pool);
411*c8dee2aaSAndroid Build Coastguard Worker         add_block(pool);
412*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(get_block(pool, 0));
413*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, block_count(pool) == 3);
414*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(pool->validate();)
415*c8dee2aaSAndroid Build Coastguard Worker     }
416*c8dee2aaSAndroid Build Coastguard Worker }
417*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorIterateAndRelease,r)418*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorIterateAndRelease, r) {
419*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<256> pool;
420*c8dee2aaSAndroid Build Coastguard Worker 
421*c8dee2aaSAndroid Build Coastguard Worker     pool->headBlock()->setMetadata(1);
422*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
423*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
424*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
425*c8dee2aaSAndroid Build Coastguard Worker 
426*c8dee2aaSAndroid Build Coastguard Worker     // Loop forward and release the blocks
427*c8dee2aaSAndroid Build Coastguard Worker     int releaseCount = 0;
428*c8dee2aaSAndroid Build Coastguard Worker     for (auto* b : pool->blocks()) {
429*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(b);
430*c8dee2aaSAndroid Build Coastguard Worker         releaseCount++;
431*c8dee2aaSAndroid Build Coastguard Worker     }
432*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, releaseCount == 4);
433*c8dee2aaSAndroid Build Coastguard Worker     // pool should have just the head block, but was reset
434*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->headBlock()->metadata() == 0);
435*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(pool) == 1);
436*c8dee2aaSAndroid Build Coastguard Worker 
437*c8dee2aaSAndroid Build Coastguard Worker     // Add more blocks
438*c8dee2aaSAndroid Build Coastguard Worker     pool->headBlock()->setMetadata(1);
439*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
440*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
441*c8dee2aaSAndroid Build Coastguard Worker     add_block(pool);
442*c8dee2aaSAndroid Build Coastguard Worker 
443*c8dee2aaSAndroid Build Coastguard Worker     // Loop in reverse and release the blocks
444*c8dee2aaSAndroid Build Coastguard Worker     releaseCount = 0;
445*c8dee2aaSAndroid Build Coastguard Worker     for (auto* b : pool->rblocks()) {
446*c8dee2aaSAndroid Build Coastguard Worker         pool->releaseBlock(b);
447*c8dee2aaSAndroid Build Coastguard Worker         releaseCount++;
448*c8dee2aaSAndroid Build Coastguard Worker     }
449*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, releaseCount == 4);
450*c8dee2aaSAndroid Build Coastguard Worker     // pool should have just the head block, but was reset
451*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->headBlock()->metadata() == 0);
452*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(pool) == 1);
453*c8dee2aaSAndroid Build Coastguard Worker }
454*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorScratchBlockReserve,r)455*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorScratchBlockReserve, r) {
456*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<256> pool;
457*c8dee2aaSAndroid Build Coastguard Worker 
458*c8dee2aaSAndroid Build Coastguard Worker     size_t added = add_block(pool);
459*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) == 0);
460*c8dee2aaSAndroid Build Coastguard Worker     size_t total = pool->totalSize();
461*c8dee2aaSAndroid Build Coastguard Worker     pool->releaseBlock(pool->currentBlock());
462*c8dee2aaSAndroid Build Coastguard Worker 
463*c8dee2aaSAndroid Build Coastguard Worker     // Total size shouldn't have changed, the released block should become scratch
464*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->totalSize() == total);
465*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) == added);
466*c8dee2aaSAndroid Build Coastguard Worker 
467*c8dee2aaSAndroid Build Coastguard Worker     // But a reset definitely deletes any scratch block
468*c8dee2aaSAndroid Build Coastguard Worker     pool->reset();
469*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) == 0);
470*c8dee2aaSAndroid Build Coastguard Worker 
471*c8dee2aaSAndroid Build Coastguard Worker     // Reserving more than what's available adds a scratch block, and current block remains avail.
472*c8dee2aaSAndroid Build Coastguard Worker     size_t avail = pool->currentBlock()->avail();
473*c8dee2aaSAndroid Build Coastguard Worker     size_t reserve = avail + 1;
474*c8dee2aaSAndroid Build Coastguard Worker     pool->reserve(reserve);
475*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, (size_t) pool->currentBlock()->avail() == avail);
476*c8dee2aaSAndroid Build Coastguard Worker     // And rounds up to the fixed size of this pool's growth policy
477*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) >= reserve &&
478*c8dee2aaSAndroid Build Coastguard Worker                        BlockAllocatorTestAccess::ScratchBlockSize(pool) % 256 == 0);
479*c8dee2aaSAndroid Build Coastguard Worker 
480*c8dee2aaSAndroid Build Coastguard Worker     // Allocating more than avail activates the scratch block (so totalSize doesn't change)
481*c8dee2aaSAndroid Build Coastguard Worker     size_t preAllocTotalSize = pool->totalSize();
482*c8dee2aaSAndroid Build Coastguard Worker     pool->allocate<1>(avail + 1);
483*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) == 0);
484*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->totalSize() == preAllocTotalSize);
485*c8dee2aaSAndroid Build Coastguard Worker 
486*c8dee2aaSAndroid Build Coastguard Worker     // When reserving less than what's still available in the current block, no scratch block is
487*c8dee2aaSAndroid Build Coastguard Worker     // added.
488*c8dee2aaSAndroid Build Coastguard Worker     pool->reserve(pool->currentBlock()->avail());
489*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) == 0);
490*c8dee2aaSAndroid Build Coastguard Worker 
491*c8dee2aaSAndroid Build Coastguard Worker     // Unless checking available bytes is disabled
492*c8dee2aaSAndroid Build Coastguard Worker     pool->reserve(pool->currentBlock()->avail(), SkBlockAllocator::kIgnoreExistingBytes_Flag);
493*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) > 0);
494*c8dee2aaSAndroid Build Coastguard Worker 
495*c8dee2aaSAndroid Build Coastguard Worker     // If kIgnoreGrowthPolicy is specified, the new scratch block should not have been updated to
496*c8dee2aaSAndroid Build Coastguard Worker     // follow the size (which in this case is a fixed 256 bytes per block).
497*c8dee2aaSAndroid Build Coastguard Worker     pool->resetScratchSpace();
498*c8dee2aaSAndroid Build Coastguard Worker     pool->reserve(32, SkBlockAllocator::kIgnoreGrowthPolicy_Flag);
499*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) > 0 &&
500*c8dee2aaSAndroid Build Coastguard Worker                        BlockAllocatorTestAccess::ScratchBlockSize(pool) < 256);
501*c8dee2aaSAndroid Build Coastguard Worker 
502*c8dee2aaSAndroid Build Coastguard Worker     // When requesting an allocation larger than the current block and the scratch block, a new
503*c8dee2aaSAndroid Build Coastguard Worker     // block is added, and the scratch block remains scratch.
504*c8dee2aaSAndroid Build Coastguard Worker     SkBlockAllocator::Block* oldTail = pool->currentBlock();
505*c8dee2aaSAndroid Build Coastguard Worker     avail = oldTail->avail();
506*c8dee2aaSAndroid Build Coastguard Worker     size_t scratchAvail = 2 * avail;
507*c8dee2aaSAndroid Build Coastguard Worker     pool->reserve(scratchAvail);
508*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) >= scratchAvail);
509*c8dee2aaSAndroid Build Coastguard Worker 
510*c8dee2aaSAndroid Build Coastguard Worker     // This allocation request is higher than oldTail's available, and the scratch size so we
511*c8dee2aaSAndroid Build Coastguard Worker     // should add a new block and scratch size should stay the same.
512*c8dee2aaSAndroid Build Coastguard Worker     scratchAvail = BlockAllocatorTestAccess::ScratchBlockSize(pool);
513*c8dee2aaSAndroid Build Coastguard Worker     pool->allocate<1>(scratchAvail + 1);
514*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->currentBlock() != oldTail);
515*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, BlockAllocatorTestAccess::ScratchBlockSize(pool) == scratchAvail);
516*c8dee2aaSAndroid Build Coastguard Worker }
517*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorStealBlocks,r)518*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorStealBlocks, r) {
519*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<256> poolA;
520*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<128> poolB;
521*c8dee2aaSAndroid Build Coastguard Worker 
522*c8dee2aaSAndroid Build Coastguard Worker     add_block(poolA);
523*c8dee2aaSAndroid Build Coastguard Worker     add_block(poolA);
524*c8dee2aaSAndroid Build Coastguard Worker     add_block(poolA);
525*c8dee2aaSAndroid Build Coastguard Worker 
526*c8dee2aaSAndroid Build Coastguard Worker     add_block(poolB);
527*c8dee2aaSAndroid Build Coastguard Worker     add_block(poolB);
528*c8dee2aaSAndroid Build Coastguard Worker 
529*c8dee2aaSAndroid Build Coastguard Worker     char* bAlloc = (char*) alloc_byte(poolB);
530*c8dee2aaSAndroid Build Coastguard Worker     *bAlloc = 't';
531*c8dee2aaSAndroid Build Coastguard Worker 
532*c8dee2aaSAndroid Build Coastguard Worker     const SkBlockAllocator::Block* allocOwner = poolB->findOwningBlock(bAlloc);
533*c8dee2aaSAndroid Build Coastguard Worker 
534*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(poolA) == 4);
535*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(poolB) == 3);
536*c8dee2aaSAndroid Build Coastguard Worker 
537*c8dee2aaSAndroid Build Coastguard Worker     size_t aSize = poolA->totalSize();
538*c8dee2aaSAndroid Build Coastguard Worker     size_t bSize = poolB->totalSize();
539*c8dee2aaSAndroid Build Coastguard Worker     size_t theftSize = bSize - poolB->preallocSize();
540*c8dee2aaSAndroid Build Coastguard Worker 
541*c8dee2aaSAndroid Build Coastguard Worker     // This steal should move B's 2 heap blocks to A, bringing A to 6 and B to just its head
542*c8dee2aaSAndroid Build Coastguard Worker     poolA->stealHeapBlocks(poolB.allocator());
543*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(poolA) == 6);
544*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(poolB) == 1);
545*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, poolB->preallocSize() == poolB->totalSize());
546*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, poolA->totalSize() == aSize + theftSize);
547*c8dee2aaSAndroid Build Coastguard Worker 
548*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, *bAlloc == 't');
549*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, (uintptr_t) poolA->findOwningBlock(bAlloc) == (uintptr_t) allocOwner);
550*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !poolB->findOwningBlock(bAlloc));
551*c8dee2aaSAndroid Build Coastguard Worker 
552*c8dee2aaSAndroid Build Coastguard Worker     // Redoing the steal now that B is just a head block should be a no-op
553*c8dee2aaSAndroid Build Coastguard Worker     poolA->stealHeapBlocks(poolB.allocator());
554*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(poolA) == 6);
555*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block_count(poolB) == 1);
556*c8dee2aaSAndroid Build Coastguard Worker }
557*c8dee2aaSAndroid Build Coastguard Worker 
558*c8dee2aaSAndroid Build Coastguard Worker // These tests ensure that the allocation padding mechanism works as intended
559*c8dee2aaSAndroid Build Coastguard Worker struct TestMeta {
560*c8dee2aaSAndroid Build Coastguard Worker     int fX1;
561*c8dee2aaSAndroid Build Coastguard Worker     int fX2;
562*c8dee2aaSAndroid Build Coastguard Worker };
563*c8dee2aaSAndroid Build Coastguard Worker struct alignas(32) TestMetaBig {
564*c8dee2aaSAndroid Build Coastguard Worker     int fX1;
565*c8dee2aaSAndroid Build Coastguard Worker     int fX2;
566*c8dee2aaSAndroid Build Coastguard Worker };
567*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorMetadata,r)568*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorMetadata, r) {
569*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<1024> pool{};
570*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
571*c8dee2aaSAndroid Build Coastguard Worker 
572*c8dee2aaSAndroid Build Coastguard Worker     // Allocation where alignment of user data > alignment of metadata
573*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(alignof(TestMeta) < 16);
574*c8dee2aaSAndroid Build Coastguard Worker     auto p1 = pool->allocate<16, sizeof(TestMeta)>(16);
575*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
576*c8dee2aaSAndroid Build Coastguard Worker 
577*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p1.fAlignedOffset - p1.fStart >= (int) sizeof(TestMeta));
578*c8dee2aaSAndroid Build Coastguard Worker     TestMeta* meta = static_cast<TestMeta*>(p1.fBlock->ptr(p1.fAlignedOffset - sizeof(TestMeta)));
579*c8dee2aaSAndroid Build Coastguard Worker     // Confirm alignment for both pointers
580*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, reinterpret_cast<uintptr_t>(meta) % alignof(TestMeta) == 0);
581*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, reinterpret_cast<uintptr_t>(p1.fBlock->ptr(p1.fAlignedOffset)) % 16 == 0);
582*c8dee2aaSAndroid Build Coastguard Worker     // Access fields to make sure 'meta' matches compilers expectations...
583*c8dee2aaSAndroid Build Coastguard Worker     meta->fX1 = 2;
584*c8dee2aaSAndroid Build Coastguard Worker     meta->fX2 = 5;
585*c8dee2aaSAndroid Build Coastguard Worker 
586*c8dee2aaSAndroid Build Coastguard Worker     // Repeat, but for metadata that has a larger alignment than the allocation
587*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(alignof(TestMetaBig) == 32);
588*c8dee2aaSAndroid Build Coastguard Worker     auto p2 = pool->allocate<alignof(TestMetaBig), sizeof(TestMetaBig)>(16);
589*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
590*c8dee2aaSAndroid Build Coastguard Worker 
591*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, p2.fAlignedOffset - p2.fStart >= (int) sizeof(TestMetaBig));
592*c8dee2aaSAndroid Build Coastguard Worker     TestMetaBig* metaBig = static_cast<TestMetaBig*>(
593*c8dee2aaSAndroid Build Coastguard Worker             p2.fBlock->ptr(p2.fAlignedOffset - sizeof(TestMetaBig)));
594*c8dee2aaSAndroid Build Coastguard Worker     // Confirm alignment for both pointers
595*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, reinterpret_cast<uintptr_t>(metaBig) % alignof(TestMetaBig) == 0);
596*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, reinterpret_cast<uintptr_t>(p2.fBlock->ptr(p2.fAlignedOffset)) % 16 == 0);
597*c8dee2aaSAndroid Build Coastguard Worker     // Access fields
598*c8dee2aaSAndroid Build Coastguard Worker     metaBig->fX1 = 3;
599*c8dee2aaSAndroid Build Coastguard Worker     metaBig->fX2 = 6;
600*c8dee2aaSAndroid Build Coastguard Worker 
601*c8dee2aaSAndroid Build Coastguard Worker     // Ensure metadata values persist after allocations
602*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, meta->fX1 == 2 && meta->fX2 == 5);
603*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, metaBig->fX1 == 3 && metaBig->fX2 == 6);
604*c8dee2aaSAndroid Build Coastguard Worker }
605*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorAllocatorMetadata,r)606*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorAllocatorMetadata, r) {
607*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<256> pool{};
608*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
609*c8dee2aaSAndroid Build Coastguard Worker 
610*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->metadata() == 0); // initial value
611*c8dee2aaSAndroid Build Coastguard Worker 
612*c8dee2aaSAndroid Build Coastguard Worker     pool->setMetadata(4);
613*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->metadata() == 4);
614*c8dee2aaSAndroid Build Coastguard Worker 
615*c8dee2aaSAndroid Build Coastguard Worker     // Releasing the head block doesn't change the allocator's metadata (even though that's where
616*c8dee2aaSAndroid Build Coastguard Worker     // it is stored).
617*c8dee2aaSAndroid Build Coastguard Worker     pool->releaseBlock(pool->headBlock());
618*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->metadata() == 4);
619*c8dee2aaSAndroid Build Coastguard Worker 
620*c8dee2aaSAndroid Build Coastguard Worker     // But resetting the whole allocator brings things back to as if it were newly constructed
621*c8dee2aaSAndroid Build Coastguard Worker     pool->reset();
622*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, pool->metadata() == 0);
623*c8dee2aaSAndroid Build Coastguard Worker }
624*c8dee2aaSAndroid Build Coastguard Worker 
625*c8dee2aaSAndroid Build Coastguard Worker template<size_t Align, size_t Padding>
run_owning_block_test(skiatest::Reporter * r,SkBlockAllocator * pool)626*c8dee2aaSAndroid Build Coastguard Worker static void run_owning_block_test(skiatest::Reporter* r, SkBlockAllocator* pool) {
627*c8dee2aaSAndroid Build Coastguard Worker     auto br = pool->allocate<Align, Padding>(1);
628*c8dee2aaSAndroid Build Coastguard Worker 
629*c8dee2aaSAndroid Build Coastguard Worker     void* userPtr = br.fBlock->ptr(br.fAlignedOffset);
630*c8dee2aaSAndroid Build Coastguard Worker     void* metaPtr = br.fBlock->ptr(br.fAlignedOffset - Padding);
631*c8dee2aaSAndroid Build Coastguard Worker 
632*c8dee2aaSAndroid Build Coastguard Worker     Block* block = pool->owningBlock<Align, Padding>(userPtr, br.fStart);
633*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block == br.fBlock);
634*c8dee2aaSAndroid Build Coastguard Worker 
635*c8dee2aaSAndroid Build Coastguard Worker     block = pool->owningBlock<Align>(metaPtr, br.fStart);
636*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block == br.fBlock);
637*c8dee2aaSAndroid Build Coastguard Worker 
638*c8dee2aaSAndroid Build Coastguard Worker     block = reinterpret_cast<Block*>(reinterpret_cast<uintptr_t>(userPtr) - br.fAlignedOffset);
639*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, block == br.fBlock);
640*c8dee2aaSAndroid Build Coastguard Worker }
641*c8dee2aaSAndroid Build Coastguard Worker 
642*c8dee2aaSAndroid Build Coastguard Worker template<size_t Padding>
run_owning_block_tests(skiatest::Reporter * r,SkBlockAllocator * pool)643*c8dee2aaSAndroid Build Coastguard Worker static void run_owning_block_tests(skiatest::Reporter* r, SkBlockAllocator* pool) {
644*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<1, Padding>(r, pool);
645*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<2, Padding>(r, pool);
646*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<4, Padding>(r, pool);
647*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<8, Padding>(r, pool);
648*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<16, Padding>(r, pool);
649*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<32, Padding>(r, pool);
650*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<64, Padding>(r, pool);
651*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_test<128, Padding>(r, pool);
652*c8dee2aaSAndroid Build Coastguard Worker }
653*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkBlockAllocatorOwningBlock,r)654*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkBlockAllocatorOwningBlock, r) {
655*c8dee2aaSAndroid Build Coastguard Worker     SkSBlockAllocator<1024> pool{};
656*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(pool->validate();)
657*c8dee2aaSAndroid Build Coastguard Worker 
658*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<1>(r, pool.allocator());
659*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<2>(r, pool.allocator());
660*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<4>(r, pool.allocator());
661*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<8>(r, pool.allocator());
662*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<16>(r, pool.allocator());
663*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<32>(r, pool.allocator());
664*c8dee2aaSAndroid Build Coastguard Worker 
665*c8dee2aaSAndroid Build Coastguard Worker     // And some weird numbers
666*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<3>(r, pool.allocator());
667*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<9>(r, pool.allocator());
668*c8dee2aaSAndroid Build Coastguard Worker     run_owning_block_tests<17>(r, pool.allocator());
669*c8dee2aaSAndroid Build Coastguard Worker }
670