Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions assets/macro_benchmarks/script_systems/query_system.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- a non-exclusive script system querying two components with an empty callback, measures building and passing the payload
local bms = import_namespace("bms")
local world = bms.world
local ComponentA = world.get_type_by_name("test_utils::test_data::CompWithFromWorldAndComponentData")
local ComponentB = world.get_type_by_name("test_utils::test_data::CompWithDefaultAndComponentData")

function on_script_loaded()
world.add_system(
world.get_schedule_by_name("Update"),
bms.system_builder("bench_system", bms.ScriptAttachment.new_static_script(bms.current_script()))
:query(world.query():component(ComponentA):component_read(ComponentB))
)
end

function bench_system(results)
end
19 changes: 19 additions & 0 deletions assets/macro_benchmarks/script_systems/query_system_iterate.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- a non-exclusive script system querying two components, measures building and passing the payload
local bms = import_namespace("bms")
local world = bms.world
local ComponentA = world.get_type_by_name("test_utils::test_data::CompWithFromWorldAndComponentData")
local ComponentB = world.get_type_by_name("test_utils::test_data::CompWithDefaultAndComponentData")

function on_script_loaded()
world.add_system(
world.get_schedule_by_name("Update"),
bms.system_builder("bench_system", bms.ScriptAttachment.new_static_script(bms.current_script()))
:query(world.query():component(ComponentA):component_read(ComponentB))
)
end

function bench_system(results)
for _, result in ipairs(results) do
local _, _ = result[2], result[3]
end
end
2 changes: 1 addition & 1 deletion assets/scripts/game_of_life.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ math.randomseed(os.time())
function fetch_life_state()
-- find the first entity with life state
local i, v = next(world.query():component(LifeState):build())
return v:components()[1]
return v[2]
end

function on_script_loaded()
Expand Down
2 changes: 1 addition & 1 deletion assets/scripts/game_of_life.rn
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn settings_type(world) {
fn fetch_life_state(world) {
// find the first entity with life state
for result in world.query().component(life_state_type(world)).build() {
return result.components()[0];
return result[1];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ end
function optional_system(query)
local with_b, without_b = 0, 0
for _, result in pairs(query) do
local components = result:components()
assert(components[1] ~= nil, "the required component should be present")
if components[2] == nil then
-- the entity, then the components
assert(result[2] ~= nil, "the required component should be present")
if result[3] == nil then
without_b = without_b + 1
else
assert(type(components[2][1]) == "string", "the optional component should be readable")
assert(type(result[3][1]) == "string", "the optional component should be readable")
with_b = with_b + 1
end
end
Expand All @@ -57,8 +57,8 @@ end
function disjoint_system(with_b, without_b)
assert(#with_b >= 1 and #without_b >= 1, "expected results for both queries")
-- `A` is written on entities with `B`, but only read on the others
with_b[1]:components()[1][1] = "written"
local ok, err = pcall(function() without_b[1]:components()[1][1] = "written" end)
with_b[1][2][1] = "written"
local ok, err = pcall(function() without_b[1][2][1] = "written" end)
assert(not ok and tostring(err):find("writing the component", 1, true), tostring(err))
runs[#runs + 1] = "disjoint_system"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ end
function check_accesses(resource, query)
assert(resource.string == "Initial Value", "Expected 'Initial Value', got: " .. resource.string)
assert(#query >= 1, "Expected results, got: " .. #query)
local component = query[1]:components()[1]
local component = query[1][2]
assert(type(component[1]) == "string", "Expected to read the component, got: " .. tostring(component[1]))

local ok, err = pcall(function() resource.string = "changed" end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ end
function check_accesses(query)
assert(#query == 1, "Expected 1 result, got: " .. #query)
local matched = query[1]
assert(matched:components()[1][1] == "Default", "the matched component should be accessible")
assert(world.get_component(matched:entity(), ComponentA)[1] == "Default",
assert(matched[2][1] == "Default", "the matched component should be accessible")
assert(world.get_component(matched[1], ComponentA)[1] == "Default",
"the matched component should be accessible through the world too")

assert_throws(function()
Expand All @@ -49,7 +49,7 @@ function check_accesses(query)

-- `with` filters by `B` without accessing it
assert_throws(function()
local component = world.get_component(matched:entity(), ComponentB)
local component = world.get_component(matched[1], ComponentB)
local _ = component[1]
end, ".*annot claim access.*")
end
Expand Down
8 changes: 4 additions & 4 deletions assets/tests/add_system/system_with_parameters.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ function my_parameterised_system(resourceA, query, resourceB)
assert(resourceB.string == "Initial Value", "Expected 'Initial Value', got: " .. resourceB.string)
assert(#query == 2, "Expected 3 results, got: " .. #query)
for i, result in pairs(query) do
components = result:components()
assert(#components == 2, "Expected 2 components, got " .. #components)
local componentA = components[1]
local componentB = components[2]
-- the entity, then the components
assert(#result == 3, "Expected the entity and 2 components, got " .. #result)
local componentA = result[2]
local componentB = result[3]
assert(componentA[1] == "Default", "Expected 'Default', got: " .. componentA[1])
assert(componentB[1] == "Default", "Expected 'Default', got: " .. componentB[1])
end
Expand Down
8 changes: 4 additions & 4 deletions assets/tests/add_system/system_with_parameters.rn
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ pub fn my_parameterised_system(resourceA, query, resourceB) {
assert!(resourceB.string == "Initial Value", "Expected 'Initial Value', got: {}", resourceB.string);
assert!(query.len() == 2, "Expected 2 results, got: {}", query.len());
for result in query {
let components = result.components();
assert!(components.len() == 2, "Expected 2 components, got {}", components.len());
let componentA = components[0];
let componentB = components[1];
// the entity, then the components
assert!(result.len() == 3, "Expected the entity and 2 components, got {}", result.len());
let componentA = result[1];
let componentB = result[2];
assert!(componentA[0] == "Default", "Expected 'Default', got: {}", componentA[0]);
assert!(componentB[0] == "Default", "Expected 'Default', got: {}", componentB[0]);
}
Expand Down
38 changes: 38 additions & 0 deletions assets/tests/bindings/transform/value_semantics.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- small values (i.e. `Vec3`, `Entity`) are stored inline instead of allocated, scripts must not be able to tell
local world = import_namespace("bms").world
local Transform = import_namespace("transform").Transform
local Vec3 = import_namespace("math").Vec3

-- two variables holding the same value see each other's changes, like for allocated values
local a = Vec3.new(1, 2, 3)
local b = a
b.x = 5
assert(a.x == 5, "aliases share the value")

-- assigning a value copies it into the place, reads of fields are places
local t = Transform.from_xyz(1, 2, 3)
local v = Vec3.new(7, 8, 9)
t.translation = v
v.x = 10
assert(t.translation.x == 7, "assignment copies the value")
t.translation.y = 1
assert(t.translation.y == 1, "fields are places")
local translation = t.translation
translation.z = 4
assert(t.translation.z == 4, "a field read into a variable is still the place")

-- functions, operators and display work on values
assert(a:length() > 0, "methods")
assert((a + Vec3.new(1, 0, 0)).x == 6, "operators")
assert(a == Vec3.new(5, 2, 3), "equality")
assert(tostring(Vec3.new(1, 2, 3)) ~= nil, "display")

-- entities returned by functions and read from fields are equal
local parent = world.spawn()
local child = world.spawn()
world.push_children(parent, { child })
local child_of = world.get_component(child, world.get_type_by_name("bevy_ecs::hierarchy::ChildOf"))
assert(child_of[1] == parent, "field == value")
assert(parent == child_of[1], "value == field")
assert(child_of:parent() == parent, "function result == value")
assert(world.get_parent(child) == parent, "function results are equal")
40 changes: 40 additions & 0 deletions assets/tests/bindings/transform/value_semantics.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// small values (i.e. `Vec3`, `Entity`) are stored inline instead of allocated, scripts must not be able to tell
pub fn main() {
let world = import_namespace("bms").world;
let Transform = import_namespace("transform").Transform;
let Vec3 = import_namespace("math").Vec3;

// two variables holding the same value see each other's changes, like for allocated values
let a = Vec3.new(1.0, 2.0, 3.0);
let b = a;
b.x = 5.0;
assert!(a.x == 5.0, "aliases share the value");

// assigning a value copies it into the place, reads of fields are places
let t = Transform.from_xyz(1.0, 2.0, 3.0);
let v = Vec3.new(7.0, 8.0, 9.0);
t.translation = v;
v.x = 10.0;
assert!(t.translation.x == 7.0, "assignment copies the value");
t.translation.y = 1.0;
assert!(t.translation.y == 1.0, "fields are places");
let translation = t.translation;
translation.z = 4.0;
assert!(t.translation.z == 4.0, "a field read into a variable is still the place");

// functions, operators and display work on values
assert!(a.length() > 0.0, "methods");
assert!((a + Vec3.new(1.0, 0.0, 0.0)).x == 6.0, "operators");
assert!(a == Vec3.new(5.0, 2.0, 3.0), "equality");
assert!(`${Vec3.new(1.0, 2.0, 3.0)}` != "", "display");

// entities returned by functions and read from fields are equal
let parent = world.spawn();
let child = world.spawn();
world.push_children(parent, [child]);
let child_of = world.get_component(child, world.get_type_by_name("bevy_ecs::hierarchy::ChildOf"));
assert!(child_of[0] == parent, "field == value");
assert!(parent == child_of[0], "value == field");
assert!(child_of.parent() == parent, "function result == value");
assert!(world.get_parent(child) == parent, "function results are equal");
}
2 changes: 1 addition & 1 deletion assets/tests/query/dynamic_component_query.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ world.add_default_component(entity_a, NewComponent)

local found_entities = {}
for i, result in pairs(world.query():component(NewComponent):build()) do
table.insert(found_entities, result:entity())
table.insert(found_entities, result[1])
end

assert(#found_entities == 1, "Expected 1 entities, got " .. #found_entities)
Expand Down
2 changes: 1 addition & 1 deletion assets/tests/query/dynamic_component_query.rn
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub fn main() {

let found_entities = [];
for result in world.query().component(NewComponent).build() {
found_entities.push(result.entity());
found_entities.push(result[0]);
}

assert!(found_entities.len() == 1, "Expected 1 entities, got {}", found_entities.len());
Expand Down
14 changes: 7 additions & 7 deletions assets/tests/query/query_can_access_components.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ local query_result = world.query():component(componentA):component(componentA):c

assert(#query_result == 1, "Expected 1 result, got " .. #query_result)
for i, result in pairs(query_result) do
assert(result:entity():index() == entity_a:index(),
"Expected entity_a, got " .. result:entity():index())
components = result:components()
assert(#components == 3, "Expected 3 components, got " .. #components)
A = components[1]
B = components[2]
C = components[3]
assert(result[1]:index() == entity_a:index(),
"Expected entity_a, got " .. result[1]:index())
-- the entity, then the components
assert(#result == 4, "Expected the entity and 3 components, got " .. #result)
A = result[2]
B = result[3]
C = result[4]
assert(A[1] == "Default", "Expected 'Default', got: " .. A[1])
assert(B[1] == "Default", "Expected 'Default', got: " .. B[1])
assert(C.strings[1] == "asd", "Expected 'asd', got: " .. C.strings[1])
Expand Down
12 changes: 6 additions & 6 deletions assets/tests/query/query_can_access_components.rn
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ pub fn main() {

assert!(query_result.len() == 1, "Expected 1 result, got {}", query_result.len());
for result in query_result {
assert!(result.entity().index() == entity_a.index(), "Expected entity_a, got {}", result.entity().index());
let components = result.components();
assert!(components.len() == 3, "Expected 3 components, got {}", components.len());
let A = components[0];
let B = components[1];
let C = components[2];
// the entity, then the components
assert!(result[0].index() == entity_a.index(), "Expected entity_a, got {}", result[0].index());
assert!(result.len() == 4, "Expected the entity and 3 components, got {}", result.len());
let A = result[1];
let B = result[2];
let C = result[3];
assert!(A[0] == "Default", "Expected 'Default', got: {}", A[0]);
assert!(B[0] == "Default", "Expected 'Default', got: {}", B[0]);
assert!(C.strings[0] == "asd", "Expected 'asd', got: {}", C.strings[0]);
Expand Down
2 changes: 1 addition & 1 deletion assets/tests/query/query_returns_all_entities_matching.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ world.add_default_component(entity_b, component_without)

local found_entities = {}
for i, result in pairs(world.query():component(component_with):without(component_without):build()) do
table.insert(found_entities, result:entity())
table.insert(found_entities, result[1])
end

assert(#found_entities == 3, "Expected 3 entities, got " .. #found_entities)
Expand Down
2 changes: 1 addition & 1 deletion assets/tests/query/query_returns_all_entities_matching.rn
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn main() {

let found_entities = [];
for result in world.query().component(component_with).without(component_without).build() {
found_entities.push(result.entity());
found_entities.push(result[0]);
}

assert!(found_entities.len() == 3, "Expected 3 entities, got {}", found_entities.len());
Expand Down
2 changes: 1 addition & 1 deletion assets/tests/script_callbacks/lua/script_callbacks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ end
-- the other entity running this script
local function other_entity()
for _, result in pairs(world.query():component(world.get_type_by_name("bevy_mod_scripting_core::script::ScriptComponent")):build()) do
local e = result:entity()
local e = result[1]
if e:index() ~= bms.current_entity():index() then
return e
end
Expand Down
2 changes: 1 addition & 1 deletion assets/tests/script_callbacks/rune/script_callbacks.rn
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn other_entity() {
let world = bms.world;
let script_component = world.get_type_by_name("bevy_mod_scripting_core::script::ScriptComponent");
for result in world.query().component(script_component).build() {
let e = result.entity();
let e = result[0];
if e.index() != bms.current_entity().index() {
return e;
}
Expand Down
45 changes: 36 additions & 9 deletions benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use regex::Regex;
use script_integration_test_harness::{
make_test_lua_plugin, perform_benchmark_with_generator, run_lua_benchmark,
run_plugin_callback_command_benchmark, run_plugin_callback_event_benchmark,
run_plugin_script_load_benchmark, test_functions::rand::Rng,
run_plugin_script_load_benchmark, run_plugin_script_system_benchmark,
test_functions::rand::Rng,
};
#[cfg(feature = "rune")]
use script_integration_test_harness::{make_test_rune_plugin, run_rune_benchmark};
Expand Down Expand Up @@ -234,12 +235,13 @@ fn conversion_benchmarks(criterion: &mut Criterion) {
BatchSize::SmallInput,
);

// references, small values are inline and have no place to borrow from
perform_benchmark_with_generator(
"Ref<T>::from",
&|rng, w| {
V::new(ReflectyVal(rng.random::<u32>()))
.into_script(w)
.unwrap()
&|rng, _| {
ScriptValue::Reference(ReflectReference::new_allocated(ReflectyVal(
rng.random::<u32>(),
)))
},
&|w, i| {
R::<ReflectyVal>::from_script(i, w).unwrap();
Expand All @@ -250,10 +252,10 @@ fn conversion_benchmarks(criterion: &mut Criterion) {

perform_benchmark_with_generator(
"Mut<T>::from",
&|rng, w| {
V::new(ReflectyVal(rng.random::<u32>()))
.into_script(w)
.unwrap()
&|rng, _| {
ScriptValue::Reference(ReflectReference::new_allocated(ReflectyVal(
rng.random::<u32>(),
)))
},
&|w, i| {
M::<ReflectyVal>::from_script(i, w).unwrap();
Expand Down Expand Up @@ -307,6 +309,30 @@ fn callback_benchmarks(criterion: &mut Criterion) {
run_plugin_callback_command_benchmark(make_test_rune_plugin, "command Rune", rune, &mut group);
}

/// benchmarks running a script system querying the components of many entities, measures building and passing the payload
fn script_system_benchmarks(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("script_systems");
let lua = include_str!("../assets/macro_benchmarks/script_systems/query_system.lua");
let lua_iterate =
include_str!("../assets/macro_benchmarks/script_systems/query_system_iterate.lua");
for entity_count in [10, 100, 1000] {
run_plugin_script_system_benchmark(
make_test_lua_plugin,
&format!("query {entity_count} entities Lua"),
lua,
entity_count,
&mut group,
);
run_plugin_script_system_benchmark(
make_test_lua_plugin,
&format!("query and iterate {entity_count} entities Lua"),
lua_iterate,
entity_count,
&mut group,
);
}
}

pub fn benches() {
maybe_with_profiler(|_profiler| {
let mut criterion: criterion::Criterion<_> = (criterion::Criterion::default())
Expand All @@ -333,6 +359,7 @@ pub fn benches() {
conversion_benchmarks(&mut criterion);
script_load_benchmarks(&mut criterion);
callback_benchmarks(&mut criterion);
script_system_benchmarks(&mut criterion);
});
}
criterion_main!(benches);
Loading
Loading