diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13d4cda6..cebafdb0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,8 @@ add_executable(tomato ./texture_uploader.hpp ./sdlrenderer_texture_uploader.hpp ./sdlrenderer_texture_uploader.cpp + ./texture_cache.hpp + ./texture_cache.cpp ./sdl_clipboard_utils.hpp ./sdl_clipboard_utils.cpp ./file_selector.hpp diff --git a/src/chat_gui4.cpp b/src/chat_gui4.cpp index de16df1c..4d03bb27 100644 --- a/src/chat_gui4.cpp +++ b/src/chat_gui4.cpp @@ -14,7 +14,6 @@ #include #include "./sdl_clipboard_utils.hpp" -#include "SDL_clipboard.h" #include #include @@ -28,7 +27,7 @@ ChatGui4::ChatGui4( RegistryMessageModel& rmm, Contact3Registry& cr, TextureUploaderI& tu -) : _conf(conf), _rmm(rmm), _cr(cr) { +) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(tu) { } void ChatGui4::render(void) { @@ -277,7 +276,7 @@ void ChatGui4::render(void) { _fss.render(); - //_tc.update(); + _contact_tc.update(); //_tc.workLoadQueue(); } @@ -514,8 +513,8 @@ bool ChatGui4::renderContactListContactBig(const Contact3 c) { } // avatar -#if 0 - const auto [id, width, height] = _tc.get("test"); +#if 1 + const auto [id, width, height] = _contact_tc.get(c); ImGui::Image( id, ImVec2{img_y, img_y}, diff --git a/src/chat_gui4.hpp b/src/chat_gui4.hpp index 29bd44d5..dc6d09d3 100644 --- a/src/chat_gui4.hpp +++ b/src/chat_gui4.hpp @@ -4,6 +4,7 @@ #include #include "./texture_uploader.hpp" +#include "./texture_cache.hpp" #include "./file_selector.hpp" #include @@ -14,6 +15,9 @@ class ChatGui4 { RegistryMessageModel& _rmm; Contact3Registry& _cr; + TextureCache _contact_tc; + //TextureCache _msg_tc; + FileSelector _fss; std::optional _selected_contact; diff --git a/src/texture_cache.cpp b/src/texture_cache.cpp new file mode 100644 index 00000000..767e5698 --- /dev/null +++ b/src/texture_cache.cpp @@ -0,0 +1,59 @@ +#include "./texture_cache.hpp" + +#include + +void TextureEntry::doAnimation(const uint64_t ts_now) { + if (frame_duration.size() > 1) { // is animation + do { // why is this loop so ugly + const uint64_t duration = getDuration(); + if (ts_now - timestamp_last_rendered >= duration) { + timestamp_last_rendered += duration; + next(); + } else { + break; + } + } while(true); + } else { + timestamp_last_rendered = ts_now; + } +} + +TextureEntry generateTestAnim(TextureUploaderI& tu) { + TextureEntry new_entry; + new_entry.timestamp_last_rendered = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + new_entry.current_texture = 0; + for (size_t i = 0; i < 4; i++) { + // hack + // generate frame + const size_t width {2}; + const size_t height {2}; + std::array pixels; + for (size_t pen = 0; pen < width*height; pen++) { + if (pen == i) { + pixels[pen*4+0] = 0xff; + pixels[pen*4+1] = 0xff; + pixels[pen*4+2] = 0xff; + pixels[pen*4+3] = 0xff; + } else { + pixels[pen*4+0] = 0x00; + pixels[pen*4+1] = 0x00; + pixels[pen*4+2] = 0x00; + pixels[pen*4+3] = 0xff; + } + } + + const auto n_t = tu.uploadRGBA(pixels.data(), width, height); + + // this is so ugly + new_entry.textures.emplace_back(n_t); + new_entry.frame_duration.emplace_back(250); + } + new_entry.width = 2; + new_entry.height = 2; + return new_entry; +} + +uint64_t getNowMS(void) { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} + diff --git a/src/texture_cache.hpp b/src/texture_cache.hpp new file mode 100644 index 00000000..f3021d6a --- /dev/null +++ b/src/texture_cache.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include "./texture_uploader.hpp" + +#include +#include + +#include + +struct TextureEntry { + uint32_t width {0}; + uint32_t height {0}; + std::vector textures; + std::vector frame_duration; // ms + size_t current_texture {0}; + + bool rendered_this_frame {false}; + // or flipped for animations + uint64_t timestamp_last_rendered {0}; // ms + + TextureEntry(void) = default; + TextureEntry(const TextureEntry& other) : + width(other.width), + height(other.height), + textures(other.textures), + frame_duration(other.frame_duration), + current_texture(other.current_texture), + + rendered_this_frame(other.rendered_this_frame), + timestamp_last_rendered(other.timestamp_last_rendered) + {} + + TextureEntry& operator=(const TextureEntry& other) { + width = other.width; + height = other.height; + textures = other.textures; + frame_duration = other.frame_duration; + current_texture = other.current_texture; + + rendered_this_frame = other.rendered_this_frame; + timestamp_last_rendered = other.timestamp_last_rendered; + return *this; + } + + uint32_t getDuration(void) const { + return frame_duration.at(current_texture); + } + + void next(void) { + current_texture = (current_texture + 1) % frame_duration.size(); + } + + void doAnimation(const uint64_t ts_now); + + template + TextureType getID(void) { + static_assert( + sizeof(TextureType) == sizeof(uint64_t) || + sizeof(TextureType) == sizeof(uint32_t) + ); + + rendered_this_frame = true; + assert(current_texture < textures.size()); + if constexpr (sizeof(TextureType) == sizeof(uint64_t)) { + return reinterpret_cast(textures.at(current_texture)); + } else if constexpr (sizeof(TextureType) == sizeof(uint32_t)) { + return reinterpret_cast(static_cast(textures.at(current_texture))); + } + } +}; + +TextureEntry generateTestAnim(TextureUploaderI& tu); + +uint64_t getNowMS(void); + +template +struct TextureCache { + static_assert( + sizeof(TextureType) == sizeof(uint64_t) || + sizeof(TextureType) == sizeof(uint32_t) + ); + + TextureUploaderI& _tu; + + TextureEntry _default_texture; + + entt::dense_map _cache; + entt::dense_set _to_load; + + const uint64_t ms_before_purge {60 * 1000ull}; + const size_t min_count_before_purge {0}; // starts purging after that + + TextureCache(TextureUploaderI& tu) : _tu(tu) { + //_image_loaders.push_back(std::make_unique()); + //_image_loaders.push_back(std::make_unique()); + _default_texture = generateTestAnim(_tu); + //_default_texture = loadTestWebPAnim(); + } + + struct GetInfo { + TextureType id; + uint32_t width; + uint32_t height; + }; + GetInfo get(const KeyType& key) { + auto it = _cache.find(key); + + if (it != _cache.end()) { + return { + it->second.template getID(), + it->second.width, + it->second.height + }; + } else { + _to_load.insert(key); + return { + _default_texture.getID(), + _default_texture.width, + _default_texture.height + }; + } + } + + void update(void) { + const uint64_t ts_now = getNowMS(); + + std::vector to_purge; + for (auto&& [key, te] : _cache) { + if (te.rendered_this_frame) { + te.doAnimation(ts_now); + te.rendered_this_frame = false; + } else if (_cache.size() > min_count_before_purge && ts_now - te.timestamp_last_rendered >= ms_before_purge) { + to_purge.push_back(key); + } + } + + for (const auto& key : to_purge) { + for (const auto& tex_id : _cache.at(key).textures) { + _tu.destroy(tex_id); + } + _cache.erase(key); + } + + _default_texture.doAnimation(ts_now); + } +}; +