From 6d5f810d45378fb4701d070089a99a998471e561 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 15 Sep 2025 17:56:44 +0700 Subject: [PATCH] experimental scheme creation --- README.md | 36 ++++- develop/all.lua | 1 + develop/benchmarks/scheme_bmarks.lua | 201 +++++++++++++++++++++++++++ develop/testing/scheme_tests.lua | 65 +++++++++ evolved.lua | 161 +++++++++++++++++++-- 5 files changed, 446 insertions(+), 18 deletions(-) create mode 100644 develop/benchmarks/scheme_bmarks.lua diff --git a/README.md b/README.md index 1436c77..f4221b7 100644 --- a/README.md +++ b/README.md @@ -1165,7 +1165,10 @@ chunk_mt:components :: fragment... -> storage... #### Scheme ``` -scheme -> scheme +scheme_boolean :: scheme +scheme_list :: scheme, integer -> scheme +scheme_number :: scheme +scheme_record :: -> scheme ``` #### Builder @@ -1676,12 +1679,39 @@ function evolved.chunk_mt:components(...) end ### Scheme -#### `evolved.scheme` +#### `evolved.scheme_boolean` ```lua ---@return evolved.scheme scheme ---@nodiscard -function evolved.scheme() end +function evolved.scheme_boolean() end +``` + +#### `evolved.scheme_list` + +```lua +---@param item_scheme evolved.scheme +---@param item_count integer +---@return evolved.scheme scheme +---@nodiscard +function evolved.scheme_list(item_scheme, item_count) end +``` + +#### `evolved.scheme_number` + +```lua +---@return evolved.scheme scheme +---@nodiscard +function evolved.scheme_number() end +``` + +#### `evolved.scheme_record` + +```lua +---@param field_schemes table +---@return evolved.scheme scheme +---@nodiscard +function evolved.scheme_record(field_schemes) end ``` ### Builder diff --git a/develop/all.lua b/develop/all.lua index bdfdf3b..c1f6d56 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -8,6 +8,7 @@ require 'develop.testing.system_as_query_tests' require 'develop.benchmarks.multi_clone_bmarks' require 'develop.benchmarks.multi_spawn_bmarks' +require 'develop.benchmarks.scheme_bmarks' require 'develop.untests' diff --git a/develop/benchmarks/scheme_bmarks.lua b/develop/benchmarks/scheme_bmarks.lua new file mode 100644 index 0000000..fdccf24 --- /dev/null +++ b/develop/benchmarks/scheme_bmarks.lua @@ -0,0 +1,201 @@ +local evo = require 'evolved' +local basics = require 'develop.basics' + +evo.debug_mode(false) + +local N = 10000 + +print '----------------------------------------' + +basics.describe_bench(string.format('Scheme Benchmarks: Evolved AoS | %d entities', N), + function(w) + evo.process(w) + end, + + function() + local wf = evo.builder() + :set(evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local pf = evo.builder():set(wf):spawn() + local vf = evo.builder():set(wf):spawn() + + for _ = 1, N do + evo.spawn { + [wf] = true, + [pf] = { x = 0, y = 0, z = 0, w = 0 }, + [vf] = { x = 0, y = 0, z = 0, w = 0 }, + } + end + + evo.builder() + :set(wf) + :set(evo.GROUP, wf) + :set(evo.QUERY, evo.builder():set(wf):include(pf, vf):spawn()) + :set(evo.EXECUTE, function(chunk, _, entity_count) + local ps, vs = chunk:components(pf, vf) + + for i = 1, entity_count do + local p, s = ps[i], vs[i] + p.x = p.x + s.x + p.y = p.y + s.y + end + end) + :spawn() + + return wf + end, + + function(w) + evo.destroy(w) + end) + +basics.describe_bench(string.format('Scheme Benchmarks: Evolved SoA | %d entities', N), + function(w) + evo.process(w) + end, + + function() + local wf = evo.builder() + :set(evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local pxf = evo.builder():set(wf):spawn() + local pyf = evo.builder():set(wf):spawn() + local pzf = evo.builder():set(wf):spawn() + local pwf = evo.builder():set(wf):spawn() + local vxf = evo.builder():set(wf):spawn() + local vyf = evo.builder():set(wf):spawn() + local vzf = evo.builder():set(wf):spawn() + local vwf = evo.builder():set(wf):spawn() + + for _ = 1, N do + evo.spawn { + [wf] = true, + [pxf] = 0, [pyf] = 0, [pzf] = 0, [pwf] = 0, + [vxf] = 0, [vyf] = 0, [vzf] = 0, [vwf] = 0, + } + end + + evo.builder() + :set(wf) + :set(evo.GROUP, wf) + :set(evo.QUERY, evo.builder():set(wf):include(pxf, pyf, vxf, vyf):spawn()) + :set(evo.EXECUTE, function(chunk, _, entity_count) + local pxs, pys = chunk:components(pxf, pyf) + local vxs, vys = chunk:components(vxf, vyf) + + for i = 1, entity_count do + pxs[i] = pxs[i] + vxs[i] + pys[i] = pys[i] + vys[i] + end + end) + :spawn() + + return wf + end, + + function(w) + evo.destroy(w) + end) + +print '----------------------------------------' + +basics.describe_bench(string.format('Scheme Benchmarks: Evolved-FFI AoS | %d entities', N), + function(w) + evo.process(w) + end, + + function() + local wf = evo.builder() + :set(evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local vector4 = evo.scheme_record { + x = evo.scheme_number(), + y = evo.scheme_number(), + z = evo.scheme_number(), + w = evo.scheme_number(), + } + + local pf = evo.builder():set(wf):set(evo.SCHEME, vector4):spawn() + local vf = evo.builder():set(wf):set(evo.SCHEME, vector4):spawn() + + for _ = 1, N do + evo.spawn { + [wf] = true, + [pf] = { x = 0, y = 0, z = 0, w = 0 }, + [vf] = { x = 0, y = 0, z = 0, w = 0 }, + } + end + + evo.builder() + :set(wf) + :set(evo.GROUP, wf) + :set(evo.QUERY, evo.builder():set(wf):include(pf, vf):spawn()) + :set(evo.EXECUTE, function(chunk, _, entity_count) + local ps, vs = chunk:components(pf, vf) + + for i = 1, entity_count do + local p, s = ps[i], vs[i] + p.x = p.x + s.x + p.y = p.y + s.y + end + end) + :spawn() + + return wf + end, + + function(w) + evo.destroy(w) + end) + +basics.describe_bench(string.format('Scheme Benchmarks: Evolved-FFI SoA | %d entities', N), + function(w) + evo.process(w) + end, + + function() + local wf = evo.builder() + :set(evo.DESTRUCTION_POLICY, evo.DESTRUCTION_POLICY_DESTROY_ENTITY) + :spawn() + + local pxf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local pyf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local pzf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local pwf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local vxf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local vyf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local vzf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + local vwf = evo.builder():set(wf):set(evo.SCHEME, evo.scheme_number()):spawn() + + for _ = 1, N do + evo.spawn { + [wf] = true, + [pxf] = 0, [pyf] = 0, [pzf] = 0, [pwf] = 0, + [vxf] = 0, [vyf] = 0, [vzf] = 0, [vwf] = 0, + } + end + + evo.builder() + :set(wf) + :set(evo.GROUP, wf) + :set(evo.QUERY, evo.builder():set(wf):include(pxf, pyf, vxf, vyf):spawn()) + :set(evo.EXECUTE, function(chunk, _, entity_count) + local pxs, pys = chunk:components(pxf, pyf) + local vxs, vys = chunk:components(vxf, vyf) + + for i = 1, entity_count do + pxs[i] = pxs[i] + vxs[i] + pys[i] = pys[i] + vys[i] + end + end) + :spawn() + + return wf + end, + + function(w) + evo.destroy(w) + end) diff --git a/develop/testing/scheme_tests.lua b/develop/testing/scheme_tests.lua index 2099a1a..b67b75c 100644 --- a/develop/testing/scheme_tests.lua +++ b/develop/testing/scheme_tests.lua @@ -1 +1,66 @@ local evo = require 'evolved' + +do + local ns1 = evo.scheme_number() + local ns2 = evo.scheme_number() + + local f1 = evo.builder():scheme(ns1):spawn() + local f2 = evo.builder():scheme(ns2):spawn() + + local e1 = evo.builder():set(f1, 1):spawn() + local e2 = evo.builder():set(f1, 3):set(f2, 4):spawn() + + do + assert(evo.get(e1, f1) == 1 and evo.get(e1, f2) == nil) + assert(evo.get(e2, f1) == 3 and evo.get(e2, f2) == 4) + end + + evo.set(e1, f2, 2) + + do + assert(evo.get(e1, f1) == 1 and evo.get(e1, f2) == 2) + assert(evo.get(e2, f1) == 3 and evo.get(e2, f2) == 4) + end + + local es = evo.builder():set(f1, 5):set(f2, 6):multi_spawn(40) + for _, e in ipairs(es) do assert(evo.get(e, f1) == 5 and evo.get(e, f2) == 6) end +end + +do + local ls1 = evo.scheme_list(evo.scheme_number(), 3) + local ls2 = evo.scheme_list(evo.scheme_boolean(), 2) + + local f1 = evo.builder():scheme(ls1):spawn() + local f2 = evo.builder():scheme(ls2):spawn() + + local e1 = evo.builder():set(f1, { 1, 2, 3 }):spawn() + local e2 = evo.builder():set(f2, { true, false }):spawn() + + do + local v = evo.get(e1, f1) + assert(#v == 3 and v[1] == 1 and v[2] == 2 and v[3] == 3) + end + + do + local v = evo.get(e2, f2) + assert(#v == 2 and v[1] == true and v[2] == false) + end +end + +do + local rs1 = evo.scheme_record { v = evo.scheme_number() } + local rs2 = evo.scheme_record { v1 = evo.scheme_number(), v2 = evo.scheme_number() } + local rs3 = evo.scheme_record { n1 = rs2, n2 = rs1 } + + local f1 = evo.builder():scheme(rs1):spawn() + local f2 = evo.builder():scheme(rs2):spawn() + local f3 = evo.builder():scheme(rs3):spawn() + + local e1 = evo.builder():set(f1, { v = 42 }):spawn() + local e2 = evo.builder():set(f2, { v1 = 21, v2 = 84 }):spawn() + local e3 = evo.builder():set(f3, { n1 = { v1 = 1, v2 = 2 }, n2 = { v = 3 } }):spawn() + + assert(evo.get(e1, f1).v == 42) + assert(evo.get(e2, f2).v1 == 21 and evo.get(e2, f2).v2 == 84) + assert(evo.get(e3, f3).n1.v1 == 1 and evo.get(e3, f3).n1.v2 == 2 and evo.get(e3, f3).n2.v == 3) +end diff --git a/evolved.lua b/evolved.lua index 8025dc4..2bb60ab 100644 --- a/evolved.lua +++ b/evolved.lua @@ -180,6 +180,11 @@ local __chunk_mt = {} __chunk_mt.__index = __chunk_mt ---@class evolved.scheme +---@field package __id evolved.id +---@field package __name string +---@field package __cdecl string +---@field package __sizeof integer? +---@field package __typeof evolved.ffi_ctype? local __scheme_mt = {} __scheme_mt.__index = __scheme_mt @@ -339,6 +344,32 @@ end)() --- --- +---@class evolved.ffi +---@field cdef fun(def: string) +---@field typeof fun(ct: evolved.ffi_ct): evolved.ffi_ctype +---@field sizeof fun(ct: evolved.ffi_ct): integer? + +---@class evolved.ffi_cdata : userdata +---@class evolved.ffi_ctype : userdata +---@class evolved.ffi_cdecl : string + +---@alias evolved.ffi_ct evolved.ffi_cdata | evolved.ffi_ctype | evolved.ffi_cdecl + +---@type evolved.ffi? +local __lua_ffi = (function() + -- https://luajit.org/ext_ffi_api.html + -- https://forum.defold.com/t/ffi-with-editor-2-solved/13683 + + local ffi_loader = package and package.preload and package.preload['ffi'] + return ffi_loader and ffi_loader() +end)() + +--- +--- +--- +--- +--- + ---@param fmt string ---@param ... any local function __error_fmt(fmt, ...) @@ -837,7 +868,12 @@ local __evolved_debug_mode local __evolved_collect_garbage local __evolved_chunk -local __evolved_scheme + +local __evolved_scheme_boolean +local __evolved_scheme_list +local __evolved_scheme_number +local __evolved_scheme_record + local __evolved_builder --- @@ -6058,11 +6094,101 @@ end ---@return evolved.scheme scheme ---@nodiscard -function __evolved_scheme() +function __evolved_scheme_boolean() return __lua_setmetatable({ + __id = __acquire_id(), + __name = 'bool', + __cdecl = 'bool', + __sizeof = __lua_ffi and __lua_ffi.sizeof('bool'), + __typeof = __lua_ffi and __lua_ffi.typeof('bool'), }, __scheme_mt) end +---@param item_scheme evolved.scheme +---@param item_count integer +---@return evolved.scheme scheme +---@nodiscard +function __evolved_scheme_list(item_scheme, item_count) + local list_id = __acquire_id() + local list_cdecl = __lua_string_format('%s[%d]', item_scheme.__name, item_count) + + return __lua_setmetatable({ + __id = list_id, + __name = list_cdecl, + __cdecl = list_cdecl, + __sizeof = __lua_ffi and __lua_ffi.sizeof(list_cdecl), + __typeof = __lua_ffi and __lua_ffi.typeof(list_cdecl), + }, __scheme_mt) +end + +---@return evolved.scheme scheme +---@nodiscard +function __evolved_scheme_number() + return __lua_setmetatable({ + __id = __acquire_id(), + __name = 'double', + __cdecl = 'double', + __sizeof = __lua_ffi and __lua_ffi.sizeof('double'), + __typeof = __lua_ffi and __lua_ffi.typeof('double'), + }, __scheme_mt) +end + +---@param field_schemes table +---@return evolved.scheme scheme +---@nodiscard +function __evolved_scheme_record(field_schemes) + local record_id = __acquire_id() + local record_name = __lua_string_format('__evolved_record_%d', record_id) + + local field_list = {} ---@type {name: string, scheme: evolved.scheme}[] + local field_count = 0 ---@type integer + + for field_name, field_scheme in __lua_next, field_schemes do + field_count = field_count + 1 + field_list[field_count] = { name = field_name, scheme = field_scheme } + end + + if __lua_ffi and field_count > 0 then + __lua_table_sort(field_list, function(a, b) + local a_name, b_name = a.scheme.__name, b.scheme.__name + local a_size, b_size = a.scheme.__sizeof or 0, b.scheme.__sizeof or 0 + return a_size > b_size or (a_size == b_size and a_name < b_name) + end) + end + + local field_cdecl_list = '' + + for field_index = 1, field_count do + local field = field_list[field_index] + + if field_cdecl_list ~= '' then + field_cdecl_list = field_cdecl_list .. ' ' + end + + field_cdecl_list = field_cdecl_list .. + __lua_string_format('%s %s;', field.scheme.__name, field.name) + end + + local record_cdecl = __lua_string_format( + 'typedef struct { %s } %s;', field_cdecl_list, record_name) + + if __lua_ffi then + __lua_ffi.cdef(record_cdecl) + end + + return __lua_setmetatable({ + __id = record_id, + __name = record_name, + __cdecl = record_cdecl, + __sizeof = __lua_ffi and __lua_ffi.sizeof(record_name), + __typeof = __lua_ffi and __lua_ffi.typeof(record_name), + }, __scheme_mt) +end + +function __scheme_mt:__tostring() + return self.__cdecl +end + --- --- --- @@ -6521,16 +6647,16 @@ end --- --- -__evolved_set(__ON_SET, __ON_INSERT, __update_major_chunks) +__evolved_set(__ON_SET, __ON_SET, __update_major_chunks) __evolved_set(__ON_SET, __ON_REMOVE, __update_major_chunks) -__evolved_set(__ON_ASSIGN, __ON_INSERT, __update_major_chunks) +__evolved_set(__ON_ASSIGN, __ON_SET, __update_major_chunks) __evolved_set(__ON_ASSIGN, __ON_REMOVE, __update_major_chunks) -__evolved_set(__ON_INSERT, __ON_INSERT, __update_major_chunks) +__evolved_set(__ON_INSERT, __ON_SET, __update_major_chunks) __evolved_set(__ON_INSERT, __ON_REMOVE, __update_major_chunks) -__evolved_set(__ON_REMOVE, __ON_INSERT, __update_major_chunks) +__evolved_set(__ON_REMOVE, __ON_SET, __update_major_chunks) __evolved_set(__ON_REMOVE, __ON_REMOVE, __update_major_chunks) --- @@ -6539,28 +6665,28 @@ __evolved_set(__ON_REMOVE, __ON_REMOVE, __update_major_chunks) --- --- -__evolved_set(__TAG, __ON_INSERT, __update_major_chunks) +__evolved_set(__TAG, __ON_SET, __update_major_chunks) __evolved_set(__TAG, __ON_REMOVE, __update_major_chunks) -__evolved_set(__UNIQUE, __ON_INSERT, __update_major_chunks) +__evolved_set(__UNIQUE, __ON_SET, __update_major_chunks) __evolved_set(__UNIQUE, __ON_REMOVE, __update_major_chunks) -__evolved_set(__EXPLICIT, __ON_INSERT, __update_major_chunks) +__evolved_set(__EXPLICIT, __ON_SET, __update_major_chunks) __evolved_set(__EXPLICIT, __ON_REMOVE, __update_major_chunks) -__evolved_set(__INTERNAL, __ON_INSERT, __update_major_chunks) +__evolved_set(__INTERNAL, __ON_SET, __update_major_chunks) __evolved_set(__INTERNAL, __ON_REMOVE, __update_major_chunks) -__evolved_set(__SCHEME, __ON_INSERT, __update_major_chunks) +__evolved_set(__SCHEME, __ON_SET, __update_major_chunks) __evolved_set(__SCHEME, __ON_REMOVE, __update_major_chunks) -__evolved_set(__DEFAULT, __ON_INSERT, __update_major_chunks) +__evolved_set(__DEFAULT, __ON_SET, __update_major_chunks) __evolved_set(__DEFAULT, __ON_REMOVE, __update_major_chunks) -__evolved_set(__DUPLICATE, __ON_INSERT, __update_major_chunks) +__evolved_set(__DUPLICATE, __ON_SET, __update_major_chunks) __evolved_set(__DUPLICATE, __ON_REMOVE, __update_major_chunks) -__evolved_set(__REQUIRES, __ON_INSERT, __update_major_chunks) +__evolved_set(__REQUIRES, __ON_SET, __update_major_chunks) __evolved_set(__REQUIRES, __ON_REMOVE, __update_major_chunks) --- @@ -6908,7 +7034,12 @@ evolved.debug_mode = __evolved_debug_mode evolved.collect_garbage = __evolved_collect_garbage evolved.chunk = __evolved_chunk -evolved.scheme = __evolved_scheme + +evolved.scheme_boolean = __evolved_scheme_boolean +evolved.scheme_list = __evolved_scheme_list +evolved.scheme_number = __evolved_scheme_number +evolved.scheme_record = __evolved_scheme_record + evolved.builder = __evolved_builder ---