Commit 04747ceb authored by kkinnunen's avatar kkinnunen Committed by Commit bot

command_buffer: Implement IdAllocator by recording ranges

Implement IdAllocator by storing complete id ranges of (min,max) in the
map instead of just ids. This should make allocating big amount of ids
scale a bit better.

Store a range by creating a mapping first id -> last id. Upon query of
id existence, find the lowest_bound with the id interested. The hit will
be found at the returned iterator position (range starts at the id) or
the position before (range starts before the id and expands to cover
it).

Changes the allocation results so that even though client has requested
AllocateIdAtOrAbove(high_id_number), the next AllocateId() will allocate
lowest available id (instead of lowest available after
high_id_number). This is why GLES2ImplementationTest::kBuffersStartId
changes.

BUG=344330

Review URL: https://codereview.chromium.org/692073002

Cr-Commit-Position: refs/heads/master@{#318663}
parent c830cece
...@@ -381,8 +381,7 @@ class GLES2ImplementationTest : public testing::Test { ...@@ -381,8 +381,7 @@ class GLES2ImplementationTest : public testing::Test {
static const GLuint kMaxTransformFeedbackSeparateAttribs = 4; static const GLuint kMaxTransformFeedbackSeparateAttribs = 4;
static const GLuint kMaxUniformBufferBindings = 36; static const GLuint kMaxUniformBufferBindings = 36;
static const GLuint kStartId = 1024; static const GLuint kStartId = 1024;
static const GLuint kBuffersStartId = static const GLuint kBuffersStartId = 1;
GLES2Implementation::kClientSideArrayId + 2 * kNumTestContexts;
static const GLuint kFramebuffersStartId = 1; static const GLuint kFramebuffersStartId = 1;
static const GLuint kProgramsAndShadersStartId = 1; static const GLuint kProgramsAndShadersStartId = 1;
static const GLuint kRenderbuffersStartId = 1; static const GLuint kRenderbuffersStartId = 1;
......
...@@ -6,84 +6,200 @@ ...@@ -6,84 +6,200 @@
#include "gpu/command_buffer/common/id_allocator.h" #include "gpu/command_buffer/common/id_allocator.h"
#include <limits>
#include "base/logging.h" #include "base/logging.h"
namespace gpu { namespace gpu {
IdAllocator::IdAllocator() {} IdAllocator::IdAllocator() {
COMPILE_ASSERT(kInvalidResource == 0u, invalid_resource_is_not_zero);
// Simplify the code by making sure that lower_bound(id) never
// returns the beginning of the map, if id is valid (eg !=
// kInvalidResource).
used_ids_.insert(std::make_pair(0u, 0u));
}
IdAllocator::~IdAllocator() {} IdAllocator::~IdAllocator() {}
ResourceId IdAllocator::AllocateID() { ResourceId IdAllocator::AllocateID() {
ResourceId id; return AllocateIDRange(1u);
ResourceIdSet::iterator iter = free_ids_.begin(); }
if (iter != free_ids_.end()) {
id = *iter; ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) {
if (desired_id == 0u || desired_id == 1u) {
return AllocateIDRange(1u);
}
ResourceIdRangeMap::iterator current = used_ids_.lower_bound(desired_id);
ResourceIdRangeMap::iterator next = current;
if (current == used_ids_.end() || current->first > desired_id) {
current--;
} else { } else {
id = LastUsedId() + 1; next++;
if (!id) { }
// We wrapped around to 0.
id = FindFirstUnusedId(); ResourceId first_id = current->first;
ResourceId last_id = current->second;
DCHECK(desired_id >= first_id);
if (desired_id - 1u <= last_id) {
// Append to current range.
last_id++;
if (last_id == 0) {
// The increment overflowed.
return AllocateIDRange(1u);
}
current->second = last_id;
if (next != used_ids_.end() && next->first - 1u == last_id) {
// Merge with next range.
current->second = next->second;
used_ids_.erase(next);
} }
return last_id;
} else if (next != used_ids_.end() && next->first - 1u == desired_id) {
// Prepend to next range.
ResourceId last_existing_id = next->second;
used_ids_.erase(next);
used_ids_.insert(std::make_pair(desired_id, last_existing_id));
return desired_id;
} }
MarkAsUsed(id); used_ids_.insert(std::make_pair(desired_id, desired_id));
return id; return desired_id;
} }
ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) { ResourceId IdAllocator::AllocateIDRange(uint32_t range) {
ResourceId id; DCHECK(range > 0u);
ResourceIdSet::iterator iter = free_ids_.lower_bound(desired_id);
if (iter != free_ids_.end()) { ResourceIdRangeMap::iterator current = used_ids_.begin();
id = *iter; ResourceIdRangeMap::iterator next = current;
} else if (LastUsedId() < desired_id) {
id = desired_id; while (++next != used_ids_.end()) {
} else { if (next->first - current->second > range) {
id = LastUsedId() + 1; break;
if (!id) {
// We wrapped around to 0.
id = FindFirstUnusedId();
} }
current = next;
}
ResourceId first_id = current->second + 1u;
ResourceId last_id = first_id + range - 1u;
if (first_id == 0u || last_id < first_id) {
return kInvalidResource;
} }
MarkAsUsed(id);
return id; current->second = last_id;
if (next != used_ids_.end() && next->first - 1u == last_id) {
// Merge with next range.
current->second = next->second;
used_ids_.erase(next);
}
return first_id;
} }
bool IdAllocator::MarkAsUsed(ResourceId id) { bool IdAllocator::MarkAsUsed(ResourceId id) {
DCHECK(id); DCHECK(id);
free_ids_.erase(id); ResourceIdRangeMap::iterator current = used_ids_.lower_bound(id);
std::pair<ResourceIdSet::iterator, bool> result = used_ids_.insert(id); if (current != used_ids_.end() && current->first == id) {
return result.second; return false;
} }
void IdAllocator::FreeID(ResourceId id) { ResourceIdRangeMap::iterator next = current;
if (id) { --current;
used_ids_.erase(id);
free_ids_.insert(id); if (current->second >= id) {
return false;
} }
DCHECK(current->first < id && current->second < id);
if (current->second + 1u == id) {
// Append to current range.
current->second = id;
if (next != used_ids_.end() && next->first - 1u == id) {
// Merge with next range.
current->second = next->second;
used_ids_.erase(next);
}
return true;
} else if (next != used_ids_.end() && next->first - 1u == id) {
// Prepend to next range.
ResourceId last_existing_id = next->second;
used_ids_.erase(next);
used_ids_.insert(std::make_pair(id, last_existing_id));
return true;
}
used_ids_.insert(std::make_pair(id, id));
return true;
} }
bool IdAllocator::InUse(ResourceId id) const { void IdAllocator::FreeID(ResourceId id) {
return id == kInvalidResource || used_ids_.find(id) != used_ids_.end(); FreeIDRange(id, 1u);
} }
ResourceId IdAllocator::LastUsedId() const { void IdAllocator::FreeIDRange(ResourceId first_id, uint32 range) {
if (used_ids_.empty()) { COMPILE_ASSERT(kInvalidResource == 0u, invalid_resource_is_not_zero);
return 0u;
} else { if (range == 0u || (first_id == 0u && range == 1u)) {
return *used_ids_.rbegin(); return;
}
if (first_id == 0u) {
first_id++;
range--;
}
ResourceId last_id = first_id + range - 1u;
if (last_id < first_id) {
last_id = std::numeric_limits<ResourceId>::max();
}
while (true) {
ResourceIdRangeMap::iterator current = used_ids_.lower_bound(last_id);
if (current == used_ids_.end() || current->first > last_id) {
--current;
}
if (current->second < first_id) {
return;
}
if (current->first >= first_id) {
ResourceId last_existing_id = current->second;
used_ids_.erase(current);
if (last_id < last_existing_id) {
used_ids_.insert(std::make_pair(last_id + 1u, last_existing_id));
}
} else if (current->second <= last_id) {
current->second = first_id - 1u;
} else {
DCHECK(current->first < first_id && current->second > last_id);
ResourceId last_existing_id = current->second;
current->second = first_id - 1u;
used_ids_.insert(std::make_pair(last_id + 1u, last_existing_id));
}
} }
} }
ResourceId IdAllocator::FindFirstUnusedId() const { bool IdAllocator::InUse(ResourceId id) const {
ResourceId id = 1; if (id == kInvalidResource) {
for (ResourceIdSet::const_iterator it = used_ids_.begin(); return false;
it != used_ids_.end(); ++it) { }
if ((*it) != id) {
return id; ResourceIdRangeMap::const_iterator current = used_ids_.lower_bound(id);
if (current != used_ids_.end()) {
if (current->first == id) {
return true;
} }
++id;
} }
return id;
--current;
return current->second >= id;
} }
} // namespace gpu } // namespace gpu
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include <stdint.h> #include <stdint.h>
#include <set> #include <map>
#include <utility> #include <utility>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
...@@ -36,27 +36,28 @@ class GPU_EXPORT IdAllocator { ...@@ -36,27 +36,28 @@ class GPU_EXPORT IdAllocator {
// Note: may wrap if it starts near limit. // Note: may wrap if it starts near limit.
ResourceId AllocateIDAtOrAbove(ResourceId desired_id); ResourceId AllocateIDAtOrAbove(ResourceId desired_id);
// Allocates |range| amount of contiguous ids.
// Returns the first id to |first_id| or |kInvalidResource| if
// allocation failed.
ResourceId AllocateIDRange(uint32_t range);
// Marks an id as used. Returns false if id was already used. // Marks an id as used. Returns false if id was already used.
bool MarkAsUsed(ResourceId id); bool MarkAsUsed(ResourceId id);
// Frees a resource ID. // Frees a resource ID.
void FreeID(ResourceId id); void FreeID(ResourceId id);
// Frees a |range| amount of contiguous ids, starting from |first_id|.
void FreeIDRange(ResourceId first_id, uint32_t range);
// Checks whether or not a resource ID is in use. // Checks whether or not a resource ID is in use.
bool InUse(ResourceId id) const; bool InUse(ResourceId id) const;
private: private:
// TODO(gman): This would work much better with ranges or a hash table. // first_id -> last_id mapping.
typedef std::set<ResourceId> ResourceIdSet; typedef std::map<ResourceId, ResourceId> ResourceIdRangeMap;
// The highest ID on the used list.
ResourceId LastUsedId() const;
// Lowest ID that isn't on the used list. This is slow, use as a last resort.
ResourceId FindFirstUnusedId() const;
ResourceIdSet used_ids_; ResourceIdRangeMap used_ids_;
ResourceIdSet free_ids_;
DISALLOW_COPY_AND_ASSIGN(IdAllocator); DISALLOW_COPY_AND_ASSIGN(IdAllocator);
}; };
......
...@@ -125,4 +125,123 @@ TEST_F(IdAllocatorTest, RedundantFreeIsIgnored) { ...@@ -125,4 +125,123 @@ TEST_F(IdAllocatorTest, RedundantFreeIsIgnored) {
EXPECT_NE(kInvalidResource, id3); EXPECT_NE(kInvalidResource, id3);
} }
TEST_F(IdAllocatorTest, AllocateIDRange) {
const ResourceId kMaxPossibleOffset = std::numeric_limits<ResourceId>::max();
IdAllocator* allocator = id_allocator();
ResourceId id1 = allocator->AllocateIDRange(1);
EXPECT_EQ(1u, id1);
ResourceId id2 = allocator->AllocateIDRange(2);
EXPECT_EQ(2u, id2);
ResourceId id3 = allocator->AllocateIDRange(3);
EXPECT_EQ(4u, id3);
ResourceId id4 = allocator->AllocateID();
EXPECT_EQ(7u, id4);
allocator->FreeID(3);
ResourceId id5 = allocator->AllocateIDRange(1);
EXPECT_EQ(3u, id5);
allocator->FreeID(5);
allocator->FreeID(2);
allocator->FreeID(4);
ResourceId id6 = allocator->AllocateIDRange(2);
EXPECT_EQ(4u, id6);
ResourceId id7 = allocator->AllocateIDAtOrAbove(kMaxPossibleOffset);
EXPECT_EQ(kMaxPossibleOffset, id7);
ResourceId id8 = allocator->AllocateIDAtOrAbove(kMaxPossibleOffset);
EXPECT_EQ(2u, id8);
ResourceId id9 = allocator->AllocateIDRange(50);
EXPECT_EQ(8u, id9);
ResourceId id10 = allocator->AllocateIDRange(50);
EXPECT_EQ(58u, id10);
// Remove all the low-numbered ids.
allocator->FreeID(1);
allocator->FreeID(15);
allocator->FreeIDRange(2, 107);
ResourceId id11 = allocator->AllocateIDRange(100);
EXPECT_EQ(1u, id11);
allocator->FreeID(kMaxPossibleOffset);
ResourceId id12 = allocator->AllocateIDRange(100);
EXPECT_EQ(101u, id12);
ResourceId id13 = allocator->AllocateIDAtOrAbove(kMaxPossibleOffset - 2u);
EXPECT_EQ(kMaxPossibleOffset - 2u, id13);
ResourceId id14 = allocator->AllocateIDRange(3);
EXPECT_EQ(201u, id14);
}
TEST_F(IdAllocatorTest, AllocateIDRangeEndNoEffect) {
const ResourceId kMaxPossibleOffset = std::numeric_limits<ResourceId>::max();
IdAllocator* allocator = id_allocator();
ResourceId id1 = allocator->AllocateIDAtOrAbove(kMaxPossibleOffset - 2u);
EXPECT_EQ(kMaxPossibleOffset - 2u, id1);
ResourceId id3 = allocator->AllocateIDRange(3);
EXPECT_EQ(1u, id3);
ResourceId id2 = allocator->AllocateIDRange(2);
EXPECT_EQ(4u, id2);
}
TEST_F(IdAllocatorTest, AllocateFullIDRange) {
const uint32_t kMaxPossibleRange = std::numeric_limits<uint32_t>::max();
const ResourceId kFreedId = 555u;
IdAllocator* allocator = id_allocator();
ResourceId id1 = allocator->AllocateIDRange(kMaxPossibleRange);
EXPECT_EQ(1u, id1);
ResourceId id2 = allocator->AllocateID();
EXPECT_EQ(0u, id2);
allocator->FreeID(kFreedId);
ResourceId id3 = allocator->AllocateID();
EXPECT_EQ(kFreedId, id3);
ResourceId id4 = allocator->AllocateID();
EXPECT_EQ(0u, id4);
allocator->FreeID(kFreedId + 1u);
allocator->FreeID(kFreedId + 4u);
allocator->FreeID(kFreedId + 3u);
allocator->FreeID(kFreedId + 5u);
allocator->FreeID(kFreedId + 2u);
ResourceId id5 = allocator->AllocateIDRange(5);
EXPECT_EQ(kFreedId + 1u, id5);
}
TEST_F(IdAllocatorTest, AllocateIDRangeNoWrapInRange) {
const uint32_t kMaxPossibleRange = std::numeric_limits<uint32_t>::max();
const ResourceId kAllocId = 10u;
IdAllocator* allocator = id_allocator();
ResourceId id1 = allocator->AllocateIDAtOrAbove(kAllocId);
EXPECT_EQ(kAllocId, id1);
ResourceId id2 = allocator->AllocateIDRange(kMaxPossibleRange - 5u);
EXPECT_EQ(0u, id2);
ResourceId id3 = allocator->AllocateIDRange(kMaxPossibleRange - kAllocId);
EXPECT_EQ(kAllocId + 1u, id3);
}
TEST_F(IdAllocatorTest, AllocateIdMax) {
const uint32_t kMaxPossibleRange = std::numeric_limits<uint32_t>::max();
IdAllocator* allocator = id_allocator();
ResourceId id = allocator->AllocateIDRange(kMaxPossibleRange);
EXPECT_EQ(1u, id);
allocator->FreeIDRange(id, kMaxPossibleRange - 1u);
ResourceId id2 = allocator->AllocateIDRange(kMaxPossibleRange);
EXPECT_EQ(0u, id2);
allocator->FreeIDRange(id, kMaxPossibleRange);
ResourceId id3 = allocator->AllocateIDRange(kMaxPossibleRange);
EXPECT_EQ(1u, id3);
}
TEST_F(IdAllocatorTest, ZeroIdCases) {
IdAllocator* allocator = id_allocator();
EXPECT_FALSE(allocator->InUse(0));
ResourceId id1 = allocator->AllocateIDAtOrAbove(0);
EXPECT_NE(0u, id1);
EXPECT_FALSE(allocator->InUse(0));
allocator->FreeID(0);
EXPECT_FALSE(allocator->InUse(0));
EXPECT_TRUE(allocator->InUse(id1));
allocator->FreeID(id1);
EXPECT_FALSE(allocator->InUse(id1));
}
} // namespace gpu } // namespace gpu
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