From 9866713ba916f9768edca02c61ed5ec580bd9557 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 27 Sep 2020 01:08:34 +0200 Subject: Reduce scroll cpu usage from 10% to 1-2% by load image files in another thread but load the texture in the main thread --- src/Body.cpp | 117 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 66 insertions(+), 51 deletions(-) (limited to 'src/Body.cpp') diff --git a/src/Body.cpp b/src/Body.cpp index 25a7bc3..477591e 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -86,6 +86,11 @@ namespace QuickMedia { item_background.setFillColor(sf::Color(55, 60, 68)); } + Body::~Body() { + if(load_thumbnail_future.valid()) + load_thumbnail_future.get(); + } + bool Body::select_previous_item() { if(items.empty()) return false; @@ -292,65 +297,49 @@ namespace QuickMedia { // TODO: Do not load thumbnails for images larger than 30mb. // TODO: Load the thumbnail embedded in the file instead. - std::shared_ptr Body::load_thumbnail_from_url(const std::string &url, bool local, sf::Vector2i thumbnail_resize_target_size) { - auto result = std::make_shared(); - result->setSmooth(true); + void Body::load_thumbnail_from_url(const std::string &url, bool local, sf::Vector2i thumbnail_resize_target_size, std::shared_ptr thumbnail_data) { assert(!loading_thumbnail); loading_thumbnail = true; - thumbnail_load_thread = std::thread([this, result, url, local, thumbnail_resize_target_size]() { + + load_thumbnail_future = std::async(std::launch::async, [this, url, local, thumbnail_resize_target_size, thumbnail_data]() { // TODO: Use sha256 instead of base64_url encoding Path thumbnail_path = get_cache_dir().join("thumbnails").join(base64_url::encode(url)); - std::string texture_data; - if(file_get_content(thumbnail_path, texture_data) == 0) { + thumbnail_data->image = std::make_unique(); + if(thumbnail_data->image->loadFromFile(thumbnail_path.data)) { fprintf(stderr, "Loaded %s from thumbnail cache\n", url.c_str()); - result->loadFromMemory(texture_data.data(), texture_data.size()); - loading_thumbnail = false; + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return; } else { if(local) { - if(file_get_content(url, texture_data) != 0) { - loading_thumbnail = false; + if(!thumbnail_data->image->loadFromFile(url)) { + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return; } } else { - if(download_to_string_cache(url, texture_data, {}, program->get_current_plugin()->use_tor, true) != DownloadResult::OK) { - loading_thumbnail = false; + std::string texture_data; + if(download_to_string_cache(url, texture_data, {}, program->get_current_plugin()->use_tor, true) != DownloadResult::OK || !thumbnail_data->image->loadFromMemory(texture_data.data(), texture_data.size())) { + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return; } } } if(thumbnail_resize_target_size.x != 0 && thumbnail_resize_target_size.y != 0) { - auto image = std::make_unique(); - // TODO: Load from file instead? decreases ram usage and we save to file above anyways - if(image->loadFromMemory(texture_data.data(), texture_data.size())) { - texture_data.resize(0); - sf::Vector2u new_image_size = to_vec2u(clamp_to_size(to_vec2f(image->getSize()), to_vec2f(thumbnail_resize_target_size))); - if(new_image_size.x < image->getSize().x || new_image_size.y < image->getSize().y) { - sf::Image destination_image; - copy_resize(*image, destination_image, new_image_size); - if(save_image_as_thumbnail_atomic(destination_image, thumbnail_path, get_ext(url))) { - image.reset(); - result->loadFromImage(destination_image); - } else { - result->loadFromImage(*image); - } - loading_thumbnail = false; - return; - } else { - result->loadFromImage(*image); - loading_thumbnail = false; - return; - } + sf::Vector2u new_image_size = to_vec2u(clamp_to_size(to_vec2f(thumbnail_data->image->getSize()), to_vec2f(thumbnail_resize_target_size))); + if(new_image_size.x < thumbnail_data->image->getSize().x || new_image_size.y < thumbnail_data->image->getSize().y) { + auto destination_image = std::make_unique(); + copy_resize(*thumbnail_data->image, *destination_image, new_image_size); + thumbnail_data->image = std::move(destination_image); + save_image_as_thumbnail_atomic(*thumbnail_data->image, thumbnail_path, get_ext(url)); + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; } } - result->loadFromMemory(texture_data.data(), texture_data.size()); - loading_thumbnail = false; + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; }); - thumbnail_load_thread.detach(); - return result; } void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) { @@ -376,10 +365,15 @@ namespace QuickMedia { image_fallback.setSize(thumbnail_fallback_size); item_background_shadow.setFillColor(line_seperator_color); + if(loading_thumbnail && load_thumbnail_future.valid() && load_thumbnail_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { + load_thumbnail_future.get(); + loading_thumbnail = false; + } + int num_items = items.size(); if(num_items == 0 || size.y <= 0.0f) { for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) { - if(!it->second.referenced) + if(!it->second->referenced) it = item_thumbnail_textures.erase(it); else ++it; @@ -388,7 +382,7 @@ namespace QuickMedia { } for(auto &thumbnail_it : item_thumbnail_textures) { - thumbnail_it.second.referenced = false; + thumbnail_it.second->referenced = false; } for(auto &body_item : items) { @@ -503,7 +497,7 @@ namespace QuickMedia { glDisable(GL_SCISSOR_TEST); for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) { - if(!it->second.referenced) + if(!it->second->referenced) it = item_thumbnail_textures.erase(it); else ++it; @@ -512,14 +506,27 @@ 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) { // TODO: Instead of generating a new hash everytime to access textures, cache the hash of the thumbnail url - // Intentionally create the item with the key item->thumbnail_url if it doesn't exist - item_thumbnail_textures[item->thumbnail_url].referenced = true; - auto &item_thumbnail = item_thumbnail_textures[item->thumbnail_url]; + std::shared_ptr item_thumbnail; + 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) { - if(!item->thumbnail_url.empty() && !loading_thumbnail && !item_thumbnail.loaded && !item_thumbnail.texture) { - item_thumbnail.loaded = true; - item_thumbnail.texture = load_thumbnail_from_url(item->thumbnail_url, item->thumbnail_is_local, thumbnail_resize_target_size); + if(!loading_thumbnail && !item->thumbnail_url.empty() && item_thumbnail->loading_state == LoadingState::NOT_LOADED) { + item_thumbnail->loading_state = LoadingState::LOADING; + load_thumbnail_from_url(item->thumbnail_url, item->thumbnail_is_local, thumbnail_resize_target_size, item_thumbnail); + } + + if(item_thumbnail->loading_state == LoadingState::FINISHED_LOADING) { + 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->image.reset(); + item_thumbnail->loading_state = LoadingState::APPLIED_TO_TEXTURE; } } @@ -541,8 +548,8 @@ namespace QuickMedia { if(draw_thumbnails) { // TODO: Verify if this is safe. The thumbnail is being modified in another thread // and it might not be fully finished before the native handle is set? - if(item_thumbnail.loaded && item_thumbnail.texture && item_thumbnail.texture->getNativeHandle() != 0) { - image.setTexture(*item_thumbnail.texture, true); + 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(); auto height_ratio = std::min(image_max_height, (float)image_size.y) / image_size.y; auto scale = image.getScale(); @@ -628,10 +635,18 @@ namespace QuickMedia { item_height += item->description_text->getHeight() - 2.0f; } if(draw_thumbnails && !item->thumbnail_url.empty()) { - auto &item_thumbnail = item_thumbnail_textures[item->thumbnail_url]; + std::shared_ptr item_thumbnail; + 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; + } + float image_height = image_fallback.getSize().y; - if(item_thumbnail.texture && item_thumbnail.texture->getNativeHandle() != 0) { - auto image_size = item_thumbnail.texture->getSize(); + if(item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { + auto image_size = item_thumbnail->texture.getSize(); image_height = std::min(image_max_height, (float)image_size.y); } item_height = std::max(item_height, image_height); -- cgit v1.2.3