Commit 88323761 by Jamie Madill Committed by Commit Bot

Introduce SizedMRUCache.

This class is an MRU cache for sized objects, like Textures or Program binaries. It is based on the base::HashingMRUCache type, and evicts objects automatically. BUG=angleproject:2044 Change-Id: I83859a0388f984e872fb9209e4c9efbf5d6c93f1 Reviewed-on: https://chromium-review.googlesource.com/517380Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent c084de14
//
// 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.
//
// SizedMRUCache.h: A hashing map that stores blobs of sized, untyped data.
#ifndef LIBANGLE_SIZED_MRU_CACHE_H_
#define LIBANGLE_SIZED_MRU_CACHE_H_
#include <anglebase/containers/mru_cache.h>
namespace angle
{
template <typename Key, typename Value>
class SizedMRUCache final : angle::NonCopyable
{
public:
SizedMRUCache(size_t maximumTotalSize)
: mMaximumTotalSize(maximumTotalSize),
mCurrentSize(0),
mStore(SizedMRUCacheStore::NO_AUTO_EVICT)
{
}
void put(const Key &key, Value &&value, size_t size)
{
ASSERT(size <= mMaximumTotalSize);
// Check for existing key.
eraseByKey(key);
mStore.Put(key, ValueAndSize(std::move(value), size));
mCurrentSize += size;
while (mCurrentSize > mMaximumTotalSize)
{
ASSERT(!mStore.empty());
auto iter = mStore.rbegin();
mCurrentSize -= iter->second.size;
mStore.Erase(iter);
}
}
bool get(const Key &key, const Value **valueOut)
{
const auto &iter = mStore.Get(key);
if (iter == mStore.end())
{
return false;
}
*valueOut = &iter->second.value;
return true;
}
bool empty() const { return mStore.empty(); }
void clear()
{
mStore.Clear();
mCurrentSize = 0;
}
bool eraseByKey(const Key &key)
{
// Check for existing key.
auto existing = mStore.Peek(key);
if (existing != mStore.end())
{
mCurrentSize -= existing->second.size;
mStore.Erase(existing);
return true;
}
return false;
}
size_t size() const { return mCurrentSize; }
private:
struct ValueAndSize
{
ValueAndSize() : value(), size(0) {}
ValueAndSize(Value &&value, size_t size) : value(std::move(value)), size(size) {}
ValueAndSize(ValueAndSize &&other) : ValueAndSize() { *this = std::move(other); }
ValueAndSize &operator=(ValueAndSize &&other)
{
std::swap(value, other.value);
std::swap(size, other.size);
return *this;
}
Value value;
size_t size;
};
using SizedMRUCacheStore = base::HashingMRUCache<Key, ValueAndSize>;
size_t mMaximumTotalSize;
size_t mCurrentSize;
SizedMRUCacheStore mStore;
};
} // namespace angle
#endif // LIBANGLE_SIZED_MRU_CACHE_H_
//
// 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.
//
// SizedMRUCache_unittest.h: Unit tests for the sized MRU cached.
#include "libANGLE/SizedMRUCache.h"
#include <gtest/gtest.h>
namespace angle
{
using Blob = std::vector<uint8_t>;
Blob MakeBlob(size_t size)
{
Blob blob;
for (uint8_t value = 0; value < size; ++value)
{
blob.push_back(value);
}
return blob;
}
// Test a cache with a value that takes up maximum size.
TEST(SizedMRUCacheTest, MaxSizedValue)
{
constexpr size_t kSize = 32;
SizedMRUCache<std::string, Blob> sizedCache(kSize);
sizedCache.put("test", MakeBlob(kSize), kSize);
EXPECT_EQ(32u, sizedCache.size());
EXPECT_FALSE(sizedCache.empty());
sizedCache.put("test2", MakeBlob(kSize), kSize);
EXPECT_EQ(32u, sizedCache.size());
EXPECT_FALSE(sizedCache.empty());
const Blob *blob = nullptr;
EXPECT_FALSE(sizedCache.get("test", &blob));
sizedCache.clear();
EXPECT_TRUE(sizedCache.empty());
}
// Test a cache with many small values, that it can handle unlimited inserts.
TEST(SizedMRUCacheTest, ManySmallValues)
{
constexpr size_t kSize = 32;
SizedMRUCache<size_t, size_t> sizedCache(kSize);
for (size_t value = 0; value < kSize; ++value)
{
sizedCache.put(value, std::move(value), 1);
const size_t *qvalue = nullptr;
EXPECT_TRUE(sizedCache.get(value, &qvalue));
if (qvalue)
{
EXPECT_EQ(value, *qvalue);
}
}
EXPECT_EQ(32u, sizedCache.size());
EXPECT_FALSE(sizedCache.empty());
// Putting one element evicts the first element.
sizedCache.put(kSize, std::move(static_cast<int>(kSize)), 1);
const size_t *qvalue = nullptr;
EXPECT_FALSE(sizedCache.get(0, &qvalue));
// Putting one large element cleans out the whole stack.
sizedCache.put(kSize + 1, kSize + 1, kSize);
EXPECT_EQ(32u, sizedCache.size());
EXPECT_FALSE(sizedCache.empty());
for (size_t value = 0; value <= kSize; ++value)
{
EXPECT_FALSE(sizedCache.get(value, &qvalue));
}
EXPECT_TRUE(sizedCache.get(kSize + 1, &qvalue));
if (qvalue)
{
EXPECT_EQ(kSize + 1, *qvalue);
}
// Put a bunch of items in the cache sequentially.
for (size_t value = 0; value < kSize * 10; ++value)
{
sizedCache.put(value, std::move(value), 1);
}
EXPECT_EQ(32u, sizedCache.size());
}
} // namespace angle
......@@ -176,6 +176,7 @@
'libANGLE/Sampler.h',
'libANGLE/Shader.cpp',
'libANGLE/Shader.h',
'libANGLE/SizedMRUCache.h',
'libANGLE/State.cpp',
'libANGLE/State.h',
'libANGLE/Stream.cpp',
......
......@@ -31,6 +31,7 @@
'<(angle_path)/src/libANGLE/ImageIndexIterator_unittest.cpp',
'<(angle_path)/src/libANGLE/Program_unittest.cpp',
'<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp',
'<(angle_path)/src/libANGLE/SizedMRUCache_unittest.cpp',
'<(angle_path)/src/libANGLE/Surface_unittest.cpp',
'<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp',
'<(angle_path)/src/libANGLE/VaryingPacking_unittest.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