Merge commit 'a51b88cccfbc2e51fbf5ce64b564002000548ec5' into Prompt/Allocator
This commit is contained in:
@@ -11,7 +11,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <iostream>
|
|
||||||
|
#include "wfc_allocator.hpp"
|
||||||
|
|
||||||
namespace WFC {
|
namespace WFC {
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ template <typename MaskType>
|
|||||||
class Wave {
|
class Wave {
|
||||||
public:
|
public:
|
||||||
Wave() = default;
|
Wave() = default;
|
||||||
Wave(size_t size, size_t variableAmount) : m_data(size)
|
Wave(size_t size, size_t variableAmount, WFCStackAllocator& allocator) : m_data(size, WFCStackAllocatorAdapter<MaskType>(allocator))
|
||||||
{
|
{
|
||||||
for (auto& wave : m_data) wave = (1 << variableAmount) - 1;
|
for (auto& wave : m_data) wave = (1 << variableAmount) - 1;
|
||||||
}
|
}
|
||||||
@@ -163,7 +164,7 @@ public:
|
|||||||
MaskType GetMask(size_t index) const { return m_data[index]; }
|
MaskType GetMask(size_t index) const { return m_data[index]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<MaskType> m_data;
|
WFCVector<MaskType> m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,7 +176,7 @@ public:
|
|||||||
using MaskType = typename VariableIDMapT::MaskType;
|
using MaskType = typename VariableIDMapT::MaskType;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Constrainer(Wave<MaskType>& wave, std::queue<size_t>& propagationQueue)
|
Constrainer(Wave<MaskType>& wave, WFCQueue<size_t>& propagationQueue)
|
||||||
: m_wave(wave)
|
: m_wave(wave)
|
||||||
, m_propagationQueue(propagationQueue)
|
, m_propagationQueue(propagationQueue)
|
||||||
{}
|
{}
|
||||||
@@ -224,7 +225,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Wave<MaskType>& m_wave;
|
Wave<MaskType>& m_wave;
|
||||||
std::queue<size_t>& m_propagationQueue;
|
WFCQueue<size_t>& m_propagationQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,14 +256,17 @@ public:
|
|||||||
public:
|
public:
|
||||||
struct SolverState {
|
struct SolverState {
|
||||||
WorldT& world;
|
WorldT& world;
|
||||||
std::queue<size_t> propagationQueue{};
|
WFCQueue<size_t> propagationQueue;
|
||||||
Wave<MaskType> wave{};
|
Wave<MaskType> wave;
|
||||||
std::mt19937& rng;
|
std::mt19937& rng;
|
||||||
|
WFCStackAllocator& allocator;
|
||||||
|
|
||||||
SolverState(WorldT& world, size_t variableAmount, std::mt19937& rng)
|
SolverState(WorldT& world, size_t variableAmount, std::mt19937& rng, WFCStackAllocator& allocator)
|
||||||
: world(world)
|
: world(world)
|
||||||
, wave(world.size(), variableAmount)
|
, propagationQueue{ WFCStackAllocatorAdapter<size_t>(allocator) }
|
||||||
|
, wave{ world.size(), variableAmount, allocator }
|
||||||
, rng(rng)
|
, rng(rng)
|
||||||
|
, allocator(allocator)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SolverState(const SolverState& other) = default;
|
SolverState(const SolverState& other) = default;
|
||||||
@@ -276,10 +280,9 @@ public:
|
|||||||
public:
|
public:
|
||||||
bool Run(WorldT& world, bool propagateInitialValues = false)
|
bool Run(WorldT& world, bool propagateInitialValues = false)
|
||||||
{
|
{
|
||||||
//auto seed = std::random_device{}();
|
WFCStackAllocator allocator{};
|
||||||
auto seed = 1844803044ul;
|
std::mt19937 random{ std::random_device{}() };
|
||||||
std::mt19937 random{ seed };
|
SolverState state(world, m_variables.size(), random, allocator);
|
||||||
SolverState state(world, m_variables.size(), random);
|
|
||||||
return Run(state, propagateInitialValues);
|
return Run(state, propagateInitialValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,6 +391,7 @@ private:
|
|||||||
|
|
||||||
{
|
{
|
||||||
// copy the state and branch out
|
// copy the state and branch out
|
||||||
|
auto stackFrame = state.allocator.createFrame();
|
||||||
SolverState newState(state);
|
SolverState newState(state);
|
||||||
newState.wave.Collapse(minEntropyCell, 1 << selectedValue);
|
newState.wave.Collapse(minEntropyCell, 1 << selectedValue);
|
||||||
newState.propagationQueue.push(minEntropyCell);
|
newState.propagationQueue.push(minEntropyCell);
|
||||||
|
|||||||
245
include/nd-wfc/wfc_allocator.hpp
Normal file
245
include/nd-wfc/wfc_allocator.hpp
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace WFC {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stack allocator specifically designed for WFC branching operations
|
||||||
|
*
|
||||||
|
* This allocator uses a stack-based approach where memory is allocated in chunks
|
||||||
|
* and automatically deallocated when branches go out of scope. It's optimized
|
||||||
|
* for the recursive branching pattern used in the WFC algorithm.
|
||||||
|
*/
|
||||||
|
class WFCStackAllocator {
|
||||||
|
private:
|
||||||
|
struct Block {
|
||||||
|
void* ptr;
|
||||||
|
size_t size;
|
||||||
|
size_t alignment;
|
||||||
|
|
||||||
|
Block() : ptr(nullptr), size(0), alignment(0) {}
|
||||||
|
Block(void* p, size_t s, size_t a) : ptr(p), size(s), alignment(a) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct allocator with initial capacity
|
||||||
|
* @param initialCapacity Initial memory pool size in bytes
|
||||||
|
*/
|
||||||
|
explicit WFCStackAllocator(size_t initialCapacity = 1024 * 1024) // 1MB default
|
||||||
|
: m_capacity(initialCapacity)
|
||||||
|
, m_used(0)
|
||||||
|
, m_pool(nullptr)
|
||||||
|
{
|
||||||
|
m_pool = std::aligned_alloc(64, m_capacity); // 64-byte alignment for cache efficiency
|
||||||
|
if (!m_pool) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~WFCStackAllocator() {
|
||||||
|
if (m_pool) {
|
||||||
|
std::free(m_pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-copyable, non-movable for safety
|
||||||
|
WFCStackAllocator(const WFCStackAllocator&) = delete;
|
||||||
|
WFCStackAllocator& operator=(const WFCStackAllocator&) = delete;
|
||||||
|
WFCStackAllocator(WFCStackAllocator&&) = delete;
|
||||||
|
WFCStackAllocator& operator=(WFCStackAllocator&&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate memory from the stack
|
||||||
|
* @param size Number of bytes to allocate
|
||||||
|
* @param alignment Memory alignment requirement (default 8)
|
||||||
|
* @return Pointer to allocated memory
|
||||||
|
*/
|
||||||
|
void* allocate(size_t size, size_t alignment = 8) {
|
||||||
|
// Align the current position
|
||||||
|
size_t alignedUsed = alignUp(m_used, alignment);
|
||||||
|
|
||||||
|
// Check if we have enough space
|
||||||
|
if (alignedUsed + size > m_capacity) {
|
||||||
|
// Grow the pool if needed
|
||||||
|
growPool(alignedUsed + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ptr = static_cast<char*>(m_pool) + alignedUsed;
|
||||||
|
m_used = alignedUsed + size;
|
||||||
|
|
||||||
|
// Track this allocation for proper cleanup
|
||||||
|
m_allocations.emplace_back(ptr, size, alignment);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deallocate memory (no-op as requested - memory freed when branch goes out of scope)
|
||||||
|
* @param ptr Pointer to deallocate
|
||||||
|
*/
|
||||||
|
void deallocate(void*) {
|
||||||
|
// No-op as requested - deallocation happens when branch goes out of scope
|
||||||
|
// The stack nature ensures automatic cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a stack frame marker for RAII-based cleanup
|
||||||
|
* @return StackFrame object that will cleanup on destruction
|
||||||
|
*/
|
||||||
|
class StackFrame {
|
||||||
|
private:
|
||||||
|
WFCStackAllocator& m_allocator;
|
||||||
|
size_t m_savedUsed;
|
||||||
|
size_t m_savedAllocCount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StackFrame(WFCStackAllocator& allocator)
|
||||||
|
: m_allocator(allocator)
|
||||||
|
, m_savedUsed(allocator.m_used)
|
||||||
|
, m_savedAllocCount(allocator.m_allocations.size())
|
||||||
|
{}
|
||||||
|
|
||||||
|
~StackFrame() {
|
||||||
|
// Restore the stack to the previous state
|
||||||
|
m_allocator.m_used = m_savedUsed;
|
||||||
|
m_allocator.m_allocations.resize(m_savedAllocCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-copyable, movable
|
||||||
|
StackFrame(const StackFrame&) = delete;
|
||||||
|
StackFrame& operator=(const StackFrame&) = delete;
|
||||||
|
StackFrame(StackFrame&&) = default;
|
||||||
|
StackFrame& operator=(StackFrame&&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new stack frame for a branch
|
||||||
|
*/
|
||||||
|
StackFrame createFrame() {
|
||||||
|
return StackFrame(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current memory usage
|
||||||
|
*/
|
||||||
|
size_t getUsed() const { return m_used; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get total capacity
|
||||||
|
*/
|
||||||
|
size_t getCapacity() const { return m_capacity; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get allocation count
|
||||||
|
*/
|
||||||
|
size_t getAllocationCount() const { return m_allocations.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the allocator (useful for reusing between WFC runs)
|
||||||
|
*/
|
||||||
|
void reset() {
|
||||||
|
m_used = 0;
|
||||||
|
m_allocations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t alignUp(size_t value, size_t alignment) const {
|
||||||
|
return (value + alignment - 1) & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void growPool(size_t requiredSize) {
|
||||||
|
size_t newCapacity = std::max(m_capacity * 2, requiredSize);
|
||||||
|
|
||||||
|
void* newPool = std::aligned_alloc(64, newCapacity);
|
||||||
|
if (!newPool) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy existing data
|
||||||
|
std::memcpy(newPool, m_pool, m_used);
|
||||||
|
|
||||||
|
// Update all existing pointers in allocations
|
||||||
|
ptrdiff_t offset = static_cast<char*>(newPool) - static_cast<char*>(m_pool);
|
||||||
|
for (auto& block : m_allocations) {
|
||||||
|
block.ptr = static_cast<char*>(block.ptr) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::free(m_pool);
|
||||||
|
m_pool = newPool;
|
||||||
|
m_capacity = newCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_capacity;
|
||||||
|
size_t m_used;
|
||||||
|
void* m_pool;
|
||||||
|
std::vector<Block> m_allocations;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Custom allocator adapter for STL containers using WFCStackAllocator
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class WFCStackAllocatorAdapter {
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using pointer = T*;
|
||||||
|
using const_pointer = const T*;
|
||||||
|
using reference = T&;
|
||||||
|
using const_reference = const T&;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
struct rebind {
|
||||||
|
using other = WFCStackAllocatorAdapter<U>;
|
||||||
|
};
|
||||||
|
|
||||||
|
WFCStackAllocatorAdapter(WFCStackAllocator& allocator)
|
||||||
|
: m_allocator(&allocator) {}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
WFCStackAllocatorAdapter(const WFCStackAllocatorAdapter<U>& other)
|
||||||
|
: m_allocator(other.m_allocator) {}
|
||||||
|
|
||||||
|
pointer allocate(size_type n) {
|
||||||
|
return static_cast<pointer>(m_allocator->allocate(n * sizeof(T), alignof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(pointer ptr, size_type) {
|
||||||
|
m_allocator->deallocate(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
bool operator==(const WFCStackAllocatorAdapter<U>& other) const {
|
||||||
|
return m_allocator == other.m_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
bool operator!=(const WFCStackAllocatorAdapter<U>& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
WFCStackAllocator* m_allocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stack-allocated vector using WFCStackAllocator
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
using WFCVector = std::vector<T, WFCStackAllocatorAdapter<T>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stack-allocated queue using WFCStackAllocator
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
using WFCQueue = std::queue<T, std::deque<T, WFCStackAllocatorAdapter<T>>>;
|
||||||
|
|
||||||
|
} // namespace WFC
|
||||||
Reference in New Issue
Block a user