Browse Source

2D: Fix `CanvasTexture` rendering when updating channels

Stuart Carnie 6 months ago
parent
commit
663917449b

+ 28 - 0
servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp

@@ -2971,8 +2971,23 @@ void RendererCanvasRenderRD::_uniform_set_invalidation_callback(void *p_userdata
 	static_cast<RendererCanvasRenderRD *>(singleton)->rid_set_to_uniform_set.erase(*key);
 	static_cast<RendererCanvasRenderRD *>(singleton)->rid_set_to_uniform_set.erase(*key);
 }
 }
 
 
+void RendererCanvasRenderRD::_canvas_texture_invalidation_callback(bool p_deleted, void *p_userdata) {
+	KeyValue<RID, TightLocalVector<RID>> *kv = static_cast<KeyValue<RID, TightLocalVector<RID>> *>(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<RendererCanvasRenderRD *>(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) {
 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(
 		RIDSetKey key(
 				p_batch->tex_info->state,
 				p_batch->tex_info->state,
 				state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
 				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);
 			const RIDCache::Pair *iter = rid_set_to_uniform_set.insert(key, rid);
 			uniform_set = &iter->data;
 			uniform_set = &iter->data;
 			RD::get_singleton()->uniform_set_set_invalidation_callback(rid, RendererCanvasRenderRD::_uniform_set_invalidation_callback, (void *)&iter->key);
 			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<RID, TightLocalVector<RID>> *kv = nullptr;
+				if (HashMap<RID, TightLocalVector<RID>>::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) {
 		if (state.current_batch_uniform_set != *uniform_set) {

+ 5 - 0
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 _before_evict(RendererCanvasRenderRD::RIDSetKey &p_key, RID &p_rid);
 	static void _uniform_set_invalidation_callback(void *p_userdata);
 	static void _uniform_set_invalidation_callback(void *p_userdata);
+	static void _canvas_texture_invalidation_callback(bool p_deleted, void *p_userdata);
 
 
 	typedef LRUCache<RIDSetKey, RID, HashableHasher<RIDSetKey>, HashMapComparatorDefault<RIDSetKey>, _before_evict> RIDCache;
 	typedef LRUCache<RIDSetKey, RID, HashableHasher<RIDSetKey>, HashMapComparatorDefault<RIDSetKey>, _before_evict> RIDCache;
 	RIDCache rid_set_to_uniform_set;
 	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<RID, TightLocalVector<RID>> canvas_texture_to_uniform_set;
 
 
 	struct Batch {
 	struct Batch {
 		// Position in the UBO measured in bytes
 		// Position in the UBO measured in bytes

+ 16 - 0
servers/rendering/renderer_rd/storage_rd/texture_storage.cpp

@@ -43,9 +43,15 @@ using namespace RendererRD;
 void TextureStorage::CanvasTexture::clear_cache() {
 void TextureStorage::CanvasTexture::clear_cache() {
 	info_cache[0] = CanvasTextureCache();
 	info_cache[0] = CanvasTextureCache();
 	info_cache[1] = CanvasTextureCache();
 	info_cache[1] = CanvasTextureCache();
+	if (invalidated_callback != nullptr) {
+		invalidated_callback(false, invalidated_callback_userdata);
+	}
 }
 }
 
 
 TextureStorage::CanvasTexture::~CanvasTexture() {
 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;
 	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 */
 /* Texture API */
 
 
 RID TextureStorage::texture_allocate() {
 RID TextureStorage::texture_allocate() {

+ 6 - 0
servers/rendering/renderer_rd/storage_rd/texture_storage.h

@@ -90,6 +90,8 @@ public:
 		_FORCE_INLINE_ bool is_null() const { return diffuse.is_null(); }
 		_FORCE_INLINE_ bool is_null() const { return diffuse.is_null(); }
 	};
 	};
 
 
+	typedef void (*InvalidationCallback)(bool p_deleted, void *p_userdata);
+
 private:
 private:
 	friend class LightStorage;
 	friend class LightStorage;
 	friend class MaterialStorage;
 	friend class MaterialStorage;
@@ -118,6 +120,9 @@ private:
 		RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
 		RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
 		CanvasTextureCache info_cache[2];
 		CanvasTextureCache info_cache[2];
 
 
+		InvalidationCallback invalidated_callback = nullptr;
+		void *invalidated_callback_userdata = nullptr;
+
 		Size2i size_cache = Size2i(1, 1);
 		Size2i size_cache = Size2i(1, 1);
 		bool use_normal_cache = false;
 		bool use_normal_cache = false;
 		bool use_specular_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;
 	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);
 	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 */
 	/* Texture API */