diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 02e2486fb9c..de376599de4 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -2971,8 +2971,23 @@ void RendererCanvasRenderRD::_uniform_set_invalidation_callback(void *p_userdata static_cast(singleton)->rid_set_to_uniform_set.erase(*key); } +void RendererCanvasRenderRD::_canvas_texture_invalidation_callback(bool p_deleted, void *p_userdata) { + KeyValue> *kv = static_cast> *>(p_userdata); + RD *rd = RD::get_singleton(); + for (RID rid : kv->value) { + // the invalidation callback will take care of clearing rid_set_to_uniform_set cache also + rd->free(rid); + } + kv->value.clear(); + if (p_deleted) { + static_cast(singleton)->canvas_texture_to_uniform_set.erase(kv->key); + } +} + void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, CanvasShaderData *p_shader_data, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info) { { + RendererRD::TextureStorage *ts = RendererRD::TextureStorage::get_singleton(); + RIDSetKey key( p_batch->tex_info->state, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]); @@ -2992,6 +3007,19 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, CanvasSha const RIDCache::Pair *iter = rid_set_to_uniform_set.insert(key, rid); uniform_set = &iter->data; RD::get_singleton()->uniform_set_set_invalidation_callback(rid, RendererCanvasRenderRD::_uniform_set_invalidation_callback, (void *)&iter->key); + + // If this is a CanvasTexture, it must be tracked so that any changes to the diffuse, normal + // or specular channels invalidate all associated uniform sets. + if (ts->owns_canvas_texture(p_batch->tex_info->state.texture)) { + KeyValue> *kv = nullptr; + if (HashMap>::Iterator i = canvas_texture_to_uniform_set.find(p_batch->tex_info->state.texture); i == canvas_texture_to_uniform_set.end()) { + kv = &*canvas_texture_to_uniform_set.insert(p_batch->tex_info->state.texture, { *uniform_set }); + } else { + i->value.push_back(rid); + kv = &*i; + } + ts->canvas_texture_set_invalidation_callback(p_batch->tex_info->state.texture, RendererCanvasRenderRD::_canvas_texture_invalidation_callback, kv); + } } if (state.current_batch_uniform_set != *uniform_set) { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 3b071cd804a..0605b18ba44 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -485,9 +485,14 @@ class RendererCanvasRenderRD : public RendererCanvasRender { static void _before_evict(RendererCanvasRenderRD::RIDSetKey &p_key, RID &p_rid); static void _uniform_set_invalidation_callback(void *p_userdata); + static void _canvas_texture_invalidation_callback(bool p_deleted, void *p_userdata); typedef LRUCache, HashMapComparatorDefault, _before_evict> RIDCache; RIDCache rid_set_to_uniform_set; + /// Maps a CanvasTexture to its associated uniform sets, which must + /// be invalidated when the CanvasTexture is updated, such as changing the + /// diffuse texture. + HashMap> canvas_texture_to_uniform_set; struct Batch { // Position in the UBO measured in bytes diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index a297b971781..69ae3d971a4 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -43,9 +43,15 @@ using namespace RendererRD; void TextureStorage::CanvasTexture::clear_cache() { info_cache[0] = CanvasTextureCache(); info_cache[1] = CanvasTextureCache(); + if (invalidated_callback != nullptr) { + invalidated_callback(false, invalidated_callback_userdata); + } } TextureStorage::CanvasTexture::~CanvasTexture() { + if (invalidated_callback != nullptr) { + invalidated_callback(true, invalidated_callback_userdata); + } } /////////////////////////////////////////////////////////////////////////// @@ -735,6 +741,16 @@ TextureStorage::CanvasTextureInfo TextureStorage::canvas_texture_get_info(RID p_ return res; } +void TextureStorage::canvas_texture_set_invalidation_callback(RID p_canvas_texture, InvalidationCallback p_callback, void *p_userdata) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + if (!ct) { + return; + } + + ct->invalidated_callback = p_callback; + ct->invalidated_callback_userdata = p_userdata; +} + /* Texture API */ RID TextureStorage::texture_allocate() { diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index bfee1e367e0..2073c34ded7 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -90,6 +90,8 @@ public: _FORCE_INLINE_ bool is_null() const { return diffuse.is_null(); } }; + typedef void (*InvalidationCallback)(bool p_deleted, void *p_userdata); + private: friend class LightStorage; friend class MaterialStorage; @@ -118,6 +120,9 @@ private: RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; CanvasTextureCache info_cache[2]; + InvalidationCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; + Size2i size_cache = Size2i(1, 1); bool use_normal_cache = false; bool use_specular_cache = false; @@ -499,6 +504,7 @@ public: virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; CanvasTextureInfo canvas_texture_get_info(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, bool p_use_srgb, bool p_texture_is_data); + void canvas_texture_set_invalidation_callback(RID p_canvas_texture, InvalidationCallback p_callback, void *p_userdata); /* Texture API */