|
|
@@ -10,19 +10,11 @@
|
|
|
|
|
|
namespace Shader {
|
|
|
|
|
|
-template <typename T, size_t chunk_size = 8192>
|
|
|
+template <typename T>
|
|
|
requires std::is_destructible_v<T> class ObjectPool {
|
|
|
public:
|
|
|
- ~ObjectPool() {
|
|
|
- std::unique_ptr<Chunk> tree_owner;
|
|
|
- Chunk* chunk{&root};
|
|
|
- while (chunk) {
|
|
|
- for (size_t obj_id = chunk->free_objects; obj_id < chunk_size; ++obj_id) {
|
|
|
- chunk->storage[obj_id].object.~T();
|
|
|
- }
|
|
|
- tree_owner = std::move(chunk->next);
|
|
|
- chunk = tree_owner.get();
|
|
|
- }
|
|
|
+ explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
|
|
|
+ node = &chunks.emplace_back(new_chunk_size);
|
|
|
}
|
|
|
|
|
|
template <typename... Args>
|
|
|
@@ -31,17 +23,21 @@ public:
|
|
|
}
|
|
|
|
|
|
void ReleaseContents() {
|
|
|
- Chunk* chunk{&root};
|
|
|
- while (chunk) {
|
|
|
- if (chunk->free_objects == chunk_size) {
|
|
|
- break;
|
|
|
- }
|
|
|
- for (; chunk->free_objects < chunk_size; ++chunk->free_objects) {
|
|
|
- chunk->storage[chunk->free_objects].object.~T();
|
|
|
- }
|
|
|
- chunk = chunk->next.get();
|
|
|
+ if (chunks.empty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Chunk& root{chunks.front()};
|
|
|
+ if (root.used_objects == root.num_objects) {
|
|
|
+ // Root chunk has been filled, squash allocations into it
|
|
|
+ const size_t total_objects{root.num_objects + new_chunk_size * (chunks.size() - 1)};
|
|
|
+ chunks.clear();
|
|
|
+ chunks.emplace_back(total_objects);
|
|
|
+ chunks.shrink_to_fit();
|
|
|
+ } else {
|
|
|
+ root.Release();
|
|
|
+ chunks.resize(1);
|
|
|
+ chunks.shrink_to_fit();
|
|
|
}
|
|
|
- node = &root;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
@@ -58,31 +54,51 @@ private:
|
|
|
};
|
|
|
|
|
|
struct Chunk {
|
|
|
- size_t free_objects = chunk_size;
|
|
|
- std::array<Storage, chunk_size> storage;
|
|
|
- std::unique_ptr<Chunk> next;
|
|
|
+ explicit Chunk() = default;
|
|
|
+ explicit Chunk(size_t size)
|
|
|
+ : num_objects{size}, storage{std::make_unique<Storage[]>(size)} {}
|
|
|
+
|
|
|
+ Chunk& operator=(Chunk&& rhs) noexcept {
|
|
|
+ Release();
|
|
|
+ used_objects = std::exchange(rhs.used_objects, 0);
|
|
|
+ num_objects = std::exchange(rhs.num_objects, 0);
|
|
|
+ storage = std::move(rhs.storage);
|
|
|
+ }
|
|
|
+
|
|
|
+ Chunk(Chunk&& rhs) noexcept
|
|
|
+ : used_objects{std::exchange(rhs.used_objects, 0)},
|
|
|
+ num_objects{std::exchange(rhs.num_objects, 0)}, storage{std::move(rhs.storage)} {}
|
|
|
+
|
|
|
+ ~Chunk() {
|
|
|
+ Release();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Release() {
|
|
|
+ std::destroy_n(storage.get(), used_objects);
|
|
|
+ used_objects = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t used_objects{};
|
|
|
+ size_t num_objects{};
|
|
|
+ std::unique_ptr<Storage[]> storage;
|
|
|
};
|
|
|
|
|
|
[[nodiscard]] T* Memory() {
|
|
|
Chunk* const chunk{FreeChunk()};
|
|
|
- return &chunk->storage[--chunk->free_objects].object;
|
|
|
+ return &chunk->storage[chunk->used_objects++].object;
|
|
|
}
|
|
|
|
|
|
[[nodiscard]] Chunk* FreeChunk() {
|
|
|
- if (node->free_objects > 0) {
|
|
|
- return node;
|
|
|
- }
|
|
|
- if (node->next) {
|
|
|
- node = node->next.get();
|
|
|
+ if (node->used_objects != node->num_objects) {
|
|
|
return node;
|
|
|
}
|
|
|
- node->next = std::make_unique<Chunk>();
|
|
|
- node = node->next.get();
|
|
|
+ node = &chunks.emplace_back(new_chunk_size);
|
|
|
return node;
|
|
|
}
|
|
|
|
|
|
- Chunk* node{&root};
|
|
|
- Chunk root;
|
|
|
+ Chunk* node{};
|
|
|
+ std::vector<Chunk> chunks;
|
|
|
+ size_t new_chunk_size{};
|
|
|
};
|
|
|
|
|
|
} // namespace Shader
|