Commit: 16ab6111f758ef77bd0e53ce2379ee8281629776 Author: Julian Eisel Date: Fri Feb 18 18:11:16 2022 +0100 Branches: master https://developer.blender.org/rB16ab6111f758ef77bd0e53ce2379ee8281629776
UI: Speedup preview icon loading from hard drive Significantly improves loading speed of preview images from disk, e.g. custom previews loaded using `bpy.utils.previews.ImagePreviewCollection.load()`. See D14144 for details & comparison videos. Differential Revision: https://developer.blender.org/D14144 Reviewed by: Bastien Montagne =================================================================== M source/blender/editors/include/ED_render.h M source/blender/editors/interface/interface_icons.c M source/blender/editors/render/render_preview.cc M source/blender/windowmanager/WM_api.h =================================================================== diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index e1b6a935d6d..ee40cc75e1e 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -24,6 +24,7 @@ struct Scene; struct ScrArea; struct bContext; struct bScreen; +struct PreviewImage; struct wmWindow; struct wmWindowManager; @@ -87,16 +88,13 @@ void ED_preview_shader_job(const struct bContext *C, ePreviewRenderMethod method); void ED_preview_icon_render(const struct bContext *C, struct Scene *scene, + struct PreviewImage *prv_img, struct ID *id, - unsigned int *rect, - int sizex, - int sizey); + enum eIconSizes icon_size); void ED_preview_icon_job(const struct bContext *C, - void *owner, + struct PreviewImage *prv_img, struct ID *id, - unsigned int *rect, - int sizex, - int sizey, + enum eIconSizes icon_size, bool delay); void ED_preview_restart_queue_free(void); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 630f83cf828..e99f6978f4c 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1399,19 +1399,17 @@ static void icon_set_image(const bContext *C, const bool delay = prv_img->rect[size] != NULL; icon_create_rect(prv_img, size); - prv_img->flag[size] |= PRV_RENDERING; if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) { /* Job (background) version */ - ED_preview_icon_job( - C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay); + ED_preview_icon_job(C, prv_img, id, size, delay); } else { if (!scene) { scene = CTX_data_scene(C); } /* Immediate version */ - ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]); + ED_preview_icon_render(C, scene, prv_img, id, size); } } diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 6aaf551a88a..eca30a6ac25 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -10,6 +10,7 @@ #include <cmath> #include <cstdlib> #include <cstring> +#include <list> #ifndef WIN32 # include <unistd.h> @@ -1370,89 +1371,73 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat ShaderPreview *sp = static_cast<ShaderPreview *>(customdata); if (sp->pr_method == PR_ICON_DEFERRED) { - PreviewImage *prv = static_cast<PreviewImage *>(sp->owner); - ImBuf *thumb; - char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(prv)); - ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); - char *path = &deferred_data[1]; - - // printf("generating deferred %d×%d preview for %s\n", sp->sizex, sp->sizey, path); - - thumb = IMB_thumb_manage(path, THB_LARGE, source); - - if (thumb) { - /* PreviewImage assumes premultiplied alhpa... */ - IMB_premultiply_alpha(thumb); - - icon_copy_rect(thumb, sp->sizex, sp->sizey, sp->pr_rect); - IMB_freeImBuf(thumb); - } + BLI_assert_unreachable(); + return; } - else { - ID *id = sp->id; - short idtype = GS(id->name); - BLI_assert(id != nullptr); - - if (idtype == ID_IM) { - Image *ima = (Image *)id; - ImBuf *ibuf = nullptr; - ImageUser iuser; - BKE_imageuser_default(&iuser); + ID *id = sp->id; + short idtype = GS(id->name); - if (ima == nullptr) { - return; - } + BLI_assert(id != nullptr); - /* setup dummy image user */ - iuser.framenr = 1; - iuser.scene = sp->scene; - - /* NOTE(@elubie): this needs to be changed: here image is always loaded if not - * already there. Very expensive for large images. Need to find a way to - * only get existing `ibuf`. */ - ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr); - if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { - BKE_image_release_ibuf(ima, ibuf, nullptr); - return; - } + if (idtype == ID_IM) { + Image *ima = (Image *)id; + ImBuf *ibuf = nullptr; + ImageUser iuser; + BKE_imageuser_default(&iuser); - icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); + if (ima == nullptr) { + return; + } - *do_update = true; + /* setup dummy image user */ + iuser.framenr = 1; + iuser.scene = sp->scene; + /* NOTE(@elubie): this needs to be changed: here image is always loaded if not + * already there. Very expensive for large images. Need to find a way to + * only get existing `ibuf`. */ + ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr); + if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { BKE_image_release_ibuf(ima, ibuf, nullptr); + return; } - else if (idtype == ID_BR) { - Brush *br = (Brush *)id; - br->icon_imbuf = icon_preview_imbuf_from_brush(br); + icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); - memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint)); + *do_update = true; - if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) { - return; - } + BKE_image_release_ibuf(ima, ibuf, nullptr); + } + else if (idtype == ID_BR) { + Brush *br = (Brush *)id; - icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + br->icon_imbuf = icon_preview_imbuf_from_brush(br); - *do_update = true; - } - else if (idtype == ID_SCR) { - bScreen *screen = (bScreen *)id; + memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint)); - ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect); - *do_update = true; + if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) { + return; } - else { - /* re-use shader job */ - shader_preview_startjob(customdata, stop, do_update); - /* world is rendered with alpha=0, so it wasn't displayed - * this could be render option for sky to, for later */ - if (idtype == ID_WO) { - set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); - } + icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + + *do_update = true; + } + else if (idtype == ID_SCR) { + bScreen *screen = (bScreen *)id; + + ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect); + *do_update = true; + } + else { + /* re-use shader job */ + shader_preview_startjob(customdata, stop, do_update); + + /* world is rendered with alpha=0, so it wasn't displayed + * this could be render option for sky to, for later */ + if (idtype == ID_WO) { + set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); } } } @@ -1670,6 +1655,197 @@ static void icon_preview_endjob(void *customdata) } } +/** + * Background job to manage requests for deferred loading of previews from the hard drive. + * + * Launches a single job to manage all incoming preview requests. The job is kept running until all + * preview requests are done loading (or it's otherwise aborted, e.g. by closing Blender). + * + * Note that this will use the OS thumbnail cache, i.e. load a preview from there or add it if not + * there yet. These two cases may lead to different performance. + */ +class PreviewLoadJob { + struct RequestedPreview { + PreviewImage *preview; + /** Requested size. */ + eIconSizes icon_size; + }; + + /** The previews that are still to be loaded. */ + ThreadQueue *todo_queue_; /* RequestedPreview * */ + /** All unfinished preview requests, #update_fn() calls #finish_preview_request() on loaded + * previews and removes them from this list. Only access from the main thread! */ + std::list<struct RequestedPreview> requested_previews_; + + public: + PreviewLoadJob(); + ~PreviewLoadJob(); + + static PreviewLoadJob &ensure_job(wmWindowManager *, wmWindow *); + static void load_jobless(PreviewImage *, eIconSizes); + + void push_load_request(PreviewImage *, eIconSizes); + + private: + static void run_fn(void *, short *, short *, float *); + static void update_fn(void *); + static void end_fn(void *); + static void free_fn(void *); + + /** Mark a single requested preview as being done, remove the request. */ + static void finish_request(RequestedPreview &); +}; + +PreviewLoadJob::PreviewLoadJob() : todo_queue_(BLI_thread_queue_init()) +{ +} + +PreviewLoadJob::~PreviewLoadJob() +{ + BLI_thread_queue_free(todo_queue_); +} + +PreviewLoadJob &PreviewLoadJob::ensure_job(wmWindowManager *wm, wmWindow *win) +{ + wmJob *wm_job = WM_jobs_get(wm, win, nullptr, "Load Previews", 0, WM_JOB_TYPE_LOAD_PREVIEW); + + if (!WM_jobs_is_running(wm_job)) { + PreviewLoadJob *job_data = MEM_new<PreviewLoadJob>("PreviewLoadJobData"); + + WM_jobs_customdata_set(wm_job, job_data, free_fn); + WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); + WM_jobs_callbacks(wm_job, run_fn, nullptr, update_fn, end_fn); + + WM_jobs_start(wm, wm_job); + } + + return *reinterpret_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job)); +} + +void PreviewLoadJob::load_jobless(PreviewImage *preview, const eIconSizes icon_size) +{ + PreviewLoadJob job_data{}; + + job_data.push_load_request(preview, icon_size); + + short stop = 0, do_update = 0; + float progress = 0; + run_fn(&job_data, &stop, &do_update, &progress); + update_fn(&job_data); + end_fn(&job_data); +} + +void PreviewLoadJob::push_load_request(PreviewImage *preview, const eIconSizes icon_size) +{ + BLI_assert(preview->tag & PRV_TAG_DEFFERED); + RequestedPreview requested_preview{}; + requested_preview.preview = preview; + requested_prev @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list [email protected] List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs
