Compare commits

..

10 Commits

Author SHA1 Message Date
010c49d100 stable names 2024-02-10 12:23:40 +01:00
ff5dbaffc0 fix some file selector glitches 2024-02-08 18:11:51 +01:00
b56d581e4b fix normal feeling sluggish 2024-02-05 16:15:10 +01:00
aa661aaaa7 default to normal fps mode again 2024-02-05 16:10:30 +01:00
cc3f430bab rework tc and move tcs out of cg into main screen, rework render pp
now respecting animation timing
2024-02-05 16:06:12 +01:00
139db5b03b faster texture cache loading in low fps modes 2024-02-05 12:50:36 +01:00
7d0e5c80bd lil dep update 2024-02-04 12:48:04 +01:00
f716ad9dd1 limit max main loop sleep 2024-02-03 20:49:52 +01:00
671772a20e min fps for inactive reduced now 1fps 2024-02-03 19:07:14 +01:00
b0173f6d68 tox iterate interval pow(1.6)
fix faux offline inbetween timer
crop by default
2024-02-03 15:00:32 +01:00
15 changed files with 235 additions and 66 deletions

View File

@ -37,7 +37,7 @@ namespace Components {
} // Components } // Components
static float lerp(float a, float b, float t) { static constexpr float lerp(float a, float b, float t) {
return a + t * (b - a); return a + t * (b - a);
} }
@ -121,8 +121,10 @@ ChatGui4::ChatGui4(
ConfigModelI& conf, ConfigModelI& conf,
RegistryMessageModel& rmm, RegistryMessageModel& rmm,
Contact3Registry& cr, Contact3Registry& cr,
TextureUploaderI& tu TextureUploaderI& tu,
) : _conf(conf), _rmm(rmm), _cr(cr), _tal(_cr), _contact_tc(_tal, tu), _msg_tc(_mil, tu), _sip(tu) { ContactTextureCache& contact_tc,
MessageTextureCache& msg_tc
) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _sip(tu) {
} }
ChatGui4::~ChatGui4(void) { ChatGui4::~ChatGui4(void) {
@ -136,20 +138,7 @@ ChatGui4::~ChatGui4(void) {
//} //}
} }
void ChatGui4::render(float time_delta) { float ChatGui4::render(float time_delta) {
if (!_cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars
std::vector<Contact3> to_purge;
_cr.view<Contact::Components::TagAvatarInvalidate>().each([&to_purge](const Contact3 c) {
to_purge.push_back(c);
});
_cr.remove<Contact::Components::TagAvatarInvalidate>(to_purge.cbegin(), to_purge.cend());
_contact_tc.invalidate(to_purge);
}
// ACTUALLY NOT IF RENDERED, MOVED LOGIC TO ABOVE
// it might unload textures, so it needs to be done before rendering
_contact_tc.update();
_msg_tc.update();
_fss.render(); _fss.render();
_sip.render(time_delta); _sip.render(time_delta);
@ -627,8 +616,7 @@ void ChatGui4::render(float time_delta) {
} }
ImGui::End(); ImGui::End();
_contact_tc.workLoadQueue(); return 1000.f; // TODO: higher min fps?
_msg_tc.workLoadQueue();
} }
void ChatGui4::sendFilePath(const char* file_path) { void ChatGui4::sendFilePath(const char* file_path) {
@ -749,7 +737,11 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
) { ) {
if (ImGui::Button("save to")) { if (ImGui::Button("save to")) {
_fss.requestFile( _fss.requestFile(
[](const auto& path) -> bool { return std::filesystem::is_directory(path); }, [](std::filesystem::path& path) -> bool {
// remove file path
path.remove_filename();
return std::filesystem::is_directory(path);
},
[this, &reg, e](const auto& path) { [this, &reg, e](const auto& path) {
if (reg.valid(e)) { // still valid if (reg.valid(e)) { // still valid
// TODO: trim file? // TODO: trim file?

View File

@ -9,7 +9,8 @@
#include "./message_image_loader.hpp" #include "./message_image_loader.hpp"
#include "./file_selector.hpp" #include "./file_selector.hpp"
#include "./send_image_popup.hpp" #include "./send_image_popup.hpp"
#include "entt/container/dense_map.hpp"
#include <entt/container/dense_map.hpp>
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
@ -17,15 +18,16 @@
#include <mutex> #include <mutex>
#include <memory> #include <memory>
using ContactTextureCache = TextureCache<void*, Contact3, ToxAvatarLoader>;
using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoader>;
class ChatGui4 { class ChatGui4 {
ConfigModelI& _conf; ConfigModelI& _conf;
RegistryMessageModel& _rmm; RegistryMessageModel& _rmm;
Contact3Registry& _cr; Contact3Registry& _cr;
ToxAvatarLoader _tal; ContactTextureCache& _contact_tc;
TextureCache<void*, Contact3, ToxAvatarLoader> _contact_tc; MessageTextureCache& _msg_tc;
MessageImageLoader _mil;
TextureCache<void*, Message3Handle, MessageImageLoader> _msg_tc;
FileSelector _fss; FileSelector _fss;
SendImagePopup _sip; SendImagePopup _sip;
@ -52,12 +54,14 @@ class ChatGui4 {
ConfigModelI& conf, ConfigModelI& conf,
RegistryMessageModel& rmm, RegistryMessageModel& rmm,
Contact3Registry& cr, Contact3Registry& cr,
TextureUploaderI& tu TextureUploaderI& tu,
ContactTextureCache& contact_tc,
MessageTextureCache& msg_tc
); );
~ChatGui4(void); ~ChatGui4(void);
public: public:
void render(float time_delta); float render(float time_delta);
public: public:
bool any_unread {false}; bool any_unread {false};

View File

@ -18,7 +18,7 @@ FileSelector::FileSelector(void) {
} }
void FileSelector::requestFile( void FileSelector::requestFile(
std::function<bool(const std::filesystem::path& path)>&& is_valid, std::function<bool(std::filesystem::path& path)>&& is_valid,
std::function<void(const std::filesystem::path& path)>&& on_choose, std::function<void(const std::filesystem::path& path)>&& on_choose,
std::function<void(void)>&& on_cancel std::function<void(void)>&& on_cancel
) { ) {
@ -77,7 +77,9 @@ void FileSelector::render(void) {
if (current_path.has_parent_path()) { if (current_path.has_parent_path()) {
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
if (ImGui::Selectable("D##..", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { if (ImGui::Selectable("D##..", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
_current_file_path = _current_file_path.parent_path(); // the first "parent_path()" only removes the filename and the ending "/"
_current_file_path = _current_file_path.parent_path().parent_path() / "";
//_current_file_path = _current_file_path.remove_filename().parent_path() / "";
} }
} }

View File

@ -8,7 +8,7 @@ struct FileSelector {
bool _open_popup {false}; bool _open_popup {false};
std::function<bool(const std::filesystem::path& path)> _is_valid = [](auto){ return true; }; std::function<bool(std::filesystem::path& path)> _is_valid = [](auto){ return true; };
std::function<void(const std::filesystem::path& path)> _on_choose = [](auto){}; std::function<void(const std::filesystem::path& path)> _on_choose = [](auto){};
std::function<void(void)> _on_cancel = [](){}; std::function<void(void)> _on_cancel = [](){};
@ -18,8 +18,9 @@ struct FileSelector {
FileSelector(void); FileSelector(void);
// TODO: supply hints // TODO: supply hints
// HACK: until we supply hints, is_valid can modify
void requestFile( void requestFile(
std::function<bool(const std::filesystem::path& path)>&& is_valid, std::function<bool(std::filesystem::path& path)>&& is_valid,
std::function<void(const std::filesystem::path& path)>&& on_choose, std::function<void(const std::filesystem::path& path)>&& on_choose,
std::function<void(void)>&& on_cancel std::function<void(void)>&& on_cancel
); );

View File

@ -178,9 +178,13 @@ int main(int argc, char** argv) {
//) //)
//)); //));
const float min_delay = std::min<float>( const float min_delay =
screen->nextTick() - time_delta_tick, std::min<float>(
screen->nextRender() - time_delta_render std::min<float>(
screen->nextTick() - time_delta_tick,
screen->nextRender() - time_delta_render
),
0.25f // dont sleep too long
) * 1000.f; ) * 1000.f;
if (min_delay > 0.f) { if (min_delay > 0.f) {

View File

@ -1,10 +1,13 @@
#include "./main_screen.hpp" #include "./main_screen.hpp"
#include <solanaceae/contact/components.hpp>
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <memory> #include <memory>
#include <cmath>
MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins) : MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins) :
renderer(renderer_), renderer(renderer_),
@ -20,7 +23,11 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
mmil(rmm), mmil(rmm),
tam(rmm, cr, conf), tam(rmm, cr, conf),
sdlrtu(renderer_), sdlrtu(renderer_),
cg(conf, rmm, cr, sdlrtu), tal(cr),
contact_tc(tal, sdlrtu),
mil(),
msg_tc(mil, sdlrtu),
cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc),
sw(conf), sw(conf),
tuiu(tc, conf), tuiu(tc, conf),
tdch(tpi) tdch(tpi)
@ -167,7 +174,22 @@ Screen* MainScreen::render(float time_delta, bool&) {
const float pm_interval = pm.render(time_delta); // render const float pm_interval = pm.render(time_delta); // render
cg.render(time_delta); // render // TODO: move this somewhere else!!!
// needs both tal and tc <.<
if (!cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars
std::vector<Contact3> to_purge;
cr.view<Contact::Components::TagAvatarInvalidate>().each([&to_purge](const Contact3 c) {
to_purge.push_back(c);
});
cr.remove<Contact::Components::TagAvatarInvalidate>(to_purge.cbegin(), to_purge.cend());
contact_tc.invalidate(to_purge);
}
// ACTUALLY NOT IF RENDERED, MOVED LOGIC TO ABOVE
// it might unload textures, so it needs to be done before rendering
const float ctc_interval = contact_tc.update();
const float msgtc_interval = msg_tc.update();
const float cg_interval = cg.render(time_delta); // render
sw.render(); // render sw.render(); // render
tuiu.render(); // render tuiu.render(); // render
tdch.render(); // render tdch.render(); // render
@ -216,20 +238,136 @@ Screen* MainScreen::render(float time_delta, bool&) {
ImGui::ShowDemoWindow(); ImGui::ShowDemoWindow();
} }
if ( float tc_unfinished_queue_interval;
_fps_perf_mode > 1 // TODO: magic { // load rendered but not loaded textures
) { bool unfinished_work_queue = contact_tc.workLoadQueue();
// powersave forces 250ms unfinished_work_queue = unfinished_work_queue || msg_tc.workLoadQueue();
_render_interval = 1.f/4.f;
} else if ( if (unfinished_work_queue) {
_time_since_event > 1.f && ( // 1sec cool down tc_unfinished_queue_interval = 0.1f; // so we can get images loaded faster
_fps_perf_mode == 1 || // TODO: magic } else {
_window_hidden tc_unfinished_queue_interval = 1.f; // TODO: higher min fps?
}
}
// calculate interval for next frame
// normal:
// - if < 1.5sec since last event
// - min all and clamp(1/60, 1/1)
// - if < 30sec since last event
// - min all (anim + everything else) clamp(1/60, 1/1) (maybe less?)
// - else
// - min without anim and clamp(1/60, 1/1) (maybe more?)
// reduced:
// - if < 1sec since last event
// - min all and clamp(1/60, 1/1)
// - if < 10sec since last event
// - min all (anim + everything else) clamp(1/10, 1/1)
// - else
// - min without anim and max clamp(1/10, 1/1)
// powersave:
// - if < 0sec since last event
// - (ignored)
// - if < 1sec since last event
// - min all (anim + everything else) clamp(1/8, 1/1)
// - else
// - min without anim and clamp(1/1, 1/1)
struct PerfProfileRender {
float low_delay_window {1.5f};
float low_delay_min {1.f/60.f};
float low_delay_max {1.f/60.f};
float mid_delay_window {30.f};
float mid_delay_min {1.f/60.f};
float mid_delay_max {1.f/2.f};
// also when main window hidden
float else_delay_min {1.f/60.f};
float else_delay_max {1.f/2.f};
};
const static PerfProfileRender normalPerfProfile{
//1.5f, // low_delay_window
//1.f/60.f, // low_delay_min
//1.f/60.f, // low_delay_max
//30.f, // mid_delay_window
//1.f/60.f, // mid_delay_min
//1.f/2.f, // mid_delay_max
//1.f/60.f, // else_delay_min
//1.f/2.f, // else_delay_max
};
const static PerfProfileRender reducedPerfProfile{
1.f, // low_delay_window
1.f/60.f, // low_delay_min
1.f/30.f, // low_delay_max
10.f, // mid_delay_window
1.f/10.f, // mid_delay_min
1.f/4.f, // mid_delay_max
1.f/10.f, // else_delay_min
1.f, // else_delay_max
};
// TODO: fix powersave by adjusting it in the events handler (make ppr member)
const static PerfProfileRender powersavePerfProfile{
// no window -> ignore first case
0.f, // low_delay_window
1.f, // low_delay_min
1.f, // low_delay_max
1.f, // mid_delay_window
1.f/8.f, // mid_delay_min
1.f/4.f, // mid_delay_max
1.f, // else_delay_min
1.f, // else_delay_max
};
const PerfProfileRender& curr_profile =
// TODO: magic
_fps_perf_mode > 1
? powersavePerfProfile
: (
_fps_perf_mode == 1
? reducedPerfProfile
: normalPerfProfile
) )
) { ;
_render_interval = std::min<float>(1.f/4.f, pm_interval);
// min over non animations in all cases
_render_interval = std::min<float>(pm_interval, cg_interval);
_render_interval = std::min<float>(_render_interval, tc_unfinished_queue_interval);
// low delay time window
if (!_window_hidden && _time_since_event < curr_profile.low_delay_window) {
_render_interval = std::min<float>(_render_interval, ctc_interval);
_render_interval = std::min<float>(_render_interval, msgtc_interval);
_render_interval = std::clamp(
_render_interval,
curr_profile.low_delay_min,
curr_profile.low_delay_max
);
// mid delay time window
} else if (!_window_hidden && _time_since_event < curr_profile.mid_delay_window) {
_render_interval = std::min<float>(_render_interval, ctc_interval);
_render_interval = std::min<float>(_render_interval, msgtc_interval);
_render_interval = std::clamp(
_render_interval,
curr_profile.mid_delay_min,
curr_profile.mid_delay_max
);
// timed out or window hidden
} else { } else {
_render_interval = std::min<float>(1.f/60.f, pm_interval); // no animation timing here
_render_interval = std::clamp(
_render_interval,
curr_profile.else_delay_min,
curr_profile.else_delay_max
);
} }
_time_since_event += time_delta; _time_since_event += time_delta;
@ -253,7 +391,9 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
mts.iterate(); // compute mts.iterate(); // compute
_min_tick_interval = std::min<float>( _min_tick_interval = std::min<float>(
tc.toxIterationInterval()/1000.f, // HACK: pow by 1.6 to increase 50 -> ~500 (~522)
// and it does not change 1
std::pow(tc.toxIterationInterval(), 1.6f)/1000.f,
pm_interval pm_interval
); );
_min_tick_interval = std::min<float>( _min_tick_interval = std::min<float>(
@ -261,6 +401,8 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
fo_interval fo_interval
); );
//std::cout << "MS: min tick interval: " << _min_tick_interval << "\n";
switch (_compute_perf_mode) { switch (_compute_perf_mode) {
// normal 1ms lower bound // normal 1ms lower bound
case 0: _min_tick_interval = std::max<float>(_min_tick_interval, 0.001f); break; case 0: _min_tick_interval = std::max<float>(_min_tick_interval, 0.001f); break;

View File

@ -21,6 +21,10 @@
#include "./tox_avatar_manager.hpp" #include "./tox_avatar_manager.hpp"
#include "./sdlrenderer_texture_uploader.hpp" #include "./sdlrenderer_texture_uploader.hpp"
#include "./texture_cache.hpp"
#include "./tox_avatar_loader.hpp"
#include "./message_image_loader.hpp"
#include "./chat_gui4.hpp" #include "./chat_gui4.hpp"
#include "./settings_window.hpp" #include "./settings_window.hpp"
#include "./tox_ui_utils.hpp" #include "./tox_ui_utils.hpp"
@ -61,6 +65,11 @@ struct MainScreen final : public Screen {
SDLRendererTextureUploader sdlrtu; SDLRendererTextureUploader sdlrtu;
//OpenGLTextureUploader ogltu; //OpenGLTextureUploader ogltu;
ToxAvatarLoader tal;
TextureCache<void*, Contact3, ToxAvatarLoader> contact_tc;
MessageImageLoader mil;
TextureCache<void*, Message3Handle, MessageImageLoader> msg_tc;
ChatGui4 cg; ChatGui4 cg;
SettingsWindow sw; SettingsWindow sw;
ToxUIUtils tuiu; ToxUIUtils tuiu;
@ -85,7 +94,7 @@ struct MainScreen final : public Screen {
// 0 - normal // 0 - normal
// 1 - reduced // 1 - reduced
// 2 - power save // 2 - power save
int _fps_perf_mode {1}; int _fps_perf_mode {0};
// 0 - normal // 0 - normal
// 1 - power save // 1 - power save
int _compute_perf_mode {0}; int _compute_perf_mode {0};

View File

@ -32,7 +32,7 @@ struct SendImagePopup {
Rect crop_rect; Rect crop_rect;
Rect crop_before_drag; Rect crop_before_drag;
bool cropping {false}; bool cropping {true};
bool dragging_last_frame_ul {false}; bool dragging_last_frame_ul {false};
bool dragging_last_frame_lr {false}; bool dragging_last_frame_lr {false};

View File

@ -2,8 +2,9 @@
#include <chrono> #include <chrono>
#include <array> #include <array>
#include <limits>
void TextureEntry::doAnimation(const int64_t ts_now) { int64_t TextureEntry::doAnimation(const int64_t ts_now) {
if (frame_duration.size() > 1) { // is animation if (frame_duration.size() > 1) { // is animation
do { // why is this loop so ugly do { // why is this loop so ugly
const int64_t duration = getDuration(); const int64_t duration = getDuration();
@ -11,11 +12,13 @@ void TextureEntry::doAnimation(const int64_t ts_now) {
timestamp_last_rendered += duration; timestamp_last_rendered += duration;
next(); next();
} else { } else {
break; // return ts for next frame
return timestamp_last_rendered + duration;
} }
} while(true); } while (true);
} else { } else {
timestamp_last_rendered = ts_now; timestamp_last_rendered = ts_now;
return std::numeric_limits<int64_t>::max(); // static image
} }
} }

View File

@ -50,7 +50,8 @@ struct TextureEntry {
current_texture = (current_texture + 1) % frame_duration.size(); current_texture = (current_texture + 1) % frame_duration.size();
} }
void doAnimation(const int64_t ts_now); // returns ts for next frame
int64_t doAnimation(const int64_t ts_now);
template<typename TextureType> template<typename TextureType>
TextureType getID(void) { TextureType getID(void) {
@ -133,14 +134,16 @@ struct TextureCache {
} }
} }
void update(void) { float update(void) {
const uint64_t ts_now = Message::getTimeMS(); const uint64_t ts_now = Message::getTimeMS();
uint64_t ts_min_next = ts_now + ms_before_purge;
std::vector<KeyType> to_purge; std::vector<KeyType> to_purge;
for (auto&& [key, te] : _cache) { for (auto&& [key, te] : _cache) {
if (te.rendered_this_frame) { if (te.rendered_this_frame) {
te.doAnimation(ts_now); const uint64_t ts_next = te.doAnimation(ts_now);
te.rendered_this_frame = false; te.rendered_this_frame = false;
ts_min_next = std::min(ts_min_next, ts_next);
} else if (_cache.size() > min_count_before_purge && ts_now - te.timestamp_last_rendered >= ms_before_purge) { } else if (_cache.size() > min_count_before_purge && ts_now - te.timestamp_last_rendered >= ms_before_purge) {
to_purge.push_back(key); to_purge.push_back(key);
} }
@ -148,7 +151,10 @@ struct TextureCache {
invalidate(to_purge); invalidate(to_purge);
// we ignore the default texture ts :)
_default_texture.doAnimation(ts_now); _default_texture.doAnimation(ts_now);
return (ts_min_next - ts_now) / 1000.f;
} }
void invalidate(const std::vector<KeyType>& to_purge) { void invalidate(const std::vector<KeyType>& to_purge) {
@ -162,16 +168,22 @@ struct TextureCache {
} }
} }
void workLoadQueue(void) { // returns true if there is still work queued up
for (auto it = _to_load.begin(); it != _to_load.end(); it++) { bool workLoadQueue(void) {
auto it = _to_load.begin();
for (; it != _to_load.end(); it++) {
auto new_entry_opt = _l.load(_tu, *it); auto new_entry_opt = _l.load(_tu, *it);
if (new_entry_opt.has_value()) { if (new_entry_opt.has_value()) {
_cache.emplace(*it, new_entry_opt.value()); _cache.emplace(*it, new_entry_opt.value());
_to_load.erase(it); it = _to_load.erase(it);
// TODO: not a good idea
// TODO: not a good idea?
break; // end load from queue/onlyload 1 per update break; // end load from queue/onlyload 1 per update
} }
} }
// peak
return it != _to_load.end();
} }
}; };

View File

@ -24,7 +24,7 @@ class ToxFriendFauxOfflineMessaging : public ToxEventI {
// TODO: increase timer? // TODO: increase timer?
const float _delay_after_cc {4.5f}; const float _delay_after_cc {4.5f};
const float _delay_inbetween {1.3f}; const float _delay_inbetween {0.3f};
const float _delay_retry {10.f}; // retry sending after 10s const float _delay_retry {10.f}; // retry sending after 10s
public: public: