xref: /aosp_15_r20/external/angle/src/libANGLE/ResourceMap.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker //
2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker //
6*8975f5c5SAndroid Build Coastguard Worker // ResourceMap:
7*8975f5c5SAndroid Build Coastguard Worker //   An optimized resource map which packs the first set of allocated objects into a
8*8975f5c5SAndroid Build Coastguard Worker //   flat array, and then falls back to an unordered map for the higher handle values.
9*8975f5c5SAndroid Build Coastguard Worker //
10*8975f5c5SAndroid Build Coastguard Worker 
11*8975f5c5SAndroid Build Coastguard Worker #ifndef LIBANGLE_RESOURCE_MAP_H_
12*8975f5c5SAndroid Build Coastguard Worker #define LIBANGLE_RESOURCE_MAP_H_
13*8975f5c5SAndroid Build Coastguard Worker 
14*8975f5c5SAndroid Build Coastguard Worker #include <mutex>
15*8975f5c5SAndroid Build Coastguard Worker #include <type_traits>
16*8975f5c5SAndroid Build Coastguard Worker 
17*8975f5c5SAndroid Build Coastguard Worker #include "common/SimpleMutex.h"
18*8975f5c5SAndroid Build Coastguard Worker #include "common/hash_containers.h"
19*8975f5c5SAndroid Build Coastguard Worker #include "libANGLE/angletypes.h"
20*8975f5c5SAndroid Build Coastguard Worker 
21*8975f5c5SAndroid Build Coastguard Worker namespace gl
22*8975f5c5SAndroid Build Coastguard Worker {
23*8975f5c5SAndroid Build Coastguard Worker // The resource map needs to be internally synchronized for maps that are placed in the share group
24*8975f5c5SAndroid Build Coastguard Worker // (as opposed to being private to the context) and that are accessed without holding the share
25*8975f5c5SAndroid Build Coastguard Worker // group lock.
26*8975f5c5SAndroid Build Coastguard Worker #if defined(ANGLE_ENABLE_SHARE_CONTEXT_LOCK)
27*8975f5c5SAndroid Build Coastguard Worker using ResourceMapMutex = angle::SimpleMutex;
28*8975f5c5SAndroid Build Coastguard Worker #else
29*8975f5c5SAndroid Build Coastguard Worker using ResourceMapMutex = angle::NoOpMutex;
30*8975f5c5SAndroid Build Coastguard Worker #endif
31*8975f5c5SAndroid Build Coastguard Worker 
32*8975f5c5SAndroid Build Coastguard Worker template <bool NeedsLock = true>
33*8975f5c5SAndroid Build Coastguard Worker struct SelectResourceMapMutex
34*8975f5c5SAndroid Build Coastguard Worker {
35*8975f5c5SAndroid Build Coastguard Worker     using type = ResourceMapMutex;
36*8975f5c5SAndroid Build Coastguard Worker };
37*8975f5c5SAndroid Build Coastguard Worker 
38*8975f5c5SAndroid Build Coastguard Worker template <>
39*8975f5c5SAndroid Build Coastguard Worker struct SelectResourceMapMutex<false>
40*8975f5c5SAndroid Build Coastguard Worker {
41*8975f5c5SAndroid Build Coastguard Worker     using type = angle::NoOpMutex;
42*8975f5c5SAndroid Build Coastguard Worker };
43*8975f5c5SAndroid Build Coastguard Worker 
44*8975f5c5SAndroid Build Coastguard Worker // Analysis of ANGLE's traces as well as Chrome usage reveals the following:
45*8975f5c5SAndroid Build Coastguard Worker //
46*8975f5c5SAndroid Build Coastguard Worker // - Buffers: Typical applications use no more than 4000 ids.  Very few use over 6000.
47*8975f5c5SAndroid Build Coastguard Worker // - Textures: Typical applications use no more than 1200 ids.  Very few use over 2000.
48*8975f5c5SAndroid Build Coastguard Worker // - Samplers: Typical applications use no more than 50 ids.  Very few use over 100.
49*8975f5c5SAndroid Build Coastguard Worker // - Shaders and Programs: Typical applications use no more than 500.  Very few use over 700.
50*8975f5c5SAndroid Build Coastguard Worker // - Sync objects: Typical applications use no more than 500.  Very few use over 1500.
51*8975f5c5SAndroid Build Coastguard Worker //
52*8975f5c5SAndroid Build Coastguard Worker // For all the other shared types, the maximum used id is small (under 100).  For
53*8975f5c5SAndroid Build Coastguard Worker // context-private parts (such as vertex arrays and queries), the id count can be in the
54*8975f5c5SAndroid Build Coastguard Worker // thousands.
55*8975f5c5SAndroid Build Coastguard Worker //
56*8975f5c5SAndroid Build Coastguard Worker // The initial size of the flat resource map is based on the above, rounded up to a multiple of
57*8975f5c5SAndroid Build Coastguard Worker // 1536.  Resource maps that need a lock (kNeedsLock == true) have the maximum flat size identical
58*8975f5c5SAndroid Build Coastguard Worker // to initial flat size to avoid reallocation.  For others, the maps start small and can grow.
59*8975f5c5SAndroid Build Coastguard Worker template <typename IDType>
60*8975f5c5SAndroid Build Coastguard Worker struct ResourceMapParams
61*8975f5c5SAndroid Build Coastguard Worker {
62*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize = 192;
63*8975f5c5SAndroid Build Coastguard Worker 
64*8975f5c5SAndroid Build Coastguard Worker     // The following are private to the context and don't need a lock:
65*8975f5c5SAndroid Build Coastguard Worker     //
66*8975f5c5SAndroid Build Coastguard Worker     // - Vertex Array Objects
67*8975f5c5SAndroid Build Coastguard Worker     // - Framebuffer Objects
68*8975f5c5SAndroid Build Coastguard Worker     // - Transform Feedback Objects
69*8975f5c5SAndroid Build Coastguard Worker     // - Query Objects
70*8975f5c5SAndroid Build Coastguard Worker     //
71*8975f5c5SAndroid Build Coastguard Worker     // The rest of the maps need a lock.  However, only a select few are currently locked as API
72*8975f5c5SAndroid Build Coastguard Worker     // relevant to the rest of the types are protected by the share group lock.  As the share group
73*8975f5c5SAndroid Build Coastguard Worker     // lock is removed from more types, the resource map lock needs to be enabled for them.
74*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock = false;
75*8975f5c5SAndroid Build Coastguard Worker };
76*8975f5c5SAndroid Build Coastguard Worker template <>
77*8975f5c5SAndroid Build Coastguard Worker struct ResourceMapParams<BufferID>
78*8975f5c5SAndroid Build Coastguard Worker {
79*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize = 6144;
80*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock                  = true;
81*8975f5c5SAndroid Build Coastguard Worker };
82*8975f5c5SAndroid Build Coastguard Worker template <>
83*8975f5c5SAndroid Build Coastguard Worker struct ResourceMapParams<TextureID>
84*8975f5c5SAndroid Build Coastguard Worker {
85*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize = 1536;
86*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock                  = false;
87*8975f5c5SAndroid Build Coastguard Worker };
88*8975f5c5SAndroid Build Coastguard Worker template <>
89*8975f5c5SAndroid Build Coastguard Worker struct ResourceMapParams<ShaderProgramID>
90*8975f5c5SAndroid Build Coastguard Worker {
91*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize = 1536;
92*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock                  = false;
93*8975f5c5SAndroid Build Coastguard Worker };
94*8975f5c5SAndroid Build Coastguard Worker template <>
95*8975f5c5SAndroid Build Coastguard Worker struct ResourceMapParams<SyncID>
96*8975f5c5SAndroid Build Coastguard Worker {
97*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize = 1536;
98*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock                  = false;
99*8975f5c5SAndroid Build Coastguard Worker };
100*8975f5c5SAndroid Build Coastguard Worker // For the purpose of unit testing, |int| is considered private (not needing lock), and
101*8975f5c5SAndroid Build Coastguard Worker // |unsigned int| is considered shared (needing lock).
102*8975f5c5SAndroid Build Coastguard Worker template <>
103*8975f5c5SAndroid Build Coastguard Worker struct ResourceMapParams<unsigned int>
104*8975f5c5SAndroid Build Coastguard Worker {
105*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize = 192;
106*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock                  = true;
107*8975f5c5SAndroid Build Coastguard Worker };
108*8975f5c5SAndroid Build Coastguard Worker 
109*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
110*8975f5c5SAndroid Build Coastguard Worker class ResourceMap final : angle::NonCopyable
111*8975f5c5SAndroid Build Coastguard Worker {
112*8975f5c5SAndroid Build Coastguard Worker   public:
113*8975f5c5SAndroid Build Coastguard Worker     ResourceMap();
114*8975f5c5SAndroid Build Coastguard Worker     ~ResourceMap();
115*8975f5c5SAndroid Build Coastguard Worker 
116*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ResourceType *query(IDType id) const
117*8975f5c5SAndroid Build Coastguard Worker     {
118*8975f5c5SAndroid Build Coastguard Worker         GLuint handle = GetIDValue(id);
119*8975f5c5SAndroid Build Coastguard Worker         // No need for a lock when accessing the flat map.  Either the flat map is static, or
120*8975f5c5SAndroid Build Coastguard Worker         // locking is not needed.
121*8975f5c5SAndroid Build Coastguard Worker         static_assert(!kNeedsLock || kInitialFlatResourcesSize == kFlatResourcesLimit);
122*8975f5c5SAndroid Build Coastguard Worker 
123*8975f5c5SAndroid Build Coastguard Worker         if (handle < mFlatResourcesSize)
124*8975f5c5SAndroid Build Coastguard Worker         {
125*8975f5c5SAndroid Build Coastguard Worker             ResourceType *value = mFlatResources[handle];
126*8975f5c5SAndroid Build Coastguard Worker             return (value == InvalidPointer() ? nullptr : value);
127*8975f5c5SAndroid Build Coastguard Worker         }
128*8975f5c5SAndroid Build Coastguard Worker 
129*8975f5c5SAndroid Build Coastguard Worker         std::lock_guard<Mutex> lock(mMutex);
130*8975f5c5SAndroid Build Coastguard Worker 
131*8975f5c5SAndroid Build Coastguard Worker         auto it = mHashedResources.find(handle);
132*8975f5c5SAndroid Build Coastguard Worker         return (it == mHashedResources.end() ? nullptr : it->second);
133*8975f5c5SAndroid Build Coastguard Worker     }
134*8975f5c5SAndroid Build Coastguard Worker 
135*8975f5c5SAndroid Build Coastguard Worker     // Returns true if the handle was reserved. Not necessarily if the resource is created.
136*8975f5c5SAndroid Build Coastguard Worker     bool contains(IDType id) const;
137*8975f5c5SAndroid Build Coastguard Worker 
138*8975f5c5SAndroid Build Coastguard Worker     // Returns the element that was at this location.
139*8975f5c5SAndroid Build Coastguard Worker     bool erase(IDType id, ResourceType **resourceOut);
140*8975f5c5SAndroid Build Coastguard Worker 
141*8975f5c5SAndroid Build Coastguard Worker     void assign(IDType id, ResourceType *resource);
142*8975f5c5SAndroid Build Coastguard Worker 
143*8975f5c5SAndroid Build Coastguard Worker     // Clears the map.
144*8975f5c5SAndroid Build Coastguard Worker     void clear();
145*8975f5c5SAndroid Build Coastguard Worker 
146*8975f5c5SAndroid Build Coastguard Worker     using IndexAndResource = std::pair<GLuint, ResourceType *>;
147*8975f5c5SAndroid Build Coastguard Worker     using HashMap          = angle::HashMap<GLuint, ResourceType *>;
148*8975f5c5SAndroid Build Coastguard Worker 
149*8975f5c5SAndroid Build Coastguard Worker     class Iterator final
150*8975f5c5SAndroid Build Coastguard Worker     {
151*8975f5c5SAndroid Build Coastguard Worker       public:
152*8975f5c5SAndroid Build Coastguard Worker         bool operator==(const Iterator &other) const;
153*8975f5c5SAndroid Build Coastguard Worker         bool operator!=(const Iterator &other) const;
154*8975f5c5SAndroid Build Coastguard Worker         Iterator &operator++();
155*8975f5c5SAndroid Build Coastguard Worker         const IndexAndResource *operator->() const;
156*8975f5c5SAndroid Build Coastguard Worker         const IndexAndResource &operator*() const;
157*8975f5c5SAndroid Build Coastguard Worker 
158*8975f5c5SAndroid Build Coastguard Worker       private:
159*8975f5c5SAndroid Build Coastguard Worker         friend class ResourceMap;
160*8975f5c5SAndroid Build Coastguard Worker         Iterator(const ResourceMap &origin,
161*8975f5c5SAndroid Build Coastguard Worker                  GLuint flatIndex,
162*8975f5c5SAndroid Build Coastguard Worker                  typename HashMap::const_iterator hashIndex,
163*8975f5c5SAndroid Build Coastguard Worker                  bool skipNulls);
164*8975f5c5SAndroid Build Coastguard Worker         void updateValue();
165*8975f5c5SAndroid Build Coastguard Worker 
166*8975f5c5SAndroid Build Coastguard Worker         const ResourceMap &mOrigin;
167*8975f5c5SAndroid Build Coastguard Worker         GLuint mFlatIndex;
168*8975f5c5SAndroid Build Coastguard Worker         typename HashMap::const_iterator mHashIndex;
169*8975f5c5SAndroid Build Coastguard Worker         IndexAndResource mValue;
170*8975f5c5SAndroid Build Coastguard Worker         bool mSkipNulls;
171*8975f5c5SAndroid Build Coastguard Worker     };
172*8975f5c5SAndroid Build Coastguard Worker 
173*8975f5c5SAndroid Build Coastguard Worker   private:
174*8975f5c5SAndroid Build Coastguard Worker     friend class Iterator;
175*8975f5c5SAndroid Build Coastguard Worker     template <typename SameResourceType, typename SameIDType>
176*8975f5c5SAndroid Build Coastguard Worker     friend class UnsafeResourceMapIter;
177*8975f5c5SAndroid Build Coastguard Worker 
178*8975f5c5SAndroid Build Coastguard Worker     // null values represent reserved handles.
179*8975f5c5SAndroid Build Coastguard Worker     Iterator begin() const;
180*8975f5c5SAndroid Build Coastguard Worker     Iterator end() const;
181*8975f5c5SAndroid Build Coastguard Worker 
182*8975f5c5SAndroid Build Coastguard Worker     Iterator beginWithNull() const;
183*8975f5c5SAndroid Build Coastguard Worker     Iterator endWithNull() const;
184*8975f5c5SAndroid Build Coastguard Worker 
185*8975f5c5SAndroid Build Coastguard Worker     // Used by iterators and related functions only (due to lack of thread safety).
186*8975f5c5SAndroid Build Coastguard Worker     GLuint nextResource(size_t flatIndex, bool skipNulls) const;
187*8975f5c5SAndroid Build Coastguard Worker 
188*8975f5c5SAndroid Build Coastguard Worker     // constexpr methods cannot contain reinterpret_cast, so we need a static method.
189*8975f5c5SAndroid Build Coastguard Worker     static ResourceType *InvalidPointer();
190*8975f5c5SAndroid Build Coastguard Worker     static constexpr intptr_t kInvalidPointer = static_cast<intptr_t>(-1);
191*8975f5c5SAndroid Build Coastguard Worker 
192*8975f5c5SAndroid Build Coastguard Worker     static constexpr bool kNeedsLock = ResourceMapParams<IDType>::kNeedsLock;
193*8975f5c5SAndroid Build Coastguard Worker     using Mutex                      = typename SelectResourceMapMutex<kNeedsLock>::type;
194*8975f5c5SAndroid Build Coastguard Worker 
195*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kInitialFlatResourcesSize =
196*8975f5c5SAndroid Build Coastguard Worker         ResourceMapParams<IDType>::kInitialFlatResourcesSize;
197*8975f5c5SAndroid Build Coastguard Worker 
198*8975f5c5SAndroid Build Coastguard Worker     // Experimental testing suggests that ~10k is a reasonable upper limit.
199*8975f5c5SAndroid Build Coastguard Worker     static constexpr size_t kFlatResourcesLimit = kNeedsLock ? kInitialFlatResourcesSize : 0x3000;
200*8975f5c5SAndroid Build Coastguard Worker     // Due to the way assign() is implemented, kFlatResourcesLimit / kInitialFlatResourcesSize must
201*8975f5c5SAndroid Build Coastguard Worker     // be a power of 2.
202*8975f5c5SAndroid Build Coastguard Worker     static_assert(kFlatResourcesLimit % kInitialFlatResourcesSize == 0);
203*8975f5c5SAndroid Build Coastguard Worker     static_assert(((kFlatResourcesLimit / kInitialFlatResourcesSize) &
204*8975f5c5SAndroid Build Coastguard Worker                    (kFlatResourcesLimit / kInitialFlatResourcesSize - 1)) == 0);
205*8975f5c5SAndroid Build Coastguard Worker 
206*8975f5c5SAndroid Build Coastguard Worker     size_t mFlatResourcesSize;
207*8975f5c5SAndroid Build Coastguard Worker     ResourceType **mFlatResources;
208*8975f5c5SAndroid Build Coastguard Worker 
209*8975f5c5SAndroid Build Coastguard Worker     // A map of GL objects indexed by object ID.
210*8975f5c5SAndroid Build Coastguard Worker     HashMap mHashedResources;
211*8975f5c5SAndroid Build Coastguard Worker 
212*8975f5c5SAndroid Build Coastguard Worker     // mFlatResources is allocated at object creation time, with a default size of
213*8975f5c5SAndroid Build Coastguard Worker     // |kInitialFlatResourcesSize|.  This is thread safe, because the allocation is done by the
214*8975f5c5SAndroid Build Coastguard Worker     // first context in the share group.  The flat map is allowed to grow up to
215*8975f5c5SAndroid Build Coastguard Worker     // |kFlatResourcesLimit|, but only for maps that don't need a lock (kNeedsLock == false).
216*8975f5c5SAndroid Build Coastguard Worker     //
217*8975f5c5SAndroid Build Coastguard Worker     // For maps that don't need a lock, this mutex is a no-op.  For those that do, the mutex is
218*8975f5c5SAndroid Build Coastguard Worker     // taken when allocating / deleting objects, as well as when accessing |mHashedResources|.
219*8975f5c5SAndroid Build Coastguard Worker     // Otherwise, access to the flat map (which never gets reallocated due to
220*8975f5c5SAndroid Build Coastguard Worker     // |kInitialFlatResourcesSize == kFlatResourcesLimit|) is lockless.  This latter is possible
221*8975f5c5SAndroid Build Coastguard Worker     // because the application is not allowed to gen/delete and bind the same ID in different
222*8975f5c5SAndroid Build Coastguard Worker     // threads at the same time.
223*8975f5c5SAndroid Build Coastguard Worker     //
224*8975f5c5SAndroid Build Coastguard Worker     // Note that because HandleAllocator is not yet thread-safe, glGen* and glDelete* functions
225*8975f5c5SAndroid Build Coastguard Worker     // cannot be free of the share group mutex yet.  To remove the share group mutex from those
226*8975f5c5SAndroid Build Coastguard Worker     // functions, likely the HandleAllocator class should be merged with this class, and the
227*8975f5c5SAndroid Build Coastguard Worker     // necessary insert/erase operations done under this same lock.
228*8975f5c5SAndroid Build Coastguard Worker     mutable Mutex mMutex;
229*8975f5c5SAndroid Build Coastguard Worker };
230*8975f5c5SAndroid Build Coastguard Worker 
231*8975f5c5SAndroid Build Coastguard Worker // A helper to retrieve the resource map iterators while being explicit that this is not thread
232*8975f5c5SAndroid Build Coastguard Worker // safe.  Usage of iterators are limited to clean up on destruction and capture/replay, neither of
233*8975f5c5SAndroid Build Coastguard Worker // which can race with other threads in their access to the resource map.
234*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
235*8975f5c5SAndroid Build Coastguard Worker class UnsafeResourceMapIter
236*8975f5c5SAndroid Build Coastguard Worker {
237*8975f5c5SAndroid Build Coastguard Worker   public:
238*8975f5c5SAndroid Build Coastguard Worker     using ResMap = ResourceMap<ResourceType, IDType>;
239*8975f5c5SAndroid Build Coastguard Worker 
240*8975f5c5SAndroid Build Coastguard Worker     UnsafeResourceMapIter(const ResMap &resourceMap) : mResourceMap(resourceMap) {}
241*8975f5c5SAndroid Build Coastguard Worker 
242*8975f5c5SAndroid Build Coastguard Worker     typename ResMap::Iterator begin() const { return mResourceMap.begin(); }
243*8975f5c5SAndroid Build Coastguard Worker     typename ResMap::Iterator end() const { return mResourceMap.end(); }
244*8975f5c5SAndroid Build Coastguard Worker 
245*8975f5c5SAndroid Build Coastguard Worker     typename ResMap::Iterator beginWithNull() const { return mResourceMap.beginWithNull(); }
246*8975f5c5SAndroid Build Coastguard Worker     typename ResMap::Iterator endWithNull() const { return mResourceMap.endWithNull(); }
247*8975f5c5SAndroid Build Coastguard Worker 
248*8975f5c5SAndroid Build Coastguard Worker     // Not a constant-time operation, should only be used for verification.
249*8975f5c5SAndroid Build Coastguard Worker     bool empty() const;
250*8975f5c5SAndroid Build Coastguard Worker 
251*8975f5c5SAndroid Build Coastguard Worker   private:
252*8975f5c5SAndroid Build Coastguard Worker     const ResMap &mResourceMap;
253*8975f5c5SAndroid Build Coastguard Worker };
254*8975f5c5SAndroid Build Coastguard Worker 
255*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
256*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::ResourceMap()
257*8975f5c5SAndroid Build Coastguard Worker     : mFlatResourcesSize(kInitialFlatResourcesSize),
258*8975f5c5SAndroid Build Coastguard Worker       mFlatResources(new ResourceType *[kInitialFlatResourcesSize])
259*8975f5c5SAndroid Build Coastguard Worker {
260*8975f5c5SAndroid Build Coastguard Worker     memset(mFlatResources, kInvalidPointer, mFlatResourcesSize * sizeof(mFlatResources[0]));
261*8975f5c5SAndroid Build Coastguard Worker }
262*8975f5c5SAndroid Build Coastguard Worker 
263*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
264*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::~ResourceMap()
265*8975f5c5SAndroid Build Coastguard Worker {
266*8975f5c5SAndroid Build Coastguard Worker     ASSERT(begin() == end());
267*8975f5c5SAndroid Build Coastguard Worker     delete[] mFlatResources;
268*8975f5c5SAndroid Build Coastguard Worker }
269*8975f5c5SAndroid Build Coastguard Worker 
270*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
271*8975f5c5SAndroid Build Coastguard Worker ANGLE_INLINE bool ResourceMap<ResourceType, IDType>::contains(IDType id) const
272*8975f5c5SAndroid Build Coastguard Worker {
273*8975f5c5SAndroid Build Coastguard Worker     GLuint handle = GetIDValue(id);
274*8975f5c5SAndroid Build Coastguard Worker     if (handle < mFlatResourcesSize)
275*8975f5c5SAndroid Build Coastguard Worker     {
276*8975f5c5SAndroid Build Coastguard Worker         return mFlatResources[handle] != InvalidPointer();
277*8975f5c5SAndroid Build Coastguard Worker     }
278*8975f5c5SAndroid Build Coastguard Worker     std::lock_guard<Mutex> lock(mMutex);
279*8975f5c5SAndroid Build Coastguard Worker     return mHashedResources.find(handle) != mHashedResources.end();
280*8975f5c5SAndroid Build Coastguard Worker }
281*8975f5c5SAndroid Build Coastguard Worker 
282*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
283*8975f5c5SAndroid Build Coastguard Worker bool ResourceMap<ResourceType, IDType>::erase(IDType id, ResourceType **resourceOut)
284*8975f5c5SAndroid Build Coastguard Worker {
285*8975f5c5SAndroid Build Coastguard Worker     GLuint handle = GetIDValue(id);
286*8975f5c5SAndroid Build Coastguard Worker     if (handle < mFlatResourcesSize)
287*8975f5c5SAndroid Build Coastguard Worker     {
288*8975f5c5SAndroid Build Coastguard Worker         auto &value = mFlatResources[handle];
289*8975f5c5SAndroid Build Coastguard Worker         if (value == InvalidPointer())
290*8975f5c5SAndroid Build Coastguard Worker         {
291*8975f5c5SAndroid Build Coastguard Worker             return false;
292*8975f5c5SAndroid Build Coastguard Worker         }
293*8975f5c5SAndroid Build Coastguard Worker         *resourceOut = value;
294*8975f5c5SAndroid Build Coastguard Worker         value        = InvalidPointer();
295*8975f5c5SAndroid Build Coastguard Worker     }
296*8975f5c5SAndroid Build Coastguard Worker     else
297*8975f5c5SAndroid Build Coastguard Worker     {
298*8975f5c5SAndroid Build Coastguard Worker         std::lock_guard<Mutex> lock(mMutex);
299*8975f5c5SAndroid Build Coastguard Worker 
300*8975f5c5SAndroid Build Coastguard Worker         auto it = mHashedResources.find(handle);
301*8975f5c5SAndroid Build Coastguard Worker         if (it == mHashedResources.end())
302*8975f5c5SAndroid Build Coastguard Worker         {
303*8975f5c5SAndroid Build Coastguard Worker             return false;
304*8975f5c5SAndroid Build Coastguard Worker         }
305*8975f5c5SAndroid Build Coastguard Worker         *resourceOut = it->second;
306*8975f5c5SAndroid Build Coastguard Worker         mHashedResources.erase(it);
307*8975f5c5SAndroid Build Coastguard Worker     }
308*8975f5c5SAndroid Build Coastguard Worker     return true;
309*8975f5c5SAndroid Build Coastguard Worker }
310*8975f5c5SAndroid Build Coastguard Worker 
311*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
312*8975f5c5SAndroid Build Coastguard Worker void ResourceMap<ResourceType, IDType>::assign(IDType id, ResourceType *resource)
313*8975f5c5SAndroid Build Coastguard Worker {
314*8975f5c5SAndroid Build Coastguard Worker     GLuint handle = GetIDValue(id);
315*8975f5c5SAndroid Build Coastguard Worker     if (handle < kFlatResourcesLimit)
316*8975f5c5SAndroid Build Coastguard Worker     {
317*8975f5c5SAndroid Build Coastguard Worker         if (handle >= mFlatResourcesSize)
318*8975f5c5SAndroid Build Coastguard Worker         {
319*8975f5c5SAndroid Build Coastguard Worker             // No need for a lock as the flat map never grows when locking is needed.
320*8975f5c5SAndroid Build Coastguard Worker             static_assert(!kNeedsLock || kInitialFlatResourcesSize == kFlatResourcesLimit);
321*8975f5c5SAndroid Build Coastguard Worker 
322*8975f5c5SAndroid Build Coastguard Worker             // Use power-of-two.
323*8975f5c5SAndroid Build Coastguard Worker             size_t newSize = mFlatResourcesSize;
324*8975f5c5SAndroid Build Coastguard Worker             while (newSize <= handle)
325*8975f5c5SAndroid Build Coastguard Worker             {
326*8975f5c5SAndroid Build Coastguard Worker                 newSize *= 2;
327*8975f5c5SAndroid Build Coastguard Worker             }
328*8975f5c5SAndroid Build Coastguard Worker 
329*8975f5c5SAndroid Build Coastguard Worker             ResourceType **oldResources = mFlatResources;
330*8975f5c5SAndroid Build Coastguard Worker 
331*8975f5c5SAndroid Build Coastguard Worker             mFlatResources = new ResourceType *[newSize];
332*8975f5c5SAndroid Build Coastguard Worker             memset(&mFlatResources[mFlatResourcesSize], kInvalidPointer,
333*8975f5c5SAndroid Build Coastguard Worker                    (newSize - mFlatResourcesSize) * sizeof(mFlatResources[0]));
334*8975f5c5SAndroid Build Coastguard Worker             memcpy(mFlatResources, oldResources, mFlatResourcesSize * sizeof(mFlatResources[0]));
335*8975f5c5SAndroid Build Coastguard Worker             mFlatResourcesSize = newSize;
336*8975f5c5SAndroid Build Coastguard Worker             delete[] oldResources;
337*8975f5c5SAndroid Build Coastguard Worker         }
338*8975f5c5SAndroid Build Coastguard Worker         ASSERT(mFlatResourcesSize > handle);
339*8975f5c5SAndroid Build Coastguard Worker         mFlatResources[handle] = resource;
340*8975f5c5SAndroid Build Coastguard Worker     }
341*8975f5c5SAndroid Build Coastguard Worker     else
342*8975f5c5SAndroid Build Coastguard Worker     {
343*8975f5c5SAndroid Build Coastguard Worker         std::lock_guard<Mutex> lock(mMutex);
344*8975f5c5SAndroid Build Coastguard Worker         mHashedResources[handle] = resource;
345*8975f5c5SAndroid Build Coastguard Worker     }
346*8975f5c5SAndroid Build Coastguard Worker }
347*8975f5c5SAndroid Build Coastguard Worker 
348*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
349*8975f5c5SAndroid Build Coastguard Worker typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::begin()
350*8975f5c5SAndroid Build Coastguard Worker     const
351*8975f5c5SAndroid Build Coastguard Worker {
352*8975f5c5SAndroid Build Coastguard Worker     return Iterator(*this, nextResource(0, true), mHashedResources.begin(), true);
353*8975f5c5SAndroid Build Coastguard Worker }
354*8975f5c5SAndroid Build Coastguard Worker 
355*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
356*8975f5c5SAndroid Build Coastguard Worker typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::end() const
357*8975f5c5SAndroid Build Coastguard Worker {
358*8975f5c5SAndroid Build Coastguard Worker     return Iterator(*this, static_cast<GLuint>(mFlatResourcesSize), mHashedResources.end(), true);
359*8975f5c5SAndroid Build Coastguard Worker }
360*8975f5c5SAndroid Build Coastguard Worker 
361*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
362*8975f5c5SAndroid Build Coastguard Worker typename ResourceMap<ResourceType, IDType>::Iterator
363*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::beginWithNull() const
364*8975f5c5SAndroid Build Coastguard Worker {
365*8975f5c5SAndroid Build Coastguard Worker     return Iterator(*this, nextResource(0, false), mHashedResources.begin(), false);
366*8975f5c5SAndroid Build Coastguard Worker }
367*8975f5c5SAndroid Build Coastguard Worker 
368*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
369*8975f5c5SAndroid Build Coastguard Worker typename ResourceMap<ResourceType, IDType>::Iterator
370*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::endWithNull() const
371*8975f5c5SAndroid Build Coastguard Worker {
372*8975f5c5SAndroid Build Coastguard Worker     return Iterator(*this, static_cast<GLuint>(mFlatResourcesSize), mHashedResources.end(), false);
373*8975f5c5SAndroid Build Coastguard Worker }
374*8975f5c5SAndroid Build Coastguard Worker 
375*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
376*8975f5c5SAndroid Build Coastguard Worker bool UnsafeResourceMapIter<ResourceType, IDType>::empty() const
377*8975f5c5SAndroid Build Coastguard Worker {
378*8975f5c5SAndroid Build Coastguard Worker     return begin() == end();
379*8975f5c5SAndroid Build Coastguard Worker }
380*8975f5c5SAndroid Build Coastguard Worker 
381*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
382*8975f5c5SAndroid Build Coastguard Worker void ResourceMap<ResourceType, IDType>::clear()
383*8975f5c5SAndroid Build Coastguard Worker {
384*8975f5c5SAndroid Build Coastguard Worker     // No need for a lock as this is only called on destruction.
385*8975f5c5SAndroid Build Coastguard Worker     memset(mFlatResources, kInvalidPointer, kInitialFlatResourcesSize * sizeof(mFlatResources[0]));
386*8975f5c5SAndroid Build Coastguard Worker     mFlatResourcesSize = kInitialFlatResourcesSize;
387*8975f5c5SAndroid Build Coastguard Worker     mHashedResources.clear();
388*8975f5c5SAndroid Build Coastguard Worker }
389*8975f5c5SAndroid Build Coastguard Worker 
390*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
391*8975f5c5SAndroid Build Coastguard Worker GLuint ResourceMap<ResourceType, IDType>::nextResource(size_t flatIndex, bool skipNulls) const
392*8975f5c5SAndroid Build Coastguard Worker {
393*8975f5c5SAndroid Build Coastguard Worker     // This function is only used by the iterators, access to which is marked by
394*8975f5c5SAndroid Build Coastguard Worker     // UnsafeResourceMapIter.  Locking is the responsibility of the caller.
395*8975f5c5SAndroid Build Coastguard Worker     for (size_t index = flatIndex; index < mFlatResourcesSize; index++)
396*8975f5c5SAndroid Build Coastguard Worker     {
397*8975f5c5SAndroid Build Coastguard Worker         if ((mFlatResources[index] != nullptr || !skipNulls) &&
398*8975f5c5SAndroid Build Coastguard Worker             mFlatResources[index] != InvalidPointer())
399*8975f5c5SAndroid Build Coastguard Worker         {
400*8975f5c5SAndroid Build Coastguard Worker             return static_cast<GLuint>(index);
401*8975f5c5SAndroid Build Coastguard Worker         }
402*8975f5c5SAndroid Build Coastguard Worker     }
403*8975f5c5SAndroid Build Coastguard Worker     return static_cast<GLuint>(mFlatResourcesSize);
404*8975f5c5SAndroid Build Coastguard Worker }
405*8975f5c5SAndroid Build Coastguard Worker 
406*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
407*8975f5c5SAndroid Build Coastguard Worker // static
408*8975f5c5SAndroid Build Coastguard Worker ResourceType *ResourceMap<ResourceType, IDType>::InvalidPointer()
409*8975f5c5SAndroid Build Coastguard Worker {
410*8975f5c5SAndroid Build Coastguard Worker     return reinterpret_cast<ResourceType *>(kInvalidPointer);
411*8975f5c5SAndroid Build Coastguard Worker }
412*8975f5c5SAndroid Build Coastguard Worker 
413*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
414*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::Iterator::Iterator(
415*8975f5c5SAndroid Build Coastguard Worker     const ResourceMap &origin,
416*8975f5c5SAndroid Build Coastguard Worker     GLuint flatIndex,
417*8975f5c5SAndroid Build Coastguard Worker     typename ResourceMap<ResourceType, IDType>::HashMap::const_iterator hashIndex,
418*8975f5c5SAndroid Build Coastguard Worker     bool skipNulls)
419*8975f5c5SAndroid Build Coastguard Worker     : mOrigin(origin), mFlatIndex(flatIndex), mHashIndex(hashIndex), mSkipNulls(skipNulls)
420*8975f5c5SAndroid Build Coastguard Worker {
421*8975f5c5SAndroid Build Coastguard Worker     updateValue();
422*8975f5c5SAndroid Build Coastguard Worker }
423*8975f5c5SAndroid Build Coastguard Worker 
424*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
425*8975f5c5SAndroid Build Coastguard Worker bool ResourceMap<ResourceType, IDType>::Iterator::operator==(const Iterator &other) const
426*8975f5c5SAndroid Build Coastguard Worker {
427*8975f5c5SAndroid Build Coastguard Worker     return (mFlatIndex == other.mFlatIndex && mHashIndex == other.mHashIndex);
428*8975f5c5SAndroid Build Coastguard Worker }
429*8975f5c5SAndroid Build Coastguard Worker 
430*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
431*8975f5c5SAndroid Build Coastguard Worker bool ResourceMap<ResourceType, IDType>::Iterator::operator!=(const Iterator &other) const
432*8975f5c5SAndroid Build Coastguard Worker {
433*8975f5c5SAndroid Build Coastguard Worker     return !(*this == other);
434*8975f5c5SAndroid Build Coastguard Worker }
435*8975f5c5SAndroid Build Coastguard Worker 
436*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
437*8975f5c5SAndroid Build Coastguard Worker typename ResourceMap<ResourceType, IDType>::Iterator &
438*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::Iterator::operator++()
439*8975f5c5SAndroid Build Coastguard Worker {
440*8975f5c5SAndroid Build Coastguard Worker     if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize))
441*8975f5c5SAndroid Build Coastguard Worker     {
442*8975f5c5SAndroid Build Coastguard Worker         mFlatIndex = mOrigin.nextResource(mFlatIndex + 1, mSkipNulls);
443*8975f5c5SAndroid Build Coastguard Worker     }
444*8975f5c5SAndroid Build Coastguard Worker     else
445*8975f5c5SAndroid Build Coastguard Worker     {
446*8975f5c5SAndroid Build Coastguard Worker         mHashIndex++;
447*8975f5c5SAndroid Build Coastguard Worker     }
448*8975f5c5SAndroid Build Coastguard Worker     updateValue();
449*8975f5c5SAndroid Build Coastguard Worker     return *this;
450*8975f5c5SAndroid Build Coastguard Worker }
451*8975f5c5SAndroid Build Coastguard Worker 
452*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
453*8975f5c5SAndroid Build Coastguard Worker const typename ResourceMap<ResourceType, IDType>::IndexAndResource *
454*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::Iterator::operator->() const
455*8975f5c5SAndroid Build Coastguard Worker {
456*8975f5c5SAndroid Build Coastguard Worker     return &mValue;
457*8975f5c5SAndroid Build Coastguard Worker }
458*8975f5c5SAndroid Build Coastguard Worker 
459*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
460*8975f5c5SAndroid Build Coastguard Worker const typename ResourceMap<ResourceType, IDType>::IndexAndResource &
461*8975f5c5SAndroid Build Coastguard Worker ResourceMap<ResourceType, IDType>::Iterator::operator*() const
462*8975f5c5SAndroid Build Coastguard Worker {
463*8975f5c5SAndroid Build Coastguard Worker     return mValue;
464*8975f5c5SAndroid Build Coastguard Worker }
465*8975f5c5SAndroid Build Coastguard Worker 
466*8975f5c5SAndroid Build Coastguard Worker template <typename ResourceType, typename IDType>
467*8975f5c5SAndroid Build Coastguard Worker void ResourceMap<ResourceType, IDType>::Iterator::updateValue()
468*8975f5c5SAndroid Build Coastguard Worker {
469*8975f5c5SAndroid Build Coastguard Worker     if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize))
470*8975f5c5SAndroid Build Coastguard Worker     {
471*8975f5c5SAndroid Build Coastguard Worker         mValue.first  = mFlatIndex;
472*8975f5c5SAndroid Build Coastguard Worker         mValue.second = mOrigin.mFlatResources[mFlatIndex];
473*8975f5c5SAndroid Build Coastguard Worker     }
474*8975f5c5SAndroid Build Coastguard Worker     else if (mHashIndex != mOrigin.mHashedResources.end())
475*8975f5c5SAndroid Build Coastguard Worker     {
476*8975f5c5SAndroid Build Coastguard Worker         mValue.first  = mHashIndex->first;
477*8975f5c5SAndroid Build Coastguard Worker         mValue.second = mHashIndex->second;
478*8975f5c5SAndroid Build Coastguard Worker     }
479*8975f5c5SAndroid Build Coastguard Worker }
480*8975f5c5SAndroid Build Coastguard Worker 
481*8975f5c5SAndroid Build Coastguard Worker }  // namespace gl
482*8975f5c5SAndroid Build Coastguard Worker 
483*8975f5c5SAndroid Build Coastguard Worker #endif  // LIBANGLE_RESOURCE_MAP_H_
484