ResourceMap.h (10524B)
1 // 2 // Copyright 2017 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // ResourceMap: 7 // An optimized resource map which packs the first set of allocated objects into a 8 // flat array, and then falls back to an unordered map for the higher handle values. 9 // 10 11 #ifndef LIBANGLE_RESOURCE_MAP_H_ 12 #define LIBANGLE_RESOURCE_MAP_H_ 13 14 #include "libANGLE/angletypes.h" 15 16 namespace gl 17 { 18 19 template <typename ResourceType, typename IDType> 20 class ResourceMap final : angle::NonCopyable 21 { 22 public: 23 ResourceMap(); 24 ~ResourceMap(); 25 26 ANGLE_INLINE ResourceType *query(IDType id) const 27 { 28 GLuint handle = GetIDValue(id); 29 if (handle < mFlatResourcesSize) 30 { 31 ResourceType *value = mFlatResources[handle]; 32 return (value == InvalidPointer() ? nullptr : value); 33 } 34 auto it = mHashedResources.find(handle); 35 return (it == mHashedResources.end() ? nullptr : it->second); 36 } 37 38 // Returns true if the handle was reserved. Not necessarily if the resource is created. 39 bool contains(IDType id) const; 40 41 // Returns the element that was at this location. 42 bool erase(IDType id, ResourceType **resourceOut); 43 44 void assign(IDType id, ResourceType *resource); 45 46 // Clears the map. 47 void clear(); 48 49 using IndexAndResource = std::pair<GLuint, ResourceType *>; 50 using HashMap = angle::HashMap<GLuint, ResourceType *>; 51 52 class Iterator final 53 { 54 public: 55 bool operator==(const Iterator &other) const; 56 bool operator!=(const Iterator &other) const; 57 Iterator &operator++(); 58 const IndexAndResource *operator->() const; 59 const IndexAndResource &operator*() const; 60 61 private: 62 friend class ResourceMap; 63 Iterator(const ResourceMap &origin, 64 GLuint flatIndex, 65 typename HashMap::const_iterator hashIndex, 66 bool skipNulls); 67 void updateValue(); 68 69 const ResourceMap &mOrigin; 70 GLuint mFlatIndex; 71 typename HashMap::const_iterator mHashIndex; 72 IndexAndResource mValue; 73 bool mSkipNulls; 74 }; 75 76 // null values represent reserved handles. 77 Iterator begin() const; 78 Iterator end() const; 79 Iterator find(IDType handle) const; 80 81 Iterator beginWithNull() const; 82 Iterator endWithNull() const; 83 84 // Not a constant-time operation, should only be used for verification. 85 bool empty() const; 86 87 private: 88 friend class Iterator; 89 90 GLuint nextResource(size_t flatIndex, bool skipNulls) const; 91 92 // constexpr methods cannot contain reinterpret_cast, so we need a static method. 93 static ResourceType *InvalidPointer(); 94 static constexpr intptr_t kInvalidPointer = static_cast<intptr_t>(-1); 95 96 // Start with 32 maximum elements in the map, which can grow. 97 static constexpr size_t kInitialFlatResourcesSize = 0x20; 98 99 // Experimental testing suggests that 16k is a reasonable upper limit. 100 static constexpr size_t kFlatResourcesLimit = 0x4000; 101 102 // Size of one map element. 103 static constexpr size_t kElementSize = sizeof(ResourceType *); 104 105 size_t mFlatResourcesSize; 106 ResourceType **mFlatResources; 107 108 // A map of GL objects indexed by object ID. 109 HashMap mHashedResources; 110 }; 111 112 template <typename ResourceType, typename IDType> 113 ResourceMap<ResourceType, IDType>::ResourceMap() 114 : mFlatResourcesSize(kInitialFlatResourcesSize), 115 mFlatResources(new ResourceType *[kInitialFlatResourcesSize]) 116 { 117 memset(mFlatResources, kInvalidPointer, mFlatResourcesSize * kElementSize); 118 } 119 120 template <typename ResourceType, typename IDType> 121 ResourceMap<ResourceType, IDType>::~ResourceMap() 122 { 123 ASSERT(empty()); 124 delete[] mFlatResources; 125 } 126 127 template <typename ResourceType, typename IDType> 128 ANGLE_INLINE bool ResourceMap<ResourceType, IDType>::contains(IDType id) const 129 { 130 GLuint handle = GetIDValue(id); 131 if (handle < mFlatResourcesSize) 132 { 133 return (mFlatResources[handle] != InvalidPointer()); 134 } 135 return (mHashedResources.find(handle) != mHashedResources.end()); 136 } 137 138 template <typename ResourceType, typename IDType> 139 bool ResourceMap<ResourceType, IDType>::erase(IDType id, ResourceType **resourceOut) 140 { 141 GLuint handle = GetIDValue(id); 142 if (handle < mFlatResourcesSize) 143 { 144 auto &value = mFlatResources[handle]; 145 if (value == InvalidPointer()) 146 { 147 return false; 148 } 149 *resourceOut = value; 150 value = InvalidPointer(); 151 } 152 else 153 { 154 auto it = mHashedResources.find(handle); 155 if (it == mHashedResources.end()) 156 { 157 return false; 158 } 159 *resourceOut = it->second; 160 mHashedResources.erase(it); 161 } 162 return true; 163 } 164 165 template <typename ResourceType, typename IDType> 166 void ResourceMap<ResourceType, IDType>::assign(IDType id, ResourceType *resource) 167 { 168 GLuint handle = GetIDValue(id); 169 if (handle < kFlatResourcesLimit) 170 { 171 if (handle >= mFlatResourcesSize) 172 { 173 // Use power-of-two. 174 size_t newSize = mFlatResourcesSize; 175 while (newSize <= handle) 176 { 177 newSize *= 2; 178 } 179 180 ResourceType **oldResources = mFlatResources; 181 182 mFlatResources = new ResourceType *[newSize]; 183 memset(&mFlatResources[mFlatResourcesSize], kInvalidPointer, 184 (newSize - mFlatResourcesSize) * kElementSize); 185 memcpy(mFlatResources, oldResources, mFlatResourcesSize * kElementSize); 186 mFlatResourcesSize = newSize; 187 delete[] oldResources; 188 } 189 ASSERT(mFlatResourcesSize > handle); 190 mFlatResources[handle] = resource; 191 } 192 else 193 { 194 mHashedResources[handle] = resource; 195 } 196 } 197 198 template <typename ResourceType, typename IDType> 199 typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::begin() 200 const 201 { 202 return Iterator(*this, nextResource(0, true), mHashedResources.begin(), true); 203 } 204 205 template <typename ResourceType, typename IDType> 206 typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::end() const 207 { 208 return Iterator(*this, static_cast<GLuint>(mFlatResourcesSize), mHashedResources.end(), true); 209 } 210 211 template <typename ResourceType, typename IDType> 212 typename ResourceMap<ResourceType, IDType>::Iterator 213 ResourceMap<ResourceType, IDType>::beginWithNull() const 214 { 215 return Iterator(*this, nextResource(0, false), mHashedResources.begin(), false); 216 } 217 218 template <typename ResourceType, typename IDType> 219 typename ResourceMap<ResourceType, IDType>::Iterator 220 ResourceMap<ResourceType, IDType>::endWithNull() const 221 { 222 return Iterator(*this, static_cast<GLuint>(mFlatResourcesSize), mHashedResources.end(), false); 223 } 224 225 template <typename ResourceType, typename IDType> 226 typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::find( 227 IDType handle) const 228 { 229 if (handle < mFlatResourcesSize) 230 { 231 return (mFlatResources[handle] != InvalidPointer() 232 ? Iterator(handle, mHashedResources.begin()) 233 : end()); 234 } 235 else 236 { 237 return mHashedResources.find(handle); 238 } 239 } 240 241 template <typename ResourceType, typename IDType> 242 bool ResourceMap<ResourceType, IDType>::empty() const 243 { 244 return (begin() == end()); 245 } 246 247 template <typename ResourceType, typename IDType> 248 void ResourceMap<ResourceType, IDType>::clear() 249 { 250 memset(mFlatResources, kInvalidPointer, kInitialFlatResourcesSize * kElementSize); 251 mFlatResourcesSize = kInitialFlatResourcesSize; 252 mHashedResources.clear(); 253 } 254 255 template <typename ResourceType, typename IDType> 256 GLuint ResourceMap<ResourceType, IDType>::nextResource(size_t flatIndex, bool skipNulls) const 257 { 258 for (size_t index = flatIndex; index < mFlatResourcesSize; index++) 259 { 260 if ((mFlatResources[index] != nullptr || !skipNulls) && 261 mFlatResources[index] != InvalidPointer()) 262 { 263 return static_cast<GLuint>(index); 264 } 265 } 266 return static_cast<GLuint>(mFlatResourcesSize); 267 } 268 269 template <typename ResourceType, typename IDType> 270 // static 271 ResourceType *ResourceMap<ResourceType, IDType>::InvalidPointer() 272 { 273 return reinterpret_cast<ResourceType *>(kInvalidPointer); 274 } 275 276 template <typename ResourceType, typename IDType> 277 ResourceMap<ResourceType, IDType>::Iterator::Iterator( 278 const ResourceMap &origin, 279 GLuint flatIndex, 280 typename ResourceMap<ResourceType, IDType>::HashMap::const_iterator hashIndex, 281 bool skipNulls) 282 : mOrigin(origin), mFlatIndex(flatIndex), mHashIndex(hashIndex), mSkipNulls(skipNulls) 283 { 284 updateValue(); 285 } 286 287 template <typename ResourceType, typename IDType> 288 bool ResourceMap<ResourceType, IDType>::Iterator::operator==(const Iterator &other) const 289 { 290 return (mFlatIndex == other.mFlatIndex && mHashIndex == other.mHashIndex); 291 } 292 293 template <typename ResourceType, typename IDType> 294 bool ResourceMap<ResourceType, IDType>::Iterator::operator!=(const Iterator &other) const 295 { 296 return !(*this == other); 297 } 298 299 template <typename ResourceType, typename IDType> 300 typename ResourceMap<ResourceType, IDType>::Iterator & 301 ResourceMap<ResourceType, IDType>::Iterator::operator++() 302 { 303 if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize)) 304 { 305 mFlatIndex = mOrigin.nextResource(mFlatIndex + 1, mSkipNulls); 306 } 307 else 308 { 309 mHashIndex++; 310 } 311 updateValue(); 312 return *this; 313 } 314 315 template <typename ResourceType, typename IDType> 316 const typename ResourceMap<ResourceType, IDType>::IndexAndResource * 317 ResourceMap<ResourceType, IDType>::Iterator::operator->() const 318 { 319 return &mValue; 320 } 321 322 template <typename ResourceType, typename IDType> 323 const typename ResourceMap<ResourceType, IDType>::IndexAndResource & 324 ResourceMap<ResourceType, IDType>::Iterator::operator*() const 325 { 326 return mValue; 327 } 328 329 template <typename ResourceType, typename IDType> 330 void ResourceMap<ResourceType, IDType>::Iterator::updateValue() 331 { 332 if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize)) 333 { 334 mValue.first = mFlatIndex; 335 mValue.second = mOrigin.mFlatResources[mFlatIndex]; 336 } 337 else if (mHashIndex != mOrigin.mHashedResources.end()) 338 { 339 mValue.first = mHashIndex->first; 340 mValue.second = mHashIndex->second; 341 } 342 } 343 344 } // namespace gl 345 346 #endif // LIBANGLE_RESOURCE_MAP_H_