diff options
author | Jeroen Bakker <jeroen@blender.org> | 2022-05-27 15:46:05 +0300 |
---|---|---|
committer | Jeroen Bakker <jeroen@blender.org> | 2022-05-27 15:46:05 +0300 |
commit | ae68c53b15912669f432b93bfd6edbe4d043a897 (patch) | |
tree | 0e3f327641c3c90b5b6409e3d4c8abd2a1490de9 | |
parent | f41c7723c93bc9e784634887da8b682787729538 (diff) |
Base implementation to share textures between mutliple image data-blocks.
-rw-r--r-- | source/blender/blenkernel/BKE_image.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/image.cc | 62 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/image_gpu.cc | 222 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.cc | 4 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_image_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_image.c | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_image_api.c | 12 |
7 files changed, 176 insertions, 135 deletions
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index b6607a92c14..8f617cce7f7 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -326,11 +326,6 @@ bool BKE_image_scale(struct Image *image, int width, int height); bool BKE_image_has_alpha(struct Image *image); /** - * Check if texture has GPU texture code. - */ -bool BKE_image_has_opengl_texture(struct Image *ima); - -/** * Get tile index for tiled images. */ void BKE_image_get_tile_label(struct Image *ima, @@ -525,7 +520,7 @@ void BKE_image_update_gputexture_delayed(struct Image *ima, * temporary disabling/enabling mipmapping on all images for quick texture * updates with glTexSubImage2D. images that didn't change don't have to be re-uploaded to OpenGL. */ -void BKE_image_paint_set_mipmap(struct Main *bmain, bool mipmap); +void BKE_image_paint_set_mipmap(bool mipmap); /** * Delayed free of OpenGL buffers by main thread. diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index bc21e706d1e..bf00eb7ab7a 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -180,12 +180,6 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c BLI_duplicatelist(&image_dst->tiles, &image_src->tiles); - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - image_dst->gputexture[i][eye] = nullptr; - } - } - if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { BKE_previewimg_id_copy(&image_dst->id, &image_src->id); } @@ -234,6 +228,7 @@ static void image_foreach_cache(ID *id, key.offset_in_ID = offsetof(Image, cache); function_callback(id, &key, (void **)&image->cache, 0, user_data); +#if 0 auto gputexture_offset = [image](int target, int eye) { constexpr size_t base_offset = offsetof(Image, gputexture); struct GPUTexture **first = &image->gputexture[0][0]; @@ -241,6 +236,7 @@ static void image_foreach_cache(ID *id, return base_offset + array_offset; }; +// TODO(jbakker): need to check how this is actually used not the GPU textures are shared we might need to reinvalidate this. for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { GPUTexture *texture = image->gputexture[a][eye]; @@ -251,6 +247,7 @@ static void image_foreach_cache(ID *id, function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data); } } +#endif key.offset_in_ID = offsetof(Image, rr); function_callback(id, &key, (void **)&image->rr, 0, user_data); @@ -328,11 +325,6 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLI_listbase_clear(&ima->anims); ima->runtime.partial_update_register = nullptr; ima->runtime.partial_update_user = nullptr; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 2; j++) { - ima->gputexture[i][j] = nullptr; - } - } ImagePackedFile *imapf; @@ -773,18 +765,6 @@ bool BKE_image_scale(Image *image, int width, int height) return (ibuf != nullptr); } -bool BKE_image_has_opengl_texture(Image *ima) -{ - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != nullptr) { - return true; - } - } - } - return false; -} - static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser) { BLI_assert(ima != nullptr && ima->tiles.first); @@ -2847,20 +2827,6 @@ void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) static void image_free_tile(Image *ima, ImageTile *tile) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other - * two. */ - if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) { - continue; - } - - for (int eye = 0; eye < 2; eye++) { - if (ima->gputexture[i][eye] != nullptr) { - GPU_texture_free(ima->gputexture[i][eye]); - ima->gputexture[i][eye] = nullptr; - } - } - } BKE_image_partial_update_mark_full_update(ima); if (BKE_image_is_multiview(ima)) { @@ -3196,17 +3162,6 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la BLI_strncpy(tile->label, label, sizeof(tile->label)); } - for (int eye = 0; eye < 2; eye++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye] = nullptr; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = nullptr; - } - } BKE_image_partial_update_mark_full_update(ima); return tile; @@ -3259,17 +3214,6 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu IMB_freeImBuf(ibuf); } - for (int eye = 0; eye < 2; eye++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye] = nullptr; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = nullptr; - } - } BKE_image_partial_update_mark_full_update(ima); } diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index bed79a318e8..3e0e0414afa 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -11,7 +11,9 @@ #include "BLI_boxpack_2d.h" #include "BLI_linklist.h" #include "BLI_listbase.h" +#include "BLI_map.hh" #include "BLI_threads.h" +#include "BLI_utility_mixins.hh" #include "DNA_image_types.h" #include "DNA_userdef_types.h" @@ -33,13 +35,133 @@ using namespace blender::bke::image::partial_update; +namespace blender::bke::image::gpu { +struct ImageGPUTextureStore { + class Entry : NonCopyable { + public: + GPUTexture *gputextures[TEXTARGET_COUNT][2]; + struct { + bool in_use : 1; + } flags; + + Entry() + { + for (int target = 0; target < TEXTARGET_COUNT; target++) { + for (int eye = 0; eye < 2; eye++) { + gputextures[target][eye] = nullptr; + } + } + } + + Entry(Entry &&other) + { + for (int target = 0; target < TEXTARGET_COUNT; target++) { + for (int eye = 0; eye < 2; eye++) { + gputextures[target][eye] = other.gputextures[target][eye]; + other.gputextures[target][eye] = nullptr; + } + } + flags.in_use = other.flags.in_use; + }; + + virtual ~Entry() + { + clear(); + } + + void set_mipmap(const bool mipmap) + { + for (int eye = 0; eye < 2; eye++) { + if (gputextures[TEXTARGET_2D][eye]) { + GPU_texture_mipmap_mode(gputextures[TEXTARGET_2D][eye], mipmap, true); + } + if (gputextures[TEXTARGET_2D_ARRAY][eye]) { + GPU_texture_mipmap_mode(gputextures[TEXTARGET_2D_ARRAY][eye], mipmap, true); + } + } + } + + void tag_used() + { + flags.in_use = true; + } + + void clear() + { + for (int target = 0; target < TEXTARGET_COUNT; target++) { + for (int eye = 0; eye < 2; eye++) { + GPU_TEXTURE_FREE_SAFE(gputextures[target][eye]); + } + } + } + }; + + using Entries = Map<std::string, Entry>; + + Entries entries; + + void reset_usage() + { + for (Entry &entry : entries.values()) { + entry.flags.in_use = false; + } + } + + void remove_unused() + { + for (auto it : entries.items()) { + if (it.value.flags.in_use) { + continue; + } + entries.remove(it.key); + } + } + + void set_mipmap(const bool mipmap) + { + for (Entry &entry : entries.values()) { + entry.set_mipmap(mipmap); + } + } + + void clear() + { + entries.clear(); + } + + std::string create_key(const Image &image) const + { + std::stringstream result; + result << "ID:" << image.id.name; + return result.str(); + } + + Entry &operator[](Image &image) + { + std::string key = create_key(image); + return entries.lookup_or_add_default(key); + } +}; + +static ImageGPUTextureStore g_texture_store; + +} // namespace blender::bke::image::gpu + extern "C" { +using namespace blender::bke::image::gpu; + /* Prototypes. */ static void gpu_free_unused_buffers(); static void image_free_gpu(Image *ima, const bool immediate); -static void image_update_gputexture_ex( - Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); +static void image_update_gputexture_ex(Image *ima, + ImageGPUTextureStore::Entry &entry, + ImageTile *tile, + ImBuf *ibuf, + int x, + int y, + int w, + int h); bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) { @@ -77,9 +199,11 @@ static int smaller_power_of_2_limit(int num) return power_of_2_min_i(GPU_texture_size_with_limit(num)); } -static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) +static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, + ImageGPUTextureStore::Entry &entry, + const int multiview_eye) { - GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; + GPUTexture *tilearray = entry.gputextures[TEXTARGET_2D_ARRAY][multiview_eye]; if (tilearray == nullptr) { return nullptr; @@ -249,7 +373,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) /** \name Regular gpu texture * \{ */ -static GPUTexture **get_image_gpu_texture_ptr(Image *ima, +static GPUTexture **get_image_gpu_texture_ptr(ImageGPUTextureStore::Entry &entry, eGPUTextureTarget textarget, const int multiview_eye) { @@ -258,7 +382,7 @@ static GPUTexture **get_image_gpu_texture_ptr(Image *ima, BLI_assert(ELEM(multiview_eye, 0, 1)); if (in_range) { - return &(ima->gputexture[textarget][multiview_eye]); + return &entry.gputextures[textarget][multiview_eye]; } return nullptr; } @@ -278,7 +402,9 @@ static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) } static void image_gpu_texture_partial_update_changes_available( - Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) + Image *image, + ImageGPUTextureStore::Entry &entry, + PartialUpdateChecker<ImageTileData>::CollectResult &changes) { while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { /* Calculate the clipping region with the tile buffer. @@ -294,6 +420,7 @@ static void image_gpu_texture_partial_update_changes_available( } image_update_gputexture_ex(image, + entry, changes.tile_data.tile, changes.tile_data.tile_buffer, clipped_update_region.xmin, @@ -307,14 +434,15 @@ static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser) { PartialUpdateChecker<ImageTileData> checker(image, iuser, image->runtime.partial_update_user); PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes(); + ImageGPUTextureStore::Entry &entry = g_texture_store[*image]; switch (changes.get_result_code()) { case ePartialUpdateCollectResult::FullUpdateNeeded: { - image_free_gpu(image, true); + entry.clear(); break; } case ePartialUpdateCollectResult::PartialChangesDetected: { - image_gpu_texture_partial_update_changes_available(image, changes); + image_gpu_texture_partial_update_changes_available(image, entry, changes); break; } @@ -371,7 +499,8 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (current_view >= 2) { current_view = 0; } - GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); + ImageGPUTextureStore::Entry &entry = g_texture_store[*ima]; + GPUTexture **tex = get_image_gpu_texture_ptr(entry, textarget, current_view); if (*tex) { return *tex; } @@ -398,7 +527,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, *tex = gpu_texture_create_tile_array(ima, ibuf_intern); } else if (textarget == TEXTARGET_TILE_MAPPING) { - *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); + *tex = gpu_texture_create_tile_mapping(ima, entry, iuser ? iuser->multiview_eye : 0); } else { const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); @@ -491,8 +620,10 @@ void BKE_image_free_unused_gpu_textures() /** \name Deletion * \{ */ -static void image_free_gpu(Image *ima, const bool immediate) +static void image_free_gpu(Image *UNUSED(ima), const bool UNUSED(immediate)) { +// TODO(jbakker)... +#if 0 for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { if (ima->gputexture[i][eye] != nullptr) { @@ -511,6 +642,7 @@ static void image_free_gpu(Image *ima, const bool immediate) } ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; +#endif } void BKE_image_free_gputextures(Image *ima) @@ -518,13 +650,9 @@ void BKE_image_free_gputextures(Image *ima) image_free_gpu(ima, BLI_thread_is_main()); } -void BKE_image_free_all_gputextures(Main *bmain) +void BKE_image_free_all_gputextures(Main *UNUSED(bmain)) { - if (bmain) { - LISTBASE_FOREACH (Image *, ima, &bmain->images) { - BKE_image_free_gputextures(ima); - } - } + blender::bke::image::gpu::g_texture_store.clear(); } void BKE_image_free_anim_gputextures(Main *bmain) @@ -538,8 +666,9 @@ void BKE_image_free_anim_gputextures(Main *bmain) } } -void BKE_image_free_old_gputextures(Main *bmain) +void BKE_image_free_old_gputextures(Main *UNUSED(bmain)) { + static int lasttime = 0; int ctime = (int)PIL_check_seconds_timer(); @@ -558,20 +687,8 @@ void BKE_image_free_old_gputextures(Main *bmain) lasttime = ctime; - LISTBASE_FOREACH (Image *, ima, &bmain->images) { - if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { - /* If it's in GL memory, deallocate and set time tag to current time - * This gives textures a "second chance" to be used before dying. */ - if (BKE_image_has_opengl_texture(ima)) { - BKE_image_free_gputextures(ima); - ima->lastused = ctime; - } - /* Otherwise, just kill the buffers */ - else { - BKE_image_free_buffers(ima); - } - } - } + blender::bke::image::gpu::g_texture_store.remove_unused(); + blender::bke::image::gpu::g_texture_store.reset_usage(); } /** \} */ @@ -802,18 +919,24 @@ static void gpu_texture_update_from_ibuf( GPU_texture_unbind(tex); } -static void image_update_gputexture_ex( - Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h) +static void image_update_gputexture_ex(Image *ima, + ImageGPUTextureStore::Entry &entry, + ImageTile *tile, + ImBuf *ibuf, + int x, + int y, + int w, + int h) { const int eye = 0; - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye]; + GPUTexture *tex = entry.gputextures[TEXTARGET_2D][eye]; /* Check if we need to update the main gputexture. */ if (tex != nullptr && tile == ima->tiles.first) { gpu_texture_update_from_ibuf(tex, ima, ibuf, nullptr, x, y, w, h); } /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye]; + tex = entry.gputextures[TEXTARGET_2D_ARRAY][eye]; if (tex != nullptr) { gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } @@ -847,30 +970,9 @@ void BKE_image_update_gputexture_delayed(struct Image *ima, } } -void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) +void BKE_image_paint_set_mipmap(bool mipmap) { - LISTBASE_FOREACH (Image *, ima, &bmain->images) { - if (BKE_image_has_opengl_texture(ima)) { - if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { - for (int eye = 0; eye < 2; eye++) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != nullptr) { - GPU_texture_mipmap_mode(tex, mipmap, true); - } - } - } - } - } - else { - BKE_image_free_gputextures(ima); - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } + blender::bke::image::gpu::g_texture_store.set_mipmap(mipmap); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index a313489885d..d5da6f00494 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -801,7 +801,7 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); } - BKE_image_paint_set_mipmap(bmain, false); + BKE_image_paint_set_mipmap(false); toggle_paint_cursor(scene, true); @@ -826,7 +826,7 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob) if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); } - BKE_image_paint_set_mipmap(bmain, true); + BKE_image_paint_set_mipmap(true); toggle_paint_cursor(scene, false); Mesh *me = BKE_mesh_from_object(ob); diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 6e4e515a0fe..beef410d362 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -138,7 +138,7 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ - struct GPUTexture *gputexture[3][2]; + // struct GPUTexture *gputexture[3][2]; /* sources from: */ ListBase anims; diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 269ebe1581f..703932f6a59 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -397,7 +397,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; + GPUTexture *tex = BKE_image_get_gpu_texture(ima, NULL, NULL); return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index bac8f214441..6f742284616 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -191,16 +191,16 @@ static int rna_Image_gl_load( return 0; /* GL_NO_ERROR */ } -static int rna_Image_gl_touch( - Image *image, ReportList *reports, int frame, int layer_index, int pass_index) +static int rna_Image_gl_touch(Image *image, + ReportList *reports, + int UNUSED(frame), + int UNUSED(layer_index), + int UNUSED(pass_index)) { int error = 0; /* GL_NO_ERROR */ BKE_image_tag_time(image); - - if (image->gputexture[TEXTARGET_2D][0] == NULL) { - error = rna_Image_gl_load(image, reports, frame, layer_index, pass_index); - } + BKE_image_partial_update_mark_full_update(image); return error; } |