diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b71d590 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third/tracy"] + path = third/tracy + url = https://github.com/wolfpld/tracy.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 15bf11d..0604b16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,16 @@ set_target_properties(sand PROPERTIES CXX_STANDARD_REQUIRED ON ) +if (EXISTS third/tracy/CMakeLists.txt) + option(TRACY_ENABLE "Enable Tracy" ON) + set(TRACY_STATIC OFF) + set(TRACY_ON_DEMAND ON) + add_subdirectory(third/tracy) + target_link_libraries(sand PUBLIC Tracy::TracyClient) +else() + target_compile_definitions(sand PUBLIC FrameMark ZoneScoped) +endif() + find_package(SDL3 CONFIG REQUIRED) add_executable(sandtest diff --git a/include/sand.h b/include/sand.h index 890b9e6..eaf7d9f 100644 --- a/include/sand.h +++ b/include/sand.h @@ -2,6 +2,7 @@ #define SAND_H #include +#include #include #include #include @@ -22,6 +23,8 @@ class TypeBuilder friend RulesBuilder; public: + TypeBuilder(); + TypeBuilder& add_type(const std::string& name); TypeBuilder& add_type(const std::string& name, const std::string& default_result); @@ -62,7 +65,7 @@ class Rule friend RulesBuilder; public: - using match_fn = bool (*)(const std::string& name); + using match_fn = std::function; ~Rule(); @@ -98,6 +101,10 @@ private: Sand(const RulesBuilder& builder, uint16_t width, uint16_t height, const std::string& initial); + uint16_t apply_rule(uint16_t tile, unsigned __int128 key) const; + + static unsigned __int128 apply_mask(unsigned __int128 key, uint8_t mask); + uint16_t width; uint16_t height; type_map types; diff --git a/src/main.cpp b/src/main.cpp index f6ed8a4..3f3a703 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,47 +6,109 @@ #include #include #include +#include -constexpr uint16_t WIDTH = 256; -constexpr uint16_t HEIGHT = 256; +constexpr uint16_t WIDTH = 128; +constexpr uint16_t HEIGHT = 128; constexpr int WINDOW_WIDTH = 1024; constexpr int WINDOW_HEIGHT = 1024; -uint8_t lookup[][3] - = { { 0, 0, 0 }, { 50, 0, 0 }, { 100, 100, 100 }, { 100, 100, 100 } }; +struct color +{ + uint8_t c[3]; +}; + +color lookup_color(uint16_t id) +{ + if (id == 0) + { + return { 0, 0, 0 }; + } + else if (id == 1) + { + return { 150, 150, 255 }; + } + else + { + color c; + c.c[0] = 257 - id; + c.c[1] = 257 - id; + c.c[2] = 257 - id; + return c; + } +} int main(int argc, char** argv) { - auto rb = sand::TypeBuilder() - .add_type("offgrid") - .add_type("air") - .add_type("stone1", "stone2") - .add_type("stone2", "stone1") - .finish(); + auto tb = sand::TypeBuilder().add_type("air"); - rb.add_rule("air", "stone2") - .top_middle([](const auto& t) { return t == "stone1"; }); - rb.add_rule("air", "stone1") - .top_middle([](const auto& t) { return t == "stone2"; }); - rb.add_rule("air", "stone2") - .top_right([](const auto& t) { return t == "stone1"; }) - .middle_right([](const auto& t) { return t != "air"; }); - rb.add_rule("air", "stone1") - .top_left([](const auto& t) { return t == "stone2"; }) - .middle_left([](const auto& t) { return t != "air"; }); + for (int i = 0; i < 255; i++) + { + tb.add_type("stone" + std::to_string(i), "stone" + std::to_string(i + 1)); + } - rb.add_rule("stone1", "air") - .bottom_middle([](const auto& t) { return t == "air"; }); - rb.add_rule("stone1", "air") - .bottom_left([](const auto& t) { return t == "air"; }) - .bottom_middle([](const auto& t) { return t != "air"; }); + auto rb = tb.finish(); - rb.add_rule("stone2", "air") - .bottom_middle([](const auto& t) { return t == "air"; }); - rb.add_rule("stone2", "air") - .bottom_right([](const auto& t) { return t == "air"; }) - .bottom_middle([](const auto& t) { return t != "air"; }); + rb.add_rule("air", "stone0") + .bottom_middle([](const std::string& t) { return t == "offgrid"; }) + .top_middle([](const std::string& t) { return t == "air"; }); + + for (int i = 0; i < 128; i++) + { + auto ar = rb.add_rule("air", "stone" + std::to_string(i + 1)); + auto afn + = [i](const std::string& t) { return t == "stone" + std::to_string(i); }; + + auto sr = rb.add_rule("stone" + std::to_string(i), "air"); + auto sfn = [](const std::string& t) { return t == "air"; }; + + switch (i % 3) + { + case 0: + ar.bottom_left(afn); + sr.top_right(sfn); + break; + + case 1: + ar.bottom_middle(afn); + sr.top_middle(sfn); + break; + + case 2: + ar.bottom_right(afn); + sr.top_left(sfn); + break; + } + } + + for (int i = 128; i < 255; i++) + { + auto ar = rb.add_rule("air", "stone" + std::to_string(i + 1)); + auto afn + = [i](const std::string& t) { return t == "stone" + std::to_string(i); }; + + auto sr = rb.add_rule("stone" + std::to_string(i), "air"); + auto sfn = [](const std::string& t) { return t == "air"; }; + + switch (i % 3) + { + case 0: + ar.top_left(afn); + sr.bottom_right(sfn); + break; + + case 1: + ar.top_middle(afn); + sr.bottom_middle(sfn); + break; + + case 2: + ar.top_right(afn); + sr.bottom_left(sfn); + break; + } + } auto s = rb.build(WIDTH, HEIGHT, "air"); @@ -77,20 +139,17 @@ int main(int argc, char** argv) float x, y; int state; - if (time > 1 / 1000.0f) + time = 0; + s.update(); + + if ((state = SDL_GetMouseState(&x, &y))) { - time = 0; - s.update(); + int ix = x * WIDTH / WINDOW_WIDTH; + int iy = y * HEIGHT / WINDOW_HEIGHT; - if ((state = SDL_GetMouseState(&x, &y))) + if (ix >= 0 && ix < WIDTH && iy >= 0 && iy < HEIGHT) { - int ix = x * WIDTH / WINDOW_WIDTH; - int iy = y * HEIGHT / WINDOW_HEIGHT; - - if (ix >= 0 && ix < WIDTH && iy >= 0 && iy < HEIGHT) - { - s.set(ix, iy, state == 1 ? "stone1" : "stone2"); - } + s.set(ix, iy, "stone0"); } } @@ -100,13 +159,15 @@ int main(int argc, char** argv) for (int j = 0; j < HEIGHT; j++) { memcpy(&static_cast(surface->pixels)[3 * (i + j * WIDTH)], - lookup[s.get(i, j)], 3); + lookup_color(s.get(i, j)).c, 3); } } SDL_UnlockSurface(surface); SDL_BlitSurfaceScaled(surface, nullptr, SDL_GetWindowSurface(window), nullptr, SDL_SCALEMODE_NEAREST); SDL_UpdateWindowSurface(window); + + FrameMark; } SDL_DestroySurface(surface); diff --git a/src/sand.cpp b/src/sand.cpp index 1da5270..83450e1 100644 --- a/src/sand.cpp +++ b/src/sand.cpp @@ -1,40 +1,59 @@ #include "sand.h" #include +#include #include +#include using namespace sand; namespace { +constexpr uint16_t OFFGRID_VALUE = 0; + template -T& binary_search(T element, T* elements, size_t begin, size_t end, - auto (*fn)(T, T)->bool) +int binary_search(T element, const T* elements, size_t begin, size_t end, + auto (*fn)(T, T)->std::strong_ordering) { if (end - begin == 1) { - return elements[begin]; + if (fn(element, elements[begin]) == std::strong_ordering::equal) + { + return begin; + } + else + { + + return -1; + } } size_t index = begin + (end - begin) / 2; - if (!fn(element, elements[index]) && !fn(elements[index], element)) - { - return elements[index]; - } - else if (fn(element, elements[index])) - { - return binary_search(element, elements, index, end, fn); - } - else + auto comparison_result = fn(element, elements[index]); + + if (comparison_result == std::strong_ordering::less) { return binary_search(element, elements, begin, index, fn); } + + if (comparison_result == std::strong_ordering::greater) + { + return binary_search(element, elements, index, end, fn); + } + + return index; } } // namespace +TypeBuilder::TypeBuilder() : + next_type_id(OFFGRID_VALUE) +{ + add_type("offgrid"); +} + TypeBuilder& TypeBuilder::add_type(const std::string& name) { if (!types.contains(name)) @@ -126,28 +145,84 @@ void RulesBuilder::finish_rule(const Rule& rule) { unsigned __int128 neighbors = 0; - neighbors += tl_type.id; + if (rule.matches[0]) + { + neighbors += tl_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += tm_type.id; + if (rule.matches[1]) + { + neighbors += tm_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += tr_type.id; + if (rule.matches[2]) + { + neighbors += tr_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += ml_type.id; + if (rule.matches[3]) + { + neighbors += ml_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += mr_type.id; + if (rule.matches[4]) + { + neighbors += mr_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += bl_type.id; + if (rule.matches[5]) + { + neighbors += bl_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += bm_type.id; + if (rule.matches[6]) + { + neighbors += bm_type.id; + } + else + { + neighbors += 0xFFFF; + } neighbors <<= 16; - neighbors += br_type.id; + if (rule.matches[7]) + { + neighbors += br_type.id; + } + else + { + neighbors += 0xFFFF; + } if (!rules.contains(rule.source)) { @@ -157,21 +232,61 @@ void RulesBuilder::finish_rule(const Rule& rule) rules[rule.source].push_back( { neighbors, rule.to }); } - } + + if (!rule.matches[7]) + { + break; + } + } // bottom right } - } + + if (!rule.matches[6]) + { + break; + } + } // bottom middle } - } + + if (!rule.matches[5]) + { + break; + } + } // bottom left } - } + + if (!rule.matches[4]) + { + break; + } + } // middle right } - } + + if (!rule.matches[3]) + { + break; + } + } // middle left } - } + + if (!rule.matches[2]) + { + break; + } + } // top right } - } + + if (!rule.matches[1]) + { + break; + } + } // top middle } - } + + if (!rule.matches[0]) + { + break; + } + } // top left } Rule::~Rule() { builder.finish_rule(*this); } @@ -253,6 +368,12 @@ Sand::Sand(const RulesBuilder& builder, uint16_t width, uint16_t height, { default_conversions[type.id] = type.default_result; } + + int cnt = 0; + for (const auto& [key, elem] : rules) + { + cnt += elem.size(); + } } void Sand::set(uint16_t x, uint16_t y, const std::string& type) @@ -267,6 +388,8 @@ uint16_t Sand::get(uint16_t x, uint16_t y) void Sand::update() { + ZoneScoped; + int n; #pragma omp parallel for private(n) for (n = 0; n < width * height; n++) @@ -274,73 +397,133 @@ void Sand::update() int i = n % width; int j = n / height; - auto& current_rule = rules[get(i, j)]; - unsigned __int128 neighbors = 0; if (i > 0 && j > 0) { neighbors += get(i - 1, j - 1); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (j > 0) { neighbors += get(i, j - 1); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (i < width - 1 && j > 0) { neighbors += get(i + 1, j - 1); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (i > 0) { neighbors += get(i - 1, j); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (i < width - 1) { neighbors += get(i + 1, j); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (i > 0 && j < height - 1) { neighbors += get(i - 1, j + 1); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (j < height - 1) { neighbors += get(i, j + 1); } + else + { + neighbors += OFFGRID_VALUE; + } neighbors <<= 16; if (i < width - 1 && j < height - 1) { neighbors += get(i + 1, j + 1); } - - uint16_t result = default_conversions[get(i, j)]; - if (!current_rule.empty()) + else { - std::pair element = { neighbors, 0 }; - bool (*fn)(decltype(element), decltype(element)) - = [](decltype(element) a, decltype(element) b) -> bool - { return a.first > b.first; }; - const auto& found = binary_search(element, current_rule.data(), 0, - current_rule.size(), fn); - if (found.first == neighbors) - { - result = found.second; - } + neighbors += OFFGRID_VALUE; } - elements[i + j * width + !iter * width * height] = result; + elements[i + j * width + !iter * width * height] + = apply_rule(get(i, j), neighbors); } iter = !iter; } + +uint16_t Sand::apply_rule(uint16_t tile, unsigned __int128 key) const +{ + if (rules.contains(tile)) + { + auto& rules_for_tile = rules.at(tile); + + if (!rules_for_tile.empty()) + { + for (int mask = 0; mask < 256; mask++) + { + std::pair element + = { apply_mask(key, mask), 0 }; + std::strong_ordering (*fn)(decltype(element), decltype(element)) + = [](auto a, auto b) { return a.first <=> b.first; }; + + auto found = binary_search(element, rules_for_tile.data(), 0, + rules_for_tile.size(), fn); + if (found >= 0) + { + return rules_for_tile[found].second; + } + } + } + } + + return default_conversions.at(tile); +} + +unsigned __int128 Sand::apply_mask(unsigned __int128 key, uint8_t mask) +{ + unsigned __int128 any = 0xFFFF; + for (int i = 0; i < 8; i++) + { + if (mask & 1) + { + key |= any; + } + + mask >>= 1; + any <<= 16; + } + return key; +} diff --git a/third/tracy b/third/tracy new file mode 160000 index 0000000..25f09be --- /dev/null +++ b/third/tracy @@ -0,0 +1 @@ +Subproject commit 25f09bee2cdb680d025d150188925e116fc1e8d4