From 9dfef8c22987c12a6aad47a8913e60943d8578e7 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 14 May 2021 16:40:49 +0200 Subject: Move thumbnail loading/unloading to AsyncImageLoader --- src/AsyncImageLoader.cpp | 29 ++++++++++++++- src/Body.cpp | 92 +++++++++++------------------------------------- src/QuickMedia.cpp | 55 +++++++++++++++++++---------- 3 files changed, 84 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 947f637..1769846 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -7,6 +7,7 @@ #include "../external/hash-library/sha256.h" #include +#include #include namespace QuickMedia { @@ -212,6 +213,32 @@ namespace QuickMedia { }); } + std::shared_ptr AsyncImageLoader::get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size) { + // TODO: Instead of generating a new hash everytime to access thumbnail, cache the hash of the thumbnail url + auto &thumbnail_data = thumbnails[url]; + if(!thumbnail_data) + thumbnail_data = std::make_shared(); + thumbnail_data->counter = counter; + load_thumbnail(url, local, resize_target_size, thumbnail_data); + return thumbnail_data; + } + + void AsyncImageLoader::update() { + bool loaded_textures_changed = false; + for(auto it = thumbnails.begin(); it != thumbnails.end();) { + if(it->second->counter != counter) { + it = thumbnails.erase(it); + loaded_textures_changed = true; + } else { + ++it; + } + } + + ++counter; + if(loaded_textures_changed) + malloc_trim(0); + } + int AsyncImageLoader::get_free_load_index() const { for(int i = 0; i < NUM_IMAGE_LOAD_THREADS; ++i) { if(!loading_image[i]) @@ -219,4 +246,4 @@ namespace QuickMedia { } return -1; } -} \ No newline at end of file +} diff --git a/src/Body.cpp b/src/Body.cpp index 09d3051..803cece 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -144,7 +144,6 @@ namespace QuickMedia { } Body::~Body() { - item_thumbnail_textures.clear(); items.clear(); malloc_trim(0); } @@ -334,7 +333,7 @@ namespace QuickMedia { void Body::clear_cache() { clear_text_cache(); - clear_thumbnails(); + malloc_trim(0); } void Body::clear_text_cache() { @@ -345,11 +344,6 @@ namespace QuickMedia { } } - void Body::clear_thumbnails() { - item_thumbnail_textures.clear(); - malloc_trim(0); - } - BodyItem* Body::get_selected() const { if(selected_item < 0 || selected_item >= (int)items.size() || !items[selected_item]->visible) return nullptr; @@ -580,7 +574,6 @@ namespace QuickMedia { int num_items = items.size(); if(num_items == 0 || size.y <= 0.0f) { - item_thumbnail_textures.clear(); for(auto &body_item : items) { clear_body_item_cache(body_item.get()); if(body_item->embedded_item) @@ -596,10 +589,6 @@ namespace QuickMedia { return; } - for(auto &thumbnail_it : item_thumbnail_textures) { - thumbnail_it.second->referenced = false; - } - if(prev_selected_item < 0 || prev_selected_item >= (int)items.size()) { prev_selected_item = selected_item; } @@ -848,15 +837,6 @@ namespace QuickMedia { if(!items_cut_off_set) items_cut_off = false; - for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) { - if(!it->second->referenced) { - it = item_thumbnail_textures.erase(it); - loaded_textures_changed = true; - } else { - ++it; - } - } - // TODO: Only do this for items that are not visible, do not loop all items. // TODO: Improve performance! right now it can use up to 5-7% cpu with a lot of items! for(auto &body_item : items) { @@ -867,11 +847,6 @@ namespace QuickMedia { clear_body_item_cache(body_item->embedded_item.get()); } - if(loaded_textures_changed) { - loaded_textures_changed = false; - malloc_trim(0); - } - mouse_left_clicked = false; if(clicked_body_item) { auto clicked_body_item_tmp = clicked_body_item; // tmp because below call to body_item_select_callback may call this same draw function @@ -1116,18 +1091,10 @@ namespace QuickMedia { } void Body::draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item, bool merge_with_previous) { - // TODO: Instead of generating a new hash everytime to access textures, cache the hash of the thumbnail url + sf::Vector2i thumbnail_size = get_item_thumbnail_size(item); std::shared_ptr item_thumbnail; - if(draw_thumbnails && !item->thumbnail_url.empty()) { - auto item_thumbnail_it = item_thumbnail_textures.find(item->thumbnail_url); - if(item_thumbnail_it == item_thumbnail_textures.end()) { - item_thumbnail = std::make_shared(); - item_thumbnail_textures.insert(std::make_pair(item->thumbnail_url, item_thumbnail)); - } else { - item_thumbnail = item_thumbnail_it->second; - } - item_thumbnail->referenced = true; - } + if(draw_thumbnails && !item->thumbnail_url.empty()) + item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, thumbnail_size); if(body_item_render_callback && include_embedded_item) body_item_render_callback(item); @@ -1156,13 +1123,13 @@ namespace QuickMedia { } float text_offset_x = padding_x; - if(draw_thumbnails && item_thumbnail && !merge_with_previous) { + if(item_thumbnail && !merge_with_previous) { // TODO: Verify if this is safe. The thumbnail is being modified in another thread if(item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { image.setTexture(item_thumbnail->texture, true); auto image_size = image.getTexture()->getSize(); sf::Vector2f image_size_f(image_size.x, image_size.y); - sf::Vector2f content_size = to_vec2f(get_item_thumbnail_size(item)); + sf::Vector2f content_size = to_vec2f(thumbnail_size); auto new_image_size = clamp_to_size(image_size_f, content_size); auto image_scale = get_ratio(image_size_f, new_image_size); image.setScale(image_scale); @@ -1177,7 +1144,7 @@ namespace QuickMedia { // We want the next image fallback to have the same size as the successful image rendering, because its likely the image fallback will have the same size (for example thumbnails on youtube) //image_fallback.setSize(sf::Vector2f(width_ratio * image_size.x, height_ratio * image_size.y)); } else if(!item->thumbnail_url.empty()) { - sf::Vector2f content_size = to_vec2f(get_item_thumbnail_size(item)); + sf::Vector2f content_size = to_vec2f(thumbnail_size); sf::Color fallback_color(52, 58, 70); if(thumbnail_mask_shader && item->thumbnail_mask_type == ThumbnailMaskType::CIRCLE) { @@ -1204,7 +1171,7 @@ namespace QuickMedia { text_offset_x += image_padding_x + content_size.x; } } else if(item->thumbnail_size.x > 0) { - text_offset_x += image_padding_x + get_item_thumbnail_size(item).x; + text_offset_x += image_padding_x + thumbnail_size.x; } const float timestamp_text_y = std::floor(item_pos.y + padding_y - std::floor(6.0f * get_ui_scale())); @@ -1320,39 +1287,20 @@ namespace QuickMedia { if(load_texture) item->last_drawn_time = elapsed_time_sec; + sf::Vector2i content_size = get_item_thumbnail_size(item); float image_height = 0.0f; float text_offset_x = padding_x; - if(draw_thumbnails && !item->thumbnail_url.empty() && !merge_with_previous) { - sf::Vector2i content_size = get_item_thumbnail_size(item); + if(draw_thumbnails && load_texture && !item->thumbnail_url.empty() && !merge_with_previous) { image_height = content_size.y; - - std::shared_ptr item_thumbnail; - auto item_thumbnail_it = item_thumbnail_textures.find(item->thumbnail_url); - if(item_thumbnail_it == item_thumbnail_textures.end()) { - if(load_texture) { - item_thumbnail = std::make_shared(); - item_thumbnail_textures.insert(std::make_pair(item->thumbnail_url, item_thumbnail)); - } - } else { - item_thumbnail = item_thumbnail_it->second; - } - - if(load_texture && item_thumbnail) { - item_thumbnail->referenced = true; - - if(!item->thumbnail_url.empty() && item_thumbnail->loading_state == LoadingState::NOT_LOADED) - AsyncImageLoader::get_instance().load_thumbnail(item->thumbnail_url, item->thumbnail_is_local, content_size, item_thumbnail); - - if(item_thumbnail->loading_state == LoadingState::FINISHED_LOADING && item_thumbnail->image->getSize().x > 0 && item_thumbnail->image->getSize().y > 0) { - if(!item_thumbnail->texture.loadFromImage(*item_thumbnail->image)) - fprintf(stderr, "Warning: failed to load texture from image: %s\n", item->thumbnail_url.c_str()); - item_thumbnail->texture.setSmooth(true); - //item_thumbnail->texture.generateMipmap(); - item_thumbnail->image.reset(); - item_thumbnail->loading_state = LoadingState::APPLIED_TO_TEXTURE; - item_thumbnail->texture_applied_time.restart(); - loaded_textures_changed = true; - } + std::shared_ptr item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, content_size); + + if(item_thumbnail && item_thumbnail->loading_state == LoadingState::FINISHED_LOADING && item_thumbnail->image->getSize().x > 0 && item_thumbnail->image->getSize().y > 0) { + if(!item_thumbnail->texture.loadFromImage(*item_thumbnail->image)) + fprintf(stderr, "Warning: failed to load texture from image: %s\n", item->thumbnail_url.c_str()); + item_thumbnail->texture.setSmooth(true); + //item_thumbnail->texture.generateMipmap(); + item_thumbnail->image.reset(); + item_thumbnail->loading_state = LoadingState::APPLIED_TO_TEXTURE; } if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { @@ -1365,7 +1313,7 @@ namespace QuickMedia { text_offset_x += image_padding_x + content_size.x; } } else if(item->thumbnail_size.x > 0) { - text_offset_x += image_padding_x + get_item_thumbnail_size(item).x; + text_offset_x += image_padding_x + content_size.x; } if(load_texture) diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index a58b3d8..903c966 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1547,7 +1547,6 @@ namespace QuickMedia { if(tabs[selected_tab].page->is_single_page()) { if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->clear(); if(new_tabs.size() == 1 && !new_tabs[0].page) { - tabs[selected_tab].body->clear_thumbnails(); tabs[selected_tab].body = std::move(new_tabs[0].body); tabs[selected_tab].page->submit_body_item = prev_selected_item; return; @@ -1871,7 +1870,6 @@ namespace QuickMedia { if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && associated_data.fetch_future.ready()) { if(!associated_data.search_text_updated) { FetchResult fetch_result = associated_data.fetch_future.get(); - tabs[i].body->clear_thumbnails(); tabs[i].body->items = std::move(fetch_result.body_items); tabs[i].body->select_first_item(); associated_data.fetched_page = 0; @@ -1924,6 +1922,7 @@ namespace QuickMedia { window.clear(back_color); page_loop_render(window, tabs, selected_tab, tab_associated_data[selected_tab], json_chapters, ui_tabs); + AsyncImageLoader::get_instance().update(); window.display(); if(!tabs[selected_tab].body->items.empty() && tabs[selected_tab].body->is_last_item_fully_visible()) @@ -2442,6 +2441,8 @@ namespace QuickMedia { break; } + AsyncImageLoader::get_instance().update(); + if(!video_loaded) { window.clear(back_color); load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); @@ -2697,6 +2698,7 @@ namespace QuickMedia { load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); window.draw(load_sprite); + AsyncImageLoader::get_instance().update(); window.display(); } } @@ -2952,6 +2954,7 @@ namespace QuickMedia { current_page = PageType::IMAGES; break; } + AsyncImageLoader::get_instance().update(); window.display(); int focused_page = image_viewer.get_focused_page(); @@ -3071,6 +3074,8 @@ namespace QuickMedia { }); }; + bool redraw = true; + Entry comment_input("Press i to begin writing a comment...", &rounded_rectangle_shader); comment_input.draw_background = false; comment_input.set_editable(false); @@ -3151,7 +3156,8 @@ namespace QuickMedia { sf::Vector2f logo_size(std::floor(plugin_logo.getSize().x * logo_sprite.getScale().x), std::floor(plugin_logo.getSize().y * logo_sprite.getScale().y)); sf::Sprite file_to_upload_sprite; - auto file_to_upload_thumbnail_data = std::make_shared(); + bool sprite_applied_texture = false; + std::shared_ptr file_to_upload_thumbnail_data; const float logo_file_to_upload_spacing = std::floor(10.0f * get_ui_scale()); @@ -3163,7 +3169,6 @@ namespace QuickMedia { sf::Vector2f body_pos; sf::Vector2f body_size; - bool redraw = true; sf::Event event; std::stack comment_navigation_stack; @@ -3408,22 +3413,28 @@ namespace QuickMedia { update_idle_state(); handle_window_close(); - if(!selected_file_for_upload.empty() && file_to_upload_thumbnail_data->loading_state == LoadingState::NOT_LOADED) - AsyncImageLoader::get_instance().load_thumbnail(selected_file_for_upload, true, sf::Vector2i(logo_size.x, logo_size.y * 4), file_to_upload_thumbnail_data); - - if(selected_file_for_upload.empty() && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { - file_to_upload_thumbnail_data = std::make_unique(); - redraw = true; + if(selected_file_for_upload.empty()) { + if(file_to_upload_thumbnail_data) { + file_to_upload_thumbnail_data.reset(); + redraw = true; + } + } else { + file_to_upload_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(selected_file_for_upload, true, sf::Vector2i(logo_size.x, logo_size.y * 4)); } - if(file_to_upload_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && file_to_upload_thumbnail_data->image->getSize().x > 0 && file_to_upload_thumbnail_data->image->getSize().y > 0) { + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && file_to_upload_thumbnail_data->image->getSize().x > 0 && file_to_upload_thumbnail_data->image->getSize().y > 0) { if(!file_to_upload_thumbnail_data->texture.loadFromImage(*file_to_upload_thumbnail_data->image)) fprintf(stderr, "Warning: failed to load texture for attached file\n"); file_to_upload_thumbnail_data->texture.setSmooth(true); //room_avatar_thumbnail_data->texture.generateMipmap(); file_to_upload_thumbnail_data->image.reset(); file_to_upload_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE; + sprite_applied_texture = false; + } + + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && !sprite_applied_texture) { file_to_upload_sprite.setTexture(file_to_upload_thumbnail_data->texture, true); + sprite_applied_texture = true; sf::Vector2f texture_size_f(file_to_upload_thumbnail_data->texture.getSize().x, file_to_upload_thumbnail_data->texture.getSize().y); sf::Vector2f image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, logo_size)); @@ -3432,7 +3443,7 @@ namespace QuickMedia { } float chat_input_height_full_images = logo_size.y; - if(file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { const float file_to_upload_height = std::floor(logo_file_to_upload_spacing + file_to_upload_sprite.getTexture()->getSize().y * file_to_upload_sprite.getScale().y); chat_input_height_full_images += file_to_upload_height; } @@ -3554,18 +3565,19 @@ namespace QuickMedia { } else if(navigation_stage == NavigationStage::REPLYING) { window.draw(comment_input_shade); window.draw(logo_sprite); - if(file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) window.draw(file_to_upload_sprite); comment_input.draw(window); thread_body->draw(window, body_pos, body_size); } else if(navigation_stage == NavigationStage::VIEWING_COMMENTS) { window.draw(comment_input_shade); window.draw(logo_sprite); - if(file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) window.draw(file_to_upload_sprite); comment_input.draw(window); thread_body->draw(window, body_pos, body_size); } + AsyncImageLoader::get_instance().update(); window.display(); } } @@ -3672,6 +3684,7 @@ namespace QuickMedia { inputs[i]->update(); inputs[i]->draw(window, background.get_size() - sf::Vector2f(padding_x * 2.0f, 0.0f), false); } + AsyncImageLoader::get_instance().update(); window.display(); } } @@ -4170,7 +4183,6 @@ namespace QuickMedia { std::string room_topic = current_room->get_topic(); room_name_text.setString(sf::String::fromUtf8(room_name.begin(), room_name.end())); room_topic_text.setString(sf::String::fromUtf8(room_topic.begin(), room_topic.end())); - room_avatar_thumbnail_data = std::make_shared(); read_marker_timeout_ms = 0; redraw = true; @@ -5215,8 +5227,8 @@ namespace QuickMedia { typing_state_queue.push(false); } - if(current_room && current_room->body_item && room_avatar_thumbnail_data->loading_state == LoadingState::NOT_LOADED) - AsyncImageLoader::get_instance().load_thumbnail(current_room->body_item->thumbnail_url, false, AVATAR_THUMBNAIL_SIZE, room_avatar_thumbnail_data); + if(!current_room->body_item->thumbnail_url.empty()) + room_avatar_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(current_room->body_item->thumbnail_url, false, AVATAR_THUMBNAIL_SIZE); if(room_avatar_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && room_avatar_thumbnail_data->image->getSize().x > 0 && room_avatar_thumbnail_data->image->getSize().y > 0) { if(!room_avatar_thumbnail_data->texture.loadFromImage(*room_avatar_thumbnail_data->image)) @@ -5225,8 +5237,10 @@ namespace QuickMedia { //room_avatar_thumbnail_data->texture.generateMipmap(); room_avatar_thumbnail_data->image.reset(); room_avatar_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE; - room_avatar_sprite.setTexture(room_avatar_thumbnail_data->texture, true); + } + if(room_avatar_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && !room_avatar_sprite.getTexture()) { + room_avatar_sprite.setTexture(room_avatar_thumbnail_data->texture, true); auto texture_size = room_avatar_sprite.getTexture()->getSize(); if(texture_size.x > 0 && texture_size.y > 0) { float width_ratio = (float)texture_size.x / (float)texture_size.y; @@ -5234,6 +5248,7 @@ namespace QuickMedia { float width_scale = height_scale * width_ratio; room_avatar_sprite.setScale(width_scale * get_ui_scale(), height_scale * get_ui_scale()); } + redraw = true; } const int selected_tab = ui_tabs.get_selected(); @@ -5549,6 +5564,7 @@ namespace QuickMedia { } } + AsyncImageLoader::get_instance().update(); window.display(); if(selected_tab == MESSAGES_TAB_INDEX) @@ -6158,6 +6174,7 @@ namespace QuickMedia { window.draw(status_text); window.draw(filename_text); window.draw(download_speed_text); + AsyncImageLoader::get_instance().update(); window.display(); frame_timer.restart(); } @@ -6233,7 +6250,6 @@ namespace QuickMedia { if(task_result == TaskResult::TRUE) { if(!new_tabs.empty()) { - file_manager_body->clear_thumbnails(); file_manager_body->items = std::move(new_tabs[0].body->items); file_manager_body->select_first_item(); search_bar->clear(); @@ -6371,6 +6387,7 @@ namespace QuickMedia { save_button.draw(window); file_name_entry.draw(window); + AsyncImageLoader::get_instance().update(); window.display(); } -- cgit v1.2.3