Skip to content

Commit 80e6349

Browse files
authored
feat: implement new hash registry method (#164)
1 parent a4b0921 commit 80e6349

File tree

9 files changed

+171
-0
lines changed

9 files changed

+171
-0
lines changed

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ ecsact_build_recipe(
158158
"ecsact_execute_systems",
159159
"ecsact_create_registry",
160160
"ecsact_clone_registry",
161+
"ecsact_hash_registry",
161162
"ecsact_destroy_registry",
162163
"ecsact_clear_registry",
163164
"ecsact_create_entity",

ecsact/entt/registry_util.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ auto copy_components( //
4040
::entt::registry& dst
4141
) -> void;
4242

43+
auto hash_registry(const ::entt::registry& reg) -> std::uint64_t;
44+
4345
} // namespace ecsact::entt

rt_entt_codegen/core/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ _CORE_CODEGEN_METHODS = {
5252
"system_notify": ["//rt_entt_codegen/shared:system_util"],
5353
"update_beforechange": [],
5454
"copy_components": [],
55+
"hash_registry": [],
5556
}
5657

5758
[cc_library(

rt_entt_codegen/core/core.hh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,9 @@ auto print_copy_components(
9696
const ecsact_entt_details& details
9797
) -> void;
9898

99+
auto print_hash_registry(
100+
codegen_plugin_context& ctx,
101+
const ecsact_entt_details& details
102+
) -> void;
103+
99104
} // namespace ecsact::rt_entt_codegen::core
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include "core.hh"
2+
3+
#include "ecsact/lang-support/lang-cc.hh"
4+
#include "ecsact/cpp_codegen_plugin_util.hh"
5+
#include "rt_entt_codegen/shared/util.hh"
6+
7+
auto ecsact::rt_entt_codegen::core::print_hash_registry(
8+
ecsact::codegen_plugin_context& ctx,
9+
const ecsact::rt_entt_codegen::ecsact_entt_details& details
10+
) -> void {
11+
using ecsact::cc_lang_support::cpp_identifier;
12+
using ecsact::cpp_codegen_plugin_util::block;
13+
using ecsact::meta::decl_full_name;
14+
using ecsact::rt_entt_codegen::util::method_printer;
15+
16+
auto printer = //
17+
method_printer{ctx, "ecsact::entt::hash_registry"}
18+
.parameter("const ::entt::registry&", "reg")
19+
.return_type("std::uint64_t");
20+
21+
ctx.writef("auto* state = XXH3_createState();\n");
22+
ctx.writef("if(!state) {{ return 0; }}\n");
23+
ctx.writef("XXH3_64bits_reset(state);\n");
24+
25+
// XXH3_64bits_update(state, buffer, count);
26+
27+
block(
28+
ctx,
29+
"for(auto&& [entity] : reg.template storage<::entt::entity>()->each())",
30+
[&] {
31+
ctx.writef("XXH3_64bits_update(state, &entity, sizeof(entity));\n");
32+
ctx.writef(
33+
"auto has_states = std::array<bool, {}>{{\n",
34+
details.all_components.size() + details.all_systems.size()
35+
);
36+
for(auto comp_id : details.all_components) {
37+
const auto cpp_comp_name = cpp_identifier(decl_full_name(comp_id));
38+
ctx.writef("\treg.all_of<{}>(entity),\n", cpp_comp_name);
39+
}
40+
41+
for(auto sys_id : details.all_systems) {
42+
const auto system_name = cpp_identifier(decl_full_name(sys_id));
43+
const auto pending_lazy_exec_struct = std::format(
44+
"::ecsact::entt::detail::pending_lazy_execution<::{}>",
45+
system_name
46+
);
47+
ctx.writef("\treg.all_of<{}>(entity),\n", pending_lazy_exec_struct);
48+
}
49+
50+
ctx.writef("\n}};\n");
51+
ctx.writef(
52+
"XXH3_64bits_update(state, has_states.data(), sizeof(bool) * "
53+
"has_states.size());\n"
54+
);
55+
}
56+
);
57+
58+
ctx.writef("\n");
59+
60+
for(auto comp_id : details.all_components) {
61+
const auto cpp_comp_name = cpp_identifier(decl_full_name(comp_id));
62+
if(ecsact::meta::get_field_ids(comp_id).empty()) {
63+
// tags are covered in the `has_states` above already
64+
continue;
65+
}
66+
67+
block(
68+
ctx,
69+
std::format(
70+
"for(auto&& [entity, comp] : reg.view<{}>().each())",
71+
cpp_comp_name
72+
),
73+
[&] {
74+
auto field_ids = ecsact::meta::get_field_ids(comp_id);
75+
for(auto field_id : field_ids) {
76+
auto field_name = ecsact::meta::field_name(comp_id, field_id);
77+
ctx.writef(
78+
"XXH3_64bits_update(state, &comp.{0}, "
79+
"sizeof(decltype(comp.{0})));\n",
80+
field_name
81+
);
82+
}
83+
}
84+
);
85+
ctx.writef("\n");
86+
}
87+
88+
ctx.writef("auto result = XXH3_64bits_digest(state);\n");
89+
ctx.writef("XXH3_freeState(state);\n");
90+
ctx.writef("return result;\n");
91+
}

rt_entt_codegen/rt_entt_codegen.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ void ecsact_codegen_plugin(
138138
inc_header(ctx, "ecsact/entt/wrapper/core.hh");
139139
inc_header(ctx, "ecsact/entt/wrapper/dynamic.hh");
140140
inc_header(ctx, "ecsact/entt/error_check.hh");
141+
inc_header(ctx, "xxhash.h");
141142
ctx.write("#include <execution>\n");
142143

143144
ctx.write("\n");
@@ -351,6 +352,7 @@ void ecsact_codegen_plugin(
351352
core::print_execute_system_like_template_specializations(ctx, details);
352353
core::print_init_registry_storage(ctx, details);
353354
core::print_copy_components(ctx, details);
355+
core::print_hash_registry(ctx, details);
354356
core::print_apply_streaming_data(ctx, details);
355357
core::print_trigger_ecsact_events_minimal(ctx, details);
356358
core::print_trigger_ecsact_events_all(ctx, details);

runtime/ecsact_rt_entt_core.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ auto ecsact_clone_registry( //
4040
return cloned_reg_id;
4141
}
4242

43+
auto ecsact_hash_registry(ecsact_registry_id reg_id) -> uint64_t {
44+
auto& reg = ecsact::entt::get_registry(reg_id);
45+
return ecsact::entt::hash_registry(reg);
46+
}
47+
4348
void ecsact_clear_registry(ecsact_registry_id reg_id) {
4449
auto& reg = ecsact::entt::get_registry(reg_id);
4550
reg = {};

test/BUILD.bazel

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,19 @@ cc_test(
139139
"@googletest//:gtest_main",
140140
],
141141
)
142+
143+
cc_test(
144+
name = "hash_test",
145+
srcs = [
146+
"hash_test.cc",
147+
"system_impls.cc",
148+
":ecsact_cc_system_impl_srcs",
149+
],
150+
args = ["--gtest_catch_exceptions=0"],
151+
copts = copts,
152+
deps = [
153+
":runtime",
154+
"@googletest//:gtest",
155+
"@googletest//:gtest_main",
156+
],
157+
)

test/hash_test.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include "gtest/gtest.h"
2+
3+
#include "ecsact/runtime/core.hh"
4+
#include "runtime_test.ecsact.hh"
5+
6+
TEST(Core, HashRegistry) {
7+
std::srand(std::time(nullptr));
8+
9+
auto reg = ecsact::core::registry{"Hash Test"};
10+
11+
auto hash_empty = reg.hash();
12+
13+
for(auto i = 0; 10000 > i; ++i) {
14+
auto entity = reg.create_entity();
15+
reg.add_component(entity, runtime_test::ComponentA{std::rand()});
16+
reg.add_component(entity, runtime_test::ComponentB{std::rand()});
17+
reg.add_component<runtime_test::TriggerTag>(entity);
18+
}
19+
20+
auto hash = reg.hash();
21+
22+
ASSERT_NE(hash_empty, hash);
23+
24+
auto new_entity = reg.create_entity();
25+
auto hash_after_new_entity = reg.hash();
26+
ASSERT_NE(hash, hash_after_new_entity);
27+
ASSERT_NE(hash_empty, hash_after_new_entity);
28+
29+
reg.add_component(new_entity, runtime_test::ComponentA{std::rand()});
30+
auto hash_after_add_component = reg.hash();
31+
ASSERT_NE(hash, hash_after_add_component);
32+
ASSERT_NE(hash_after_new_entity, hash_after_add_component);
33+
ASSERT_NE(hash_empty, hash_after_add_component);
34+
35+
ecsact_destroy_entity(reg.id(), new_entity);
36+
auto hash_after_destroy_entity = reg.hash();
37+
38+
ASSERT_NE(hash_after_new_entity, hash_after_add_component);
39+
ASSERT_NE(hash_after_new_entity, hash_after_destroy_entity);
40+
ASSERT_NE(hash_empty, hash_after_destroy_entity);
41+
ASSERT_EQ(hash, hash_after_destroy_entity); // reset to orig
42+
43+
reg.clear();
44+
auto hash_after_clear = reg.hash();
45+
ASSERT_NE(hash, hash_after_clear);
46+
ASSERT_EQ(hash_empty, hash_after_clear);
47+
ASSERT_NE(hash_after_new_entity, hash_after_clear);
48+
}

0 commit comments

Comments
 (0)