blob: abf368dfc712ae30c06287168d4e5afcaa0b4c39 [file] [log] [blame]
//
// 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 <typename ResourceType, typename IDType>
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<GLuint, ResourceType *>;
using HashMap = std::unordered_map<GLuint, ResourceType *>;
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);
void updateValue();
const ResourceMap &mOrigin;
GLuint mFlatIndex;
typename HashMap::const_iterator mHashIndex;
IndexAndResource mValue;
};
// null values represent reserved handles.
Iterator begin() const;
Iterator end() const;
Iterator find(IDType handle) const;
// Not a constant-time operation, should only be used for verification.
bool empty() const;
private:
friend class Iterator;
GLuint nextNonNullResource(size_t flatIndex) const;
// constexpr methods cannot contain reinterpret_cast, so we need a static method.
static ResourceType *InvalidPointer();
static constexpr intptr_t kInvalidPointer = static_cast<intptr_t>(-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 <typename ResourceType, typename IDType>
ResourceMap<ResourceType, IDType>::ResourceMap()
: mFlatResourcesSize(kInitialFlatResourcesSize),
mFlatResources(new ResourceType *[kInitialFlatResourcesSize])
{
memset(mFlatResources, kInvalidPointer, mFlatResourcesSize * kElementSize);
}
template <typename ResourceType, typename IDType>
ResourceMap<ResourceType, IDType>::~ResourceMap()
{
ASSERT(empty());
delete[] mFlatResources;
}
template <typename ResourceType, typename IDType>
ANGLE_INLINE bool ResourceMap<ResourceType, IDType>::contains(IDType id) const
{
GLuint handle = GetIDValue(id);
if (handle < mFlatResourcesSize)
{
return (mFlatResources[handle] != InvalidPointer());
}
return (mHashedResources.find(handle) != mHashedResources.end());
}
template <typename ResourceType, typename IDType>
bool ResourceMap<ResourceType, IDType>::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 <typename ResourceType, typename IDType>
void ResourceMap<ResourceType, IDType>::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 ResourceType, typename IDType>
typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::begin()
const
{
return Iterator(*this, nextNonNullResource(0), mHashedResources.begin());
}
template <typename ResourceType, typename IDType>
typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::end() const
{
return Iterator(*this, static_cast<GLuint>(mFlatResourcesSize), mHashedResources.end());
}
template <typename ResourceType, typename IDType>
typename ResourceMap<ResourceType, IDType>::Iterator ResourceMap<ResourceType, IDType>::find(
IDType handle) const
{
if (handle < mFlatResourcesSize)
{
return (mFlatResources[handle] != InvalidPointer()
? Iterator(handle, mHashedResources.begin())
: end());
}
else
{
return mHashedResources.find(handle);
}
}
template <typename ResourceType, typename IDType>
bool ResourceMap<ResourceType, IDType>::empty() const
{
return (begin() == end());
}
template <typename ResourceType, typename IDType>
void ResourceMap<ResourceType, IDType>::clear()
{
memset(mFlatResources, kInvalidPointer, kInitialFlatResourcesSize * kElementSize);
mFlatResourcesSize = kInitialFlatResourcesSize;
mHashedResources.clear();
}
template <typename ResourceType, typename IDType>
GLuint ResourceMap<ResourceType, IDType>::nextNonNullResource(size_t flatIndex) const
{
for (size_t index = flatIndex; index < mFlatResourcesSize; index++)
{
if (mFlatResources[index] != nullptr && mFlatResources[index] != InvalidPointer())
{
return static_cast<GLuint>(index);
}
}
return static_cast<GLuint>(mFlatResourcesSize);
}
template <typename ResourceType, typename IDType>
// static
ResourceType *ResourceMap<ResourceType, IDType>::InvalidPointer()
{
return reinterpret_cast<ResourceType *>(kInvalidPointer);
}
template <typename ResourceType, typename IDType>
ResourceMap<ResourceType, IDType>::Iterator::Iterator(
const ResourceMap &origin,
GLuint flatIndex,
typename ResourceMap<ResourceType, IDType>::HashMap::const_iterator hashIndex)
: mOrigin(origin), mFlatIndex(flatIndex), mHashIndex(hashIndex)
{
updateValue();
}
template <typename ResourceType, typename IDType>
bool ResourceMap<ResourceType, IDType>::Iterator::operator==(const Iterator &other) const
{
return (mFlatIndex == other.mFlatIndex && mHashIndex == other.mHashIndex);
}
template <typename ResourceType, typename IDType>
bool ResourceMap<ResourceType, IDType>::Iterator::operator!=(const Iterator &other) const
{
return !(*this == other);
}
template <typename ResourceType, typename IDType>
typename ResourceMap<ResourceType, IDType>::Iterator &ResourceMap<ResourceType, IDType>::Iterator::
operator++()
{
if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResourcesSize))
{
mFlatIndex = mOrigin.nextNonNullResource(mFlatIndex + 1);
}
else
{
mHashIndex++;
}
updateValue();
return *this;
}
template <typename ResourceType, typename IDType>
const typename ResourceMap<ResourceType, IDType>::IndexAndResource
*ResourceMap<ResourceType, IDType>::Iterator::operator->() const
{
return &mValue;
}
template <typename ResourceType, typename IDType>
const typename ResourceMap<ResourceType, IDType>::IndexAndResource
&ResourceMap<ResourceType, IDType>::Iterator::operator*() const
{
return mValue;
}
template <typename ResourceType, typename IDType>
void ResourceMap<ResourceType, IDType>::Iterator::updateValue()
{
if (mFlatIndex < static_cast<GLuint>(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_