Fix loop to be more accuratly timed, and added custom ruby libs inclusion setup

This commit is contained in:
2026-05-20 17:30:38 +01:00
parent 1a337608f3
commit 7d48b226ab
11 changed files with 1117 additions and 27 deletions
+2
View File
@@ -4,3 +4,5 @@ build/*
bin/*
.clangd
include/binding/ruby_compiled.h
+12 -4
View File
@@ -14,6 +14,7 @@ CXX := $(CCACHE) clang++
CC := $(CCACHE) clang
# ---------------- SDL3 ----------------
SDL_CFLAGS := $(shell pkg-config --cflags sdl3 sdl3-ttf sdl3-image)
SDL_LIBS_DEBUG := $(shell pkg-config --libs sdl3 sdl3-ttf sdl3-image)
SDL_LIBS_RELEASE := $(shell pkg-config --libs sdl3 sdl3-ttf sdl3-image)
@@ -42,9 +43,11 @@ PCH_CFLAGS_RELEASE := $(CFLAGS_RELEASE) -x c++-header
LIBS := ./libs/mruby/build/host/lib/libmruby.a
MGEM_DIR := ./libs/l_mgems
# ---------------- SOURCES ----------------
SRC := $(wildcard $(SRC_DIR)/*.cc) $(wildcard $(SRC_DIR)/**/*.cc)
SRC := $(shell find $(SRC_DIR) -name "*.cc")
OBJ_DEBUG := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/debug/%.o,$(SRC))
OBJ_RELEASE := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/release/%.o,$(SRC))
@@ -54,13 +57,13 @@ DEP_RELEASE := $(OBJ_RELEASE:.o=.d)
# ---------------- TARGETS ----------------
.PHONY: all debug release clean setup
.PHONY: all debug release clean setup mgems
all: debug
debug: $(TARGET_DEBUG)
debug: mgems $(TARGET_DEBUG)
release: $(TARGET_RELEASE)
release: mgems $(TARGET_RELEASE)
# ---------------- CLANGD ----------------
@@ -68,6 +71,11 @@ setup: setup.rb
@echo "Setting up clangd configuration..."
ruby setup.rb
# ---------------- MRB_MGEMS ----------------
mgems:
$(MGEM_DIR)/compile.sh
# ---------------- PCH ----------------
$(PCH_DEBUG): $(INCLUDE_DIR)/pch.h
+12 -8
View File
@@ -54,8 +54,7 @@ struct EImage : Element {
Animation &animation = *animation_pool[animation_id];
if (animation.frames.empty())
return;
int frame_index = (int)((abs_time * animation.frame_rate) / 1000) % animation.frames.size();
int frame_index = (int)((abs_time * animation.frame_rate) / 1e9) % animation.frames.size();
window.draw(animation.frames[frame_index], position, z, rotation, scale, alpha);
}
};
@@ -166,11 +165,11 @@ struct Scene {
}
void update_scene(uint64_t delta_time) {
if (!mrb_nil_p(update.proc)) {
mrb_value dt_val = mrb_int_value(Ruby::mrb, delta_time);
if (mrb_nil_p(update.proc))
return;
mrb_value dt_val = mrb_float_value(Ruby::mrb, delta_time / 1e9);
update.call(1, &dt_val);
}
}
void click(Vec2<float> position) {
std::vector<uint64_t> clicked_elements = elements_at(position);
@@ -234,7 +233,7 @@ struct App {
}
void loop() {
const uint64_t target_ms = 1000 / 60;
const uint64_t TARGET_NS = 1e9 / 60;
uint64_t time_a = window.get_time();
uint64_t time_b = window.get_time();
@@ -242,8 +241,13 @@ struct App {
while (running) {
time_a = window.get_time();
uint64_t frame_time = time_a - time_b;
if (frame_time <= target_ms)
usleep((target_ms - frame_time) * 1000);
if (frame_time <= TARGET_NS) {
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = TARGET_NS - frame_time;
nanosleep(&req, nullptr);
}
time_b = window.get_time();
+10
View File
@@ -4,6 +4,8 @@
#include "pch.h"
#include "utils.h"
#include "binding/ruby_compiled.h"
#define DEF_SYM(name) inline mrb_sym sym_##name = mrb_intern_cstr(Ruby::mrb, #name);
#define SYM(name) mrb_symbol_value(sym_##name)
@@ -83,7 +85,15 @@ struct Block {
};
inline void run_string(const char *code, size_t length) {
int ai = mrb_gc_arena_save(mrb);
mrb_load_nstring(mrb, code, length);
mrb_gc_arena_restore(mrb, ai);
}
inline void load_mgems() {
int ai = mrb_gc_arena_save(mrb);
mrb_load_irep(mrb, mgems);
mrb_gc_arena_restore(mrb, ai);
}
}; // namespace Ruby
+2 -2
View File
@@ -341,8 +341,8 @@ public:
// Time
uint64_t get_time(); // returns the time in milliseconds since the window was created
uint64_t delta_time(); // returns the time in milliseconds since the last render call
uint64_t get_time(); // returns the time in nanoseconds since the window was created
uint64_t delta_time(); // returns the time in nanoseconds since the last render call
private:
// internal data and functions
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
OUTPUT_TMP=$(mktemp)
OUTPUT="$SCRIPT_DIR/../../include/binding/ruby_compiled.h"
if test "$1" = "clean"; then
rm -f "$OUTPUT"
exit 0
fi
trap 'rm -f "$OUTPUT_TMP"' EXIT
mapfile -t FILES < <(find "$SCRIPT_DIR" -name "*.rb" | sort)
LATEST_FILE_TIME=$(for file in "${FILES[@]}"; do
echo $(stat -c "%Y" "$file")
done | sort -n | tail -1)
OUTPUT_TIME=$(stat -c "%Y" "$OUTPUT" 2>/dev/null || echo 0)
if test "$OUTPUT_TIME" -ge "$LATEST_FILE_TIME"; then
echo "mgems is up to date."
exit 0
fi
"$SCRIPT_DIR/../mruby/bin/mrbc" -o "$OUTPUT_TMP" "${FILES[@]}"
{
echo "#ifndef MGEMS_H"
echo "#define MGEMS_H"
xxd -i -n mgems $OUTPUT_TMP \
| sed -e 's/^unsigned char /constexpr unsigned char /' \
-e 's/^unsigned int /constexpr unsigned int /'
echo "#endif"
} > "$OUTPUT"
echo "Compiled mgems!"
+1010
View File
File diff suppressed because it is too large Load Diff
+19 -8
View File
@@ -22,6 +22,7 @@ rect.on_click do
rect.color = rect.color == 0xFF0000 ?
0x00FF00 :
0xFF0000
main_scene << text unless main_scene.elements.include?(text)
text.params[:name] = text.params[:name] == "Me" ?
"You" :
"Me"
@@ -30,35 +31,45 @@ end
App.map_key :mouse_left, :press
start = Time.now
c_ = 0.0
c_c = 0
main_scene.on_update do |delta_time|
# puts "Delta time: #{delta_time}ms"
c_ += delta_time
c_c += 1
# puts "Delta time: #{delta_time}s"
# commented out to avoid spamming the console, but you can uncomment to see the delta time being printed every frame
# runs at 60fps, so you should see around 16-17ms being printed
el = main_scene.elements_at(App.mouse_position)
puts App.text_input if App.text_input != ""
pp App.text_input if App.text_input != ""
next unless App.pressed?(:press)
puts "#{App.pressed?(:press)}, #{App.down?(:press)}, #{App.released?(:press)}"
pp "#{App.pressed?(:press)}, #{App.down?(:press)}, #{App.released?(:press)}"
for elem in el
if elem == image_element
puts "Down over image element"
pp "Down over image element"
elsif elem == rect
puts "Down over rect element"
pp "Down over rect element"
elsif elem == text
puts "Down over text element"
pp "Down over text element"
end
end
end
main_scene << text
main_scene << rect
main_scene << image_element
# main_scene.delete(text) # Uncomment to test element deletion
main_scene.delete(text)
App.start main_scene
p "Average delta time: #{c_ / c_c}s"
p "Time since start (real): #{Time.now - start}s"
p "Time since start (from dt): #{c_}s"
+1
View File
@@ -1,6 +1,7 @@
#include "binding/definitions.h"
int main(int argc, char *argv[]) {
Ruby::load_mgems();
definitions::setup();
char *startup_script = nullptr;
+5 -2
View File
@@ -451,14 +451,17 @@ const char *Window::paste() {
}
uint64_t Window::get_time() {
return SDL_GetTicks();
return SDL_GetTicksNS();
}
uint64_t Window::delta_time() {
static uint64_t last_time = 0;
uint64_t current_time = SDL_GetTicks();
static bool first_call = 1;
uint64_t current_time = SDL_GetTicksNS();
uint64_t delta = current_time - last_time;
last_time = current_time;
if (first_call)
return (first_call = 0);
return delta;
}
+1
View File
@@ -234,6 +234,7 @@ Other features that a game engine should have/has (need to go in depth)
- audio playing
- network interface, with a lib to work with rack backends (and generic socket api)
- persistence store
- rng helpers
## Embedded scripting language choice.