• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #if SK_SUPPORT_GPU
9 
10 #include "gl/GrGLNameAllocator.h"
11 #include "Test.h"
12 
13 ////////////////////////////////////////////////////////////////////////////////
14 
15 class NameLeakTest {
16     static const GrGLuint kFirstName = 101;
17     static const GrGLuint kRange = 1013;
18 
19 public:
NameLeakTest(skiatest::Reporter * reporter)20     NameLeakTest(skiatest::Reporter* reporter)
21         : fReporter(reporter),
22           fAllocator(kFirstName, kFirstName + kRange),
23           fAllocatedCount(0),
24           fRandomName(kFirstName + 4 * kRange / 7) {
25         memset(fAllocatedNames, 0, sizeof(fAllocatedNames));
26     }
27 
run()28     bool run() {
29         if (!this->allocateAllRemaining()) {
30             return false;
31         }
32 
33         for (GrGLuint freeCount = 1; freeCount <= kRange; ++freeCount) {
34             if (!this->freeRandomNames(freeCount)) {
35                 return false;
36             }
37             if (!this->allocateAllRemaining()) {
38                 return false;
39             }
40         }
41 
42         return true;
43     }
44 
45 private:
isAllocated(GrGLuint name) const46     bool isAllocated(GrGLuint name) const {
47         return fAllocatedNames[name - kFirstName];
48     }
49 
setAllocated(GrGLuint name,bool allocated)50     void setAllocated(GrGLuint name, bool allocated) {
51         fAllocatedNames[name - kFirstName] = allocated;
52     }
53 
allocateAllRemaining()54     bool allocateAllRemaining() {
55         for (; fAllocatedCount < kRange; ++fAllocatedCount) {
56             GrGLuint name = fAllocator.allocateName();
57             if (0 == name) {
58                 ERRORF(fReporter,
59                        "Name allocate failed, but there should still be %u free names",
60                        kRange - fAllocatedCount);
61                 return false;
62             }
63             if (name < kFirstName || name >= kFirstName + kRange) {
64                 ERRORF(fReporter,
65                        "Name allocate returned name %u outside its bounds [%u, %u)",
66                        name, kFirstName, kFirstName + kRange);
67                 return false;
68             }
69             if (this->isAllocated(name)) {
70                 ERRORF(fReporter, "Name allocate returned name that is already allocated");
71                 return false;
72             }
73 
74             this->setAllocated(name, true);
75         }
76 
77         // Ensure it returns 0 once all the names are allocated.
78         GrGLuint name = fAllocator.allocateName();
79         if (0 != name) {
80             ERRORF(fReporter,
81                    "Name allocate did not fail when all names were already in use");
82             return false;
83         }
84 
85         // Ensure every unique name is allocated.
86         for (GrGLuint i = 0; i < kRange; ++i) {
87             if (!this->isAllocated(kFirstName + i)) {
88                 ERRORF(fReporter, "Not all unique names are allocated after allocateAllRemaining()");
89                 return false;
90             }
91         }
92 
93         return true;
94     }
95 
freeRandomNames(GrGLuint count)96     bool freeRandomNames(GrGLuint count) {
97         // The values a and c make up an LCG (pseudo-random generator). These
98         // values must satisfy the Hull-Dobell Theorem (with m=kRange):
99         // http://en.wikipedia.org/wiki/Linear_congruential_generator
100         // We use our own generator to guarantee it hits each unique value
101         // within kRange exactly once before repeating.
102         const GrGLuint seed = (count + fRandomName) / 2;
103         const GrGLuint a = seed * kRange + 1;
104         const GrGLuint c = (seed * 743) % kRange;
105 
106         for (GrGLuint i = 0; i < count; ++i) {
107             fRandomName = (a * fRandomName + c) % kRange;
108             const GrGLuint name = kFirstName + fRandomName;
109             if (!this->isAllocated(name)) {
110                 ERRORF(fReporter, "Test bug: Should not free a not-allocated name at this point (%u)", i);
111                 return false;
112             }
113 
114             fAllocator.free(name);
115             this->setAllocated(name, false);
116             --fAllocatedCount;
117         }
118 
119         return true;
120     }
121 
122     skiatest::Reporter* fReporter;
123     GrGLNameAllocator fAllocator;
124     bool fAllocatedNames[kRange];
125     GrGLuint fAllocatedCount;
126     GrGLuint fRandomName;
127 };
128 
DEF_GPUTEST(NameAllocator,reporter,factory)129 DEF_GPUTEST(NameAllocator, reporter, factory) {
130     // Ensure no names are leaked or double-allocated during heavy usage.
131     {
132         NameLeakTest nameLeakTest(reporter);
133         nameLeakTest.run();
134     }
135 
136     static const GrGLuint range = 32;
137     GrGLNameAllocator allocator(1, 1 + range);
138     for (GrGLuint i = 1; i <= range; ++i) {
139         allocator.allocateName();
140     }
141     REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
142 
143     // Test freeing names out of range.
144     allocator.free(allocator.firstName() - 1);
145     allocator.free(allocator.endName());
146     REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
147 
148     // Test freeing not-allocated names.
149     for (GrGLuint i = 1; i <= range/2; i += 2) {
150         allocator.free(i);
151     }
152     for (GrGLuint i = 1; i <= range/2; i += 2) {
153         // None of these names will be allocated.
154         allocator.free(i);
155     }
156     for (GrGLuint i = 1; i <= range/2; ++i) {
157         // Every other name will not be be allocated.
158         allocator.free(i);
159     }
160     for (GrGLuint i = 1; i <= range/2; ++i) {
161         if (0 == allocator.allocateName()) {
162             ERRORF(reporter, "Name allocate failed when there should be free names");
163             break;
164         }
165     }
166     REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
167 }
168 
169 #endif
170