diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca37570 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# ---> Vim +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +subprojects +builddir +ttassets +backup +*.exe +*.dll +*.world +coverage +coverage/* +.venv diff --git a/.vimrc_proj b/.vimrc_proj new file mode 100644 index 0000000..2b745b4 --- /dev/null +++ b/.vimrc_proj @@ -0,0 +1 @@ +set makeprg=meson\ compile\ -C\ . diff --git a/LICENSE b/LICENSE index d64aa11..913f2fa 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,9 @@ -It's Copyright. All Rights Reserved. You have no license at all other than permission to view it. +MIT License + +Copyright (c) Zed A. Shaw + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..28f2cb8 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +all: build test + +reset: +ifeq '$(OS)' 'Windows_NT' + powershell -executionpolicy bypass .\scripts\reset_build.ps1 +else + sh -x ./scripts/reset_build.sh +endif + +build: + meson compile -j 10 -C builddir + +release_build: + meson --wipe builddir -Db_ndebug=true --buildtype release + meson compile -j 10 -C builddir + +debug_build: + meson setup --wipe builddir -Db_ndebug=true --buildtype debugoptimized + meson compile -j 10 -C builddir + +test: build + ./builddir/sol2_test + +run: build test +ifeq '$(OS)' 'Windows_NT' + powershell "cp ./builddir/sol2_test.exe ." + ./sol2_test +else + ./builddir/sol2_test +endif + +debug: build + gdb --nx -x .gdbinit --ex run --args builddir/sol2_test + +debug_run: build + gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/sol2_test + +clean: + meson compile --clean -C builddir + +debug_test: build + gdb --nx -x .gdbinit --ex run --args builddir/sol2_test -e diff --git a/README.md b/README.md index 12fc7e4..bdb50bc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ -# sol2_test +# sol2 testj -Experiments in using Lua with sol2 and how best to structure the data. Contains a shoot out of SOA, vs AOS vs. MOA. - -SOA = Struct of Arrays -AOS = Array of Structs -MOA = Many Array of Structs (kind of a hybrid) \ No newline at end of file +Just a quick test of using sol2 and also how a God Object System would work as an alternative to ECS +but using sol2/lua. diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..6c801b1 --- /dev/null +++ b/meson.build @@ -0,0 +1,41 @@ +project('sol2_test', 'cpp', + version: '0.1.0', + default_options: [ + 'cpp_std=c++23', + 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', + ]) + +# use this for common options only for our executables +cpp_args=[ + '-Wno-unused-parameter', + '-Wno-unused-function', + '-Wno-unused-variable', + '-Wno-unused-but-set-variable', + '-Wno-deprecated-declarations', + ] +link_args=[] +# these are passed as override_defaults +exe_defaults = [ 'warning_level=2', 'werror=false'] + +fmt = subproject('fmt').get_variable('fmt_dep') +fuc2 = subproject('fuc2').get_variable('fuc2_dep') +sol2 = subproject('sol2').get_variable('sol2_dep') + +dependencies = [fmt, fuc2, sol2] + +sources = [ +] + +executable('sol2_test', [ + 'tests/sol2_test.cpp', + 'tests/bao_tests.cpp', + 'tests/soa_tests.cpp', + 'tests/perf_tests.cpp', + 'src/stats.cpp', + 'tests/main.cpp', + ], + cpp_args: cpp_args, + link_args: link_args, + include_directories: include_directories('src'), + override_options: exe_defaults, + dependencies: dependencies) diff --git a/scripts/reset_build.ps1 b/scripts/reset_build.ps1 new file mode 100644 index 0000000..975852d --- /dev/null +++ b/scripts/reset_build.ps1 @@ -0,0 +1,7 @@ +mv .\subprojects\packagecache . +rm -recurse -force .\subprojects\,.\builddir\ +mkdir subprojects +mv .\packagecache .\subprojects\ +mkdir builddir +cp wraps\*.wrap subprojects\ +meson setup --default-library=static --prefer-static builddir diff --git a/scripts/reset_build.sh b/scripts/reset_build.sh new file mode 100644 index 0000000..89931e7 --- /dev/null +++ b/scripts/reset_build.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +mv -f ./subprojects/packagecache . +rm -rf subprojects builddir +mkdir subprojects +mv -f packagecache ./subprojects/ && true +mkdir builddir +cp wraps/*.wrap subprojects/ +# on OSX you can't do this with static +meson setup --default-library=static --prefer-static builddir diff --git a/src/stats.cpp b/src/stats.cpp new file mode 100644 index 0000000..5d26c83 --- /dev/null +++ b/src/stats.cpp @@ -0,0 +1,10 @@ +#include "stats.hpp" +#include + +void Stats::dump(std::string msg) +{ + fmt::println("{}: sum: {}, sumsq: {}, n: {}, " + "min: {}, max: {}, mean: {}, stddev: {}", + msg, sum, sumsq, n, min, max, mean(), + stddev()); +} diff --git a/src/stats.hpp b/src/stats.hpp new file mode 100644 index 0000000..8c8749e --- /dev/null +++ b/src/stats.hpp @@ -0,0 +1,59 @@ +#pragma once +#include +#include + +struct Stats { + using TimeBullshit = std::chrono::time_point; + + double sum = 0.0; + double sumsq = 0.0; + double n = 0.0; + double min = 0.0; + double max = 0.0; + + inline void reset() { + sum = 0.0; + sumsq = 0.0; + n = 0.0; + min = 0.0; + max = 0.0; + } + + inline double mean() { + return sum / n; + } + + inline double stddev() { + return std::sqrt((sumsq - (sum * sum / n)) / (n - 1)); + } + + inline void sample(double s) { + sum += s; + sumsq += s * s; + + if (n == 0) { + min = s; + max = s; + } else { + if (min > s) min = s; + if (max < s) max = s; + } + + n += 1; + } + + inline TimeBullshit time_start() { + return std::chrono::high_resolution_clock::now(); + } + + inline void sample_time(TimeBullshit start) { + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration(end - start); + + if(elapsed.count() > 0.0) { + sample(1.0/elapsed.count()); + } + } + + void dump(std::string msg=""); +}; diff --git a/tests/bao_tests.cpp b/tests/bao_tests.cpp new file mode 100644 index 0000000..e8c6a1c --- /dev/null +++ b/tests/bao_tests.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#define SOL_ALL_SAFETIES_ON 1 +#include + +using namespace fuc2; + +namespace bao_tests { + + struct BAEntity { + std::string name; + int hp=0; + int damage=0; + bool dead=false; + }; + + void test_bao_basics() { + std::vector entities; + + for(int i = 0; i < 10; i++) { + auto name = fmt::format("Fighter-{}", i); + entities.emplace_back(name, i+10, i+1); + } + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("BAEntity", + "name", &BAEntity::name, + "hp", &BAEntity::hp, + "damage", &BAEntity::damage, + "dead", &BAEntity::dead); + + lua.script_file("tests/bao_tests.lua"); + sol::function handler = lua["handler"]; + + for(auto& fighter : entities) { + handler(fighter); + } + } + + fuc2::Set TESTS{ + .name="bao", + .options={ .fail_fast=false }, + .tests={ + TEST(test_bao_basics), + } + }; +} diff --git a/tests/bao_tests.lua b/tests/bao_tests.lua new file mode 100644 index 0000000..d8a541a --- /dev/null +++ b/tests/bao_tests.lua @@ -0,0 +1,18 @@ +function combat(fighter) + fighter.hp = fighter.hp - fighter.damage +end + +function cull_dead(fighter) + if fighter.hp < 0 then + fighter.dead = true + end +end + + + +function handler(fighter) + combat(fighter) + cull_dead(fighter) + + print(fighter.name, "HP now", fighter.hp, "damage", fighter.damage, "dead", fighter.dead) +end diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..a2aa2f6 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,18 @@ +#include +TEST_SET(sol2_tests); +TEST_SET(bao_tests); +TEST_SET(soa_tests); +TEST_SET(perf_tests); + +using namespace fuc2; + +int main(int argc, char* argv[]) { + std::vector tests{ + sol2_tests::TESTS, + bao_tests::TESTS, + soa_tests::TESTS, + perf_tests::TESTS, + }; + + return run_tests(tests, argc, argv); +} diff --git a/tests/perf_tests.cpp b/tests/perf_tests.cpp new file mode 100644 index 0000000..0b34b10 --- /dev/null +++ b/tests/perf_tests.cpp @@ -0,0 +1,498 @@ +#include +#include +#include +#include "stats.hpp" + +using namespace fuc2; + +namespace perf_tests { + struct LargeSOA { + std::vector a01; + std::vector a02; + std::vector a03; + std::vector a04; + std::vector a05; + std::vector a06; + std::vector a07; + std::vector a08; + std::vector a09; + std::vector a10; + std::vector a11; + std::vector a12; + std::vector a13; + std::vector a14; + std::vector a15; + std::vector a16; + std::vector a17; + std::vector a18; + std::vector a19; + std::vector a20; + + void push_back(int x) { + a01.push_back(x); + a02.push_back(x); + a03.push_back(x); + a04.push_back(x); + a05.push_back(x); + a06.push_back(x); + a07.push_back(x); + a08.push_back(x); + a09.push_back(x); + a10.push_back(x); + a11.push_back(x); + a12.push_back(x); + a13.push_back(x); + a14.push_back(x); + a15.push_back(x); + a16.push_back(x); + a17.push_back(x); + a18.push_back(x); + a19.push_back(x); + a20.push_back(x); + } + }; + + struct LargeStruct { + int a01; + int a02; + int a03; + int a04; + int a05; + int a06; + int a07; + int a08; + int a09; + int a10; + int a11; + int a12; + int a13; + int a14; + int a15; + int a16; + int a17; + int a18; + int a19; + int a20; + + LargeStruct(int x) : + a01(x), + a02(x), + a03(x), + a04(x), + a05(x), + a06(x), + a07(x), + a08(x), + a09(x), + a10(x), + a11(x), + a12(x), + a13(x), + a14(x), + a15(x), + a16(x), + a17(x), + a18(x), + a19(x), + a20(x) {} + }; + + + struct S1 { + int a01; + int a02; + int a03; + int a04; + + S1(int x) : a01(x), a02(x), a03(x), a04(x) { } + }; + + struct S2 { + int a05; + int a06; + int a07; + int a08; + S2(int x) : a05(x), a06(x), a07(x), a08(x) { } + }; + + struct S3 { + int a09; + int a10; + int a11; + int a12; + S3(int x) : a09(x), a10(x), a11(x), a12(x) { } + }; + + struct S4 { + int a13; + int a14; + int a15; + int a16; + S4(int x) : a13(x), a14(x), a15(x), a16(x) { } + }; + + struct S5 { + int a17; + int a18; + int a19; + int a20; + S5(int x) : a17(x), a18(x), a19(x), a20(x) { } + }; + + struct MultipleSOA { + std::vector s1; + std::vector s2; + std::vector s3; + std::vector s4; + std::vector s5; + + void push_back(int x) { + s1.push_back({x}); + s2.push_back({x}); + s3.push_back({x}); + s4.push_back({x}); + s5.push_back({x}); + } + }; + + struct Samples { + std::vector soa_big_for; + std::vector soa_many_for; + std::vector ls_big_for; + std::vector ls_many_for; + std::vector ls_many_for_fast; + std::vector moa_big_for; + std::vector moa_many_for; + std::vector moa_many_for_fast; + }; + + void run_test(Samples& samples, int sample_size, int sample_number) { + auto soa = new LargeSOA(); + auto ls = new std::vector; + auto moa = new MultipleSOA(); + + for(int i = 0; i < sample_size; i++) { + soa->push_back(i); + ls->push_back({i}); + moa->push_back(i); + } + + fmt::println("Data Size: {}", sizeof(LargeStruct) * sample_size); + + Stats big_for; + for(int i = 0; i < sample_number; i++) { + auto start = big_for.time_start(); + for(int i = 0; i < sample_size; i++) { + soa->a01[i] += soa->a02[i] + i; + soa->a02[i] += soa->a03[i] + i; + soa->a03[i] += soa->a04[i] + i; + soa->a04[i] += soa->a05[i] + i; + soa->a05[i] += soa->a06[i] + i; + soa->a06[i] += soa->a07[i] + i; + soa->a07[i] += soa->a08[i] + i; + soa->a08[i] += soa->a09[i] + i; + soa->a09[i] += soa->a10[i] + i; + soa->a10[i] += soa->a11[i] + i; + soa->a11[i] += soa->a12[i] + i; + soa->a12[i] += soa->a13[i] + i; + soa->a13[i] += soa->a14[i] + i; + soa->a14[i] += soa->a15[i] + i; + soa->a15[i] += soa->a16[i] + i; + soa->a16[i] += soa->a17[i] + i; + soa->a17[i] += soa->a18[i] + i; + soa->a18[i] += soa->a19[i] + i; + soa->a19[i] += soa->a20[i] + i; + } + big_for.sample_time(start); + } + big_for.dump("soa big for"); + fmt::println("-------------\n"); + samples.soa_big_for.push_back(big_for); + + Stats many_for; + for(int i = 0; i < sample_number; i++) { + auto start = many_for.time_start(); + for(int i = 0; i < sample_size; i++) soa->a01[i] += soa->a02[i] + i; + for(int i = 0; i < sample_size; i++) soa->a02[i] += soa->a03[i] + i; + for(int i = 0; i < sample_size; i++) soa->a03[i] += soa->a04[i] + i; + for(int i = 0; i < sample_size; i++) soa->a04[i] += soa->a05[i] + i; + for(int i = 0; i < sample_size; i++) soa->a05[i] += soa->a06[i] + i; + for(int i = 0; i < sample_size; i++) soa->a06[i] += soa->a07[i] + i; + for(int i = 0; i < sample_size; i++) soa->a07[i] += soa->a08[i] + i; + for(int i = 0; i < sample_size; i++) soa->a08[i] += soa->a09[i] + i; + for(int i = 0; i < sample_size; i++) soa->a09[i] += soa->a10[i] + i; + for(int i = 0; i < sample_size; i++) soa->a10[i] += soa->a11[i] + i; + for(int i = 0; i < sample_size; i++) soa->a11[i] += soa->a12[i] + i; + for(int i = 0; i < sample_size; i++) soa->a12[i] += soa->a13[i] + i; + for(int i = 0; i < sample_size; i++) soa->a13[i] += soa->a14[i] + i; + for(int i = 0; i < sample_size; i++) soa->a14[i] += soa->a15[i] + i; + for(int i = 0; i < sample_size; i++) soa->a15[i] += soa->a16[i] + i; + for(int i = 0; i < sample_size; i++) soa->a16[i] += soa->a17[i] + i; + for(int i = 0; i < sample_size; i++) soa->a17[i] += soa->a18[i] + i; + for(int i = 0; i < sample_size; i++) soa->a18[i] += soa->a19[i] + i; + for(int i = 0; i < sample_size; i++) soa->a19[i] += soa->a20[i] + i; + many_for.sample_time(start); + } + many_for.dump("soa many for"); + fmt::println("-------------\n"); + samples.soa_many_for.push_back(many_for); + + Stats ls_big_for; + for(int i = 0; i < sample_number; i++) { + auto start = ls_big_for.time_start(); + for(auto& el : *ls) { + el.a01 += el.a02 + i; + el.a02 += el.a03 + i; + el.a03 += el.a04 + i; + el.a04 += el.a05 + i; + el.a05 += el.a06 + i; + el.a06 += el.a07 + i; + el.a07 += el.a08 + i; + el.a08 += el.a09 + i; + el.a09 += el.a10 + i; + el.a10 += el.a11 + i; + el.a11 += el.a12 + i; + el.a12 += el.a13 + i; + el.a13 += el.a14 + i; + el.a14 += el.a15 + i; + el.a15 += el.a16 + i; + el.a16 += el.a17 + i; + el.a17 += el.a18 + i; + el.a18 += el.a19 + i; + el.a19 += el.a20 + i; + } + ls_big_for.sample_time(start); + } + ls_big_for.dump("ls big for"); + fmt::println("-------------\n"); + samples.ls_big_for.push_back(ls_big_for); + + Stats ls_many_for; + for(int i = 0; i < sample_number; i++) { + auto start = ls_many_for.time_start(); + for(auto& el : *ls) el.a01 += el.a02 + i; + for(auto& el : *ls) el.a02 += el.a03 + i; + for(auto& el : *ls) el.a03 += el.a04 + i; + for(auto& el : *ls) el.a04 += el.a05 + i; + for(auto& el : *ls) el.a05 += el.a06 + i; + for(auto& el : *ls) el.a06 += el.a07 + i; + for(auto& el : *ls) el.a07 += el.a08 + i; + for(auto& el : *ls) el.a08 += el.a09 + i; + for(auto& el : *ls) el.a09 += el.a10 + i; + for(auto& el : *ls) el.a10 += el.a11 + i; + for(auto& el : *ls) el.a11 += el.a12 + i; + for(auto& el : *ls) el.a12 += el.a13 + i; + for(auto& el : *ls) el.a13 += el.a14 + i; + for(auto& el : *ls) el.a14 += el.a15 + i; + for(auto& el : *ls) el.a15 += el.a16 + i; + for(auto& el : *ls) el.a16 += el.a17 + i; + for(auto& el : *ls) el.a17 += el.a18 + i; + for(auto& el : *ls) el.a18 += el.a19 + i; + for(auto& el : *ls) el.a19 += el.a20 + i; + ls_many_for.sample_time(start); + } + ls_many_for.dump("ls many for"); + fmt::println("-------------\n"); + samples.ls_many_for.push_back(ls_many_for); + + Stats ls_many_for_fast; + for(int i = 0; i < sample_number; i++) { + auto start = ls_many_for_fast.time_start(); + for(int i = 0; i < sample_size; i++) (*ls)[i].a01 += (*ls)[i].a02 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a02 += (*ls)[i].a03 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a03 += (*ls)[i].a04 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a04 += (*ls)[i].a05 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a05 += (*ls)[i].a06 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a06 += (*ls)[i].a07 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a07 += (*ls)[i].a08 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a08 += (*ls)[i].a09 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a09 += (*ls)[i].a10 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a10 += (*ls)[i].a11 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a11 += (*ls)[i].a12 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a12 += (*ls)[i].a13 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a13 += (*ls)[i].a14 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a14 += (*ls)[i].a15 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a15 += (*ls)[i].a16 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a16 += (*ls)[i].a17 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a17 += (*ls)[i].a18 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a18 += (*ls)[i].a19 + i; + for(int i = 0; i < sample_size; i++) (*ls)[i].a19 += (*ls)[i].a20 + i; + ls_many_for_fast.sample_time(start); + } + ls_many_for_fast.dump("ls many for"); + fmt::println("-------------\n"); + samples.ls_many_for_fast.push_back(ls_many_for_fast); + + Stats moa_big_for; + for(int i = 0; i < sample_number; i++) { + auto start = moa_big_for.time_start(); + for(int i = 0; i < sample_size; i++) { + moa->s1[i].a01 += moa->s1[i].a02 + i; + moa->s1[i].a02 += moa->s1[i].a03 + i; + moa->s1[i].a03 += moa->s1[i].a04 + i; + moa->s1[i].a04 += moa->s2[i].a05 + i; + + moa->s2[i].a05 += moa->s2[i].a06 + i; + moa->s2[i].a06 += moa->s2[i].a07 + i; + moa->s2[i].a07 += moa->s2[i].a08 + i; + moa->s2[i].a08 += moa->s3[i].a09 + i; + + moa->s3[i].a09 += moa->s3[i].a10 + i; + moa->s3[i].a10 += moa->s3[i].a11 + i; + moa->s3[i].a11 += moa->s3[i].a12 + i; + moa->s3[i].a12 += moa->s4[i].a13 + i; + + moa->s4[i].a13 += moa->s4[i].a14 + i; + moa->s4[i].a14 += moa->s4[i].a15 + i; + moa->s4[i].a15 += moa->s4[i].a16 + i; + moa->s4[i].a16 += moa->s5[i].a17 + i; + + moa->s5[i].a17 += moa->s5[i].a18 + i; + moa->s5[i].a18 += moa->s5[i].a19 + i; + moa->s5[i].a19 += moa->s5[i].a20 + i; + } + moa_big_for.sample_time(start); + } + moa_big_for.dump("moa big for"); + fmt::println("-------------\n"); + samples.moa_big_for.push_back(moa_big_for); + + Stats moa_many_for; + for(int i = 0; i < sample_number; i++) { + auto start = moa_many_for.time_start(); + for(int i = 0; i < sample_size; i++) { + for(auto& el : moa->s1) { + el.a01 += el.a02 + i; + el.a02 += el.a03 + i; + el.a03 += el.a04 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s1[i].a04 += moa->s2[i].a05 + i; + } + + for(auto& el : moa->s2) { + el.a05 += el.a06 + i; + el.a06 += el.a07 + i; + el.a07 += el.a08 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s2[i].a08 += moa->s3[i].a09 + i; + } + + + for(auto& el : moa->s3) { + el.a09 += el.a10 + i; + el.a10 += el.a11 + i; + el.a11 += el.a12 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s3[i].a12 += moa->s4[i].a13 + i; + } + + for(auto& el : moa->s4) { + el.a13 += el.a14 + i; + el.a14 += el.a15 + i; + el.a15 += el.a16 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s4[i].a16 += moa->s5[i].a17 + i; + } + + for(auto& el : moa->s5) { + el.a17 += el.a18 + i; + el.a18 += el.a19 + i; + el.a19 += el.a20 + i; + } + } + moa_many_for.sample_time(start); + } + moa_many_for.dump("moa many for"); + fmt::println("-------------\n"); + samples.moa_many_for.push_back(moa_many_for); + + Stats moa_many_for_fast; + for(int i = 0; i < sample_number; i++) { + auto start = moa_many_for_fast.time_start(); + for(int i = 0; i < sample_size; i++) { + moa->s1[i].a01 += moa->s1[i].a02 + i; + moa->s1[i].a02 += moa->s1[i].a03 + i; + moa->s1[i].a03 += moa->s1[i].a04 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s1[i].a04 += moa->s2[i].a05 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s2[i].a05 += moa->s2[i].a06 + i; + moa->s2[i].a06 += moa->s2[i].a07 + i; + moa->s2[i].a07 += moa->s2[i].a08 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s2[i].a08 += moa->s3[i].a09 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s3[i].a09 += moa->s3[i].a10 + i; + moa->s3[i].a10 += moa->s3[i].a11 + i; + moa->s3[i].a11 += moa->s3[i].a12 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s3[i].a12 += moa->s4[i].a13 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s4[i].a13 += moa->s4[i].a14 + i; + moa->s4[i].a14 += moa->s4[i].a15 + i; + moa->s4[i].a15 += moa->s4[i].a16 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s4[i].a16 += moa->s5[i].a17 + i; + } + + for(int i = 0; i < sample_size; i++) { + moa->s5[i].a17 += moa->s5[i].a18 + i; + moa->s5[i].a18 += moa->s5[i].a19 + i; + moa->s5[i].a19 += moa->s5[i].a20 + i; + } + moa_many_for_fast.sample_time(start); + } + moa_many_for_fast.dump("moa many for"); + fmt::println("-------------\n"); + samples.moa_many_for_fast.push_back(moa_many_for_fast); + } + + void test_soa_performance() { + Samples samples; + + for(int i = 0; i < 10; i++) { + run_test(samples, 100, 100); + } + + for(size_t i = 0; i < samples.soa_big_for.size(); i++) { + fmt::println("{:.2f} {:.2f} {:.2f} {:.2f}!={:.2f} {:.2f} {:.2f}!={:.2f}", + samples.soa_big_for[i].mean(), + samples.soa_many_for[i].mean(), + samples.ls_big_for[i].mean(), + samples.ls_many_for[i].mean(), + samples.ls_many_for_fast[i].mean(), + samples.moa_big_for[i].mean(), + samples.moa_many_for[i].mean(), + samples.moa_many_for_fast[i].mean()); + } + } + + fuc2::Set TESTS{ + .name="perf", + .tests={ + TEST(test_soa_performance), + } + }; +} diff --git a/tests/player_script.lua b/tests/player_script.lua new file mode 100644 index 0000000..eef600a --- /dev/null +++ b/tests/player_script.lua @@ -0,0 +1,31 @@ +p1 = player.new(2) + +-- p2 is still here from being +-- set with lua["p2"] = player(0); below +local p2shoots = p2:shoot() +assert(not p2shoots) +-- had 0 ammo + +-- set variable property setter +p1.hp = 545 +-- get variable through property unqualified_getter +print(p1.hp) +assert(p1.hp == 545) + +local did_shoot_1 = p1:shoot() +print(did_shoot_1) +print(p1.bullets) +local did_shoot_2 = p1:shoot() +print(did_shoot_2) +print(p1.bullets) +local did_shoot_3 = p1:shoot() +print(did_shoot_3) + +-- can read +print(p1.bullets) +-- would error: is a readonly variable, cannot write +-- p1.bullets = 20 + +p1:boost() +-- call the function we define at runtime from a Lua script +p1:brake() diff --git a/tests/prelude_script.lua b/tests/prelude_script.lua new file mode 100644 index 0000000..a1d4746 --- /dev/null +++ b/tests/prelude_script.lua @@ -0,0 +1,4 @@ +function player:brake () + self.speed = 0 + print("we hit the brakes!") +end diff --git a/tests/soa_tests.cpp b/tests/soa_tests.cpp new file mode 100644 index 0000000..9c00d66 --- /dev/null +++ b/tests/soa_tests.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#define SOL_ALL_SAFETIES_ON 1 +#include +#include "stats.hpp" + +using namespace fuc2; + +namespace soa_tests { + + + + struct SOAStyle { + std::vector name; + std::vector hp; + std::vector damage; + std::vector dead; + size_t count=0; + }; + + void test_soa_basics() { + SOAStyle world; + + for(int i = 0; i < 10; i++) { + auto name = fmt::format("Fighter-{}", i); + world.name.emplace_back(name); + world.hp.emplace_back(i+10); + world.damage.emplace_back(i+1); + world.dead.emplace_back(false); + world.count++; + } + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("SOAStyle", + "name", &SOAStyle::name, + "hp", &SOAStyle::hp, + "damage", &SOAStyle::damage, + "dead", &SOAStyle::dead, + "count", &SOAStyle::count); + + lua.script_file("tests/soa_tests.lua"); + sol::function handler = lua["handler"]; + + handler(world); + } + + fuc2::Set TESTS{ + .name="soa", + .options={ .fail_fast=false }, + .tests={ + TEST(test_soa_basics), + } + }; +} diff --git a/tests/soa_tests.lua b/tests/soa_tests.lua new file mode 100644 index 0000000..11366f4 --- /dev/null +++ b/tests/soa_tests.lua @@ -0,0 +1,25 @@ +function combat(world, index) + world.hp[index] = world.hp[index] - world.damage[index] +end + +function cull_dead(world, index) + if world.hp[index] < 0 then + world.dead[index] = true + end +end + + +function handler(world) + print("world count", world.count) + + for i = 1, world.count + 1, 1 + do + combat(world, i) + cull_dead(world, i) + + print(world.name[i], + "HP now", world.hp[i], + "damage", world.damage[i], + "dead", world.dead[i]) + end +end diff --git a/tests/sol2_test.cpp b/tests/sol2_test.cpp new file mode 100644 index 0000000..c01bd23 --- /dev/null +++ b/tests/sol2_test.cpp @@ -0,0 +1,321 @@ +#include +#include +#include +#include +#define SOL_ALL_SAFETIES_ON 1 +#include + +using namespace fuc2; + +namespace sol2_tests { + + void test_basic_lua_run() { + sol::state lua; + + int x = 0; + + lua.set_function("beep", [&x]{++x; }); + lua.script("beep()"); + + EQUAL(x, 1, "Lua failed to set x"); + } + + struct vars { + int boop = 0; + }; + + void test_big_ass_vector() { + std::vector variables; + for(int i = 0; i < 100; i++) { + variables.emplace_back(i); + } + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("vars", "boop", &vars::boop); + lua.script(R"( + function handler(v) + v.boop = v.boop + 1 + end + )"); + + sol::function handler = lua["handler"]; + + for(auto& v : variables) { + int boop_before = v.boop; + + handler(v); + + CHECK(boop_before == v.boop - 1); + } + } + + void test_usertype_vars() { + sol::state lua; + lua.new_usertype("vars", "boop", &vars::boop); + lua.script("beep = vars.new()\n" + "beep.boop = 1"); + + EQUAL(lua.get("beep").boop, 1); + } + + void test_base_libraries() { + using L = sol::lib; + + sol::state lua; + lua.open_libraries( + L::base, + L::package, + L::coroutine, + L::string, + L::os, + L::math, + L::table, + L::debug, + L::bit32, + L::io, + L::ffi, + L::jit); + + lua.script("print('bark bark bark!')"); + fmt::println(""); + } + + void test_sol_lua_error() { + BLOWS_UP([]() { + sol::state lua; + auto result1 = lua.safe_script("bad.code"); + }); + } + + void test_variables_from_file() { + sol::state lua; + lua.script_file("tests/variables.lua"); + + CHECK(lua["config"]["fullscreen"] == false); + + sol::table config = lua["config"]; + CHECK(config["fullscreen"] == false); + } + + void test_passing_args_to_scripts() { + sol::state lua; + lua.open_libraries(sol::lib::base); + + const auto& my_script = R"( +local a,b,c = ... +print(a, b, c) + )"; + + sol::load_result fx = lua.load(my_script); + if(!fx.valid()) { + sol::error err = fx; + throw err; + } + + fx("your", "arguments", "here"); + } + + void test_transfer_func() { + sol::state lua; + sol::state lua2; + + lua2.open_libraries(sol::lib::base); + sol::load_result lr = lua.load("a = function(v) print(v) return v end"); + CHECK(lr.valid()); + + sol::protected_function target = lr.get(); + sol::bytecode target_bc = target.dump(); + + auto result2 = lua2.safe_script(target_bc.as_string_view(), sol::script_pass_on_error); + CHECK(result2.valid()); + + sol::protected_function pf = lua2["a"]; + int v = pf(25557); + CHECK(v == 25557); + } + + void test_get_variables() { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set("number", 24); + CHECK(lua["number"] == 24); + + lua["number"] = 24.5; + CHECK(lua["number"] == 24.5); + + // how to compare a string? + lua["important_string"] = "test"; + std::string a_string = "test"; + CHECK(lua["important_string"] == a_string); + + lua["a_function"] = []() { return 100; }; + auto result = lua["a_function"](); + CHECK(result.valid()); + + int as_int = result; + CHECK(as_int == 100); + + lua["some_table"] = lua.create_table_with("value", 24); + CHECK(lua["some_table"]["value"] == 24); + + } + + void test_this_has_a_bug_in_sol2() { + sol::state lua; + + lua.script(R"( + function handler (message) + return "Handled this message: " .. message + end + + function f (a) + if a < 0 then + error("negative number detected") + end + return a + 5 + end + )"); + + sol::protected_function f = lua["f"]; + // this fails, error_handler is private + // f.error_handler = lua["handler"]; + + sol::protected_function_result result = f(-500); + if (result.valid()) { + // Call succeeded + int x = result; + } + else { + // Call failed + sol::error err = result; + std::string what = err.what(); + // 'what' Should read + // "Handled this message: negative number detected" + } + } + + void test_multiple_returns() { + sol::state lua; + + lua.script("function f(a,b,c) return a, b, c end"); + + std::tuple result; + result = lua["f"](1, 2, 3); + + CHECK(std::get<0>(result) == 1); + CHECK(std::get<1>(result) == 2); + CHECK(std::get<2>(result) == 3); + + int a; + int b; + std::string c; + sol::tie(a, b, c) = lua["f"](1, 2, "bar"); + + CHECK(a == 1); + CHECK(b == 2); + CHECK(c == "bar"); + } + + sol::object fancy_func(sol::object a, sol::object b, sol::this_state s) { + sol::state_view lua(s); + if(a.is() && b.is()) { + return sol::make_object(lua, a.as() + b.as()); + } else if(a.is()) { + bool do_triple = a.as(); + return sol::make_object(lua, b.as() * (do_triple ? 3 : 1)); + } else { + return sol::make_object(lua, sol::lua_nil); + } + } + + void test_any_object_types() { + sol::state lua; + + lua["f"] = fancy_func; + + int result = lua["f"](1, 2); + CHECK(result == 3); + + double result2 = lua["f"](false, 2.5); + CHECK(result2 == 2.5); + + lua.script("result3 = f(true, 5.5)"); + double result3 = lua["result3"]; + CHECK(result3 == 16.5); + } + + struct player { + int bullets; + int speed; + + player() : player(3, 100) {} + player(int ammo) : player(ammo, 100) {} + player(int ammo, int hitpoints) : + bullets(ammo), hp(hitpoints) {} + + void boost() { speed += 10; } + bool shoot() { + if(bullets < 1) { + return false; + } + --bullets; + return true; + } + + void set_hp(int value) { + hp = value; + } + + int get_hp() const { + return hp; + } + private: + int hp; + }; + + void test_udt_operations() { + sol::state lua; + + lua.open_libraries(sol::lib::base); + + lua["p2"] = player(0); + + sol::usertype player_type = lua.new_usertype("player", + sol::constructors< + player(), + player(int), + player(int, int)>()); + + player_type["shoot"] = &player::shoot; + player_type["boost"] = &player::boost; + player_type["hp"] = sol::property(&player::get_hp, &player::set_hp); + + player_type["speed"] = &player::speed; + player_type.set("bullets", sol::readonly(&player::bullets)); + + lua.script_file("tests/prelude_script.lua"); + lua.script_file("tests/player_script.lua"); + } + + fuc2::Set TESTS{ + .name="sol2", + .options={ .fail_fast=false }, + .tests={ + TEST(test_basic_lua_run), + TEST(test_usertype_vars), + TEST(test_base_libraries), + TEST(test_sol_lua_error), + TEST(test_variables_from_file), + TEST(test_big_ass_vector), + TEST(test_passing_args_to_scripts), + TEST(test_get_variables), + TEST(test_this_has_a_bug_in_sol2), + TEST(test_multiple_returns), + TEST(test_any_object_types), + TEST(test_udt_operations), + } + }; +} diff --git a/tests/variables.lua b/tests/variables.lua new file mode 100644 index 0000000..7e125fb --- /dev/null +++ b/tests/variables.lua @@ -0,0 +1,4 @@ +config = { + fullscreen = false, + resolution = { x = 1024, y = 768} +} diff --git a/wraps/fmt.wrap b/wraps/fmt.wrap new file mode 100644 index 0000000..7e9c5c4 --- /dev/null +++ b/wraps/fmt.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = fmt-12.0.0 +source_url = https://github.com/fmtlib/fmt/archive/12.0.0.tar.gz +source_filename = fmt-12.0.0.tar.gz +source_hash = aa3e8fbb6a0066c03454434add1f1fc23299e85758ceec0d7d2d974431481e40 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_12.0.0-1/fmt-12.0.0.tar.gz +patch_filename = fmt_12.0.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_12.0.0-1/get_patch +patch_hash = 307f288ebf3850abf2f0c50ac1fb07de97df9538d39146d802f3c0d6cada8998 +wrapdb_version = 12.0.0-1 + +[provide] +dependency_names = fmt diff --git a/wraps/fuc2.wrap b/wraps/fuc2.wrap new file mode 100644 index 0000000..50fe0df --- /dev/null +++ b/wraps/fuc2.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=fuc2-0.1.0 +url=https://lcthw.dev/cpp/fuc2.git +revision=HEAD +depth=1 +method=meson + +[provide] +fuc2 = fuc2_dep diff --git a/wraps/sol2.wrap b/wraps/sol2.wrap new file mode 100644 index 0000000..603882c --- /dev/null +++ b/wraps/sol2.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=sol2-HEAD +url=https://github.com/ThePhD/sol2.git +revision=HEAD +depth=1 +method=meson + +[provide] +sol2 = sol2_dep