Переглянути джерело

resource: implement texture hot-reloading

Fixes: #186
Daniele Bartolini 1 рік тому
батько
коміт
7e5e916e29

+ 4 - 1
src/device/device.cpp

@@ -873,10 +873,13 @@ void Device::refresh(const char *json)
 		StringId64 resource_type(type);
 		StringId64 resource_name(resource.c_str(), len);
 
-		_resource_manager->reload(resource_type, resource_name);
+		const void *old_resource = _resource_manager->get(resource_type, resource_name);
+		const void *new_resource = _resource_manager->reload(resource_type, resource_name);
 
 		if (resource_type == RESOURCE_TYPE_SCRIPT) {
 			refresh_lua = true;
+		} else if (resource_type == RESOURCE_TYPE_TEXTURE) {
+			_material_manager->reload_textures((TextureResource *)old_resource, (TextureResource *)new_resource);
 		}
 	}
 

+ 29 - 0
src/world/material.cpp

@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
+#include "core/containers/array.inl"
 #include "core/strings/string_id.inl"
 #include "resource/material_resource.h"
 #include "resource/resource_manager.h"
@@ -13,6 +14,14 @@
 
 namespace crown
 {
+Material::Material(Allocator &a)
+#if CROWN_CAN_RELOAD
+	: _texture_resources(a)
+#endif
+{
+	CE_UNUSED(a);
+}
+
 void Material::bind(ShaderManager &sm, u8 view, s32 depth) const
 {
 	using namespace material_resource;
@@ -88,6 +97,26 @@ void Material::set_texture(StringId32 sampler_name, ResourceId texture_resource)
 	u32 idx = material_resource::texture_data_index(_resource, td, sampler_name);
 	TextureHandle *th = (TextureHandle *)material_resource::texture_handle(td, idx, _data);
 	th->texture_handle = tr->handle.idx;
+#if CROWN_CAN_RELOAD
+	u32 index = material_resource::texture_data_index(_resource, td, sampler_name);
+	_texture_resources[index] = (TextureResource *)tr;
+#endif
+}
+
+void Material::reload_textures(const TextureResource *old_resource, const TextureResource *new_resource)
+{
+#if CROWN_CAN_RELOAD
+	const TextureData *td = material_resource::texture_data_array(_resource);
+	for (u32 i = 0; i < array::size(_texture_resources); ++i) {
+		if (_texture_resources[i] == old_resource) {
+			_texture_resources[i] = (TextureResource *)new_resource;
+			TextureHandle *th = material_resource::texture_handle(td, i, _data);
+			th->texture_handle = new_resource->handle.idx;
+		}
+	}
+#else
+	CE_UNUSED_2(old_resource, new_resource);
+#endif
 }
 
 } // namespace crown

+ 10 - 0
src/world/material.h

@@ -5,6 +5,7 @@
 
 #pragma once
 
+#include "config.h"
 #include "core/math/types.h"
 #include "resource/resource_id.h"
 #include "resource/types.h"
@@ -20,6 +21,12 @@ struct Material
 	ResourceManager *_resource_manager;
 	const MaterialResource *_resource;
 	char *_data;
+#if CROWN_CAN_RELOAD
+	Array<TextureResource *> _texture_resources;
+#endif
+
+	///
+	explicit Material(Allocator &a);
 
 	///
 	void bind(ShaderManager &sm, u8 view, s32 depth = 0) const;
@@ -41,6 +48,9 @@ struct Material
 
 	/// Sets the @a texture_resource of the sampler @a sampler_name.
 	void set_texture(StringId32 sampler_name, ResourceId texture_resource);
+
+	///
+	void reload_textures(const TextureResource *old_resource, const TextureResource *new_resource);
 };
 
 } // namespace crown

+ 44 - 31
src/world/material_manager.cpp

@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
+#include "core/containers/array.inl"
 #include "core/containers/hash_map.inl"
 #include "core/filesystem/file.h"
 #include "core/strings/string_id.inl"
@@ -48,39 +49,36 @@ void MaterialManager::online(StringId64 id, ResourceManager &rm)
 	using namespace material_resource;
 
 	MaterialResource *mr = (MaterialResource *)rm.get(RESOURCE_TYPE_MATERIAL, id);
-
-	char *base = (char *)mr + mr->dynamic_data_offset;
+	Material *material = create_material(mr);
 
 	const TextureData *td = texture_data_array(mr);
 	for (u32 i = 0; i < mr->num_textures; ++i) {
-		TextureHandle *th  = texture_handle(td, i, base);
+		TextureHandle *th   = texture_handle(td, i, material->_data);
 		TextureResource *tr = (TextureResource *)rm.get(RESOURCE_TYPE_TEXTURE, td->id);
 		th->sampler_handle = bgfx::createUniform(texture_name(mr, td, i), bgfx::UniformType::Sampler).idx;
 		th->texture_handle = tr->handle.idx;
+#if CROWN_CAN_RELOAD
+		material->_texture_resources[i] = tr;
+#endif
 	}
 
 	const UniformData *ud = uniform_data_array(mr);
 	for (u32 i = 0; i < mr->num_uniforms; ++i) {
-		UniformHandle *uh  = uniform_handle(ud, i, base);
+		UniformHandle *uh  = uniform_handle(ud, i, material->_data);
 		uh->uniform_handle = bgfx::createUniform(uniform_name(mr, ud, i), s_bgfx_uniform_type[ud->type]).idx;
 	}
-
-	create_material(mr);
 }
 
 void MaterialManager::offline(StringId64 id, ResourceManager &rm)
 {
 	using namespace material_resource;
 
-	MaterialResource *mr = (MaterialResource *)rm.get(RESOURCE_TYPE_MATERIAL, id);
-
-	destroy_material(mr);
-
-	char *base = (char *)mr + mr->dynamic_data_offset;
+	const MaterialResource *mr = (MaterialResource *)rm.get(RESOURCE_TYPE_MATERIAL, id);
+	Material *material = hash_map::get(_materials, mr, (Material *)NULL);
 
 	const TextureData *td = texture_data_array(mr);
 	for (u32 i = 0; i < mr->num_textures; ++i) {
-		TextureHandle *th = texture_handle(td, i, base);
+		TextureHandle *th = texture_handle(td, i, material->_data);
 		bgfx::UniformHandle sh;
 		sh.idx = th->sampler_handle;
 		bgfx::destroy(sh);
@@ -88,38 +86,38 @@ void MaterialManager::offline(StringId64 id, ResourceManager &rm)
 
 	const UniformData *ud = uniform_data_array(mr);
 	for (u32 i = 0; i < mr->num_uniforms; ++i) {
-		UniformHandle *uh = uniform_handle(ud, i, base);
+		UniformHandle *uh = uniform_handle(ud, i, material->_data);
 		bgfx::UniformHandle bgfx_uh;
 		bgfx_uh.idx = uh->uniform_handle;
 		bgfx::destroy(bgfx_uh);
 	}
+
+	CE_DELETE(*_allocator, material);
+	hash_map::remove(_materials, mr);
 }
 
 Material *MaterialManager::create_material(const MaterialResource *resource)
 {
-	Material *mat = hash_map::get(_materials, resource, (Material *)NULL);
-	if (mat != NULL)
-		return mat;
+	Material *material = hash_map::get(_materials, resource, (Material *)NULL);
+	if (material != NULL)
+		return material;
 
 	const u32 size = sizeof(Material) + resource->dynamic_data_size;
-	mat = (Material *)_allocator->allocate(size);
-	mat->_resource_manager = _resource_manager;
-	mat->_resource         = resource;
-	mat->_data             = (char *)&mat[1];
-
-	const char *data = (char *)resource + resource->dynamic_data_offset;
-	memcpy(mat->_data, data, resource->dynamic_data_size);
+	material = (Material *)_allocator->allocate(size);
+	new (material) Material(*_allocator);
+	material->_resource_manager = _resource_manager;
+	material->_resource = resource;
+	material->_data = (char *)&material[1];
 
-	hash_map::set(_materials, resource, mat);
-	return mat;
-}
+	const char *dynamic_data = (char *)resource + resource->dynamic_data_offset;
+	memcpy(material->_data, dynamic_data, resource->dynamic_data_size);
 
-void MaterialManager::destroy_material(const MaterialResource *resource)
-{
-	Material *mat = hash_map::get(_materials, resource, (Material *)NULL);
-	_allocator->deallocate(mat);
+#if CROWN_CAN_RELOAD
+	array::resize(material->_texture_resources, resource->num_textures);
+#endif
 
-	hash_map::remove(_materials, resource);
+	hash_map::set(_materials, resource, material);
+	return material;
 }
 
 Material *MaterialManager::get(const MaterialResource *resource)
@@ -128,4 +126,19 @@ Material *MaterialManager::get(const MaterialResource *resource)
 	return hash_map::get(_materials, resource, (Material *)NULL);
 }
 
+void MaterialManager::reload_textures(const TextureResource *old_resource, const TextureResource *new_resource)
+{
+#if CROWN_CAN_RELOAD
+	auto cur = hash_map::begin(_materials);
+	auto end = hash_map::end(_materials);
+	for (; cur != end; ++cur) {
+		HASH_MAP_SKIP_HOLE(_materials, cur);
+
+		cur->second->reload_textures(old_resource, new_resource);
+	}
+#else
+	CE_UNUSED_2(old_resource, new_resource);
+#endif
+}
+
 } // namespace crown

+ 3 - 3
src/world/material_manager.h

@@ -35,11 +35,11 @@ struct MaterialManager
 	/// Instantiates the material @a resource.
 	Material *create_material(const MaterialResource *resource);
 
-	/// Destroys the instance of the material @a resource.
-	void destroy_material(const MaterialResource *resource);
-
 	/// Returns the instance of the material @a resource.
 	Material *get(const MaterialResource *resource);
+
+	///
+	void reload_textures(const TextureResource *old_resource, const TextureResource *new_resource);
 };
 
 } // namespace crown