Merge remote-tracking branch 'origin/master' into feature/debug_hierarchy

This commit is contained in:
BlackMATov
2020-01-10 17:06:12 +07:00
63 changed files with 1090 additions and 403 deletions

View File

@@ -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"

View File

@@ -12,7 +12,6 @@
#include <curly.hpp/curly.hpp>
#include <promise.hpp/invoke.hpp>
#include <promise.hpp/jobber.hpp>
#include <promise.hpp/promise.hpp>
#include <promise.hpp/scheduler.hpp>
@@ -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;

View File

@@ -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);

View File

@@ -33,6 +33,8 @@ namespace e2d
template < typename T >
void active_safe_wait_promise(const stdex::promise<T>& promise) noexcept;
void frame_tick() noexcept;
private:
stdex::jobber worker_;
stdex::scheduler scheduler_;

View File

@@ -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<internal_state> 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_;
};
}

View File

@@ -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<profiler> {
public:
using args_t = flat_map<str, str>;
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<sink>;
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<event_info> events;
};
template < typename Rep, typename Period >
stdex::promise<recording_info> record_for(
const std::chrono::duration<Rep, Period>& timeout_duration);
template < typename Clock, typename Duration >
stdex::promise<recording_info> record_until(
const std::chrono::time_point<Clock, Duration>& 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<sink_uptr> 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<profiler>() ? &the<profiler>() : 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<profiler>() ? &the<profiler>() : nullptr,\
name,\
__VA_ARGS__);
#define E2D_PROFILER_THREAD_EVENT(name)\
if ( modules::is_initialized<profiler>() ) {\
the<profiler>().thread_event(name);\
}
#define E2D_PROFILER_THREAD_EVENT_EX(name, ...)\
if ( modules::is_initialized<profiler>() ) {\
the<profiler>().thread_event(name, __VA_ARGS___);\
}
#define E2D_PROFILER_GLOBAL_EVENT(name)\
if ( modules::is_initialized<profiler>() ) {\
the<profiler>().global_event(name);\
}
#define E2D_PROFILER_GLOBAL_EVENT_EX(name, ...)\
if ( modules::is_initialized<profiler>() ) {\
the<profiler>().global_event(name, __VA_ARGS__);\
}
namespace e2d
{
template < typename Rep, typename Period >
stdex::promise<profiler::recording_info> profiler::record_for(
const std::chrono::duration<Rep, Period>& timeout_duration)
{
return record_until(std::chrono::steady_clock::now() + timeout_duration);
}
template < typename Clock, typename Duration >
stdex::promise<profiler::recording_info> profiler::record_until(
const std::chrono::time_point<Clock, Duration>& timeout_time)
{
using promise_t = stdex::promise<recording_info>;
using time_point_t = std::chrono::time_point<Clock, Duration>;
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<begin_scope_info>(event) ) {
++depth_;
} else if ( std::holds_alternative<end_scope_info>(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<temp_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<decltype(info)>(info);
}, std::forward<decltype(info)>(info));
});
}
template < typename T, typename... Args >
T& profiler::register_sink(Args&&... args) {
return static_cast<T&>(
register_sink(std::make_unique<T>(std::forward<Args>(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;
}

View File

@@ -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);

View File

@@ -25,9 +25,6 @@ namespace e2d
class vfs final : public module<vfs> {
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<file_source>;
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<buffer> load(const url& url) const;
stdex::promise<buffer> load_async(const url& url) const;
bool load_as_string(const url& url, str& dst) const;
std::optional<str> load_as_string(const url& url) const;
stdex::promise<str> load_as_string_async(const url& url) const;
template < typename Iter >

View File

@@ -89,9 +89,9 @@ namespace e2d
std::unique_ptr<state> 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)

View File

@@ -24,8 +24,8 @@ namespace e2d
template < typename F >
decltype(auto) with_state(F&& f) const;
std::optional<script> load_script(str_view src);
std::optional<script> load_script(buffer_view src);
std::optional<script> load_script(const input_stream_uptr& src);
private:
sol::state state_;
};

View File

@@ -27,9 +27,6 @@ namespace e2d
template < typename T >
buffer_view(const vector<T>& buffer) noexcept;
template < typename Char >
buffer_view(const basic_string<Char>& buffer) noexcept;
template < typename T, std::size_t N >
buffer_view(const std::array<T,N>& buffer) noexcept;
@@ -60,11 +57,6 @@ namespace e2d
: data_(buffer.data())
, size_(buffer.size() * sizeof(T)) {}
template < typename Char >
buffer_view::buffer_view(const basic_string<Char>& buffer) noexcept
: data_(buffer.data())
, size_(buffer.size() * sizeof(Char)) {}
template < typename T, std::size_t N >
buffer_view::buffer_view(const std::array<T,N>& buffer) noexcept
: data_(buffer.data())

View File

@@ -13,27 +13,16 @@ namespace e2d
namespace impl
{
template < typename F >
class defer_impl {
class defer_impl final : noncopyable {
public:
defer_impl() = delete;
defer_impl(const defer_impl&) = delete;
defer_impl& operator=(const defer_impl&) = delete;
explicit defer_impl(F f)
: f_(std::move(f)) {}
~defer_impl() noexcept(std::is_nothrow_invocable_v<F>) {
if ( !cancelled_ ) {
f_();
}
}
void cancel() noexcept {
cancelled_ = true;
}
private:
F f_;
bool cancelled_{false};
};
}
@@ -44,4 +33,4 @@ namespace e2d
}
#define E2D_DEFER(lambda)\
auto E2D_PP_CAT(e2d_defer_, __LINE__) = ::e2d::make_defer(lambda)
auto E2D_PP_CAT(e2d_generated_defer_, __LINE__) = ::e2d::make_defer(lambda)

View File

@@ -76,6 +76,7 @@ namespace e2d::filesystem
bool try_read_all(str& dst, str_view path) noexcept;
bool try_read_all(buffer& dst, str_view path) noexcept;
bool try_write_all(str_view src, str_view path, bool append) noexcept;
bool try_write_all(buffer_view src, str_view path, bool append) noexcept;
ENUM_HPP_CLASS_DECL(predef_path, u8,

View File

@@ -81,6 +81,7 @@ namespace e2d
output_sequence& seek(std::ptrdiff_t offset, bool relative) noexcept;
output_sequence& write(const void* src, std::size_t size) noexcept;
output_sequence& write_all(str_view src) noexcept;
output_sequence& write_all(buffer_view src) noexcept;
output_sequence& flush() noexcept;
@@ -113,6 +114,10 @@ namespace e2d::streams
buffer& dst,
const input_stream_uptr& stream) noexcept;
bool try_write_tail(
str_view src,
const output_stream_uptr& stream) noexcept;
bool try_write_tail(
buffer_view src,
const output_stream_uptr& stream) noexcept;

View File

@@ -39,6 +39,8 @@ namespace e2d
const str& scheme() const noexcept;
const str& path() const noexcept;
str schemepath() const;
url& operator+=(str_view path);
url& operator/=(str_view path);
private:

View File

@@ -97,12 +97,19 @@ namespace
"ships",
url("piratepack://PNG/Retina/Ships"));
shader_ = the<render>().create_shader(
vs_source_cstr, fs_source_cstr);
texture1_ = the<render>().create_texture(
the<vfs>().read(url("ships://ship (2).png")));
texture2_ = the<render>().create_texture(
the<vfs>().read(url("ships://ship (19).png")));
image texture1_image;
if ( !images::try_load_image(texture1_image, the<vfs>().read(url("ships://ship (2).png"))) ) {
return false;
}
image texture2_image;
if ( !images::try_load_image(texture2_image, the<vfs>().read(url("ships://ship (19).png"))) ) {
return false;
}
shader_ = the<render>().create_shader(vs_source_cstr, fs_source_cstr);
texture1_ = the<render>().create_texture(texture1_image);
texture2_ = the<render>().create_texture(texture2_image);
if ( !shader_ || !texture1_ || !texture2_ ) {
return false;

View File

@@ -157,11 +157,13 @@ namespace
"ships",
url("piratepack://PNG/Retina/Ships"));
shader_ = the<render>().create_shader(
vs_source_cstr, fs_source_cstr);
image texture_image;
if ( !images::try_load_image(texture_image, the<vfs>().read(url("ships://ship (3).png"))) ) {
return false;
}
texture_ = the<render>().create_texture(
the<vfs>().read(url("ships://ship (3).png")));
shader_ = the<render>().create_shader(vs_source_cstr, fs_source_cstr);
texture_ = the<render>().create_texture(texture_image);
if ( !shader_ || !texture_ ) {
return false;

View File

@@ -112,11 +112,13 @@ namespace
"ships",
url("piratepack://PNG/Retina/Ships"));
shader_ = the<render>().create_shader(
vs_source_cstr, fs_source_cstr);
image texture_image;
if ( !images::try_load_image(texture_image, the<vfs>().read(url("ships://ship (3).png"))) ) {
return false;
}
texture_ = the<render>().create_texture(
the<vfs>().read(url("ships://ship (3).png")));
shader_ = the<render>().create_shader(vs_source_cstr, fs_source_cstr);
texture_ = the<render>().create_texture(texture_image);
if ( !shader_ || !texture_ ) {
return false;

View File

@@ -20,8 +20,8 @@ namespace
"audio",
url("rpgaudio://Audio"));
auto sstream1 = the<audio>().preload_stream(the<vfs>().read(url("audio://chop.ogg")));
auto sstream2 = the<audio>().create_stream(the<vfs>().read(url("audio://footstep00.ogg")));
auto sstream1 = the<audio>().create_stream(the<vfs>().read(url("audio://chop.ogg")));
auto sstream2 = the<audio>().create_stream(*the<vfs>().load(url("audio://footstep00.ogg")));
if ( !sstream1 || !sstream2 ) {
return false;

View File

@@ -6,8 +6,9 @@
#pragma once
#include <enduro2d/core/debug.hpp>
#include <enduro2d/core/audio.hpp>
#include <enduro2d/core/debug.hpp>
#include <enduro2d/core/profiler.hpp>
#define E2D_AUDIO_MODE_NONE 1
#define E2D_AUDIO_MODE_BASS 2

View File

@@ -143,7 +143,7 @@ namespace e2d
audio::~audio() noexcept {
}
sound_stream_ptr audio::preload_stream(
sound_stream_ptr audio::create_stream(
buffer_view sound_data)
{
if ( !state_->initialized() ) {
@@ -156,6 +156,8 @@ namespace e2d
return nullptr;
}
E2D_PROFILER_SCOPE("audio.create_stream");
HSAMPLE sample = BASS_SampleLoad(
TRUE,
sound_data.data(),
@@ -176,23 +178,6 @@ namespace e2d
state_->dbg(), sample, nullptr));
}
sound_stream_ptr audio::preload_stream(
const input_stream_uptr& file_stream)
{
if ( !state_->initialized() ) {
state_->dbg().error("AUDIO: Not initialized");
return nullptr;
}
buffer file_data;
if ( !streams::try_read_tail(file_data, file_stream) ) {
state_->dbg().error("AUDIO: Failed to read file");
return nullptr;
}
return preload_stream(buffer_view(file_data));
}
sound_stream_ptr audio::create_stream(
input_stream_uptr file_stream)
{
@@ -206,6 +191,8 @@ namespace e2d
return nullptr;
}
E2D_PROFILER_SCOPE("audio.create_stream");
BASS_FILEPROCS file_procs = {
sound_stream_close_proc,
sound_stream_length_proc,
@@ -243,6 +230,8 @@ namespace e2d
return nullptr;
}
E2D_PROFILER_SCOPE("audio.create_source");
HCHANNEL channel = stream->state().stream()
? stream->state().sound()
: BASS_SampleGetChannel(stream->state().sound(), FALSE);

View File

@@ -119,20 +119,13 @@ namespace e2d
audio::~audio() noexcept {
}
sound_stream_ptr audio::preload_stream(
sound_stream_ptr audio::create_stream(
buffer_view sound_data)
{
E2D_UNUSED(sound_data);
return nullptr;
}
sound_stream_ptr audio::preload_stream(
const input_stream_uptr& file_stream)
{
E2D_UNUSED(file_stream);
return nullptr;
}
sound_stream_ptr audio::create_stream(
input_stream_uptr file_stream)
{

View File

@@ -101,9 +101,9 @@ namespace e2d
io.DisplaySize =
window_.real_size().cast_to<f32>();
io.DisplayFramebufferScale =
window_.framebuffer_size().cast_to<f32>() /
window_.real_size().cast_to<f32>();
io.DisplayFramebufferScale = io.DisplaySize.x > 0.f && io.DisplaySize.y > 0.f
? window_.framebuffer_size().cast_to<f32>() / v2f(io.DisplaySize)
: v2f(1.f, 1.f);
window_.set_cursor_shape(
imgui::convert_mouse_cursor(
@@ -436,6 +436,7 @@ namespace e2d
}
void dbgui::frame_tick() {
E2D_PROFILER_SCOPE("dbgui.frame_tick");
state_->frame_tick();
if ( visible() ) {
@@ -444,6 +445,7 @@ namespace e2d
}
void dbgui::frame_render() {
E2D_PROFILER_SCOPE("dbgui.frame_render");
state_->frame_render();
}
}

View File

@@ -11,6 +11,7 @@
#include <enduro2d/core/debug.hpp>
#include <enduro2d/core/engine.hpp>
#include <enduro2d/core/input.hpp>
#include <enduro2d/core/profiler.hpp>
#include <enduro2d/core/render.hpp>
#include <enduro2d/core/window.hpp>

View File

@@ -6,6 +6,8 @@
#include <enduro2d/core/deferrer.hpp>
#include <enduro2d/core/profiler.hpp>
namespace e2d
{
deferrer::deferrer()
@@ -26,4 +28,9 @@ namespace e2d
const stdex::scheduler& deferrer::scheduler() const noexcept {
return scheduler_;
}
void deferrer::frame_tick() noexcept {
E2D_PROFILER_SCOPE("deferrer.frame_tick");
scheduler_.process_all_tasks();
}
}

View File

@@ -13,6 +13,7 @@
#include <enduro2d/core/input.hpp>
#include <enduro2d/core/network.hpp>
#include <enduro2d/core/platform.hpp>
#include <enduro2d/core/profiler.hpp>
#include <enduro2d/core/render.hpp>
#include <enduro2d/core/vfs.hpp>
#include <enduro2d/core/window.hpp>
@@ -319,6 +320,8 @@ namespace e2d
}
public:
void calculate_end_frame_timers() noexcept {
E2D_PROFILER_SCOPE("engine.wait_for_target_fps");
const auto second_us = time::second_us<u64>();
const auto minimal_delta_time_us =
@@ -382,6 +385,11 @@ namespace e2d
safe_module_initialize<deferrer>();
// setup profiler
safe_module_initialize<profiler>(
the<deferrer>());
// setup debug
safe_module_initialize<debug>();
@@ -477,6 +485,7 @@ namespace e2d
modules::shutdown<input>();
modules::shutdown<vfs>();
modules::shutdown<debug>();
modules::shutdown<profiler>();
modules::shutdown<deferrer>();
modules::shutdown<platform>();
}
@@ -492,7 +501,7 @@ namespace e2d
while ( true ) {
try {
the<dbgui>().frame_tick();
the<deferrer>().scheduler().process_all_tasks();
the<deferrer>().frame_tick();
if ( !app->frame_tick() ) {
break;
@@ -510,8 +519,11 @@ namespace e2d
app->shutdown();
throw;
}
the<input>().frame_tick();
window::poll_events();
E2D_PROFILER_GLOBAL_EVENT("engine.end_of_frame");
}
app->shutdown();

View File

@@ -6,6 +6,8 @@
#include <enduro2d/core/input.hpp>
#include <enduro2d/core/profiler.hpp>
namespace e2d
{
//
@@ -364,35 +366,8 @@ namespace e2d
}
void input::frame_tick() noexcept {
E2D_PROFILER_SCOPE("input.frame_tick");
state_->mouse.state_->frame_tick();
state_->keyboard.state_->frame_tick();
}
//
// class window_input_source
//
window_input_source::window_input_source(input& input) noexcept
: input_(input) {}
void window_input_source::on_input_char(char32_t uchar) noexcept {
input_.post_event(input::input_char_event{uchar});
}
void window_input_source::on_move_cursor(const v2f& pos) noexcept {
input_.post_event(input::move_cursor_event{pos});
}
void window_input_source::on_mouse_scroll(const v2f& delta) noexcept {
input_.post_event(input::mouse_scroll_event{delta});
}
void window_input_source::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
input_.post_event(input::mouse_button_event{btn, act});
}
void window_input_source::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
E2D_UNUSED(scancode);
input_.post_event(input::keyboard_key_event{key, act});
}
}

View File

@@ -0,0 +1,248 @@
/*******************************************************************************
* 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)
******************************************************************************/
#include <enduro2d/core/profiler.hpp>
#include <enduro2d/core/engine.hpp>
#include <enduro2d/core/vfs.hpp>
namespace
{
using namespace e2d;
str recording_args_to_json(const profiler::args_t& args) {
str dst = "{";
for ( auto iter = args.begin(); iter != args.end(); ) {
dst += strings::rformat("\"%0\":\"%1\"", iter->first, iter->second);
if ( ++iter != args.end() ) {
dst += ",";
}
}
dst += "}";
return dst;
}
str recording_info_to_json(const profiler::recording_info& src) {
str dst("{\"traceEvents\":[\n");
dst += strings::rformat(
" {\"name\":\"thread_name\",\"ph\":\"M\",\"tid\":%0,\"pid\":0,\"args\":{\"name\":\"%1\"}},\n",
std::hash<std::thread::id>()(the<engine>().main_thread()),
"Main");
for ( std::thread::id tid : the<vfs>().worker().thread_ids() ) {
dst += strings::rformat(
" {\"name\":\"thread_name\",\"ph\":\"M\",\"tid\":%0,\"pid\":0,\"args\":{\"name\":\"%1\"}},\n",
std::hash<std::thread::id>()(tid),
"VFS");
}
for ( std::thread::id tid : the<deferrer>().worker().thread_ids() ) {
dst += strings::rformat(
" {\"name\":\"thread_name\",\"ph\":\"M\",\"tid\":%0,\"pid\":0,\"args\":{\"name\":\"%1\"}},\n",
std::hash<std::thread::id>()(tid),
"Worker");
}
for ( auto iter = src.events.begin(); iter != src.events.end(); ) {
std::visit(utils::overloaded {
[&dst](const profiler::begin_scope_info& e){
char buf[512] = {'\0'};
strings::format(buf, std::size(buf),
" {\"name\":\"%0\",\"ph\":\"B\",\"ts\":%1,\"tid\":%2,\"pid\":0,\"cat\":\"\",\"args\":%3}",
e.name,
e.tp.count(),
std::hash<std::thread::id>()(e.tid),
recording_args_to_json(e.args));
dst += buf;
},
[&dst](const profiler::end_scope_info& e){
char buf[512] = {'\0'};
strings::format(buf, std::size(buf),
" {\"ph\":\"E\",\"ts\":%0,\"tid\":%1,\"pid\":0,\"cat\":\"\"}",
e.tp.count(),
std::hash<std::thread::id>()(e.tid));
dst += buf;
},
[&dst](const profiler::thread_event_info& e){
char buf[512] = {'\0'};
strings::format(buf, std::size(buf),
" {\"name\":\"%0\",\"ph\":\"i\",\"s\":\"t\",\"ts\":%1,\"tid\":%2,\"pid\":0,\"cat\":\"\",\"args\":%3}",
e.name,
e.tp.count(),
std::hash<std::thread::id>()(e.tid),
recording_args_to_json(e.args));
dst += buf;
},
[&dst](const profiler::global_event_info& e){
char buf[512] = {'\0'};
strings::format(buf, std::size(buf),
" {\"name\":\"%0\",\"ph\":\"i\",\"s\":\"g\",\"ts\":%1,\"tid\":%2,\"pid\":0,\"cat\":\"\",\"args\":%3}",
e.name,
e.tp.count(),
std::hash<std::thread::id>()(e.tid),
recording_args_to_json(e.args));
dst += buf;
}
}, *iter);
if ( ++iter != src.events.end() ) {
dst += ",\n";
}
}
dst += "\n]}";
return dst;
}
}
namespace e2d
{
profiler::auto_scope::auto_scope(profiler* profiler, str name)
: profiler_(profiler) {
if ( profiler_ ) {
profiler_->begin_scope(std::move(name));
}
}
profiler::auto_scope::auto_scope(profiler* profiler, str name, args_t args)
: profiler_(profiler) {
if ( profiler_ ) {
profiler_->begin_scope(std::move(name), std::move(args));
}
}
profiler::auto_scope::~auto_scope() noexcept {
if ( profiler_ ) {
profiler_->end_scope();
}
}
}
namespace e2d
{
profiler::profiler(deferrer& d)
: deferrer_(d) {}
profiler::~profiler() noexcept = default;
profiler::sink& profiler::register_sink(sink_uptr sink) {
E2D_ASSERT(sink);
std::lock_guard guard(rmutex_);
sinks_.push_back(std::move(sink));
return *sinks_.back();
}
void profiler::unregister_sink(const sink& sink) noexcept {
std::lock_guard guard(rmutex_);
for ( auto iter = sinks_.begin(); iter != sinks_.end(); ) {
if ( iter->get() == &sink ) {
iter = sinks_.erase(iter);
} else {
++iter;
}
}
}
void profiler::begin_scope(str name) noexcept {
return begin_scope(std::move(name), {});
}
void profiler::begin_scope(str name, args_t args) noexcept {
++depth_;
begin_scope_info event{
std::move(name),
std::move(args),
std::this_thread::get_id(),
time::to_chrono(time::now_us())};
std::lock_guard guard(rmutex_);
for ( const auto& sink : sinks_ ) {
if ( sink ) {
sink->on_event(event);
}
}
}
void profiler::end_scope() noexcept {
E2D_ASSERT(depth_);
--depth_;
end_scope_info event{
std::this_thread::get_id(),
time::to_chrono(time::now_us())};
std::lock_guard guard(rmutex_);
for ( const auto& sink : sinks_ ) {
if ( sink ) {
sink->on_event(event);
}
}
}
void profiler::thread_event(str name) noexcept {
return thread_event(std::move(name), {});
}
void profiler::thread_event(str name, args_t args) noexcept {
thread_event_info event{
std::move(name),
std::move(args),
std::this_thread::get_id(),
time::to_chrono(time::now_us())};
std::lock_guard guard(rmutex_);
for ( const auto& sink : sinks_ ) {
if ( sink ) {
sink->on_event(event);
}
}
}
void profiler::global_event(str name) noexcept {
return global_event(std::move(name), {});
}
void profiler::global_event(str name, args_t args) noexcept {
global_event_info event{
std::move(name),
std::move(args),
std::this_thread::get_id(),
time::to_chrono(time::now_us())};
std::lock_guard guard(rmutex_);
for ( const auto& sink : sinks_ ) {
if ( sink ) {
sink->on_event(event);
}
}
}
}
namespace e2d::profilers
{
bool try_save_recording_info(
const profiler::recording_info& src,
buffer& dst) noexcept
{
try {
str json = recording_info_to_json(src);
dst.assign(json.data(), json.size());
return true;
} catch (...) {
return false;
}
}
bool try_save_recording_info(
const profiler::recording_info& src,
const output_stream_uptr& dst) noexcept
{
buffer file_data;
return try_save_recording_info(src, file_data)
&& streams::try_write_tail(file_data, dst);
}
}

View File

@@ -7,6 +7,7 @@
#pragma once
#include <enduro2d/core/debug.hpp>
#include <enduro2d/core/profiler.hpp>
#include <enduro2d/core/render.hpp>
#include <enduro2d/core/window.hpp>

View File

@@ -181,10 +181,10 @@ namespace e2d
}
shader_ptr render::create_shader(
const input_stream_uptr& vertex_stream,
const input_stream_uptr& fragment_stream)
buffer_view vertex_source,
buffer_view fragment_source)
{
E2D_UNUSED(vertex_stream, fragment_stream);
E2D_UNUSED(vertex_source, fragment_source);
return nullptr;
}
@@ -193,11 +193,6 @@ namespace e2d
return nullptr;
}
texture_ptr render::create_texture(const input_stream_uptr& image_stream) {
E2D_UNUSED(image_stream);
return nullptr;
}
texture_ptr render::create_texture(const v2u& size, const pixel_declaration& decl) {
E2D_UNUSED(size, decl);
return nullptr;

View File

@@ -497,7 +497,12 @@ namespace e2d
{
E2D_ASSERT(is_in_main_thread());
gl_shader_id vs = gl_compile_shader(
gl_shader_id vs(state_->dbg());
{
E2D_PROFILER_SCOPE("render.compile_vertex_shader");
vs = gl_compile_shader(
state_->dbg(),
vertex_shader_header_cstr(device_capabilities().profile),
vertex_source,
@@ -506,8 +511,14 @@ namespace e2d
if ( vs.empty() ) {
return nullptr;
}
}
gl_shader_id fs = gl_compile_shader(
gl_shader_id fs(state_->dbg());
{
E2D_PROFILER_SCOPE("render.compile_fragment_shader");
fs = gl_compile_shader(
state_->dbg(),
fragment_shader_header_cstr(device_capabilities().profile),
fragment_source,
@@ -516,8 +527,14 @@ namespace e2d
if ( fs.empty() ) {
return nullptr;
}
}
gl_program_id ps = gl_link_program(
gl_program_id ps(state_->dbg());
{
E2D_PROFILER_SCOPE("render.link_shader_program");
ps = gl_link_program(
state_->dbg(),
std::move(vs),
std::move(fs));
@@ -525,6 +542,7 @@ namespace e2d
if ( ps.empty() ) {
return nullptr;
}
}
return std::make_shared<shader>(
std::make_unique<shader::internal_state>(
@@ -532,16 +550,14 @@ namespace e2d
}
shader_ptr render::create_shader(
const input_stream_uptr& vertex,
const input_stream_uptr& fragment)
buffer_view vertex_source,
buffer_view fragment_source)
{
E2D_ASSERT(is_in_main_thread());
str vertex_source, fragment_source;
return streams::try_read_tail(vertex_source, vertex)
&& streams::try_read_tail(fragment_source, fragment)
? create_shader(vertex_source, fragment_source)
: nullptr;
return create_shader(
str_view(reinterpret_cast<const char*>(vertex_source.data()), vertex_source.size()),
str_view(reinterpret_cast<const char*>(fragment_source.data()), fragment_source.size()));
}
texture_ptr render::create_texture(
@@ -585,6 +601,11 @@ namespace e2d
}
}
E2D_PROFILER_SCOPE_EX("render.create_texture_from_image", {
{"size", strings::rformat("%0", image.size())},
{"format", strings::rformat("%0", image.format())}
});
gl_texture_id id = gl_texture_id::create(state_->dbg(), GL_TEXTURE_2D);
if ( id.empty() ) {
state_->dbg().error("RENDER: Failed to create texture:\n"
@@ -622,18 +643,6 @@ namespace e2d
state_->dbg(), std::move(id), image.size(), decl));
}
texture_ptr render::create_texture(
const input_stream_uptr& image_stream)
{
E2D_ASSERT(is_in_main_thread());
image image;
if ( !images::try_load_image(image, image_stream) ) {
return nullptr;
}
return create_texture(image);
}
texture_ptr render::create_texture(
const v2u& size,
const pixel_declaration& decl)
@@ -673,6 +682,11 @@ namespace e2d
}
}
E2D_PROFILER_SCOPE_EX("render.create_empty_texture", {
{"size", strings::rformat("%0", size)},
{"format", strings::rformat("%0", decl.type())}
});
gl_texture_id id = gl_texture_id::create(state_->dbg(), GL_TEXTURE_2D);
if ( id.empty() ) {
state_->dbg().error("RENDER: Failed to create texture:\n"
@@ -737,6 +751,8 @@ namespace e2d
return nullptr;
}
E2D_PROFILER_SCOPE("render.create_index_buffer");
gl_buffer_id id = gl_buffer_id::create(state_->dbg(), GL_ELEMENT_ARRAY_BUFFER);
if ( id.empty() ) {
state_->dbg().error("RENDER: Failed to create index buffer:\n"
@@ -771,6 +787,8 @@ namespace e2d
return nullptr;
}
E2D_PROFILER_SCOPE("render.create_vertex_buffer");
gl_buffer_id id = gl_buffer_id::create(state_->dbg(), GL_ARRAY_BUFFER);
if ( id.empty() ) {
state_->dbg().error("RENDER: Failed to create vertex buffer:\n"
@@ -827,6 +845,8 @@ namespace e2d
}
}
E2D_PROFILER_SCOPE("render.create_render_target");
gl_framebuffer_id id = gl_framebuffer_id::create(state_->dbg(), GL_FRAMEBUFFER);
if ( id.empty() ) {
state_->dbg().error("RENDER: Failed to create framebuffer:\n",

View File

@@ -6,6 +6,8 @@
#include <enduro2d/core/vfs.hpp>
#include <enduro2d/core/profiler.hpp>
#include <3rdparty/miniz/miniz.h>
namespace
@@ -108,9 +110,18 @@ namespace e2d
vfs::vfs()
: state_(new state()){}
vfs::~vfs() noexcept = default;
stdex::jobber& vfs::worker() noexcept {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->worker;
}
const stdex::jobber& vfs::worker() const noexcept {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->worker;
}
bool vfs::register_scheme(str_view scheme, file_source_uptr source) {
std::lock_guard<std::mutex> guard(state_->mutex);
return (source && source->valid())
@@ -163,16 +174,20 @@ namespace e2d
}, output_stream_uptr());
}
bool vfs::load(const url& url, buffer& dst) const {
return load_async(url)
.then([&dst](auto&& src){
dst = std::forward<decltype(src)>(src);
return true;
}).get_or_default(false);
std::optional<buffer> vfs::load(const url& url) const {
E2D_PROFILER_SCOPE_EX("vfs.sync_load", {
{"url", url.schemepath()}
});
return load_async(url).then([](auto&& src){
return std::optional<buffer>(std::forward<decltype(src)>(src));
}).get_or_default(std::nullopt);
}
stdex::promise<buffer> vfs::load_async(const url& url) const {
return state_->worker.async([this, url](){
E2D_PROFILER_SCOPE_EX("vfs.load_async", {
{"url", url.schemepath()}
});
buffer content;
const input_stream_uptr stream = read(url);
if ( !stream || !streams::try_read_tail(content, stream) ) {
@@ -182,16 +197,20 @@ namespace e2d
});
}
bool vfs::load_as_string(const url& url, str& dst) const {
return load_as_string_async(url)
.then([&dst](auto&& src){
dst = std::forward<decltype(src)>(src);
return true;
}).get_or_default(false);
std::optional<str> vfs::load_as_string(const url& url) const {
E2D_PROFILER_SCOPE_EX("vfs.sync_load_as_string", {
{"url", url.schemepath()}
});
return load_as_string_async(url).then([](auto&& src){
return std::optional<str>(std::forward<decltype(src)>(src));
}).get_or_default(std::nullopt);
}
stdex::promise<str> vfs::load_as_string_async(const url& url) const {
return state_->worker.async([this, url](){
E2D_PROFILER_SCOPE_EX("vfs.load_as_string_async", {
{"url", url.schemepath()}
});
str content;
const input_stream_uptr stream = read(url);
if ( !stream || !streams::try_read_tail(content, stream) ) {

View File

@@ -7,13 +7,10 @@
#include <enduro2d/core/window.hpp>
#include <enduro2d/core/debug.hpp>
#include <enduro2d/core/input.hpp>
namespace e2d
{
//
// class window::event_listener
//
void window::event_listener::on_input_char(char32_t uchar) noexcept {
E2D_UNUSED(uchar);
}
@@ -52,56 +49,77 @@ namespace e2d
void window::event_listener::on_window_minimize(bool minimized) noexcept {
E2D_UNUSED(minimized);
}
}
//
// class trace_window_event_listener
//
window_trace_event_listener::window_trace_event_listener(debug& debug) noexcept
namespace e2d
{
window_event_tracer::window_event_tracer(debug& debug) noexcept
: debug_(debug) {}
void window_trace_event_listener::on_input_char(char32_t uchar) noexcept {
void window_event_tracer::on_input_char(char32_t uchar) noexcept {
debug_.trace("WINDOW: on_input_char(uchar: %0)", str32_view(&uchar, 1));
}
void window_trace_event_listener::on_move_cursor(const v2f& pos) noexcept {
void window_event_tracer::on_move_cursor(const v2f& pos) noexcept {
debug_.trace("WINDOW: on_move_cursor(pos: %0)", pos);
}
void window_trace_event_listener::on_mouse_scroll(const v2f& delta) noexcept {
void window_event_tracer::on_mouse_scroll(const v2f& delta) noexcept {
debug_.trace("WINDOW: on_scroll(delta: %0)", delta);
}
void window_trace_event_listener::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
debug_.trace("WINDOW: on_mouse_button(btn: %0 act: %1)",
btn,
act);
void window_event_tracer::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
debug_.trace("WINDOW: on_mouse_button(btn: %0 act: %1)", btn, act);
}
void window_trace_event_listener::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
debug_.trace("WINDOW: on_keyboard_key(key: %0 scancode: %1 act: %2)",
key,
scancode,
act);
void window_event_tracer::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
debug_.trace("WINDOW: on_keyboard_key(key: %0 scancode: %1 act: %2)", key, scancode, act);
}
void window_trace_event_listener::on_window_size(const v2u& size) noexcept {
void window_event_tracer::on_window_size(const v2u& size) noexcept {
debug_.trace("WINDOW: on_window_size(size: %0)", size);
}
void window_trace_event_listener::on_framebuffer_size(const v2u& size) noexcept {
void window_event_tracer::on_framebuffer_size(const v2u& size) noexcept {
debug_.trace("WINDOW: on_framebuffer_size(size: %0)", size);
}
void window_trace_event_listener::on_window_close() noexcept {
void window_event_tracer::on_window_close() noexcept {
debug_.trace("WINDOW: on_window_close()");
}
void window_trace_event_listener::on_window_focus(bool focused) noexcept {
void window_event_tracer::on_window_focus(bool focused) noexcept {
debug_.trace("WINDOW: on_window_focus(focused: %0)", focused);
}
void window_trace_event_listener::on_window_minimize(bool minimized) noexcept {
void window_event_tracer::on_window_minimize(bool minimized) noexcept {
debug_.trace("WINDOW: on_window_minimize(minimized: %0)", minimized);
}
}
namespace e2d
{
window_input_source::window_input_source(input& input) noexcept
: input_(input) {}
void window_input_source::on_input_char(char32_t uchar) noexcept {
input_.post_event(input::input_char_event{uchar});
}
void window_input_source::on_move_cursor(const v2f& pos) noexcept {
input_.post_event(input::move_cursor_event{pos});
}
void window_input_source::on_mouse_scroll(const v2f& delta) noexcept {
input_.post_event(input::mouse_scroll_event{delta});
}
void window_input_source::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
input_.post_event(input::mouse_button_event{btn, act});
}
void window_input_source::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
E2D_UNUSED(scancode);
input_.post_event(input::keyboard_key_event{key, act});
}
}

View File

@@ -7,6 +7,7 @@
#pragma once
#include <enduro2d/core/debug.hpp>
#include <enduro2d/core/profiler.hpp>
#include <enduro2d/core/window.hpp>
#define E2D_WINDOW_MODE_NONE 1

View File

@@ -691,6 +691,7 @@ namespace e2d
}
void window::swap_buffers() noexcept {
E2D_PROFILER_SCOPE("window.swap_buffers");
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
E2D_ASSERT(
state_->window &&
@@ -699,6 +700,7 @@ namespace e2d
}
bool window::poll_events() noexcept {
E2D_PROFILER_SCOPE("window.poll_events");
return glfw_state::poll_events();
}

View File

@@ -26,8 +26,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::load_result& font_data){
return the<deferrer>().do_in_worker_thread([font_data](){
.then([
address = str(address)
](const binary_asset::load_result& font_data){
return the<deferrer>().do_in_worker_thread([
font_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("font_asset.parsing", {
{"address", address}
});
font content;
if ( !fonts::try_load_font(content, font_data->content()) ) {
throw font_asset_loading_exception();
@@ -35,15 +43,14 @@ namespace e2d
return content;
});
})
.then([
.then_tuple([
&library,
parent_address = path::parent_path(address)
](const font& content){
return stdex::make_tuple_promise(std::make_tuple(
return std::make_tuple(
stdex::make_resolved_promise(content),
library.load_asset_async<texture_asset>(
path::combine(parent_address, content.info().atlas_file))
));
path::combine(parent_address, content.info().atlas_file)));
})
.then([](const std::tuple<
font,

View File

@@ -24,8 +24,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::load_result& image_data){
return the<deferrer>().do_in_worker_thread([image_data](){
.then([
address = str(address)
](const binary_asset::load_result& image_data){
return the<deferrer>().do_in_worker_thread([
image_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("image_asset.load_async", {
{"address", address}
});
image content;
if ( !images::try_load_image(content, image_data->content()) ) {
throw image_asset_loading_exception();

View File

@@ -24,8 +24,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<text_asset>(address)
.then([](const text_asset::load_result& json_data){
return the<deferrer>().do_in_worker_thread([json_data](){
.then([
address = str(address)
](const text_asset::load_result& json_data){
return the<deferrer>().do_in_worker_thread([
json_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("json_asset.parsing", {
{"address", address}
});
auto json = std::make_shared<rapidjson::Document>();
if ( json->Parse(json_data->content().c_str()).HasParseError() ) {
throw json_asset_loading_exception();

View File

@@ -24,8 +24,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::load_result& mesh_data){
return the<deferrer>().do_in_worker_thread([mesh_data](){
.then([
address = str(address)
](const binary_asset::load_result& mesh_data){
return the<deferrer>().do_in_worker_thread([
mesh_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("mesh_asset.parsing", {
{"address", address}
});
mesh content;
if ( !meshes::try_load_mesh(content, mesh_data->content()) ) {
throw mesh_asset_loading_exception();

View File

@@ -26,8 +26,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::load_result& script_data){
return the<deferrer>().do_in_main_thread([script_data](){
.then([
address = str(address)
](const binary_asset::load_result& script_data){
return the<deferrer>().do_in_main_thread([
script_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("script_asset.parsing", {
{"address", address}
});
std::optional<script> script_opt = the<luasol>().load_script(
script_data->content());
if ( !script_opt ) {

View File

@@ -54,21 +54,32 @@ namespace
const rapidjson::Value& root)
{
E2D_ASSERT(root.HasMember("vertex") && root["vertex"].IsString());
auto vertex_p = library.load_asset_async<text_asset>(
path::combine(parent_address, root["vertex"].GetString()));
auto vertex_a = path::combine(parent_address, root["vertex"].GetString());
auto vertex_p = library.load_asset_async<text_asset>(vertex_a);
E2D_ASSERT(root.HasMember("fragment") && root["fragment"].IsString());
auto fragment_p = library.load_asset_async<text_asset>(
path::combine(parent_address, root["fragment"].GetString()));
auto fragment_a = path::combine(parent_address, root["fragment"].GetString());
auto fragment_p = library.load_asset_async<text_asset>(fragment_a);
return stdex::make_tuple_promise(std::make_tuple(
std::move(vertex_p),
std::move(fragment_p)))
.then([](const std::tuple<
.then([
vertex_a = std::move(vertex_a),
fragment_a = std::move(fragment_a)
](const std::tuple<
text_asset::load_result,
text_asset::load_result
>& results){
return the<deferrer>().do_in_main_thread([results](){
return the<deferrer>().do_in_main_thread([
results,
vertex_a = std::move(vertex_a),
fragment_a = std::move(fragment_a)
](){
E2D_PROFILER_SCOPE_EX("shader_asset.create_shader", {
{"vertex_address", vertex_a},
{"fragment_address", fragment_a}
});
const shader_ptr content = the<render>().create_shader(
std::get<0>(results)->content(),
std::get<1>(results)->content());

View File

@@ -24,8 +24,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::load_result& shape_data){
return the<deferrer>().do_in_worker_thread([shape_data](){
.then([
address = str(address)
](const binary_asset::load_result& shape_data){
return the<deferrer>().do_in_worker_thread([
shape_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("shape_asset.parsing", {
{"address", address}
});
shape content;
if ( !shapes::try_load_shape(content, shape_data->content()) ) {
throw shape_asset_loading_exception();

View File

@@ -77,7 +77,7 @@ namespace
return library.load_asset_async<binary_asset>(sound_address)
.then([](const binary_asset::load_result& sound_data){
return the<deferrer>().do_in_worker_thread([sound_data](){
sound_stream_ptr content = the<audio>().preload_stream(
sound_stream_ptr content = the<audio>().create_stream(
sound_data->content());
if ( !content ) {
throw sound_asset_loading_exception();

View File

@@ -116,8 +116,20 @@ namespace
return library.load_asset_async<binary_asset>(atlas_path)
.then([
&library,
atlas_path,
atlas_folder
](const binary_asset::load_result& atlas_data){
return the<deferrer>().do_in_worker_thread([](){})
.then([
&library,
atlas_data,
atlas_path,
atlas_folder
](){
E2D_PROFILER_SCOPE_EX("spine_asset.atlas_parsing", {
{"address", atlas_path}
});
auto atlas_internal = std::make_unique<atlas_internal_state>();
spine::atlas_ptr atlas(
@@ -133,23 +145,24 @@ namespace
throw spine_asset_loading_exception();
}
return stdex::make_tuple_promise(std::make_tuple(
atlas_internal->loading.load_async(library),
stdex::make_resolved_promise(atlas_data)));
return atlas_internal->loading.load_async(library);
})
.then([
atlas_data,
atlas_path,
atlas_folder
](const std::tuple<
asset_group,
binary_asset::load_result
>& results){
](auto&& dependencies){
E2D_PROFILER_SCOPE_EX("spine_asset.atlas_parsing", {
{"address", atlas_path}
});
auto atlas_internal = std::make_unique<atlas_internal_state>();
atlas_internal->loaded = std::get<0>(results);
atlas_internal->loaded = std::forward<decltype(dependencies)>(dependencies);
spine::atlas_ptr atlas(
spAtlas_create(
reinterpret_cast<const char*>(std::get<1>(results)->content().data()),
math::numeric_cast<int>(std::get<1>(results)->content().size()),
reinterpret_cast<const char*>(atlas_data->content().data()),
math::numeric_cast<int>(atlas_data->content().size()),
atlas_folder.c_str(),
atlas_internal.get()),
spAtlas_dispose);
@@ -169,6 +182,7 @@ namespace
atlas->rendererObject = nullptr;
return atlas;
});
});
}
stdex::promise<spine::skeleton_data_ptr> load_skeleton_data(
@@ -178,14 +192,23 @@ namespace
f32 skeleton_scale,
const spine::atlas_ptr& atlas)
{
return library.load_asset_async<binary_asset>(
path::combine(parent_address, skeleton_address))
str address = path::combine(parent_address, skeleton_address);
return library.load_asset_async<binary_asset>(address)
.then([
atlas,
skeleton_scale,
skeleton_address
address = std::move(address)
](const binary_asset::load_result& skeleton_data){
if ( strings::ends_with(skeleton_address, ".skel") ) {
return the<deferrer>().do_in_worker_thread([
atlas,
skeleton_scale,
address = std::move(address),
skeleton_data
](){
E2D_PROFILER_SCOPE_EX("spine_asset.skeleton_parsing", {
{"address", address}
});
if ( strings::ends_with(address, ".skel") ) {
using skeleton_bin_ptr = std::unique_ptr<
spSkeletonBinary,
decltype(&::spSkeletonBinary_dispose)>;
@@ -248,6 +271,7 @@ namespace
return data_skeleton;
}
});
});
}
stdex::promise<spine> parse_spine(

View File

@@ -24,8 +24,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<image_asset>(address)
.then([](const image_asset::load_result& texture_data){
return the<deferrer>().do_in_main_thread([texture_data](){
.then([
address = str(address)
](const image_asset::load_result& texture_data){
return the<deferrer>().do_in_main_thread([
texture_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("texture_asset.create_texture", {
{"address", address}
});
const texture_ptr content = the<render>().create_texture(
texture_data->content());
if ( !content ) {

View File

@@ -24,8 +24,16 @@ namespace e2d
const library& library, str_view address)
{
return library.load_asset_async<text_asset>(address)
.then([](const text_asset::load_result& xml_data){
return the<deferrer>().do_in_worker_thread([xml_data](){
.then([
address = str(address)
](const text_asset::load_result& xml_data){
return the<deferrer>().do_in_worker_thread([
xml_data,
address = std::move(address)
](){
E2D_PROFILER_SCOPE_EX("xml_asset.parsing", {
{"address", address}
});
auto xml = std::make_shared<pugi::xml_document>();
if ( !xml->load_string(xml_data->content().c_str()) ) {
throw xml_asset_loading_exception();

View File

@@ -38,12 +38,13 @@ namespace e2d
state_.collect_garbage();
}
std::optional<script> luasol::load_script(buffer_view src) {
std::optional<script> luasol::load_script(str_view src) {
E2D_ASSERT(is_in_main_thread());
E2D_PROFILER_SCOPE("luasol.load_script");
sol::load_result result = state_.load_buffer(
reinterpret_cast<const char*>(src.data()),
src.size());
src.data(), src.size());
if ( !result.valid() ) {
sol::error err = result;
@@ -56,15 +57,11 @@ namespace e2d
return script(sol::protected_function(result));
}
std::optional<script> luasol::load_script(const input_stream_uptr& src) {
std::optional<script> luasol::load_script(buffer_view src) {
E2D_ASSERT(is_in_main_thread());
buffer file_data;
if ( !streams::try_read_tail(file_data, src) ) {
the<debug>().error("LUASOL: Failed to read script stream");
return std::nullopt;
}
return load_script(file_data);
return load_script(str_view(
reinterpret_cast<const char*>(src.data()),
src.size()));
}
}

View File

@@ -72,32 +72,69 @@ namespace
}
bool frame_tick() final {
E2D_PROFILER_SCOPE("application.frame_tick");
world& w = the<world>();
engine& e = the<engine>();
const f32 dt = e.delta_time();
const f32 time = e.time();
ecs::registry& registry = the<world>().registry();
registry.process_event(systems::pre_update_event{dt,time});
registry.process_event(systems::update_event{dt,time});
registry.process_event(systems::post_update_event{dt,time});
{
E2D_PROFILER_SCOPE("ecs.pre_update");
w.registry().process_event(systems::pre_update_event{dt,time});
}
{
E2D_PROFILER_SCOPE("ecs.update");
w.registry().process_event(systems::update_event{dt,time});
}
{
E2D_PROFILER_SCOPE("ecs.post_update");
w.registry().process_event(systems::post_update_event{dt,time});
}
return !the<window>().should_close()
|| (application_ && !application_->on_should_close());
}
void frame_render() final {
ecs::registry& registry = the<world>().registry();
registry.process_event(systems::pre_render_event{});
registry.process_event(systems::render_event{});
registry.process_event(systems::post_render_event{});
E2D_PROFILER_SCOPE("application.frame_render");
world& w = the<world>();
{
E2D_PROFILER_SCOPE("ecs.pre_render");
w.registry().process_event(systems::pre_render_event{});
}
{
E2D_PROFILER_SCOPE("ecs.render");
w.registry().process_event(systems::render_event{});
}
{
E2D_PROFILER_SCOPE("ecs.post_render");
w.registry().process_event(systems::post_render_event{});
}
}
void frame_finalize() final {
E2D_PROFILER_SCOPE("application.frame_finalize");
world& w = the<world>();
ecs::registry& registry = w.registry();
registry.process_event(systems::frame_finalize_event{});
{
E2D_PROFILER_SCOPE("ecs.frame_finalize");
w.registry().process_event(systems::frame_finalize_event{});
}
{
E2D_PROFILER_SCOPE("world.finalize_instances");
w.finalize_instances();
}
}
private:
starter::application_uptr application_;
};

View File

@@ -100,6 +100,7 @@ namespace e2d
ecs::registry& owner,
const ecs::after<systems::update_event>& trigger)
{
E2D_PROFILER_SCOPE("flipbook_system.process");
state_->process(trigger.event.dt, owner);
}
}

View File

@@ -396,6 +396,7 @@ namespace e2d
const ecs::after<systems::update_event>& trigger)
{
E2D_UNUSED(trigger);
E2D_PROFILER_SCOPE("label_system.process");
state_->process(owner);
}
}

View File

@@ -21,9 +21,12 @@ namespace
using namespace e2d::render_system_impl;
void for_all_scenes(drawer::context& ctx, const ecs::registry& owner) {
E2D_PROFILER_SCOPE("render_system.for_all_scenes");
const auto comp = [](const auto& l, const auto& r) noexcept {
return std::get<scene>(l).depth() < std::get<scene>(r).depth();
};
const auto func = [&ctx](
const ecs::const_entity&,
const scene&,
@@ -33,6 +36,7 @@ namespace
ctx.draw(node);
});
};
systems::for_extracted_sorted_components<scene, actor>(
owner,
comp,
@@ -41,9 +45,12 @@ namespace
}
void for_all_cameras(drawer& drawer, const ecs::registry& owner) {
E2D_PROFILER_SCOPE("render_system.for_all_cameras");
const auto comp = [](const auto& l, const auto& r) noexcept {
return std::get<camera>(l).depth() < std::get<camera>(r).depth();
};
const auto func = [&drawer, &owner](
const ecs::const_entity&,
const camera& cam,
@@ -53,6 +60,7 @@ namespace
for_all_scenes(ctx, owner);
});
};
systems::for_extracted_sorted_components<camera, actor>(
owner,
comp,
@@ -93,6 +101,7 @@ namespace e2d
const ecs::after<systems::render_event>& trigger)
{
E2D_UNUSED(trigger);
E2D_PROFILER_SCOPE("render_system.process");
state_->process(owner);
}
}

View File

@@ -192,8 +192,8 @@ namespace e2d::render_system_impl
float* uvs = nullptr;
unsigned short* indices = nullptr;
std::size_t index_count = 0;
std::size_t vertex_count = 0;
int index_count = 0;
int vertex_count = 0;
const spAtlasPage* atlas_page = nullptr;
const spColor* attachment_color = nullptr;
@@ -208,8 +208,10 @@ namespace e2d::render_system_impl
try {
vertex_count = 8;
if ( temp_vertices.size() < vertex_count ) {
temp_vertices.resize(math::max(temp_vertices.size() * 2u, vertex_count));
if ( temp_vertices.size() < math::numeric_cast<std::size_t>(vertex_count) ) {
temp_vertices.resize(math::max(
temp_vertices.size() * 2u,
math::numeric_cast<std::size_t>(vertex_count)));
}
} catch (...) {
property_cache_.clear();
@@ -239,8 +241,10 @@ namespace e2d::render_system_impl
try {
vertex_count = mesh->super.worldVerticesLength;
if ( temp_vertices.size() < vertex_count ) {
temp_vertices.resize(math::max(temp_vertices.size() * 2u, vertex_count));
if ( temp_vertices.size() < math::numeric_cast<std::size_t>(vertex_count) ) {
temp_vertices.resize(math::max(
temp_vertices.size() * 2u,
math::numeric_cast<std::size_t>(vertex_count)));
}
} catch (...) {
property_cache_.clear();
@@ -417,7 +421,7 @@ namespace e2d::render_system_impl
batcher_.batch(
mat_a,
property_cache_,
indices, index_count,
indices, math::numeric_cast<std::size_t>(index_count),
batch_vertices.data(), batch_vertex_count);
} catch (...) {
property_cache_.clear();

View File

@@ -125,6 +125,7 @@ namespace e2d
const systems::update_event& event)
{
E2D_UNUSED(event);
E2D_PROFILER_SCOPE("script_system.process");
state_->update_process(owner);
}
@@ -133,6 +134,7 @@ namespace e2d
const ecs::before<systems::update_event>& trigger)
{
E2D_UNUSED(trigger);
E2D_PROFILER_SCOPE("script_system.process");
state_->process_events(owner);
}
}

View File

@@ -258,6 +258,7 @@ namespace e2d
ecs::registry& owner,
const ecs::after<systems::update_event>& trigger)
{
E2D_PROFILER_SCOPE("spine_system.process");
state_->process(trigger.event.dt, owner);
}
}

View File

@@ -195,6 +195,8 @@ namespace e2d
}
gobject world::instantiate(const prefab& prefab, const node_iptr& parent) {
E2D_PROFILER_SCOPE("world.instantiate");
gobject inst = new_instance(*this, prefab);
if ( parent ) {
@@ -216,6 +218,8 @@ namespace e2d
}
gobject world::instantiate(const prefab& prefab, const node_iptr& parent, const t2f& transform) {
E2D_PROFILER_SCOPE("world.instantiate");
gobject inst = new_instance(*this, prefab);
inst.component<actor>()->node()->transform(transform);
@@ -244,6 +248,7 @@ namespace e2d
}
void world::finalize_instances() noexcept {
E2D_PROFILER_SCOPE("world.finalize_instances");
while ( !destroying_states_.empty() ) {
gobject inst{&destroying_states_.front()};
destroying_states_.pop_front();

View File

@@ -105,6 +105,11 @@ namespace e2d::filesystem
dst, make_read_file(path));
}
bool try_write_all(str_view src, str_view path, bool append) noexcept {
return streams::try_write_tail(
src, make_write_file(path, append));
}
bool try_write_all(buffer_view src, str_view path, bool append) noexcept {
return streams::try_write_tail(
src, make_write_file(path, append));

View File

@@ -170,6 +170,12 @@ namespace e2d
return *this;
}
output_sequence& output_sequence::write_all(str_view src) noexcept {
return success_
? write(src.data(), src.size())
: *this;
}
output_sequence& output_sequence::write_all(buffer_view src) noexcept {
return success_
? write(src.data(), src.size())
@@ -222,6 +228,14 @@ namespace e2d::streams
: false;
}
bool try_write_tail(str_view src, const output_stream_uptr& stream) noexcept {
return stream
? output_sequence(*stream)
.write_all(src)
.success()
: false;
}
bool try_write_tail(buffer_view src, const output_stream_uptr& stream) noexcept {
return stream
? output_sequence(*stream)

View File

@@ -135,6 +135,15 @@ namespace e2d
return path_;
}
str url::schemepath() const {
str result;
result.reserve(scheme_.size() + scheme_separator.size() + path_.size());
result.append(scheme_);
result.append(scheme_separator);
result.append(path_);
return result;
}
url& url::operator+=(str_view path) {
return concat(path);
}

View File

@@ -33,15 +33,15 @@ TEST_CASE("vfs"){
REQUIRE(v.read({"file", nofile_path}) == input_stream_uptr());
}
{
buffer b0;
REQUIRE(v.load({"file", file_path}, b0));
auto b0 = v.load({"file", file_path});
REQUIRE(b0);
REQUIRE(b0 == buffer{"hello", 5});
auto b1 = v.load_async({"file", file_path}).get();
REQUIRE(b1 == buffer{"hello", 5});
str b2;
REQUIRE(v.load_as_string({"file", file_path}, b2));
auto b2 = v.load_as_string({"file", file_path});
REQUIRE(b2);
REQUIRE(b2 == "hello");
auto b3 = v.load_as_string_async({"file", file_path}).get();
@@ -135,15 +135,15 @@ TEST_CASE("vfs"){
REQUIRE(b == buffer("hello", 5));
}
{
buffer b0;
REQUIRE(v.load(url("archive://test.txt"), b0));
auto b0 = v.load(url("archive://test.txt"));
REQUIRE(b0);
REQUIRE(b0 == buffer("hello", 5));
auto b1 = v.load_async(url("archive://test.txt")).get();
REQUIRE(b1 == buffer("hello", 5));
str b2;
REQUIRE(v.load_as_string(url("archive://test.txt"), b2));
auto b2 = v.load_as_string(url("archive://test.txt"));
REQUIRE(b2);
REQUIRE(b2 == "hello");
auto b3 = v.load_as_string_async(url("archive://test.txt")).get();
@@ -157,15 +157,15 @@ TEST_CASE("vfs"){
REQUIRE(b == buffer("world", 5));
}
{
buffer b0;
REQUIRE(v.load(url("archive://folder/file.txt"), b0));
auto b0 = v.load(url("archive://folder/file.txt"));
REQUIRE(b0);
REQUIRE(b0 == buffer("world", 5));
auto b1 = v.load_async(url("archive://folder/file.txt")).get();
REQUIRE(b1 == buffer("world", 5));
str b2;
REQUIRE(v.load_as_string(url("archive://folder/file.txt"), b2));
auto b2 = v.load_as_string(url("archive://folder/file.txt"));
REQUIRE(b2);
REQUIRE(b2 == "world");
auto b3 = v.load_as_string_async(url("archive://folder/file.txt")).get();
@@ -174,17 +174,15 @@ TEST_CASE("vfs"){
{
REQUIRE(v.read(url("archive://TEst.txt")) == input_stream_uptr());
buffer b0;
REQUIRE_FALSE(v.load(url("archive://TEst.txt"), b0));
REQUIRE(b0.empty());
auto b0 = v.load(url("archive://TEst.txt"));
REQUIRE_FALSE(b0);
REQUIRE_THROWS_AS(
v.load_async(url("archive://TEst.txt")).get(),
vfs_load_async_exception);
str b2;
REQUIRE_FALSE(v.load_as_string(url("archive://TEst.txt"), b2));
REQUIRE(b2.empty());
auto b2 = v.load_as_string(url("archive://TEst.txt"));
REQUIRE_FALSE(b2);
REQUIRE_THROWS_AS(
v.load_as_string_async(url("archive://TEst.txt")).get(),

View File

@@ -221,10 +221,10 @@ TEST_CASE("buffer_view") {
REQUIRE(v5.data() == b2.data());
REQUIRE(v5.size() == 20);
str32 b3 = make_utf32("hello");
u32 b3[3] = {10, 20, 30};
buffer_view v6(b3);
REQUIRE(v6.data() == b3.data());
REQUIRE(v6.size() == 20);
REQUIRE(v6.data() == &b3[0]);
REQUIRE(v6.size() == 12);
}
{
const char* s0 = "hell";

View File

@@ -19,24 +19,28 @@ TEST_CASE("url") {
REQUIRE(u.empty());
REQUIRE(u.scheme().empty());
REQUIRE(u.path().empty());
REQUIRE(u.schemepath() == "://");
}
{
url u("://file");
REQUIRE(!u.empty());
REQUIRE(u.scheme().empty());
REQUIRE(u.path() == "file");
REQUIRE(u.schemepath() == "://file");
}
{
url u("file://");
REQUIRE(u.empty());
REQUIRE(u.scheme() == "file");
REQUIRE(u.path().empty());
REQUIRE(u.schemepath() == "file://");
}
{
url u("file://test_file");
REQUIRE(!u.empty());
REQUIRE(u.scheme() == "file");
REQUIRE(u.path() == "test_file");
REQUIRE(u.schemepath() == "file://test_file");
}
{
url u("dir/file");