Commit 96a483bc by Jamie Madill Committed by Commit Bot

Add a ResourceMap class for faster GL resource maps.

This gives a very fast query time for handles that are within a fixed range. For WebGL, where we don't allow create-on-bind, this will be 100% of the time, unless we create a very large number of resources. It is implemented as a two-tier map - the first uses a flat array to index into a handle buffer. The second tier uses a map for out-of- range values. BUG=angleproject:1458 Change-Id: I421bb3725cf523918cdfdbfaab035ad0dd3bf82d Reviewed-on: https://chromium-review.googlesource.com/544684 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent c8a8b843
......@@ -409,6 +409,7 @@ egl::Error Context::onDestroy(const egl::Display *display)
{
SafeDelete(fence.second);
}
mFenceNVMap.clear();
for (auto query : mQueryMap)
{
......@@ -417,6 +418,7 @@ egl::Error Context::onDestroy(const egl::Display *display)
query.second->release(this);
}
}
mQueryMap.clear();
for (auto vertexArray : mVertexArrayMap)
{
......@@ -425,6 +427,7 @@ egl::Error Context::onDestroy(const egl::Display *display)
vertexArray.second->onDestroy(this);
}
}
mVertexArrayMap.clear();
for (auto transformFeedback : mTransformFeedbackMap)
{
......@@ -433,6 +436,7 @@ egl::Error Context::onDestroy(const egl::Display *display)
transformFeedback.second->release(this);
}
}
mTransformFeedbackMap.clear();
for (auto &zeroTexture : mZeroTextures)
{
......@@ -606,8 +610,8 @@ GLuint Context::createPaths(GLsizei range)
GLuint Context::createVertexArray()
{
GLuint vertexArray = mVertexArrayHandleAllocator.allocate();
mVertexArrayMap[vertexArray] = nullptr;
GLuint vertexArray = mVertexArrayHandleAllocator.allocate();
mVertexArrayMap.assign(vertexArray, nullptr);
return vertexArray;
}
......@@ -618,8 +622,8 @@ GLuint Context::createSampler()
GLuint Context::createTransformFeedback()
{
GLuint transformFeedback = mTransformFeedbackAllocator.allocate();
mTransformFeedbackMap[transformFeedback] = nullptr;
GLuint transformFeedback = mTransformFeedbackAllocator.allocate();
mTransformFeedbackMap.assign(transformFeedback, nullptr);
return transformFeedback;
}
......@@ -632,9 +636,7 @@ GLuint Context::createFramebuffer()
GLuint Context::createFenceNV()
{
GLuint handle = mFenceNVHandleAllocator.allocate();
mFenceNVMap[handle] = new FenceNV(mImplementation->createFenceNV());
mFenceNVMap.assign(handle, new FenceNV(mImplementation->createFenceNV()));
return handle;
}
......@@ -642,9 +644,7 @@ GLuint Context::createFenceNV()
GLuint Context::createQuery()
{
GLuint handle = mQueryHandleAllocator.allocate();
mQueryMap[handle] = nullptr;
mQueryMap.assign(handle, nullptr);
return handle;
}
......@@ -790,17 +790,15 @@ void Context::setPathStencilFunc(GLenum func, GLint ref, GLuint mask)
void Context::deleteVertexArray(GLuint vertexArray)
{
auto iter = mVertexArrayMap.find(vertexArray);
if (iter != mVertexArrayMap.end())
VertexArray *vertexArrayObject = nullptr;
if (mVertexArrayMap.erase(vertexArray, &vertexArrayObject))
{
VertexArray *vertexArrayObject = iter->second;
if (vertexArrayObject != nullptr)
{
detachVertexArray(vertexArray);
vertexArrayObject->onDestroy(this);
}
mVertexArrayMap.erase(iter);
mVertexArrayHandleAllocator.release(vertexArray);
}
}
......@@ -822,17 +820,15 @@ void Context::deleteTransformFeedback(GLuint transformFeedback)
return;
}
auto iter = mTransformFeedbackMap.find(transformFeedback);
if (iter != mTransformFeedbackMap.end())
TransformFeedback *transformFeedbackObject = nullptr;
if (mTransformFeedbackMap.erase(transformFeedback, &transformFeedbackObject))
{
TransformFeedback *transformFeedbackObject = iter->second;
if (transformFeedbackObject != nullptr)
{
detachTransformFeedback(transformFeedback);
transformFeedbackObject->release(this);
}
mTransformFeedbackMap.erase(iter);
mTransformFeedbackAllocator.release(transformFeedback);
}
}
......@@ -849,27 +845,24 @@ void Context::deleteFramebuffer(GLuint framebuffer)
void Context::deleteFenceNV(GLuint fence)
{
auto fenceObject = mFenceNVMap.find(fence);
if (fenceObject != mFenceNVMap.end())
FenceNV *fenceObject = nullptr;
if (mFenceNVMap.erase(fence, &fenceObject))
{
mFenceNVHandleAllocator.release(fenceObject->first);
delete fenceObject->second;
mFenceNVMap.erase(fenceObject);
mFenceNVHandleAllocator.release(fence);
delete fenceObject;
}
}
void Context::deleteQuery(GLuint query)
{
auto queryObject = mQueryMap.find(query);
if (queryObject != mQueryMap.end())
Query *queryObject = nullptr;
if (mQueryMap.erase(query, &queryObject))
{
mQueryHandleAllocator.release(queryObject->first);
if (queryObject->second)
mQueryHandleAllocator.release(query);
if (queryObject)
{
queryObject->second->release(this);
queryObject->release(this);
}
mQueryMap.erase(queryObject);
}
}
......@@ -896,8 +889,7 @@ FenceSync *Context::getFenceSync(GLsync handle) const
VertexArray *Context::getVertexArray(GLuint handle) const
{
auto vertexArray = mVertexArrayMap.find(handle);
return (vertexArray != mVertexArrayMap.end()) ? vertexArray->second : nullptr;
return mVertexArrayMap.query(handle);
}
Sampler *Context::getSampler(GLuint handle) const
......@@ -907,8 +899,7 @@ Sampler *Context::getSampler(GLuint handle) const
TransformFeedback *Context::getTransformFeedback(GLuint handle) const
{
auto iter = mTransformFeedbackMap.find(handle);
return (iter != mTransformFeedbackMap.end()) ? iter->second : nullptr;
return mTransformFeedbackMap.query(handle);
}
LabeledObject *Context::getLabeledObject(GLenum identifier, GLuint name) const
......@@ -1270,41 +1261,29 @@ Framebuffer *Context::getFramebuffer(GLuint handle) const
FenceNV *Context::getFenceNV(GLuint handle)
{
auto fence = mFenceNVMap.find(handle);
if (fence == mFenceNVMap.end())
{
return nullptr;
}
else
{
return fence->second;
}
return mFenceNVMap.query(handle);
}
Query *Context::getQuery(GLuint handle, bool create, GLenum type)
{
auto query = mQueryMap.find(handle);
if (query == mQueryMap.end())
if (!mQueryMap.contains(handle))
{
return nullptr;
}
else
Query *query = mQueryMap.query(handle);
if (!query && create)
{
if (!query->second && create)
{
query->second = new Query(mImplementation->createQuery(type), handle);
query->second->addRef();
}
return query->second;
query = new Query(mImplementation->createQuery(type), handle);
query->addRef();
mQueryMap.assign(handle, query);
}
return query;
}
Query *Context::getQuery(GLuint handle) const
{
auto iter = mQueryMap.find(handle);
return (iter != mQueryMap.end()) ? iter->second : nullptr;
return mQueryMap.query(handle);
}
Texture *Context::getTargetTexture(GLenum target) const
......@@ -2309,7 +2288,7 @@ VertexArray *Context::checkVertexArrayAllocation(GLuint vertexArrayHandle)
vertexArray = new VertexArray(mImplementation.get(), vertexArrayHandle,
mCaps.maxVertexAttributes, mCaps.maxVertexAttribBindings);
mVertexArrayMap[vertexArrayHandle] = vertexArray;
mVertexArrayMap.assign(vertexArrayHandle, vertexArray);
}
return vertexArray;
......@@ -2324,7 +2303,7 @@ TransformFeedback *Context::checkTransformFeedbackAllocation(GLuint transformFee
transformFeedback =
new TransformFeedback(mImplementation.get(), transformFeedbackHandle, mCaps);
transformFeedback->addRef();
mTransformFeedbackMap[transformFeedbackHandle] = transformFeedback;
mTransformFeedbackMap.assign(transformFeedbackHandle, transformFeedback);
}
return transformFeedback;
......@@ -2332,14 +2311,14 @@ TransformFeedback *Context::checkTransformFeedbackAllocation(GLuint transformFee
bool Context::isVertexArrayGenerated(GLuint vertexArray)
{
ASSERT(mVertexArrayMap.find(0) != mVertexArrayMap.end());
return mVertexArrayMap.find(vertexArray) != mVertexArrayMap.end();
ASSERT(mVertexArrayMap.contains(0));
return mVertexArrayMap.contains(vertexArray);
}
bool Context::isTransformFeedbackGenerated(GLuint transformFeedback)
{
ASSERT(mTransformFeedbackMap.find(0) != mTransformFeedbackMap.end());
return mTransformFeedbackMap.find(transformFeedback) != mTransformFeedbackMap.end();
ASSERT(mTransformFeedbackMap.contains(0));
return mTransformFeedbackMap.contains(transformFeedback);
}
void Context::detachTexture(GLuint texture)
......
......@@ -22,6 +22,7 @@
#include "libANGLE/Error.h"
#include "libANGLE/HandleAllocator.h"
#include "libANGLE/RefCountObject.h"
#include "libANGLE/ResourceMap.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/Workarounds.h"
#include "libANGLE/angletypes.h"
......
......@@ -128,4 +128,13 @@ void HandleAllocator::reserve(GLuint handle)
mUnallocatedList.insert(placementIt, HandleRange(begin, handle - 1));
}
void HandleAllocator::reset()
{
mUnallocatedList.clear();
mUnallocatedList.push_back(HandleRange(1, std::numeric_limits<GLuint>::max()));
mReleasedList.clear();
mBaseValue = 1;
mNextValue = 1;
}
} // namespace gl
......@@ -24,7 +24,7 @@ class HandleAllocator final : angle::NonCopyable
public:
// Maximum handle = MAX_UINT-1
HandleAllocator();
// Specify maximum handle value
// Specify maximum handle value. Used for testing.
HandleAllocator(GLuint maximumHandleValue);
~HandleAllocator();
......@@ -34,6 +34,7 @@ class HandleAllocator final : angle::NonCopyable
GLuint allocate();
void release(GLuint handle);
void reserve(GLuint handle);
void reset();
private:
GLuint mBaseValue;
......
......@@ -135,4 +135,19 @@ TEST(HandleAllocatorTest, SortedOrderHandle)
EXPECT_EQ(6u, allocatedList[4]);
}
// Tests the reset method.
TEST(HandleAllocatorTest, Reset)
{
gl::HandleAllocator allocator;
for (int iteration = 0; iteration < 1; ++iteration)
{
allocator.reserve(3);
EXPECT_EQ(1u, allocator.allocate());
EXPECT_EQ(2u, allocator.allocate());
EXPECT_EQ(4u, allocator.allocate());
allocator.reset();
}
}
} // anonymous namespace
......@@ -12,10 +12,10 @@
#include "angle_gl.h"
#include "common/angleutils.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/Error.h"
#include "libANGLE/HandleAllocator.h"
#include "libANGLE/HandleRangeAllocator.h"
#include "libANGLE/ResourceMap.h"
namespace rx
{
......@@ -66,7 +66,7 @@ class TypedResourceManager : public ResourceManagerBase<HandleAllocatorType>
bool isHandleGenerated(GLuint handle) const
{
// Zero is always assumed to have been generated implicitly.
return handle == 0 || mObjectMap.find(handle) != mObjectMap.end();
return handle == 0 || mObjectMap.contains(handle);
}
protected:
......@@ -76,11 +76,10 @@ class TypedResourceManager : public ResourceManagerBase<HandleAllocatorType>
template <typename... ArgTypes>
ResourceType *checkObjectAllocation(rx::GLImplFactory *factory, GLuint handle, ArgTypes... args)
{
auto objectMapIter = mObjectMap.find(handle);
if (objectMapIter != mObjectMap.end() && objectMapIter->second != nullptr)
ResourceType *value = mObjectMap.query(handle);
if (value)
{
return objectMapIter->second;
return value;
}
if (handle == 0)
......@@ -88,14 +87,16 @@ class TypedResourceManager : public ResourceManagerBase<HandleAllocatorType>
return nullptr;
}
return allocateObject<ArgTypes...>(objectMapIter, factory, handle, args...);
}
ResourceType *object = ImplT::AllocateNewObject(factory, handle, args...);
template <typename... ArgTypes>
ResourceType *allocateObject(typename ResourceMap<ResourceType>::iterator &objectMapIter,
rx::GLImplFactory *factory,
GLuint handle,
ArgTypes... args);
if (!mObjectMap.contains(handle))
{
this->mHandleAllocator.reserve(handle);
}
mObjectMap.assign(handle, object);
return object;
}
void reset(const Context *context) override;
......@@ -153,7 +154,7 @@ class TextureManager : public TypedResourceManager<Texture, HandleAllocator, Tex
GLuint createTexture();
Texture *getTexture(GLuint handle) const;
void invalidateTextureComplenessCache();
void invalidateTextureComplenessCache() const;
Texture *checkTextureAllocation(rx::GLImplFactory *factory, GLuint handle, GLenum target)
{
......@@ -241,7 +242,7 @@ class FramebufferManager
Framebuffer *getFramebuffer(GLuint handle) const;
void setDefaultFramebuffer(Framebuffer *framebuffer);
void invalidateFramebufferComplenessCache();
void invalidateFramebufferComplenessCache() const;
Framebuffer *checkFramebufferAllocation(rx::GLImplFactory *factory,
const Caps &caps,
......
//
// 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>
class ResourceMap final : angle::NonCopyable
{
public:
ResourceMap();
~ResourceMap();
ResourceType *query(GLuint handle) const;
// Returns true if the handle was reserved. Not necessarily if the resource is created.
bool contains(GLuint handle) const;
// Returns the element that was at this location.
bool erase(GLuint handle, ResourceType **resourceOut);
void assign(GLuint handle, 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(GLuint 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;
std::vector<ResourceType *> mFlatResources;
// A map of GL objects indexed by object ID.
HashMap mHashedResources;
};
template <typename ResourceType>
ResourceMap<ResourceType>::ResourceMap()
: mFlatResources(kInitialFlatResourcesSize, InvalidPointer()), mHashedResources()
{
}
template <typename ResourceType>
ResourceMap<ResourceType>::~ResourceMap()
{
ASSERT(empty());
}
template <typename ResourceType>
ResourceType *ResourceMap<ResourceType>::query(GLuint handle) const
{
if (handle < mFlatResources.size())
{
auto value = mFlatResources[handle];
return (value == InvalidPointer() ? nullptr : value);
}
auto it = mHashedResources.find(handle);
return (it == mHashedResources.end() ? nullptr : it->second);
}
template <typename ResourceType>
bool ResourceMap<ResourceType>::contains(GLuint handle) const
{
if (handle < mFlatResources.size())
{
return (mFlatResources[handle] != InvalidPointer());
}
return (mHashedResources.find(handle) != mHashedResources.end());
}
template <typename ResourceType>
bool ResourceMap<ResourceType>::erase(GLuint handle, ResourceType **resourceOut)
{
if (handle < mFlatResources.size())
{
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>
void ResourceMap<ResourceType>::assign(GLuint handle, ResourceType *resource)
{
if (handle < kFlatResourcesLimit)
{
if (handle >= mFlatResources.size())
{
// Use power-of-two.
size_t newSize = mFlatResources.size();
while (newSize <= handle)
{
newSize *= 2;
}
mFlatResources.resize(newSize, nullptr);
}
ASSERT(mFlatResources.size() > handle);
mFlatResources[handle] = resource;
}
else
{
mHashedResources[handle] = resource;
}
}
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator ResourceMap<ResourceType>::begin() const
{
return Iterator(*this, nextNonNullResource(0), mHashedResources.begin());
}
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator ResourceMap<ResourceType>::end() const
{
return Iterator(*this, static_cast<GLuint>(mFlatResources.size()), mHashedResources.end());
}
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator ResourceMap<ResourceType>::find(GLuint handle) const
{
if (handle < mFlatResources.size())
{
return (mFlatResources[handle] != InvalidPointer()
? Iterator(handle, mHashedResources.begin())
: end());
}
else
{
return mHashedResources.find(handle);
}
}
template <typename ResourceType>
bool ResourceMap<ResourceType>::empty() const
{
return (begin() == end());
}
template <typename ResourceType>
void ResourceMap<ResourceType>::clear()
{
mFlatResources.assign(kInitialFlatResourcesSize, InvalidPointer());
mHashedResources.clear();
}
template <typename ResourceType>
GLuint ResourceMap<ResourceType>::nextNonNullResource(size_t flatIndex) const
{
for (size_t index = flatIndex; index < mFlatResources.size(); index++)
{
if (mFlatResources[index] != nullptr && mFlatResources[index] != InvalidPointer())
{
return static_cast<GLuint>(index);
}
}
return static_cast<GLuint>(mFlatResources.size());
}
template <typename ResourceType>
// static
ResourceType *ResourceMap<ResourceType>::InvalidPointer()
{
return reinterpret_cast<ResourceType *>(kInvalidPointer);
}
template <typename ResourceType>
ResourceMap<ResourceType>::Iterator::Iterator(
const ResourceMap &origin,
GLuint flatIndex,
typename ResourceMap<ResourceType>::HashMap::const_iterator hashIndex)
: mOrigin(origin), mFlatIndex(flatIndex), mHashIndex(hashIndex), mValue()
{
updateValue();
}
template <typename ResourceType>
bool ResourceMap<ResourceType>::Iterator::operator==(const Iterator &other) const
{
return (mFlatIndex == other.mFlatIndex && mHashIndex == other.mHashIndex);
}
template <typename ResourceType>
bool ResourceMap<ResourceType>::Iterator::operator!=(const Iterator &other) const
{
return !(*this == other);
}
template <typename ResourceType>
typename ResourceMap<ResourceType>::Iterator &ResourceMap<ResourceType>::Iterator::operator++()
{
if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResources.size()))
{
mFlatIndex = mOrigin.nextNonNullResource(mFlatIndex + 1);
}
else
{
mHashIndex++;
}
updateValue();
return *this;
}
template <typename ResourceType>
const typename ResourceMap<ResourceType>::IndexAndResource
*ResourceMap<ResourceType>::Iterator::operator->() const
{
return &mValue;
}
template <typename ResourceType>
const typename ResourceMap<ResourceType>::IndexAndResource
&ResourceMap<ResourceType>::Iterator::operator*() const
{
return mValue;
}
template <typename ResourceType>
void ResourceMap<ResourceType>::Iterator::updateValue()
{
if (mFlatIndex < static_cast<GLuint>(mOrigin.mFlatResources.size()))
{
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_
......@@ -211,7 +211,7 @@ bool TextureState::isSamplerComplete(const SamplerState &samplerState,
return mCompletenessCache.samplerComplete;
}
void TextureState::invalidateCompletenessCache()
void TextureState::invalidateCompletenessCache() const
{
mCompletenessCache.context = 0;
}
......@@ -868,7 +868,7 @@ egl::Stream *Texture::getBoundStream() const
return mBoundStream;
}
void Texture::invalidateCompletenessCache()
void Texture::invalidateCompletenessCache() const
{
mState.invalidateCompletenessCache();
mDirtyChannel.signal();
......
......@@ -100,7 +100,7 @@ struct TextureState final : private angle::NonCopyable
bool isCubeComplete() const;
bool isSamplerComplete(const SamplerState &samplerState, const ContextState &data) const;
void invalidateCompletenessCache();
void invalidateCompletenessCache() const;
const ImageDesc &getImageDesc(GLenum target, size_t level) const;
const ImageDesc &getImageDesc(const ImageIndex &imageIndex) const;
......@@ -358,7 +358,7 @@ class Texture final : public egl::ImageSibling,
egl::Surface *getBoundSurface() const;
egl::Stream *getBoundStream() const;
void invalidateCompletenessCache();
void invalidateCompletenessCache() const;
rx::TextureImpl *getImplementation() const { return mTexture; }
......
......@@ -320,11 +320,6 @@ using UniformBlockBindingMask = angle::BitSet<IMPLEMENTATION_MAX_COMBINED_SHADER
// Used in Framebuffer
using DrawBufferMask = angle::BitSet<IMPLEMENTATION_MAX_DRAW_BUFFERS>;
// A map of GL objects indexed by object ID. The specific map implementation may change.
// Client code should treat it as a std::map.
template <class ResourceT>
using ResourceMap = std::unordered_map<GLuint, ResourceT *>;
using ContextID = uintptr_t;
}
......
......@@ -174,6 +174,7 @@
'libANGLE/Renderbuffer.h',
'libANGLE/ResourceManager.cpp',
'libANGLE/ResourceManager.h',
'libANGLE/ResourceMap.h',
'libANGLE/Sampler.cpp',
'libANGLE/Sampler.h',
'libANGLE/Shader.cpp',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment