diff --git a/headers/enduro2d/core/_all.hpp b/headers/enduro2d/core/_all.hpp index af113a40..2b0721a7 100644 --- a/headers/enduro2d/core/_all.hpp +++ b/headers/enduro2d/core/_all.hpp @@ -16,6 +16,7 @@ #include "input.hpp" #include "network.hpp" #include "platform.hpp" +#include "profiler.hpp" #include "render.hpp" #include "render.inl" #include "vfs.hpp" diff --git a/headers/enduro2d/core/_core.hpp b/headers/enduro2d/core/_core.hpp index a993592d..b36cd0a4 100644 --- a/headers/enduro2d/core/_core.hpp +++ b/headers/enduro2d/core/_core.hpp @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -26,7 +25,6 @@ namespace e2d namespace stdex { - using namespace invoke_hpp; using namespace jobber_hpp; using namespace promise_hpp; using namespace scheduler_hpp; @@ -44,6 +42,7 @@ namespace e2d class input; class network; class platform; + class profiler; class render; class shader; class texture; diff --git a/headers/enduro2d/core/audio.hpp b/headers/enduro2d/core/audio.hpp index cccfcbab..997e78fd 100644 --- a/headers/enduro2d/core/audio.hpp +++ b/headers/enduro2d/core/audio.hpp @@ -83,12 +83,9 @@ namespace e2d audio(debug& d); ~audio() noexcept; - [[nodiscard]] sound_stream_ptr preload_stream( + [[nodiscard]] sound_stream_ptr create_stream( buffer_view sound_data); - [[nodiscard]] sound_stream_ptr preload_stream( - const input_stream_uptr& file_stream); - [[nodiscard]] sound_stream_ptr create_stream( input_stream_uptr file_stream); @@ -99,7 +96,7 @@ namespace e2d void volume(f32 value) noexcept; [[nodiscard]] f32 volume() const noexcept; - + void resume() noexcept; void pause() noexcept; private: diff --git a/headers/enduro2d/core/deferrer.hpp b/headers/enduro2d/core/deferrer.hpp index f7489d62..5eb80f82 100644 --- a/headers/enduro2d/core/deferrer.hpp +++ b/headers/enduro2d/core/deferrer.hpp @@ -33,6 +33,8 @@ namespace e2d template < typename T > void active_safe_wait_promise(const stdex::promise& promise) noexcept; + + void frame_tick() noexcept; private: stdex::jobber worker_; stdex::scheduler scheduler_; diff --git a/headers/enduro2d/core/input.hpp b/headers/enduro2d/core/input.hpp index 3517b91f..7af711a8 100644 --- a/headers/enduro2d/core/input.hpp +++ b/headers/enduro2d/core/input.hpp @@ -8,8 +8,6 @@ #include "_core.hpp" -#include "window.hpp" - namespace e2d { class mouse final : private noncopyable { @@ -120,16 +118,4 @@ namespace e2d class internal_state; std::unique_ptr state_; }; - - class window_input_source : public window::event_listener { - public: - window_input_source(input& input) noexcept; - void on_input_char(char32_t uchar) noexcept final; - void on_move_cursor(const v2f& pos) noexcept final; - void on_mouse_scroll(const v2f& delta) noexcept final; - void on_mouse_button(mouse_button btn, mouse_button_action act) noexcept final; - void on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept final; - private: - input& input_; - }; } diff --git a/headers/enduro2d/core/profiler.hpp b/headers/enduro2d/core/profiler.hpp new file mode 100644 index 00000000..d161231f --- /dev/null +++ b/headers/enduro2d/core/profiler.hpp @@ -0,0 +1,221 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "_core.hpp" + +#include "deferrer.hpp" + +namespace e2d +{ + class profiler final : public module { + public: + using args_t = flat_map; + + struct begin_scope_info { + str name; + args_t args; + std::thread::id tid; + std::chrono::microseconds tp; + }; + + struct end_scope_info { + std::thread::id tid; + std::chrono::microseconds tp; + }; + + struct thread_event_info { + str name; + args_t args; + std::thread::id tid; + std::chrono::microseconds tp; + }; + + struct global_event_info { + str name; + args_t args; + std::thread::id tid; + std::chrono::microseconds tp; + }; + + using event_info = std::variant< + begin_scope_info, + end_scope_info, + thread_event_info, + global_event_info>; + public: + class sink : private e2d::noncopyable { + public: + virtual ~sink() noexcept = default; + virtual void on_event(const event_info& event) noexcept = 0; + }; + using sink_uptr = std::unique_ptr; + public: + class auto_scope final : private e2d::noncopyable { + public: + auto_scope(profiler* profiler, str name); + auto_scope(profiler* profiler, str name, args_t args); + ~auto_scope() noexcept; + private: + profiler* profiler_ = nullptr; + }; + public: + profiler(deferrer& d); + ~profiler() noexcept final; + + void begin_scope(str name) noexcept; + void begin_scope(str name, args_t args) noexcept; + void end_scope() noexcept; + + void thread_event(str name) noexcept; + void thread_event(str name, args_t args) noexcept; + + void global_event(str name) noexcept; + void global_event(str name, args_t args) noexcept; + public: + struct recording_info { + vector events; + }; + + template < typename Rep, typename Period > + stdex::promise record_for( + const std::chrono::duration& timeout_duration); + + template < typename Clock, typename Duration > + stdex::promise record_until( + const std::chrono::time_point& timeout_time); + public: + template < typename T, typename... Args > + T& register_sink(Args&&... args); + sink& register_sink(sink_uptr sink); + void unregister_sink(const sink& sink) noexcept; + private: + deferrer& deferrer_; + std::size_t depth_{0u}; + vector sinks_; + std::recursive_mutex rmutex_; + }; +} + +#define E2D_PROFILER_SCOPE(name)\ + auto E2D_PP_CAT(e2d_generated_profiler_auto_scope_, __LINE__) =\ + ::e2d::profiler::auto_scope(\ + modules::is_initialized() ? &the() : nullptr,\ + name); + +#define E2D_PROFILER_SCOPE_EX(name, ...)\ + auto E2D_PP_CAT(e2d_generated_profiler_auto_scope_, __LINE__) =\ + ::e2d::profiler::auto_scope(\ + modules::is_initialized() ? &the() : nullptr,\ + name,\ + __VA_ARGS__); + +#define E2D_PROFILER_THREAD_EVENT(name)\ + if ( modules::is_initialized() ) {\ + the().thread_event(name);\ + } + +#define E2D_PROFILER_THREAD_EVENT_EX(name, ...)\ + if ( modules::is_initialized() ) {\ + the().thread_event(name, __VA_ARGS___);\ + } + +#define E2D_PROFILER_GLOBAL_EVENT(name)\ + if ( modules::is_initialized() ) {\ + the().global_event(name);\ + } + +#define E2D_PROFILER_GLOBAL_EVENT_EX(name, ...)\ + if ( modules::is_initialized() ) {\ + the().global_event(name, __VA_ARGS__);\ + } + +namespace e2d +{ + template < typename Rep, typename Period > + stdex::promise profiler::record_for( + const std::chrono::duration& timeout_duration) + { + return record_until(std::chrono::steady_clock::now() + timeout_duration); + } + + template < typename Clock, typename Duration > + stdex::promise profiler::record_until( + const std::chrono::time_point& timeout_time) + { + using promise_t = stdex::promise; + using time_point_t = std::chrono::time_point; + + class temp_sink final : public sink { + public: + temp_sink(std::size_t depth, time_point_t time_point) + : depth_(depth) + , time_point_(time_point) {} + + void on_event(const event_info& event) noexcept final { + const bool skip = info_.events.empty() && depth_; + + if ( std::holds_alternative(event) ) { + ++depth_; + } else if ( std::holds_alternative(event) ) { + E2D_ASSERT(depth_); + --depth_; + } + + try { + if ( !skip ) { + info_.events.push_back(event); + } + + if ( !depth_ && time_point_ <= Clock::now() ) { + promise_.resolve(std::move(info_)); + } + } catch (...) { + promise_.reject(std::current_exception()); + } + } + + promise_t& promise() noexcept { + return promise_; + } + + const promise_t& promise() const noexcept { + return promise_; + } + private: + promise_t promise_; + recording_info info_; + std::size_t depth_{0u}; + time_point_t time_point_; + }; + + temp_sink& s = register_sink(depth_, timeout_time); + return s.promise().then([&s, this](auto&& info){ + return deferrer_.do_in_main_thread([&s, this](auto&& info){ + unregister_sink(s); + return std::forward(info); + }, std::forward(info)); + }); + } + + template < typename T, typename... Args > + T& profiler::register_sink(Args&&... args) { + return static_cast( + register_sink(std::make_unique(std::forward(args)...))); + } +} + +namespace e2d::profilers +{ + bool try_save_recording_info( + const profiler::recording_info& src, + buffer& dst) noexcept; + + bool try_save_recording_info( + const profiler::recording_info& src, + const output_stream_uptr& dst) noexcept; +} diff --git a/headers/enduro2d/core/render.hpp b/headers/enduro2d/core/render.hpp index e708ffff..adf88318 100644 --- a/headers/enduro2d/core/render.hpp +++ b/headers/enduro2d/core/render.hpp @@ -908,15 +908,12 @@ namespace e2d str_view fragment_source); shader_ptr create_shader( - const input_stream_uptr& vertex_stream, - const input_stream_uptr& fragment_stream); + buffer_view vertex_source, + buffer_view fragment_source); texture_ptr create_texture( const image& image); - texture_ptr create_texture( - const input_stream_uptr& image_stream); - texture_ptr create_texture( const v2u& size, const pixel_declaration& decl); diff --git a/headers/enduro2d/core/vfs.hpp b/headers/enduro2d/core/vfs.hpp index 08f4fdc1..24094ac5 100644 --- a/headers/enduro2d/core/vfs.hpp +++ b/headers/enduro2d/core/vfs.hpp @@ -25,9 +25,6 @@ namespace e2d class vfs final : public module { public: - vfs(); - ~vfs() noexcept final; - class file_source : private e2d::noncopyable { public: virtual ~file_source() noexcept = default; @@ -38,6 +35,12 @@ namespace e2d virtual bool trace(str_view path, filesystem::trace_func func) const = 0; }; using file_source_uptr = std::unique_ptr; + public: + vfs(); + ~vfs() noexcept final; + + stdex::jobber& worker() noexcept; + const stdex::jobber& worker() const noexcept; template < typename T, typename... Args > bool register_scheme(str_view scheme, Args&&... args); @@ -52,10 +55,10 @@ namespace e2d input_stream_uptr read(const url& url) const; output_stream_uptr write(const url& url, bool append) const; - bool load(const url& url, buffer& dst) const; + std::optional load(const url& url) const; stdex::promise load_async(const url& url) const; - bool load_as_string(const url& url, str& dst) const; + std::optional load_as_string(const url& url) const; stdex::promise load_as_string_async(const url& url) const; template < typename Iter > diff --git a/headers/enduro2d/core/window.hpp b/headers/enduro2d/core/window.hpp index 32c0781d..f75bc306 100644 --- a/headers/enduro2d/core/window.hpp +++ b/headers/enduro2d/core/window.hpp @@ -89,9 +89,9 @@ namespace e2d std::unique_ptr state_; }; - class window_trace_event_listener final : public window::event_listener { + class window_event_tracer final : public window::event_listener { public: - window_trace_event_listener(debug& debug) noexcept; + window_event_tracer(debug& debug) noexcept; void on_input_char(char32_t uchar) noexcept final; void on_move_cursor(const v2f& pos) noexcept final; void on_mouse_scroll(const v2f& delta) noexcept final; @@ -105,6 +105,18 @@ namespace e2d private: debug& debug_; }; + + class window_input_source final : public window::event_listener { + public: + window_input_source(input& input) noexcept; + void on_input_char(char32_t uchar) noexcept final; + void on_move_cursor(const v2f& pos) noexcept final; + void on_mouse_scroll(const v2f& delta) noexcept final; + void on_mouse_button(mouse_button btn, mouse_button_action act) noexcept final; + void on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept final; + private: + input& input_; + }; } ENUM_HPP_REGISTER_TRAITS(e2d::window::cursor_shapes) diff --git a/headers/enduro2d/high/luasol.hpp b/headers/enduro2d/high/luasol.hpp index f06883b7..30086a56 100644 --- a/headers/enduro2d/high/luasol.hpp +++ b/headers/enduro2d/high/luasol.hpp @@ -24,8 +24,8 @@ namespace e2d template < typename F > decltype(auto) with_state(F&& f) const; + std::optional