// // Copyright 2017 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ResourceMap: // An optimized resource map which packs the first set of allocated objects into a // flat array, and then falls back to an unordered map for the higher handle values. // #ifndef LIBANGLE_RESOURCE_MAP_H_ #define LIBANGLE_RESOURCE_MAP_H_ #include "libANGLE/angletypes.h" namespace gl { template class ResourceMap final : angle::NonCopyable { public: ResourceMap(); ~ResourceMap(); ANGLE_INLINE ResourceType *query(IDType id) const { GLuint handle = GetIDValue(id); if (handle < mFlatResourcesSize) { ResourceType *value = mFlatResources[handle]; return (value == InvalidPointer() ? nullptr : value); } auto it = mHashedResources.find(handle); return (it == mHashedResources.end() ? nullptr : it->second); } // Returns true if the handle was reserved. Not necessarily if the resource is created. bool contains(IDType id) const; // Returns the element that was at this location. bool erase(IDType id, ResourceType **resourceOut); void assign(IDType id, ResourceType *resource); // Clears the map. void clear(); using IndexAndResource = std::pair; using HashMap = std::unordered_map; class Iterator final { public: bool operator==(const Iterator &other) const; bool operator!=(const Iterator &other) const; Iterator &operator++(); const IndexAndResource *operator->() const; const IndexAndResource &operator*() const; private: friend class ResourceMap; Iterator(const ResourceMap &origin, GLuint flatIndex, typename HashMap::const_iterator hashIndex, bool skipNulls); void updateValue(); const ResourceMap &mOrigin; GLuint mFlatIndex; typename HashMap::const_iterator mHashIndex; IndexAndResource mValue; bool mSkipNulls; }; // null values represent reserved handles. Iterator begin() const; Iterator end() const; Iterator find(IDType handle) const; Iterator beginWithNull() const; Iterator endWithNull() const; // Not a constant-time operation, should only be used for verification. bool empty() const; private: friend class Iterator; GLuint nextResource(size_t flatIndex, bool skipNulls) const; // constexpr methods cannot contain reinterpret_cast, so we need a static method. static ResourceType *InvalidPointer(); static constexpr intptr_t kInvalidPointer = static_cast(-1); // Start with 32 maximum elements in the map, which can grow. static constexpr size_t kInitialFlatResourcesSize = 0x20; // Experimental testing suggests that 16k is a reasonable upper limit. static constexpr size_t kFlatResourcesLimit = 0x4000; // Size of one map element. static constexpr size_t kElementSize = sizeof(ResourceType *); size_t mFlatResourcesSize; ResourceType **mFlatResources; // A map of GL objects indexed by object ID. HashMap mHashedResources; }; template ResourceMap::ResourceMap() : mFlatResourcesSize(kInitialFlatResourcesSize), mFlatResources(new ResourceType *[kInitialFlatResourcesSize]) { memset(mFlatResources, kInvalidPointer, mFlatResourcesSize * kElementSize); } template ResourceMap::~ResourceMap() { ASSERT(empty()); delete[] mFlatResources; } template ANGLE_INLINE bool ResourceMap::contains(IDType id) const { GLuint handle = GetIDValue(id); if (handle < mFlatResourcesSize) { return (mFlatResources[handle] != InvalidPointer()); } return (mHashedResources.find(handle) != mHashedResources.end()); } template bool ResourceMap::erase(IDType id, ResourceType **resourceOut) { GLuint handle = GetIDValue(id); if (handle < mFlatResourcesSize) { auto &value = mFlatResources[handle]; if (value == InvalidPointer()) { return false; } *resourceOut = value; value = InvalidPointer(); } else { auto it = mHashedResources.find(handle); if (it == mHashedResources.end()) { return false; } *resourceOut = it->second; mHashedResources.erase(it); } return true; } template void ResourceMap::assign(IDType id, ResourceType *resource) { GLuint handle = GetIDValue(id); if (handle < kFlatResourcesLimit) { if (handle >= mFlatResourcesSize) { // Use power-of-two. size_t newSize = mFlatResourcesSize; while (newSize <= handle) { newSize *= 2; } ResourceType **oldResources = mFlatResources; mFlatResources = new ResourceType *[newSize]; memset(&mFlatResources[mFlatResourcesSize], kInvalidPointer, (newSize - mFlatResourcesSize) * kElementSize); memcpy(mFlatResources, oldResources, mFlatResourcesSize * kElementSize); mFlatResourcesSize = newSize; delete[] oldResources; } ASSERT(mFlatResourcesSize > handle); mFlatResources[handle] = resource; } else { mHashedResources[handle] = resource; } } template typename ResourceMap::Iterator ResourceMap::begin() const { return Iterator(*this, nextResource(0, true), mHashedResources.begin(), true); } template typename ResourceMap::Iterator ResourceMap::end() const { return Iterator(*this, static_cast(mFlatResourcesSize), mHashedResources.end(), true); } template typename ResourceMap::Iterator ResourceMap::beginWithNull() const { return Iterator(*this, nextResource(0, false), mHashedResources.begin(), false); } template typename ResourceMap::Iterator ResourceMap::endWithNull() const { return Iterator(*this, static_cast(mFlatResourcesSize), mHashedResources.end(), false); } template typename ResourceMap::Iterator ResourceMap::find( IDType handle) const { if (handle < mFlatResourcesSize) { return (mFlatResources[handle] != InvalidPointer() ? Iterator(handle, mHashedResources.begin()) : end()); } else { return mHashedResources.find(handle); } } template bool ResourceMap::empty() const { return (begin() == end()); } template void ResourceMap::clear() { memset(mFlatResources, kInvalidPointer, kInitialFlatResourcesSize * kElementSize); mFlatResourcesSize = kInitialFlatResourcesSize; mHashedResources.clear(); } template GLuint ResourceMap::nextResource(size_t flatIndex, bool skipNulls) const { for (size_t index = flatIndex; index < mFlatResourcesSize; index++) { if ((mFlatResources[index] != nullptr || !skipNulls) && mFlatResources[index] != InvalidPointer()) { return static_cast(index); } } return static_cast(mFlatResourcesSize); } template // static ResourceType *ResourceMap::InvalidPointer() { return reinterpret_cast(kInvalidPointer); } template ResourceMap::Iterator::Iterator( const ResourceMap &origin, GLuint flatIndex, typename ResourceMap::HashMap::const_iterator hashIndex, bool skipNulls) : mOrigin(origin), mFlatIndex(flatIndex), mHashIndex(hashIndex), mSkipNulls(skipNulls) { updateValue(); } template bool ResourceMap::Iterator::operator==(const Iterator &other) const { return (mFlatIndex == other.mFlatIndex && mHashIndex == other.mHashIndex); } template bool ResourceMap::Iterator::operator!=(const Iterator &other) const { return !(*this == other); } template typename ResourceMap::Iterator &ResourceMap::Iterator:: operator++() { if (mFlatIndex < static_cast(mOrigin.mFlatResourcesSize)) { mFlatIndex = mOrigin.nextResource(mFlatIndex + 1, mSkipNulls); } else { mHashIndex++; } updateValue(); return *this; } template const typename ResourceMap::IndexAndResource *ResourceMap::Iterator::operator->() const { return &mValue; } template const typename ResourceMap::IndexAndResource &ResourceMap::Iterator::operator*() const { return mValue; } template void ResourceMap::Iterator::updateValue() { if (mFlatIndex < static_cast(mOrigin.mFlatResourcesSize)) { mValue.first = mFlatIndex; mValue.second = mOrigin.mFlatResources[mFlatIndex]; } else if (mHashIndex != mOrigin.mHashedResources.end()) { mValue.first = mHashIndex->first; mValue.second = mHashIndex->second; } } } // namespace gl #endif // LIBANGLE_RESOURCE_MAP_H_