1
0
Fork 0

Generational id

This commit is contained in:
May B. 2020-08-04 17:44:53 +02:00
parent 8d0c9194b0
commit ff9942691a
13 changed files with 261 additions and 65 deletions

View File

@ -37,6 +37,8 @@
- [ ] ZeroMQ
- [x] Logger
- [ ] FastNoiseSIMD / HastyNoise double precision
- [ ] Generation identifier
- [ ] Limit map usage
## Rendering
- [x] Render triangle

View File

@ -110,15 +110,16 @@ namespace contouring {
void FlatDualMC::render(const surrounding::corners &surrounding, buffer::ShortIndexed::Data &out) const {
const int SIZE = CHUNK_LENGTH + 3;
std::vector<dualmc::DualMC<float>::Point> grid;
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
{
grid.reserve(SIZE * SIZE * SIZE);
for (int z = 0; z < SIZE; z++) {
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
auto &cell = grid[((z * SIZE) + y) * SIZE + x];
const auto &chunk = surrounding[(z >= CHUNK_LENGTH) + (y >= CHUNK_LENGTH)*2 + (x >= CHUNK_LENGTH)*4];
const auto &voxel = chunk->get(glm::toIdx(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
grid.emplace_back(voxel.density() * 1.f / world::Voxel::DENSITY_MAX, voxel.material());
cell.x = voxel.density() * 1.f / world::Voxel::DENSITY_MAX;
cell.w = voxel.material();
}}}
}
{

View File

@ -36,9 +36,9 @@ namespace contouring {
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
struct report {
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
circular_buffer<float> load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> loaded = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
report_buffer count;
report_buffer load;
report_buffer loaded;
} reports;
bool running = true;

View File

@ -34,9 +34,9 @@ namespace contouring {
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
struct report {
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
circular_buffer<float> load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> loaded = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
report_buffer count;
report_buffer load;
report_buffer loaded;
} reports;
bool running = true;

View File

@ -25,4 +25,8 @@ namespace data {
return buffer[last];
}
};
struct report_buffer: circular_buffer<float> {
report_buffer() : circular_buffer(256, 0) {}
};
}

190
src/data/generational.hpp Normal file
View File

@ -0,0 +1,190 @@
#pragma once
#include <cassert>
#include <vector>
#include <optional>
namespace data::generational {
struct id {
id(size_t index, size_t generation = 0): index(index), generation(generation) { }
id(): id(0) { }
size_t index;
size_t generation;
bool operator==(const id &i) const { return index == i.index && generation == i.generation; }
};
class allocator {
public:
allocator() { }
template <typename C>
allocator(const C& map) {
for(const auto& [key, _]: map) {
if (const auto size = entries.size(); key >= size) {
entries.resize(key + 1);
for (size_t i = size; i < entries.size(); i++) {
entries[i].is_live = false;
freed.push_back(i);
}
}
assert(!entries[key].is_live);
entries[key].is_live = true;
}
}
id alloc() {
if(freed.empty()) {
const auto idx = entries.size();
entries.emplace_back();
return id(idx);
} else {
const auto idx = freed.back();
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.is_live);
entry.is_live = true;
return id(idx, ++entries[idx].generation);
}
}
bool is_live(id id) const {
return id.index < entries.size() &&
entries[id.index].is_live &&
entries[id.index].generation == id.generation;
}
bool free(id id) {
if(!is_live(id))
return false;
entries[id.index].is_live = false;
freed.push_back(id.index);
return true;
}
private:
struct entry {
bool is_live = true;
size_t generation = 0;
};
std::vector<entry> entries;
std::vector<size_t> freed;
};
template<typename T>
class vector {
public:
vector() { }
template<typename C>
vector(const C& map) {
for(const auto& [key, value]: map) {
if (const auto size = entries.size(); key >= size) {
entries.resize(key + 1);
}
entries[key].value = value;
}
for (size_t i = 0; i < entries.size(); i++) {
if(!entries[i].value.has_value())
freed.push_back(i);
}
}
id push(const T& in) {
if(freed.empty()) {
const auto idx = entries.size();
entries.emplace_back(in);
return id(idx);
} else {
const auto idx = freed.back();
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.value.has_value());
entry.value = in;
return id(idx, ++entries[idx].generation);
}
}
template <typename... _Args>
id emplace(_Args &&... __args) {
if(freed.empty()) {
const auto idx = entries.size();
entries.emplace_back(std::forward<_Args>(__args)...);
return id(idx);
} else {
const auto idx = freed.back();
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.value.has_value());
entry.value = std::make_optional<T>(std::forward<_Args>(__args)...);
return id(idx, ++entries[idx].generation);
}
}
bool put(id idx, const T& in) {
if(idx.index >= entries.size())
return false;
if(entries[idx.index].generation != idx.generation || entries[idx.index].value.has_value())
return false;
entries[idx.index].value = in;
return true;
}
bool contains(id idx) const {
return idx.index < entries.size() &&
entries[idx.index].generation == idx.generation &&
entries[idx.index].value.has_value();
}
bool free(id idx) {
if(!contains(idx))
return false;
entries[idx.index].value = std::nullopt;
freed.push_back(idx.index);
return true;
}
template<typename apply>
void iter(apply fn) const {
for (size_t i = 0; i < entries.size(); i++) {
const auto &entry = entries[i];
if(entry.value.has_value()) {
fn(id(i, entry.generation), entry.value.value());
}
}
}
template<typename extractor>
void extract(extractor fn) {
for (size_t i = 0; i < entries.size(); i++) {
auto &entry = entries[i];
if(entry.value.has_value()) {
if(fn(id(i, entry.generation), entry.value.value()))
entry.value = std::nullopt;
}
}
}
size_t size() const {
return std::count_if(entries.begin(), entries.end(),
[](const entry &e) { return e.value.has_value(); });
}
private:
struct entry {
entry(): value(std::nullopt), generation(0) { }
entry(const T &in, size_t gen = 0):
value(in), generation(gen) { }
std::optional<T> value;
size_t generation;
};
std::vector<entry> entries;
std::vector<size_t> freed;
};
}
namespace std {
template<>
struct hash<data::generational::id> {
std::size_t operator()(const data::generational::id& i) const noexcept {
return std::hash<size_t>{}(i.index);
}
};
}

View File

@ -167,7 +167,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::Begin("Editor", &options.editor_show, ImGuiWindowFlags_AlwaysAutoResize);
if (state.look_at.has_value()) {
const auto &look = state.look_at.value();
ImGui::Text("Look at: (%ld: %lld, %lld, %lld) (%s, %.1f)", look.pos.first, look.pos.second.x, look.pos.second.y, look.pos.second.z,
ImGui::Text("Look at: (%ld: %lld, %lld, %lld) (%s, %.1f)", look.pos.first.index, look.pos.second.x, look.pos.second.y, look.pos.second.z,
world::materials::textures[look.value.material()].c_str(), look.value.density() * 1. / world::Voxel::DENSITY_MAX);
const auto w_pos = look.pos.second + look.offset;
ImGui::Text("(%.3f, %.3f, %.3f)", w_pos.x * 1. / options.voxel_density, w_pos.y * 1. / options.voxel_density, w_pos.z * 1. / options.voxel_density);

View File

@ -17,14 +17,15 @@
#include "contouring/index.hpp"
inline glm::vec4 fromHex(const std::string& str) {
int rgb[3] = {UCHAR_MAX};
std::array<int, 3> rgb = {UCHAR_MAX};
sscanf(str.c_str() + 1, "%02X%02X%02X", (unsigned int *)&rgb[0], (unsigned int *)&rgb[1], (unsigned int *)&rgb[2]);
return glm::vec4(rgb[0] * 1.f / UCHAR_MAX, rgb[1] * 1.f / UCHAR_MAX, rgb[2] * 1.f / UCHAR_MAX, 1);
}
inline std::string toHexa(const glm::vec4& rgb) {
auto out = (char*)malloc(8 * sizeof(char));
sprintf(out, "#%02X%02X%02X", (int)(rgb.x * UCHAR_MAX), (int)(rgb.y * UCHAR_MAX), (int)(rgb.z * UCHAR_MAX));
return std::string(out);
std::ostringstream sstr;
sstr << std::hex << std::setw(2) << std::setfill('0') <<
static_cast<int>(rgb.x * UCHAR_MAX) << static_cast<int>(rgb.y * UCHAR_MAX) << static_cast<int>(rgb.z * UCHAR_MAX) << std::endl;
return sstr.str();
}
/// Savable game options
@ -107,9 +108,9 @@ struct options {
{"mode", contouring::names[contouring_idx]},
{"options", toml::table()}
}));
for(const auto opt: contouring_data) {
if(!opt.second.empty())
config["mesh"]["options"].as_table()->insert_or_assign(opt.first, opt.second);
for(const auto& [key, val]: contouring_data) {
if(!val.empty())
config["mesh"]["options"].as_table()->insert_or_assign(key, val);
}
config.insert_or_assign("camera", toml::table({
@ -185,7 +186,7 @@ struct state {
std::shared_ptr<contouring::Abstract> contouring;
char console_buffer[256];
std::array<char, 256> console_buffer;
};
/// Readonly metrics
@ -193,11 +194,11 @@ struct reports {
struct main {
size_t tris_count = 0;
size_t models_count = 0;
circular_buffer<float> fps = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> update = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> render = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> swap = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> wait = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
report_buffer fps;
report_buffer update;
report_buffer render;
report_buffer swap;
report_buffer wait;
} main;
world::Universe::report world;
};

View File

@ -26,26 +26,29 @@ Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"
if(index.good()) {
size_t size = 0;
index.read(reinterpret_cast<char *>(&size), sizeof(size));
std::map<size_t, Area::params> tmp;
while(!index.eof()) {
area_id id = 0;
index.read(reinterpret_cast<char *>(&id), sizeof(area_id));
size_t id = UINT32_MAX;
index.read(reinterpret_cast<char *>(&id), sizeof(size_t));
Area::params params{voxel_pos(0), 0};
index.read(reinterpret_cast<char *>(&params.center.x), sizeof(voxel_pos::value_type));
index.read(reinterpret_cast<char *>(&params.center.y), sizeof(voxel_pos::value_type));
index.read(reinterpret_cast<char *>(&params.center.z), sizeof(voxel_pos::value_type));
index.read(reinterpret_cast<char *>(&params.radius), sizeof(int));
index.read(reinterpret_cast<char *>(&params.seed), sizeof(int));
auto ok = far_areas.emplace(id, params).second;
[[maybe_unused]]
auto ok = tmp.emplace(id, params).second;
assert(ok && "Duplicated area");
index.peek();
}
assert(far_areas.size() == size && "Corrupted areas index");
assert(tmp.size() == size && "Corrupted areas index");
far_areas = data::generational::vector<Area::params>(tmp);
LOG_D(far_areas.size() << " areas loaded");
} else {
LOG_E("No index file!!! Probably a new world...");
//TODO: generate universe
far_areas.emplace(1, Area::params{voxel_pos(0), 1 << 20});
far_areas.emplace(2, Area::params{voxel_pos(0), 1, 43});
far_areas.emplace(Area::params{voxel_pos(0), 1 << 20});
far_areas.emplace(Area::params{voxel_pos(0), 1, 43});
}
index.close();
}
@ -144,24 +147,19 @@ void Universe::saveAreas() const {
size_t size = areas.size() + far_areas.size();
index.write(reinterpret_cast<char *>(&size), sizeof(size));
}
for(const auto& area: areas) {
auto id = area.first;
auto params = area.second->getParams();
index.write(reinterpret_cast<char *>(&id), sizeof(area_id));
std::function write = [&](area_id id, Area::params params) {
auto idx = id.index;
index.write(reinterpret_cast<char *>(&idx), sizeof(size_t));
index.write(reinterpret_cast<char *>(&params.center.x), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&params.center.y), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&params.center.z), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&params.radius), sizeof(int));
index.write(reinterpret_cast<char *>(&params.seed), sizeof(int));
};
for(const auto& area: areas) {
write(area.first, area.second->getParams());
}
for(auto far: far_areas) {
index.write(reinterpret_cast<char *>(&far.first), sizeof(area_id));
index.write(reinterpret_cast<char *>(&far.second.center.x), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&far.second.center.y), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&far.second.center.z), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&far.second.radius), sizeof(int));
index.write(reinterpret_cast<char *>(&far.second.seed), sizeof(int));
}
far_areas.iter(write);
if(!index.good())
LOG_E("Areas index write error");
@ -176,17 +174,15 @@ void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& r
if(chunkChange) {
rmt_ScopedCPUSample(Far, 0);
auto it = far_areas.begin();
while(it != far_areas.end()) {
const chunk_pos diff = glm::divide(pos - it->second.center);
if (glm::length2(diff) > glm::pow2(loadDistance + it->second.radius)) {
it++;
} else {
LOG_I("Load area " << it->first);
areas.emplace(it->first, std::make_shared<Area>(it->second));
it = far_areas.erase(it);
}
}
far_areas.extract([&](area_id id, Area::params params){
if (const chunk_pos diff = glm::divide(pos - params.center);
glm::length2(diff) > glm::pow2(loadDistance + params.radius))
return false;
LOG_I("Load area " << id.index);
areas.emplace(id, std::make_shared<Area>(params));
return true;
});
}
{ // Update alive areas
rmt_ScopedCPUSample(Update, 0);
@ -197,7 +193,7 @@ void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& r
auto it = areas.begin();
while (it != areas.end()) {
rmt_ScopedCPUSample(Area, 0);
const bool chunkChangeArea = (it->first == 2 && it->second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity
const bool chunkChangeArea = (false && it->first == 1 && it->second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity
const chunk_pos diff = glm::divide(pos - it->second->getOffset().as_voxel());
auto &chunks = it->second->setChunks();
if (glm::length2(diff) > glm::pow2(keepDistance + it->second->getChunks().getRadius())) {
@ -206,8 +202,10 @@ void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& r
saveQueue.emplace(*it, *it_c);
it_c = chunks.erase(it_c);
}
LOG_I("Remove area " << it->first);
far_areas.emplace(it->first, it->second->getParams());
LOG_I("Unload area " << it->first.index);
[[maybe_unused]]
auto ok = far_areas.put(it->first, it->second->getParams());
assert(ok);
it = areas.erase(it);
saveAreas();
} else {
@ -224,7 +222,7 @@ void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& r
const area_<chunk_pos> acPos = std::make_pair(it->first, it_c->first);
if (const auto neighbors = it_c->second->update()) {
contouring->onUpdate(acPos, diff, chunks, neighbors.value());
} else if (chunkChange || it->first == 2) {
} else if (chunkChangeArea) {
contouring->onNotify(acPos, diff, chunks);
}
++it_c;

View File

@ -35,13 +35,13 @@ namespace world {
/// Reports to UI
struct report {
/// Chunks in memory
circular_buffer<float> chunk_count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
report_buffer chunk_count;
/// Loaded chunks
circular_buffer<float> chunk_load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
report_buffer chunk_load;
/// Saved chunks
circular_buffer<float> chunk_unload = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
report_buffer chunk_unload;
/// Regions in memory
circular_buffer<float> region_count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
report_buffer region_count;
};
Universe(const options &);
@ -80,7 +80,7 @@ namespace world {
area_map areas;
using area_it_t = robin_hood::pair<area_id, std::shared_ptr<Area>>;
/// Dead areas
robin_hood::unordered_flat_map<area_id, Area::params> far_areas;
data::generational::vector<Area::params> far_areas;
void saveAreas() const;
bool running = true;

View File

@ -21,6 +21,7 @@
#include <functional>
#include "../data/glm.hpp"
#include "../data/generational.hpp"
const auto CHUNK_LENGTH = glm::IDX_LENGTH;
const auto REGION_LENGTH = glm::IDX_LENGTH;
@ -33,7 +34,7 @@ using region_pos = glm::ivec3;
using region_chunk_pos = glm::ucvec3;
using region_chunk_idx = glm::u16;
using area_id = uint64_t;
using area_id = data::generational::id;
template <class pos>
using area_ = std::pair<area_id, pos>;
struct area_hash {
@ -45,6 +46,5 @@ struct area_hash {
}
};
using area_pos = glm::ifvec3;
using entity_id = uint64_t;
using camera_pos = glm::ifvec3;

View File

@ -7,7 +7,7 @@ using namespace world;
#define REMOVE_CORRUPTED 1
FileRegion::FileRegion(const std::string &folderPath, const area_<region_pos> &pos) {
path = folderPath + '/' + std::to_string(pos.first) + '.' + std::to_string(pos.second.x) + '.' +
path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' +
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
load();

View File

@ -6,7 +6,7 @@ using namespace world;
#define LAZYNESS 8
MemoryRegion::MemoryRegion(const std::string &folderPath, const area_<region_pos> &pos) {
path = folderPath + '/' + std::to_string(pos.first) + '.' + std::to_string(pos.second.x) + '.' +
path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' +
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
load();