Commit f9a062c9 by Mohan Maiya Committed by Commit Bot

Vulkan: Add FastIntegerSet and FastIntegerMap class

Add FastIntegerSet container to enable fast contains operation for a set of integer keys. The class uses a BitSet vector to achieve performance. Add FastIntegerMap container to improve buffer serial tracking performance. FastIntegerMap uses FastIntegerSet container to track buffer serial keys. It also provides an ensureCapacity method to reserve space, for the expected buffer count, upfront. CommandBufferHelper::mUsedBuffers and ContextVk::descriptorSetCache are now FastIntegerMap CommandBufferHelper::mRenderPassUsedImages is now a FastIntegerSet Based on a CL by Jamie Bug: angleproject:4950 Test: angle_unittests.exe --gtest_filter=FastInteger* Change-Id: Ib58be20143f588baab99acadac796f2435f72d54 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2369466Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
parent 7ce9947d
......@@ -11,6 +11,7 @@
#ifndef COMMON_FASTVECTOR_H_
#define COMMON_FASTVECTOR_H_
#include "bitset_utils.h"
#include "common/debug.h"
#include <algorithm>
......@@ -505,6 +506,163 @@ class FastUnorderedSet final
private:
FastVector<T, N> mData;
};
class FastIntegerSet final
{
public:
static constexpr size_t kWindowSize = 64;
static constexpr size_t kOneLessThanKWindowSize = kWindowSize - 1;
static constexpr size_t kShiftForDivision =
static_cast<size_t>(rx::Log2(static_cast<unsigned int>(kWindowSize)));
using KeyBitSet = angle::BitSet64<kWindowSize>;
ANGLE_INLINE FastIntegerSet();
ANGLE_INLINE ~FastIntegerSet();
ANGLE_INLINE void ensureCapacity(size_t size)
{
if (capacity() <= size)
{
reserve(size * 2);
}
}
ANGLE_INLINE void insert(uint64_t key)
{
size_t sizedKey = static_cast<size_t>(key);
ASSERT(!contains(sizedKey));
ensureCapacity(sizedKey);
ASSERT(capacity() > sizedKey);
size_t index = sizedKey >> kShiftForDivision;
size_t offset = sizedKey & kOneLessThanKWindowSize;
mKeyData[index].set(offset, true);
}
ANGLE_INLINE bool contains(uint64_t key) const
{
size_t sizedKey = static_cast<size_t>(key);
size_t index = sizedKey >> kShiftForDivision;
size_t offset = sizedKey & kOneLessThanKWindowSize;
return (sizedKey < capacity()) && (mKeyData[index].test(offset));
}
ANGLE_INLINE void clear() { mKeyData.assign(mKeyData.capacity(), KeyBitSet::Zero()); }
ANGLE_INLINE bool empty() const
{
for (KeyBitSet it : mKeyData)
{
if (it.any())
{
return false;
}
}
return true;
}
ANGLE_INLINE size_t size() const
{
size_t valid_entries = 0;
for (KeyBitSet it : mKeyData)
{
valid_entries += it.count();
}
return valid_entries;
}
private:
ANGLE_INLINE size_t capacity() const { return kWindowSize * mKeyData.size(); }
ANGLE_INLINE void reserve(size_t newSize)
{
size_t alignedSize = rx::roundUp(newSize, kWindowSize);
size_t count = alignedSize >> kShiftForDivision;
mKeyData.resize(count, KeyBitSet::Zero());
}
std::vector<KeyBitSet> mKeyData;
};
// This is needed to accommodate the chromium style guide error -
// [chromium-style] Complex constructor has an inlined body.
ANGLE_INLINE FastIntegerSet::FastIntegerSet() {}
ANGLE_INLINE FastIntegerSet::~FastIntegerSet() {}
template <typename Value>
class FastIntegerMap final
{
public:
FastIntegerMap() {}
~FastIntegerMap() {}
ANGLE_INLINE void ensureCapacity(size_t size)
{
// Ensure key set has capacity
mKeySet.ensureCapacity(size);
// Ensure value vector has capacity
ensureCapacityImpl(size);
}
ANGLE_INLINE void insert(uint64_t key, Value value)
{
// Insert key
ASSERT(!mKeySet.contains(key));
mKeySet.insert(key);
// Insert value
size_t sizedKey = static_cast<size_t>(key);
ensureCapacityImpl(sizedKey);
ASSERT(capacity() > sizedKey);
mValueData[sizedKey] = value;
}
ANGLE_INLINE bool contains(uint64_t key) const { return mKeySet.contains(key); }
ANGLE_INLINE bool get(uint64_t key, Value *out) const
{
if (!mKeySet.contains(key))
{
return false;
}
size_t sizedKey = static_cast<size_t>(key);
*out = mValueData[sizedKey];
return true;
}
ANGLE_INLINE void clear() { mKeySet.clear(); }
ANGLE_INLINE bool empty() const { return mKeySet.empty(); }
ANGLE_INLINE size_t size() const { return mKeySet.size(); }
private:
ANGLE_INLINE size_t capacity() const { return mValueData.size(); }
ANGLE_INLINE void ensureCapacityImpl(size_t size)
{
if (capacity() <= size)
{
reserve(size * 2);
}
}
ANGLE_INLINE void reserve(size_t newSize)
{
size_t alignedSize = rx::roundUp(newSize, FastIntegerSet::kWindowSize);
mValueData.resize(alignedSize);
}
FastIntegerSet mKeySet;
std::vector<Value> mValueData;
};
} // namespace angle
#endif // COMMON_FASTVECTOR_H_
......@@ -295,4 +295,71 @@ TEST(FastUnorderedSet, BasicUsage)
EXPECT_TRUE(testMap.contains(i));
}
}
// Basic functionality for FastIntegerSet
TEST(FastIntegerSet, BasicUsage)
{
FastIntegerSet testMap;
EXPECT_TRUE(testMap.empty());
testMap.insert(5);
EXPECT_TRUE(testMap.contains(5));
EXPECT_FALSE(testMap.contains(6));
EXPECT_FALSE(testMap.empty());
testMap.clear();
EXPECT_TRUE(testMap.empty());
for (int i = 0; i < 10; ++i)
{
testMap.insert(i);
}
for (int i = 0; i < 10; ++i)
{
EXPECT_TRUE(testMap.contains(i));
}
}
// Basic functionality for FastIntegerMap
TEST(FastIntegerMap, BasicUsage)
{
using KeyValuePair = std::pair<int, std::string>;
std::set<KeyValuePair> entries = {KeyValuePair(17, "testing"), KeyValuePair(63, "fast"),
KeyValuePair(97, "integer"), KeyValuePair(256, "map")};
FastIntegerMap<std::string> testMap;
EXPECT_TRUE(testMap.empty());
std::string str;
testMap.insert(entries.begin()->first, entries.begin()->second);
EXPECT_TRUE(testMap.contains(entries.begin()->first));
EXPECT_FALSE(testMap.contains(entries.end()->first));
EXPECT_FALSE(testMap.empty());
EXPECT_EQ(testMap.size(), 1u);
EXPECT_TRUE(testMap.get(entries.begin()->first, &str));
EXPECT_EQ(entries.begin()->second, str);
EXPECT_FALSE(testMap.get(1, &str));
testMap.clear();
EXPECT_TRUE(testMap.empty());
EXPECT_EQ(testMap.size(), 0u);
for (KeyValuePair entry : entries)
{
testMap.insert(entry.first, entry.second);
}
EXPECT_EQ(testMap.size(), 4u);
for (KeyValuePair entry : entries)
{
std::string str;
EXPECT_TRUE(testMap.get(entry.first, &str));
EXPECT_EQ(entry.second, str);
}
testMap.clear();
EXPECT_TRUE(testMap.empty());
EXPECT_EQ(testMap.size(), 0u);
}
} // namespace angle
......@@ -3816,10 +3816,9 @@ angle::Result ContextVk::updateDriverUniformsDescriptorSet(
const vk::BufferHelper *buffer = driverUniforms->dynamicBuffer.getCurrentBuffer();
vk::BufferSerial bufferSerial = buffer->getBufferSerial();
// Look up in the cache first
auto iter = driverUniforms->descriptorSetCache.find(bufferSerial);
if (iter != driverUniforms->descriptorSetCache.end())
if (driverUniforms->descriptorSetCache.get(bufferSerial.getValue(),
&driverUniforms->descriptorSet))
{
driverUniforms->descriptorSet = iter->second;
return angle::Result::Continue;
}
......@@ -3846,7 +3845,8 @@ angle::Result ContextVk::updateDriverUniformsDescriptorSet(
writeInfo.pBufferInfo = &bufferInfo;
// Add into descriptor set cache
driverUniforms->descriptorSetCache.emplace(bufferSerial, driverUniforms->descriptorSet);
driverUniforms->descriptorSetCache.insert(bufferSerial.getValue(),
driverUniforms->descriptorSet);
return angle::Result::Continue;
}
......
......@@ -664,7 +664,7 @@ class ContextVk : public ContextImpl, public vk::Context
uint32_t dynamicOffset;
vk::BindingPointer<vk::DescriptorSetLayout> descriptorSetLayout;
vk::RefCountedDescriptorPoolBinding descriptorPoolBinding;
std::unordered_map<vk::BufferSerial, VkDescriptorSet> descriptorSetCache;
angle::FastIntegerMap<VkDescriptorSet> descriptorSetCache;
DriverUniformsDescriptorSet();
~DriverUniformsDescriptorSet();
......
......@@ -604,6 +604,8 @@ CommandBufferHelper::~CommandBufferHelper()
void CommandBufferHelper::initialize(bool isRenderPassCommandBuffer)
{
ASSERT(mUsedBuffers.empty());
constexpr size_t kInitialBufferCount = 128;
mUsedBuffers.ensureCapacity(kInitialBufferCount);
mAllocator.initialize(kDefaultPoolAllocatorPageSize, 1);
// Push a scope into the pool allocator so we can easily free and re-init on reset()
......@@ -614,13 +616,13 @@ void CommandBufferHelper::initialize(bool isRenderPassCommandBuffer)
bool CommandBufferHelper::usesBuffer(const BufferHelper &buffer) const
{
return mUsedBuffers.contains(buffer.getBufferSerial());
return mUsedBuffers.contains(buffer.getBufferSerial().getValue());
}
bool CommandBufferHelper::usesBufferForWrite(const BufferHelper &buffer) const
{
BufferAccess access;
if (!mUsedBuffers.get(buffer.getBufferSerial(), &access))
if (!mUsedBuffers.get(buffer.getBufferSerial().getValue(), &access))
{
return false;
}
......@@ -640,9 +642,9 @@ void CommandBufferHelper::bufferRead(ResourceUseList *resourceUseList,
}
ASSERT(!usesBufferForWrite(*buffer));
if (!mUsedBuffers.contains(buffer->getBufferSerial()))
if (!mUsedBuffers.contains(buffer->getBufferSerial().getValue()))
{
mUsedBuffers.insert(buffer->getBufferSerial(), BufferAccess::Read);
mUsedBuffers.insert(buffer->getBufferSerial().getValue(), BufferAccess::Read);
}
}
......@@ -666,7 +668,7 @@ void CommandBufferHelper::bufferWrite(ResourceUseList *resourceUseList,
if (aliasingMode == AliasingMode::Disallowed)
{
ASSERT(!usesBuffer(*buffer));
mUsedBuffers.insert(buffer->getBufferSerial(), BufferAccess::Write);
mUsedBuffers.insert(buffer->getBufferSerial().getValue(), BufferAccess::Write);
}
}
......@@ -694,7 +696,7 @@ void CommandBufferHelper::imageRead(ResourceUseList *resourceUseList,
// We allow duplicate uses in the RP to accomodate for normal GL sampler usage.
if (!usesImageInRenderPass(*image))
{
mRenderPassUsedImages.insert(image->getImageSerial());
mRenderPassUsedImages.insert(image->getImageSerial().getValue());
}
}
}
......@@ -725,7 +727,7 @@ void CommandBufferHelper::imageWrite(ResourceUseList *resourceUseList,
}
if (!usesImageInRenderPass(*image))
{
mRenderPassUsedImages.insert(image->getImageSerial());
mRenderPassUsedImages.insert(image->getImageSerial().getValue());
}
}
}
......
......@@ -1095,9 +1095,8 @@ class CommandBufferHelper : angle::NonCopyable
// Tracks resources used in the command buffer.
// For Buffers, we track the read/write access type so we can enable simuntaneous reads.
// Images have unique layouts unlike buffers therefore we don't support multi-read.
static constexpr uint32_t kFastMapSize = 16;
angle::FastUnorderedMap<BufferSerial, BufferAccess, kFastMapSize> mUsedBuffers;
angle::FastUnorderedSet<ImageSerial, kFastMapSize> mRenderPassUsedImages;
angle::FastIntegerMap<BufferAccess> mUsedBuffers;
angle::FastIntegerSet mRenderPassUsedImages;
};
static constexpr uint32_t kInvalidAttachmentIndex = -1;
......@@ -2051,7 +2050,7 @@ class ActiveHandleCounter final : angle::NonCopyable
ANGLE_INLINE bool CommandBufferHelper::usesImageInRenderPass(const ImageHelper &image) const
{
ASSERT(mIsRenderPassCommandBuffer);
return mRenderPassUsedImages.contains(image.getImageSerial());
return mRenderPassUsedImages.contains(image.getImageSerial().getValue());
}
} // namespace vk
} // namespace rx
......
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