From fc49d40c0d2f6edbbe9dde1f1b53d6a17e9d9f7d Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 17 Oct 2021 05:53:22 +0200 Subject: Limit image loading to one thread in async image loader --- src/AsyncImageLoader.cpp | 138 ++++++++++++++++++++++++++++++++++------------- src/Body.cpp | 2 +- 2 files changed, 102 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 762408c..4661960 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -147,29 +147,33 @@ namespace QuickMedia { return true; } - // Create thumbnail and load it. On failure load the original image - static void create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) { + void AsyncImageLoader::load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) { FileAnalyzer file_analyzer; if(!file_analyzer.load_file(thumbnail_path.data.c_str(), false)) { - fprintf(stderr, "Failed to convert %s to a thumbnail, using the original image\n", thumbnail_path.data.c_str()); + fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str()); + thumbnail_data->image = std::make_unique(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return; } if(is_content_type_video(file_analyzer.get_content_type())) { - if(video_get_first_frame(thumbnail_path.data.c_str(), thumbnail_path_resized.data.c_str(), resize_target_size.x, resize_target_size.y)) - load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data); - thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + if(video_get_first_frame(thumbnail_path.data.c_str(), thumbnail_path_resized.data.c_str(), resize_target_size.x, resize_target_size.y)) { + thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; + } else { + fprintf(stderr, "Failed to get first frame of %s\n", thumbnail_path.data.c_str()); + thumbnail_data->image = std::make_unique(); + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + } return; } if(create_thumbnail(thumbnail_path, thumbnail_path_resized, resize_target_size, file_analyzer.get_content_type(), true)) { - load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data); + thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; } else { - load_image_from_file(*thumbnail_data->image, thumbnail_path.data); - fprintf(stderr, "Failed to convert %s to a thumbnail, using the original image\n", thumbnail_path.data.c_str()); + fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str()); + thumbnail_data->image = std::make_unique(); + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; } - thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; } AsyncImageLoader& AsyncImageLoader::get_instance() { @@ -179,7 +183,31 @@ namespace QuickMedia { return *instance; } - static void process_thumbnail(ThumbnailLoadData &thumbnail_load_data) { + void AsyncImageLoader::process_thumbnail(ThumbnailLoadData &thumbnail_load_data) { + Path thumbnail_path_resized = thumbnail_load_data.thumbnail_path; + if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) + thumbnail_path_resized.append("_" + std::to_string(thumbnail_load_data.resize_target_size.x) + "x" + std::to_string(thumbnail_load_data.resize_target_size.y)); + + if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) { + fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str()); + thumbnail_load_data.thumbnail_data->image = std::make_unique(); + thumbnail_load_data.thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; + return; + } + + Path thumbnail_original_path; + if(thumbnail_load_data.local) + thumbnail_original_path = thumbnail_load_data.path; + else + thumbnail_original_path = thumbnail_load_data.thumbnail_path; + + if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) + load_create_thumbnail(thumbnail_original_path, thumbnail_path_resized, thumbnail_load_data.thumbnail_data.get(), thumbnail_load_data.resize_target_size); + else + thumbnail_load_data.thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; + } + + static void load_processed_thumbnail(ThumbnailLoadData &thumbnail_load_data) { thumbnail_load_data.thumbnail_data->image = std::make_unique(); Path thumbnail_path_resized = thumbnail_load_data.thumbnail_path; @@ -189,7 +217,6 @@ namespace QuickMedia { if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) { load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data); thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; - fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str()); return; } @@ -199,12 +226,13 @@ namespace QuickMedia { else thumbnail_original_path = thumbnail_load_data.thumbnail_path; - if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) - create_thumbnail(thumbnail_original_path, thumbnail_path_resized, thumbnail_load_data.thumbnail_data.get(), thumbnail_load_data.resize_target_size); - else + if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) { + load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data); + thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + } else { load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_original_path.data); - - thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + } } AsyncImageLoader::AsyncImageLoader() { @@ -215,10 +243,26 @@ namespace QuickMedia { load_thread = AsyncTask([this]() mutable { std::optional thumbnail_load_data_opt; - while(image_load_queue.is_running()) { - thumbnail_load_data_opt = image_load_queue.pop_if_available(); - if(thumbnail_load_data_opt) + while(image_thumbnail_create_queue.is_running()) { + ThumbnailLoadData load_image_data; + { + std::lock_guard lock(image_load_mutex); + if(current_loading_image.thumbnail_data) { + load_image_data = std::move(current_loading_image); + current_loading_image = ThumbnailLoadData(); + } + } + + if(load_image_data.thumbnail_data) { + load_processed_thumbnail(load_image_data); + load_image_data = ThumbnailLoadData(); + } + + thumbnail_load_data_opt = image_thumbnail_create_queue.pop_if_available(); + if(thumbnail_load_data_opt) { process_thumbnail(thumbnail_load_data_opt.value()); + thumbnail_load_data_opt = std::nullopt; + } for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) { Download &download = downloads[i]; @@ -246,7 +290,7 @@ namespace QuickMedia { } AsyncImageLoader::~AsyncImageLoader() { - image_load_queue.close(); + image_thumbnail_create_queue.close(); } static bool download_file_async(const char *url, const char *save_filepath, ReadProgram *read_program) { @@ -260,19 +304,16 @@ namespace QuickMedia { return exec_program_pipe(args, read_program) == 0; } - void AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr thumbnail_data) { + bool AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr thumbnail_data, Path &thumbnail_path) { if(thumbnail_data->loading_state != LoadingState::NOT_LOADED) - return; + return true; if(url.empty()) { thumbnail_data->image = std::make_unique(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; - return; + return false; } - SHA256 sha256; - sha256.add(url.data(), url.size()); - Path thumbnail_path = get_cache_dir().join("thumbnails").join(sha256.getHash()); if(local) { struct stat file_stat; memset(&file_stat, 0, sizeof(file_stat)); @@ -280,24 +321,24 @@ namespace QuickMedia { fprintf(stderr, "Failed to load thumbnail %s: no such file\n", url.c_str()); thumbnail_data->image = std::make_unique(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; - return; + return false; } thumbnail_path.append("_" + std::to_string(file_stat.st_mtim.tv_sec)); thumbnail_data->loading_state = LoadingState::LOADING; - image_load_queue.push({ url, std::move(thumbnail_path), true, thumbnail_data, resize_target_size }); - return; + image_thumbnail_create_queue.push({ url, thumbnail_path, true, thumbnail_data, resize_target_size }); + return true; } if(get_file_type(thumbnail_path) == FileType::REGULAR) { thumbnail_data->loading_state = LoadingState::LOADING; - image_load_queue.push({ url, std::move(thumbnail_path), false, thumbnail_data, resize_target_size }); - return; + image_thumbnail_create_queue.push({ url, thumbnail_path, false, thumbnail_data, resize_target_size }); + return true; } int free_index = get_free_load_index(); if(free_index == -1) - return; + return false; std::lock_guard lock(download_mutex); thumbnail_data->loading_state = LoadingState::LOADING; @@ -305,14 +346,15 @@ namespace QuickMedia { tmp_thumbnail_path.append(".tmp"); if(!download_file_async(url.c_str(), tmp_thumbnail_path.data.c_str(), &downloads[free_index].read_program)) { fprintf(stderr, "Failed to start download of %s\n", url.c_str()); - return; + return false; } downloads[free_index].download_start = get_boottime_milliseconds(); - downloads[free_index].thumbnail_path = std::move(thumbnail_path); + downloads[free_index].thumbnail_path = thumbnail_path; downloads[free_index].thumbnail_data = thumbnail_data; downloads[free_index].resize_target_size = resize_target_size; downloads[free_index].url = url; + return true; } std::shared_ptr AsyncImageLoader::get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size) { @@ -321,7 +363,22 @@ namespace QuickMedia { if(!thumbnail_data) thumbnail_data = std::make_shared(); thumbnail_data->counter = counter; - load_thumbnail(url, local, resize_target_size, thumbnail_data); + + if(thumbnail_data->thumbnail_path.data.empty()) { + SHA256 sha256; + sha256.add(url.data(), url.size()); + thumbnail_data->thumbnail_path = get_cache_dir().join("thumbnails").join(sha256.getHash()); + } + + if(!load_thumbnail(url, local, resize_target_size, thumbnail_data, thumbnail_data->thumbnail_path)) + return thumbnail_data; + + if(thumbnail_data->loading_state == LoadingState::READY_TO_LOAD) { + std::lock_guard lock(image_load_mutex); + if(!current_loading_image.thumbnail_data) + current_loading_image = { url, thumbnail_data->thumbnail_path, local, thumbnail_data, resize_target_size }; + } + return thumbnail_data; } @@ -342,9 +399,16 @@ namespace QuickMedia { } } - image_load_queue.erase_if([&it](ThumbnailLoadData &load_data) { + image_thumbnail_create_queue.erase_if([&it](ThumbnailLoadData &load_data) { return load_data.path.data == it->first; }); + + { + std::lock_guard lock(image_load_mutex); + if(current_loading_image.thumbnail_data == it->second) + current_loading_image = ThumbnailLoadData(); + } + it = thumbnails.erase(it); loaded_textures_changed = true; } else { diff --git a/src/Body.cpp b/src/Body.cpp index d4b6c5c..eb7a1be 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -1070,7 +1070,7 @@ namespace QuickMedia { std::shared_ptr &item = items[index]; assert(item->visible); - int prev_index; + int prev_index = 0; if(attach_side == AttachSide::BOTTOM) { prev_index = get_previous_visible_item(index); if(prev_index == -1) -- cgit v1.2.3