From 872d878ddc507bfc810e551c9ea430fb24d95263 Mon Sep 17 00:00:00 2001 From: Daanish Date: Thu, 21 May 2026 21:56:36 +0100 Subject: [PATCH] bugs and perfomance cleanup. --- include/app/app.h | 14 ++- include/binding/definitions.h | 6 +- include/binding/ruby.h | 2 - include/localization/localization.h | 2 +- include/utils.h | 6 +- samples/stress.rb | 129 ++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 samples/stress.rb diff --git a/include/app/app.h b/include/app/app.h index add83e3..30481de 100644 --- a/include/app/app.h +++ b/include/app/app.h @@ -39,9 +39,7 @@ struct Element { struct EImage : Element { uint64_t animation_id = UINT64_MAX; - EImage(uint64_t animation_id) : Element(Type::IMAGE), animation_id(animation_id) { - animation_pool.use(animation_id); - } + EImage(uint64_t animation_id) : Element(Type::IMAGE), animation_id(animation_id) {} ~EImage() { if (animation_id != UINT64_MAX) @@ -86,6 +84,13 @@ struct EText : Element { return window.get_text_size(text_id); } + void reload_text() { + if (text_id != UINT64_MAX) { + text_pool.release(text_id); + text_id = UINT64_MAX; + } + } + void render(Window &window, uint64_t) override { if ( last_language != Localization::current_language @@ -93,13 +98,14 @@ struct EText : Element { || last_color != color || params_changed ) { - std::string localized_str = Localization::get(Ruby::mrb, key, params); + std::string localized_str = Localization::get(key, params); if (text_id != UINT64_MAX) text_pool.release(text_id); text_id = window.create_text(font_id, localized_str.c_str(), localized_str.size(), color); last_language = Localization::current_language; last_key = key; last_color = color; + params_changed = false; } window.write(text_id, position, z, rotation, pivot, scale, alpha); } diff --git a/include/binding/definitions.h b/include/binding/definitions.h index 9b56080..ae06344 100644 --- a/include/binding/definitions.h +++ b/include/binding/definitions.h @@ -65,6 +65,7 @@ static mrb_value params_set(mrb_state *mrb, mrb_value self) { app::EText *element = (app::EText *)app::element_pool[id]; element->params[mrb_sym2name(mrb, key)] = mrb_str_to_cstr(mrb, mrb_obj_as_string(mrb, value)); + element->reload_text(); return mrb_nil_value(); } @@ -509,7 +510,7 @@ static mrb_value element_text_inspect(mrb_state *mrb, mrb_value self) { static mrb_value element_text_to_s(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementText(mrb, self); app::EText *element = (app::EText *)app::element_pool[id]; - std::string localized_text = Localization::get(Ruby::mrb, element->key, element->params); + std::string localized_text = Localization::get(element->key, element->params); return mrb_str_new_cstr(mrb, localized_text.c_str()); } @@ -1173,6 +1174,7 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { } uint64_t id = get_id_Animation(mrb, animation_val); + animation_pool.use(id); app::EImage *element = new app::EImage(id); element->position = {x, y}; @@ -1572,7 +1574,7 @@ static mrb_value scene_elements_at(mrb_state *mrb, mrb_value self) { mrb_value position_val; mrb_get_args(mrb, "o", &position_val); - float x, y; + float x = 0, y = 0; HASH_FLOAT(position_val, x, x); HASH_FLOAT(position_val, y, y); Vec2 position = {x, y}; diff --git a/include/binding/ruby.h b/include/binding/ruby.h index c111dcf..f12f94b 100644 --- a/include/binding/ruby.h +++ b/include/binding/ruby.h @@ -20,8 +20,6 @@ out = mrb_float(v); \ else if (mrb_fixnum_p(v)) \ out = (float)mrb_fixnum(v); \ - else \ - out = 0; \ } while (0) #define HASH_BOOL(h, key, out) \ diff --git a/include/localization/localization.h b/include/localization/localization.h index fbfefcc..2825b9f 100644 --- a/include/localization/localization.h +++ b/include/localization/localization.h @@ -71,7 +71,7 @@ inline void set(LanguageCode lang_code, Key key, const std::string &text) { localized_strings[join_keys(lang_code, key)] = parse_localized(text); } -inline std::string get(mrb_state *mrb, Key key, const std::unordered_map ¶ms) { +inline std::string get(Key key, const std::unordered_map ¶ms) { auto it = localized_strings.find(join_keys(current_language, key)); if (it == localized_strings.end()) return "Localization not set!"; diff --git a/include/utils.h b/include/utils.h index 3d0a0d3..9b53636 100644 --- a/include/utils.h +++ b/include/utils.h @@ -163,6 +163,10 @@ struct Rect { template struct Pool { + uint32_t size() const { + return slots.size() - free_indices.size(); + } + T *operator[](uint64_t index) { if (!is_valid(index)) { fprintf(stderr, "Invalid pool index: %lu\n", (uint64_t)index); @@ -191,7 +195,7 @@ struct Pool { slots.push_back({}); } slots[id].ptr = item; - slots[id].refcount = 1; + slots[id].refcount = 0; slots[id].alive = true; slots[id].generation++; uint64_t index = ((uint64_t)slots[id].generation << 32) | id; diff --git a/samples/stress.rb b/samples/stress.rb new file mode 100644 index 0000000..bd45693 --- /dev/null +++ b/samples/stress.rb @@ -0,0 +1,129 @@ +App.run 720, 480, "Stress Test", fps: 120 + +scene = Scene.new + +font = Font.new "assets/fonts/charybdis.ttf", 24, pixel_art: true + +# primary factor in slowing teh system down is number of gpu textures +# i.e if we increase images or unique texts, we will see a significant drop in fps, +# but if we just increase number of elements with same image, the engine should be able to handle it much better + +# --- Generate images --- +images = [] +1.times do |i| + images << Image.new("assets/images/menu_bg.png", pixel_art: true) +end + +# --- Animation pool stress --- +animations = [] +1.times do |i| + anim = Animation.new(images.sample(5), fps: 5 + (i % 30)) + animations << anim +end + +# --- Massive element pool --- +elements = [] + +App.language = :en + +App.localize(:en, :btn_text, "Stress Test %{count}") + +App.localize(:en, :text, "Random Text") + +1.times do |i| + elements << ElementText.new( + :text, + font, + rand(0xFFFFFF), + {}, + position: { x: rand(0..700), y: rand(0..450) }, + z: rand(-10..10), + click_mode: :pass, + alpha: rand + ) +end + +500.times do |i| + elements << ElementImage.new( + animations.sample, + position: { x: rand(0..700), y: rand(0..450) }, + z: rand(-10..10), + alpha: rand, + scale: { x: rand * 2, y: rand * 2 }, + click_mode: :pass + ) +end + +500.times do |i| + elements << ElementRect.new( + 20 + rand(100), + 20 + rand(100), + rand(0xFFFFFF), + position: { x: rand(0..700), y: rand(0..450) }, + z: rand(-10..10), + rotation: rand(360), + alpha: rand, + click_mode: :pass + ) +end + +text = ElementText.new( + :btn_text, + font, + 0xFF0000, + { count: 0 }, + position: { x: 20, y: 20 }, + z: 100, + click_mode: :block +) + +scene << text +elements.each { |e| scene << e } + +# --- FPS thrashing test --- +fps_values = [30, 60, 120, 240] + +i = 0 +click_counter = 0 + +text.click do + click_counter += 1 + text.params[:count] = click_counter +end + +start_time ||= Time.now +frame_count ||= 0 +accum_dt ||= 0.0 +last_log ||= 0.0 + +App.update do |dt| + frame_count += 1 + accum_dt += dt + + now = Time.now + elapsed = now - start_time + + # log every second + if elapsed - last_log >= 1.0 + dt_fps = frame_count / accum_dt + real_fps = frame_count / elapsed + + puts "---- FPS REPORT ----" + puts "Engine FPS (dt): #{dt_fps.round(2)}" + puts "Real FPS (wall): #{real_fps.round(2)}" + puts "Avg dt: #{(accum_dt / frame_count * 1000).round(3)} ms" + puts "Frame count: #{frame_count}" + puts "--------------------" + + last_log = elapsed + end + + i += 1 + + # --- Input spam check --- + if App.pressed?(:mouse_left) + puts "Mouse pressed at #{App.mouse_position}" + end +end + +App.start scene \ No newline at end of file