diff options
-rw-r--r-- | editor/app.cpp | 1 | ||||
-rw-r--r-- | editor/app.hpp | 9 | ||||
-rw-r--r-- | editor/camera.cpp | 5 | ||||
-rw-r--r-- | editor/events.cpp | 1 | ||||
-rw-r--r-- | editor/keys.hpp | 1 | ||||
-rw-r--r-- | editor/update.cpp | 20 | ||||
-rw-r--r-- | floormat/app.hpp | 6 | ||||
-rw-r--r-- | include/mg/Time.h | 2 | ||||
-rw-r--r-- | include/mg/TimeStl.h | 2 | ||||
-rw-r--r-- | main/ctor.cpp | 2 | ||||
-rw-r--r-- | main/draw.cpp | 43 | ||||
-rw-r--r-- | main/main-impl.hpp | 14 | ||||
-rw-r--r-- | main/setup.cpp | 2 | ||||
-rw-r--r-- | src/anim.hpp | 2 | ||||
-rw-r--r-- | src/critter.cpp | 62 | ||||
-rw-r--r-- | src/critter.hpp | 8 | ||||
-rw-r--r-- | src/light.cpp | 3 | ||||
-rw-r--r-- | src/light.hpp | 2 | ||||
-rw-r--r-- | src/object.cpp | 36 | ||||
-rw-r--r-- | src/object.hpp | 6 | ||||
-rw-r--r-- | src/scenery.cpp | 16 | ||||
-rw-r--r-- | src/scenery.hpp | 6 | ||||
-rw-r--r-- | src/timer-fwd.hpp | 27 | ||||
-rw-r--r-- | src/timer.cpp | 76 | ||||
-rw-r--r-- | src/timer.hpp | 13 | ||||
-rw-r--r-- | test/app.cpp | 1 | ||||
-rw-r--r-- | test/app.hpp | 1 | ||||
-rw-r--r-- | test/serializer.cpp | 7 | ||||
-rw-r--r-- | test/time.cpp | 28 |
29 files changed, 304 insertions, 98 deletions
diff --git a/editor/app.cpp b/editor/app.cpp index db395b98..10f4b25d 100644 --- a/editor/app.cpp +++ b/editor/app.cpp @@ -5,6 +5,7 @@ #include "editor.hpp" #include "src/anim-atlas.hpp" #include "src/critter.hpp" +#include "src/timer-fwd.hpp" #include "src/world.hpp" #include "floormat/main.hpp" #include "floormat/settings.hpp" diff --git a/editor/app.hpp b/editor/app.hpp index ea618bac..32193fe6 100644 --- a/editor/app.hpp +++ b/editor/app.hpp @@ -93,14 +93,14 @@ private: int exec(); - void update(float dt) override; - void update_world(float dt); + void update(Ns dt) override; + void update_world(Ns dt); void update_cursor_tile(const Optional<Vector2i>& pixel); z_bounds get_z_bounds() override; void set_cursor(); void maybe_initialize_chunk(const chunk_coords_& pos, chunk& c) override; void maybe_initialize_chunk_(const chunk_coords_& pos, chunk& c); - void update_character(float dt); + void update_character(Ns dt); void reset_world(); void reset_world(class world&& w); @@ -157,6 +157,7 @@ private: void do_key(key k); void do_set_mode(editor_mode mode); void do_rotate(bool backward); + static void do_emit_timestamp(); void apply_commands(const key_set& k); int get_key_modifiers(); void clear_keys(key min_inclusive, key max_exclusive); @@ -164,7 +165,7 @@ private: void clear_non_global_keys(); void clear_non_repeated_keys(); - void do_camera(float dt, const key_set& cmds, int mods); + void do_camera(Ns dt, const key_set& cmds, int mods); void reset_camera_offset(); [[nodiscard]] bool tests_handle_key(const key_event& e, bool is_down); diff --git a/editor/camera.cpp b/editor/camera.cpp index e103c844..e71be739 100644 --- a/editor/camera.cpp +++ b/editor/camera.cpp @@ -7,13 +7,14 @@ #include "src/object.hpp" #include "src/world.hpp" #include "src/camera-offset.hpp" +#include "src/timer.hpp" #include "compat/enum-bitset.hpp" #include <bit> #include <Magnum/Math/Functions.h> namespace floormat { -void app::do_camera(float dt, const key_set& cmds, int mods) +void app::do_camera(Ns dt, const key_set& cmds, int mods) { if (cmds[key_camera_reset]) { @@ -44,7 +45,7 @@ void app::do_camera(float dt, const key_set& cmds, int mods) auto camera_offset = shader.camera_offset(); const auto max_camera_offset = Vector2d(sz * 10); - camera_offset -= dir.normalized() * (double)dt * pixels_per_second; + camera_offset -= dir.normalized() * (double)Time::to_seconds(dt) * pixels_per_second; camera_offset = Math::clamp(camera_offset, -max_camera_offset, max_camera_offset); shader.set_camera_offset(camera_offset, shader.depth_offset()); diff --git a/editor/events.cpp b/editor/events.cpp index cc1dbb3d..2ba39ff5 100644 --- a/editor/events.cpp +++ b/editor/events.cpp @@ -137,6 +137,7 @@ auto app::resolve_keybinding(int k_, int mods_) -> std::tuple<key, int> case SDLK_d: return key_camera_right; case SDLK_HOME: return key_camera_reset; case SDLK_r: return key_rotate_tile; + case SDLK_F2: return key_emit_timestamp; case SDLK_1: return key_mode_none; case SDLK_2: return key_mode_floor; case SDLK_3: return key_mode_walls; diff --git a/editor/keys.hpp b/editor/keys.hpp index a7f042d6..407d1f63 100644 --- a/editor/keys.hpp +++ b/editor/keys.hpp @@ -19,6 +19,7 @@ enum key : unsigned { key_rotate_tile, key_mode_none, key_mode_floor, key_mode_walls, key_mode_scenery, key_mode_vobj, key_mode_tests, key_render_collision_boxes, key_render_clickables, key_render_vobjs, key_render_all_z_levels, + key_emit_timestamp, key_GLOBAL, key_new_file, key_quit, diff --git a/editor/update.cpp b/editor/update.cpp index 05b12e51..dffc1fdd 100644 --- a/editor/update.cpp +++ b/editor/update.cpp @@ -12,6 +12,7 @@ #include "floormat/main.hpp" #include "src/critter.hpp" #include "src/tile-iterator.hpp" +#include "src/timer.hpp" #include "keys.hpp" #include "loader/loader.hpp" #include "compat/enum-bitset.hpp" @@ -121,6 +122,17 @@ void app::do_rotate(bool backward) } } +void app::do_emit_timestamp() +{ + static struct + { + Time time = Time::now(); + unsigned ctr = 0; + } s; + char buf[fm_DATETIME_BUF_SIZE]; + fm_debug("%s -- MARK -- 0x%08X", format_datetime_to_string(buf), ++s.ctr & 0xffff'ffffU); +} + void app::do_set_mode(editor_mode mode) { if (mode != _editor->mode()) @@ -152,6 +164,8 @@ void app::do_key(key k, int mods) return; case key_rotate_tile: return do_rotate(false); + case key_emit_timestamp: + return do_emit_timestamp(); case key_mode_none: return do_set_mode(editor_mode::none); case key_mode_floor: @@ -193,7 +207,7 @@ void app::apply_commands(const key_set& keys) do_key(k, key_modifiers[i]); } -void app::update_world(float dt) +void app::update_world(Ns dt) { auto& world = M->world(); world.increment_frame_no(); @@ -214,7 +228,7 @@ void app::update_world(float dt) } } -void app::update_character([[maybe_unused]] float dt) +void app::update_character([[maybe_unused]] Ns dt) { auto& keys = *keys_; if (_character_id) @@ -244,7 +258,7 @@ auto app::get_z_bounds() -> z_bounds return { chunk_z_min, chunk_z_max, _z_level, !_render_all_z_levels }; } -void app::update(float dt) +void app::update(Ns dt) { //M->world().collect(); M->world().collect(true); diff --git a/floormat/app.hpp b/floormat/app.hpp index c07c48e1..01f94b7f 100644 --- a/floormat/app.hpp +++ b/floormat/app.hpp @@ -1,9 +1,11 @@ #pragma once -namespace Magnum::Math { template<typename T> class Vector2; } +namespace Magnum::Math { template<typename T> class Vector2; template<class T> class Nanoseconds; } namespace floormat { +using Ns = Math::Nanoseconds<int64_t>; + struct mouse_move_event; struct mouse_button_event; struct mouse_scroll_event; @@ -28,7 +30,7 @@ struct floormat_app [[deprecated]] floormat_app(floormat_app&&) = default; [[deprecated]] floormat_app& operator=(floormat_app&&) = default; - virtual void update(float dt) = 0; + virtual void update(Ns t) = 0; virtual void maybe_initialize_chunk(const chunk_coords_& pos, chunk& c) = 0; virtual void draw() = 0; virtual z_bounds get_z_bounds() = 0; diff --git a/include/mg/Time.h b/include/mg/Time.h new file mode 100644 index 00000000..cd7f8228 --- /dev/null +++ b/include/mg/Time.h @@ -0,0 +1,2 @@ +#pragma once +#include <Magnum/Math/Time.h> diff --git a/include/mg/TimeStl.h b/include/mg/TimeStl.h new file mode 100644 index 00000000..31f958ff --- /dev/null +++ b/include/mg/TimeStl.h @@ -0,0 +1,2 @@ +#pragma once +#include <Magnum/Math/TimeStl.h> diff --git a/main/ctor.cpp b/main/ctor.cpp index da38fb18..19a4546a 100644 --- a/main/ctor.cpp +++ b/main/ctor.cpp @@ -23,7 +23,7 @@ main_impl::main_impl(floormat_app& app, fm_settings&& se, int& argc, char** argv (void)setSwapInterval(0); set_fp_mask(); arrayReserve(_clickable_scenery, 128); - timeline.start(); + tm.timeline = Time::now(); } class world& main_impl::reset_world(class world&& w) noexcept diff --git a/main/draw.cpp b/main/draw.cpp index b0f97fab..79e9ccfe 100644 --- a/main/draw.cpp +++ b/main/draw.cpp @@ -6,6 +6,7 @@ #include "main/clickable.hpp" #include "src/light.hpp" #include "src/log.hpp" +#include "src/timer.hpp" #include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/ArrayView.h> #include <Magnum/GL/DefaultFramebuffer.h> @@ -71,8 +72,7 @@ void main_impl::recalc_viewport(Vector2i fb_size, Vector2i win_size) noexcept // -- user-- app.on_viewport_event(fb_size); - - fps_sample_timeline.start(); + tm.timeline = Time::now(); } global_coords main_impl::pixel_to_tile(Vector2d position) const noexcept @@ -216,34 +216,33 @@ bool floormat_main::check_chunk_visible(const Vector2d& offset, const Vector2i& return X + W > 0 && X < size[x] && Y + H > 0 && Y < size[y]; } +#if 0 #ifndef FM_NO_DEBUG static size_t good_frames, bad_frames; // NOLINT #endif +#endif -void main_impl::do_update() +void main_impl::do_update() // todo! move to separate file { - - // todo switch to using microseconds - float dt = timeline.previousFrameDuration(); - if (dt > 0) + constexpr auto eps = 1e-5f; + const auto dt = tm.timeline.update(); + if (auto secs = Time::to_seconds(dt); secs > eps) { - if (fps_sample_timeline.currentFrameDuration() > 1.f/15) - { - fps_sample_timeline.nextFrame(); - constexpr float RC = 10; - constexpr float alpha = 1/(1 + RC); - _frame_time = _frame_time*(1-alpha) + alpha*dt; - } +#if 1 + constexpr float RC = 60; + constexpr auto alpha = 1 / (1 + RC); + _frame_time = _frame_time * (1 - alpha) + alpha * secs; +#else + _frame_time = secs; +#endif static size_t ctr = 0; - if (dt >= 1.f/55 && !dt_expected.do_sleep) - fm_debug("%zu frame took %.1f milliseconds", ctr++, dt*1e3f); + if (secs > 35e-3f /* && !dt_expected.do_sleep */) + fm_debug("%zu frame took %.2f milliseconds", ctr++, (double)secs*1e3); } else - { swapBuffers(); - timeline.nextFrame(); - } +#if 0 #ifndef FM_NO_DEBUG if (dt_expected.value == 0 || !is_log_verbose()) [[likely]] void(); @@ -286,6 +285,7 @@ void main_impl::do_update() t = {}; } } +#endif app.update(dt); } @@ -295,7 +295,7 @@ void main_impl::bind() noexcept framebuffer.fb.bind(); } -void main_impl::drawEvent() +void main_impl::drawEvent() // todo! move to separate file (with `do_update') { _shader.set_tint({1, 1, 1, 1}); @@ -318,8 +318,8 @@ void main_impl::drawEvent() swapBuffers(); redraw(); - timeline.nextFrame(); +#if 0 if (dt_expected.do_sleep && (false)) { constexpr float ε = 1e-3f; @@ -338,6 +338,7 @@ void main_impl::drawEvent() dt_expected.jitter = 0; dt_expected.value = 0; } +#endif } ArrayView<const clickable> main_impl::clickable_scenery() const noexcept diff --git a/main/main-impl.hpp b/main/main-impl.hpp index 0f1bae35..01be6187 100644 --- a/main/main-impl.hpp +++ b/main/main-impl.hpp @@ -2,6 +2,8 @@ #include "floormat/main.hpp" #include "floormat/settings.hpp" #include "src/world.hpp" +#include "src/timer-fwd.hpp" +#include "src/timer.hpp" #include "draw/ground.hpp" #include "draw/wall.hpp" #include "draw/anim.hpp" @@ -11,7 +13,6 @@ #include "main/clickable.hpp" #include <Corrade/Containers/Array.h> #include <Corrade/Containers/String.h> -#include <Magnum/Timeline.h> #include <Magnum/Math/Range.h> #include <Magnum/GL/DebugOutput.h> #include <Magnum/Platform/Sdl2Application.h> @@ -102,6 +103,12 @@ struct main_impl final : Platform::Sdl2Application, floormat_main class astar& astar() override; private: + struct { + Ns last_frame_duration{0}; + Time frame_start; + Time timeline; // todo rename + } tm; + struct texture_unit_cache _tuc; fm_settings s; [[maybe_unused]] char _dummy = (register_debug_callback(), '\0'); @@ -110,7 +117,6 @@ private: struct lightmap_shader _lightmap_shader{_tuc}; Array<clickable> _clickable_scenery; class world _world{}; - Magnum::Timeline timeline; uint32_t _mouse_cursor = (uint32_t)-1; ground_mesh _ground_mesh; wall_mesh _wall_mesh; @@ -121,14 +127,14 @@ private: safe_ptr<path_search> _search; safe_ptr<class astar> _astar; +#if 0 struct { float value = 0; float jitter = 0; bool do_sleep = false; bool has_focus = true; } dt_expected; - - Timeline fps_sample_timeline; +#endif void recalc_viewport(Vector2i fb_size, Vector2i win_size) noexcept; void draw_world() noexcept; diff --git a/main/setup.cpp b/main/setup.cpp index a16165ba..7014cc0d 100644 --- a/main/setup.cpp +++ b/main/setup.cpp @@ -59,6 +59,7 @@ void main_impl::update_window_state() { const auto flags = (SDL_WindowFlags)SDL_GetWindowFlags(window()); +#if 0 dt_expected.do_sleep = true; dt_expected.jitter = 0; dt_expected.has_focus = true; @@ -83,6 +84,7 @@ void main_impl::update_window_state() dt_expected.do_sleep = false; dt_expected.value = 1e-1f; } +#endif } auto main_impl::meshes() noexcept -> struct meshes diff --git a/src/anim.hpp b/src/anim.hpp index 3c25f51c..e22d1e6b 100644 --- a/src/anim.hpp +++ b/src/anim.hpp @@ -57,7 +57,7 @@ struct anim_def final Array<anim_group> groups{}; Vector2ui pixel_size{}; anim_scale scale = anim_scale::ratio{1}; - size_t nframes = 0, fps = 0, action_frame = 0, action_frame2 = 0; + uint32_t nframes = 0, fps = 0, action_frame = 0, action_frame2 = 0; }; } // namespace floormat diff --git a/src/critter.cpp b/src/critter.cpp index 1a4f77a1..fb3d7e52 100644 --- a/src/critter.cpp +++ b/src/critter.cpp @@ -4,13 +4,13 @@ #include "loader/loader.hpp" #include "src/world.hpp" #include "src/object.hpp" +#include "src/timer.hpp" #include "shaders/shader.hpp" #include "compat/exception.hpp" #include <cmath> #include <utility> #include <algorithm> #include <mg/Functions.h> -#include <mg/Timeline.h> namespace floormat { @@ -110,17 +110,6 @@ bool critter_proto::operator==(const object_proto& e0) const return name == s0.name && playable == s0.playable; } -int critter::allocate_frame_time(float dt) -{ - auto d = (double)delta / 65535. + (double)dt; - d = std::min(1., d); - auto ret = (int)(d / frame_time); - d -= ret; - d = Math::clamp(d, 0., 1.); - delta = (uint16_t)(d * 65535); - return ret; -} - void critter::set_keys(bool L, bool R, bool U, bool D) { b_L = L; @@ -140,10 +129,30 @@ Vector2 critter::ordinal_offset(Vector2b offset) const return Vector2(offset); } -void critter::update(size_t i, float dt) +void critter::update(size_t i, Ns dt) { if (playable) { +#if 0 + static auto TL = Time::now(); + static Ns TIME{0}; + static unsigned FRAMES; + + if (++FRAMES == 0) + TL = Time::now(); + else + TIME += dt; + + if (++FRAMES > 240) + { + auto t = TL.update(); + Debug{} << "player time" << Time::to_milliseconds(TIME) << Time::to_milliseconds(t); + Debug{} << Time::to_milliseconds(TIME) / Time::to_milliseconds(t); + TIME = Ns{0}; + FRAMES = 0; + } +#endif + const auto new_r = arrows_to_dir(b_L, b_R, b_U, b_D); if (new_r == rotation_COUNT) { @@ -157,21 +166,21 @@ void critter::update(size_t i, float dt) update_nonplayable(i, dt); } -void critter::update_nonplayable(size_t i, float dt) +void critter::update_nonplayable(size_t i, Ns dt) { (void)i; (void)dt; (void)playable; } -void critter::update_movement(size_t i, float dt, rotation new_r) +void critter::update_movement(size_t i, Ns dt, rotation new_r) { fm_assert(new_r < rotation_COUNT); fm_assert(is_dynamic()); - int nframes = allocate_frame_time(dt * speed); + auto nframes = allocate_frame_time(dt * speed); if (nframes == 0) { static unsigned foo; - Debug{} << ++foo << "stopped"; + //Debug{} << ++foo << "stopped"; return; } @@ -184,24 +193,7 @@ void critter::update_movement(size_t i, float dt, rotation new_r) c->ensure_passability(); - static Timeline TL{}; - static double TIME; - static unsigned FRAMES; - - if (++FRAMES == 0) - TL.start(); - else - TIME += (double)dt; - - if (++FRAMES > 30) - { - Debug{} << "player time" << TIME; - TL.stop(); - TIME = 0; - FRAMES = 0; - } - - for (int k = 0; k < nframes; k++) + for (auto k = 0u; k < nframes; k++) { for (unsigned j = 0; j < nvecs; j++) { diff --git a/src/critter.hpp b/src/critter.hpp index a6e9e4cc..01159710 100644 --- a/src/critter.hpp +++ b/src/critter.hpp @@ -26,9 +26,9 @@ struct critter final : object object_type type() const noexcept override; explicit operator critter_proto() const; - void update(size_t i, float dt) override; - void update_movement(size_t i, float dt, rotation r); - void update_nonplayable(size_t i, float dt); + void update(size_t i, Ns dt) override; + void update_movement(size_t i, Ns dt, rotation r); + void update_nonplayable(size_t i, Ns dt); void set_keys(bool L, bool R, bool U, bool D); Vector2 ordinal_offset(Vector2b offset) const override; float depth_offset() const override; @@ -40,8 +40,6 @@ struct critter final : object bool playable : 1 = false; private: - int allocate_frame_time(float dt); - friend class world; critter(object_id id, class chunk& c, const critter_proto& proto); }; diff --git a/src/light.cpp b/src/light.cpp index 3de02885..ead434a2 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -1,4 +1,5 @@ #include "light.hpp" +#include "timer.hpp" #include "tile-constants.hpp" #include "shaders/shader.hpp" #include "loader/loader.hpp" @@ -52,7 +53,7 @@ light::operator light_proto() const } object_type light::type() const noexcept { return object_type::light; } -void light::update(size_t, float) {} +void light::update(size_t, Ns) {} bool light::is_dynamic() const { return true; } bool light::is_virtual() const { return true; } diff --git a/src/light.hpp b/src/light.hpp index 92da2a19..91709b1b 100644 --- a/src/light.hpp +++ b/src/light.hpp @@ -33,7 +33,7 @@ struct light final : object Vector2 ordinal_offset(Vector2b offset) const override; float depth_offset() const override; object_type type() const noexcept override; - void update(size_t i, float dt) override; + void update(size_t i, Ns dt) override; bool is_dynamic() const override; bool is_virtual() const override; diff --git a/src/object.cpp b/src/object.cpp index 550f8a6b..9aca4705 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -4,8 +4,9 @@ #include "rotation.inl" #include "anim-atlas.hpp" #include "src/RTree-search.hpp" +#include "src/timer.hpp" +#include "compat/debug.hpp" #include "compat/exception.hpp" -#include "shaders/shader.hpp" #include <cmath> #include <algorithm> #include <Corrade/Containers/GrowableArray.h> @@ -268,6 +269,39 @@ void object::move_to(Magnum::Vector2i delta) move_to(i, delta, r); } +uint32_t object::allocate_frame_time(Ns dt, uint16_t& accum, uint32_t hz) +{ + fm_assert(hz > 0); + fm_assert(dt >= Ns{0}); + constexpr auto ns_in_sec = Ns::Type{Ns(1e9)}; + //const auto count = Ns::Type{ns_in_sec / hz} + accum}; + const auto nsecs = Ns::Type{dt} + accum * ns_in_sec / Ns::Type{65535}; + const auto frame_duration = ns_in_sec / hz; + const auto frames = (uint32_t)(nsecs / frame_duration); + const auto rem = nsecs % frame_duration; + const auto new_accum_ = rem * Ns::Type{65535} / ns_in_sec; + const auto new_accum = (uint16_t)Math::clamp(new_accum_, Ns::Type{0}, Ns::Type{65535}); + [[maybe_unused]] const auto old_accum = accum; + accum = new_accum; +#if 0 + DBG_nospace << "alloc-frame-time: " + << "dt:" << fraction(Time::to_milliseconds(dt)) << "ms" + << ", secs:" << fraction(Time::to_milliseconds(Ns{nsecs}), 1) << " ms" + << ", frames:" << frames + << ", acc:" << new_accum_ + << ", rem:" << rem; +#endif + return frames; +} + +uint32_t object::allocate_frame_time(Ns dt) +{ + fm_assert(atlas); + auto hz = atlas->info().fps; + fm_assert(hz > 0); + return allocate_frame_time(dt, delta, hz); +} + void object::set_bbox_(Vector2b offset_, Vector2b bb_offset_, Vector2ub bb_size_, pass_mode pass_) { const_cast<Vector2b&>(offset) = offset_; diff --git a/src/object.hpp b/src/object.hpp index 2d33885d..e462c8db 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -6,6 +6,7 @@ #include "src/object-type.hpp" #include "src/object-id.hpp" #include "src/point.hpp" +#include "src/timer-fwd.hpp" #include <memory> namespace floormat { @@ -68,7 +69,7 @@ struct object virtual object_type type() const noexcept = 0; virtual bool can_activate(size_t i) const; virtual bool activate(size_t i); - virtual void update(size_t i, float dt) = 0; + virtual void update(size_t i, Ns dt) = 0; virtual void rotate(size_t i, rotation r); virtual bool can_rotate(global_coords coord, rotation new_r, rotation old_r, Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size); virtual bool can_move_to(Vector2i delta, global_coords coord, Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_aize); @@ -84,6 +85,9 @@ struct object void move_to(size_t& i, Vector2i delta, rotation new_r); void move_to(Vector2i delta); + static uint32_t allocate_frame_time(Ns dt, uint16_t& accum, uint32_t hz); + uint32_t allocate_frame_time(Ns dt); + protected: object(object_id id, class chunk& c, const object_proto& proto); void set_bbox_(Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size, pass_mode pass); diff --git a/src/scenery.cpp b/src/scenery.cpp index ed5b643f..b5fd3b59 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -7,6 +7,7 @@ #include "shaders/shader.hpp" #include "src/rotation.inl" #include "compat/exception.hpp" +#include "src/timer.hpp" #include <algorithm> namespace floormat { @@ -50,7 +51,7 @@ scenery_proto::operator bool() const { return atlas != nullptr; } bool generic_scenery_proto::operator==(const generic_scenery_proto& p) const = default; enum scenery_type generic_scenery_proto::scenery_type() const { return scenery_type::generic; } -void generic_scenery::update(scenery&, size_t, float) {} +void generic_scenery::update(scenery&, size_t, Ns) {} Vector2 generic_scenery::ordinal_offset(const scenery&, Vector2b offset) const { return Vector2(offset); } bool generic_scenery::can_activate(const scenery&, size_t) const { return interactive; } bool generic_scenery::activate(floormat::scenery&, size_t) { return false; } @@ -80,23 +81,16 @@ door_scenery::door_scenery(object_id, class chunk&, const door_scenery_proto& p) closing{p.closing}, active{p.active}, interactive{p.interactive} {} -void door_scenery::update(scenery& s, size_t, float dt) +void door_scenery::update(scenery& s, size_t, Ns dt) { if (!s.atlas || !active) return; fm_assert(s.atlas); auto& anim = *s.atlas; - const auto hz = uint8_t(s.atlas->info().fps); const auto nframes = (int)anim.info().nframes; fm_debug_assert(anim.info().fps > 0 && anim.info().fps <= 0xff); - - auto delta_ = int(s.delta) + int(65535u * dt); - delta_ = std::min(65535, delta_); - const auto frame_time = int(1.f/hz * 65535); - const auto n = (uint8_t)std::clamp(delta_ / frame_time, 0, 255); - s.delta = (uint16_t)std::clamp(delta_ - frame_time*n, 0, 65535); - fm_debug_assert(s.delta >= 0); + const auto n = (int)s.allocate_frame_time(dt); if (n == 0) return; const int8_t dir = closing ? 1 : -1; @@ -154,7 +148,7 @@ bool scenery::can_activate(size_t i) const ); } -void scenery::update(size_t i, float dt) +void scenery::update(size_t i, Ns dt) { return std::visit( [&]<typename T>(T& sc) { sc.update(*this, i, dt); }, diff --git a/src/scenery.hpp b/src/scenery.hpp index 8b003e73..25d40889 100644 --- a/src/scenery.hpp +++ b/src/scenery.hpp @@ -63,7 +63,7 @@ struct generic_scenery unsigned char active : 1 = false; unsigned char interactive : 1 = false; - void update(scenery& sc, size_t i, float dt); + void update(scenery& sc, size_t i, Ns dt); Vector2 ordinal_offset(const scenery& sc, Vector2b offset) const; bool can_activate(const scenery& sc, size_t i) const; bool activate(scenery& sc, size_t i); @@ -80,7 +80,7 @@ struct door_scenery unsigned char active : 1 = false; unsigned char interactive : 1 = false; - void update(scenery& sc, size_t i, float dt); + void update(scenery& sc, size_t i, Ns dt); Vector2 ordinal_offset(const scenery& sc, Vector2b offset) const; bool can_activate(const scenery& sc, size_t i) const; bool activate(scenery& sc, size_t i); @@ -97,7 +97,7 @@ struct scenery final : object { scenery_variants subtype; - void update(size_t i, float dt) override; + void update(size_t i, Ns dt) override; Vector2 ordinal_offset(Vector2b offset) const override; float depth_offset() const override; bool can_activate(size_t i) const override; diff --git a/src/timer-fwd.hpp b/src/timer-fwd.hpp new file mode 100644 index 00000000..5e11ce7b --- /dev/null +++ b/src/timer-fwd.hpp @@ -0,0 +1,27 @@ +#pragma once +#include <compare> + +namespace Magnum::Math { template<class T> class Nanoseconds; } + +namespace floormat { + +using Ns = Math::Nanoseconds<int64_t>; + +struct Time final +{ + static Time now() noexcept; + bool operator==(const Time&) const noexcept; + std::strong_ordering operator<=>(const Time&) const noexcept; + friend Ns operator-(const Time& a, const Time& b) noexcept; + [[nodiscard]] Ns update(const Time& ts = now()) & noexcept; + + static float to_seconds(const Ns& ts) noexcept; + static float to_milliseconds(const Ns& ts) noexcept; + + uint64_t stamp = init(); + +private: + static uint64_t init() noexcept; +}; + +} // namespace floormat diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 00000000..0b87ac06 --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,76 @@ +#include "timer.hpp" +#include "compat/assert.hpp" +#include <ctime> +#include <cstdio> +#include <chrono> +#include <mg/Time.h> + +namespace floormat { + +using std::chrono::duration_cast; +using std::chrono::duration; + +using Clock = std::chrono::high_resolution_clock; +using SystemClock = std::chrono::system_clock; +using Nsecs = duration<uint64_t, std::nano>; +using Millis = duration<unsigned, std::milli>; + +namespace { + +template<typename T> struct array_size_; +template<typename T, size_t N> struct array_size_<T(&)[N]> : std::integral_constant<size_t, N> {}; +template<typename T, size_t N> struct array_size_<std::array<T, N>> : std::integral_constant<size_t, N> {}; +template<typename T> constexpr inline auto array_size = array_size_<T>::value; + +uint64_t get_time() noexcept { return duration_cast<Nsecs>(Clock::now().time_since_epoch()).count(); } + +const uint64_t Epoch = get_time(); + +} // namespace + +Time Time::now() noexcept +{ + auto val = get_time(); + auto ret = val - Epoch; + return {ret}; +} + +Ns operator-(const Time& a, const Time& b) noexcept +{ + fm_assert(a.stamp >= b.stamp); + auto ret = a.stamp - b.stamp; + fm_assert(ret < uint64_t{1} << 63); + return Ns{ int64_t(ret) }; +} + +Ns Time::update(const Time& ts) & noexcept +{ + auto ret = ts - *this; + stamp = ts.stamp; + return ret; +} + +uint64_t Time::init() noexcept { return get_time(); } +bool Time::operator==(const Time&) const noexcept = default; +std::strong_ordering Time::operator<=>(const Time&) const noexcept = default; + +float Time::to_seconds(const Ns& ts) noexcept { return float(Ns::Type{ts} * 1e-9L); } +float Time::to_milliseconds(const Ns& ts) noexcept { return float(Ns::Type{ts} * 1e-6L); } + +const char* format_datetime_to_string(char (&buf)[fm_DATETIME_BUF_SIZE]) +{ + constexpr const char* fmt = "%a, %d %b %Y %H:%M:%S."; + constexpr size_t fmtsize = std::size("Thu 01 Mon 197000 00:00:00."); + static_assert(array_size<decltype(buf)> - fmtsize == 4); + const auto t = SystemClock::now(); + const auto ms = duration_cast<Millis>(t.time_since_epoch()) % 1000; + const auto time = SystemClock::to_time_t(t); + const auto* tm = std::localtime(&time); + auto len = std::strftime(buf, std::size(buf), fmt, tm); + fm_assert(len > 0 && len <= fmtsize); + auto len2 = std::sprintf(buf + len, "%03u", unsigned{ms.count()}); + fm_assert(len2 > 0 && len + (size_t)len2 < std::size(buf)); + return buf; +} + +} // namespace floormat diff --git a/src/timer.hpp b/src/timer.hpp new file mode 100644 index 00000000..24554363 --- /dev/null +++ b/src/timer.hpp @@ -0,0 +1,13 @@ +#pragma once +#include "timer-fwd.hpp" +#include <mg/Time.h> + +namespace floormat { + +long double operator/(Ns a, Ns b) noexcept; +using namespace Magnum::Math::Literals::TimeLiterals; + +constexpr inline size_t fm_DATETIME_BUF_SIZE = 32; +const char* format_datetime_to_string(char(&buf)[fm_DATETIME_BUF_SIZE]); + +} // namespace floormat diff --git a/test/app.cpp b/test/app.cpp index af5cc1f9..4677de39 100644 --- a/test/app.cpp +++ b/test/app.cpp @@ -53,6 +53,7 @@ int test_app::exec() FM_TEST(test_hash), FM_TEST(test_raycast), FM_TEST(test_json), + FM_TEST(test_time), FM_TEST(test_loader), FM_TEST(test_region), FM_TEST(test_wall_atlas), diff --git a/test/app.hpp b/test/app.hpp index e4b77614..d100ab13 100644 --- a/test/app.hpp +++ b/test/app.hpp @@ -42,6 +42,7 @@ struct test_app final : private FM_APPLICATION static void test_saves(); static void test_serializer1(); static void test_tile_iter(); + static void test_time(); static void test_wall_atlas(); static void test_wall_atlas2(); diff --git a/test/serializer.cpp b/test/serializer.cpp index 44746449..cc9405ef 100644 --- a/test/serializer.cpp +++ b/test/serializer.cpp @@ -7,6 +7,7 @@ #include "src/ground-atlas.hpp" #include "src/anim-atlas.hpp" #include "src/tile-iterator.hpp" +#include "src/timer.hpp" #include <Corrade/Utility/Path.h> namespace floormat { @@ -52,15 +53,17 @@ chunk& test_app::make_test_chunk(world& w, chunk_coords_ ch) auto& e = *w.make_object<scenery>(w.make_id(), {ch, {K+3, K+1}}, door); const auto index = e.index(); const auto end = e.atlas->info().nframes-1; + constexpr auto second = Ns(1e9); + constexpr auto dt = second / 60; fm_assert(e.frame == end); { auto& x = std::get<door_scenery>(e.subtype); fm_assert(!x.active); e.activate(e.index()); fm_assert(x.active); - e.update(index, 1.f/60); + e.update(index, dt); fm_assert(e.frame != end); for (int i = 0; i < 60*3; i++) - e.update(index, 1.f/60); + e.update(index, dt); fm_assert(e.frame == 0); fm_assert(!x.active); } diff --git a/test/time.cpp b/test/time.cpp new file mode 100644 index 00000000..dac8a2e4 --- /dev/null +++ b/test/time.cpp @@ -0,0 +1,28 @@ +#include "app.hpp" +#include "src/timer.hpp" +#include <cstdio> +#include <thread> +#include <mg/TimeStl.h> + +namespace floormat { + +using namespace std::chrono_literals; + +void test_app::test_time() +{ +#if 0 + constexpr auto to_ms = [](Ns dt) { return Time::to_seconds(dt); }; + Timer::maybe_start(); + + Debug{} << ""; + auto t1 = Time::now(); + std::this_thread::sleep_for(8ms); + auto t2 = Time::now(); + Debug{} << "- foo1" << to_ms(t2 - t1); + Debug{} << "- foo2" << to_ms(Time::now() - t1); + Debug{} << "- foo3" << to_ms(Timer::since_epoch()); + std::fputc('\t', stdout); +#endif +} + +} // namespace floormat |