Skip to content

Commit

Permalink
world: add shaders and materials hot-reloading
Browse files Browse the repository at this point in the history
  • Loading branch information
dbartolini committed Feb 1, 2025
1 parent e8a225b commit 9faa820
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Changelog
* Lua: fixed max temporaries check failing to trigger in some circumnstances.
* Fixed a crash when moving many objects simultaneusly.
* Fixed a crash when reloading unloaded or unsupported resources.
* Added support to shaders and materials hot-reloading.

0.54.0 --- 13 Jan 2025
----------------------
Expand Down
12 changes: 12 additions & 0 deletions src/device/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,8 @@ void Device::refresh(const char *json)
StringId64 resource_name(resource.c_str(), len);
bool is_type_reloadable = resource_type == RESOURCE_TYPE_SCRIPT
|| resource_type == RESOURCE_TYPE_TEXTURE
|| resource_type == RESOURCE_TYPE_SHADER
|| resource_type == RESOURCE_TYPE_MATERIAL
;

if (is_type_reloadable && _resource_manager->can_get(resource_type, resource_name)) {
Expand All @@ -951,6 +953,16 @@ void Device::refresh(const char *json)
refresh_lua = true;
} else if (resource_type == RESOURCE_TYPE_TEXTURE) {
_material_manager->reload_textures((TextureResource *)old_resource, (TextureResource *)new_resource);
} else if (resource_type == RESOURCE_TYPE_SHADER) {
_pipeline->reload_shaders((ShaderResource *)old_resource, (ShaderResource *)new_resource);
_material_manager->reload_shaders((ShaderResource *)old_resource, (ShaderResource *)new_resource);
} else if (resource_type == RESOURCE_TYPE_MATERIAL) {
ListNode *cur;
list_for_each(cur, &_worlds)
{
World *w = (World *)container_of(cur, World, _node);
w->reload_materials((MaterialResource *)old_resource, (MaterialResource *)new_resource);
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/device/pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,10 @@ void Pipeline::render(u16 width, u16 height)
#endif
}

void Pipeline::reload_shaders(const ShaderResource *old_resource, const ShaderResource *new_resource)
{
CE_UNUSED_2(old_resource, new_resource);
lookup_default_shaders(*this);
}

} // namespace crown
3 changes: 3 additions & 0 deletions src/device/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ struct Pipeline

///
void render(u16 width, u16 height);

///
void reload_shaders(const ShaderResource *old_resource, const ShaderResource *new_resource);
};

} // namespace crown
4 changes: 4 additions & 0 deletions src/resource/shader_resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#pragma once

#include "config.h"
#include "core/containers/types.h"
#include "core/filesystem/types.h"
#include "core/memory/types.h"
Expand Down Expand Up @@ -45,6 +46,9 @@ struct ShaderData
u64 state;
ShaderResource::Sampler samplers[4];
bgfx::ProgramHandle program;
#if CROWN_CAN_RELOAD
const ShaderResource *resource;
#endif
};

namespace shader_resource_internal
Expand Down
20 changes: 20 additions & 0 deletions src/world/material_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,26 @@ void MaterialManager::reload_textures(const TextureResource *old_resource, const
}
#else
CE_UNUSED_2(old_resource, new_resource);
CE_NOOP();
#endif
}

void MaterialManager::reload_shaders(const ShaderResource *old_resource, const ShaderResource *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);

Material *m = cur->second;
if (m->_shader.resource == old_resource) {
m->_shader = _shader_manager->shader(m->_resource->shader);
}
}
#else
CE_UNUSED_2(old_resource, new_resource);
CE_NOOP();
#endif
}

Expand Down
3 changes: 3 additions & 0 deletions src/world/material_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct MaterialManager

///
void reload_textures(const TextureResource *old_resource, const TextureResource *new_resource);

///
void reload_shaders(const ShaderResource *old_resource, const ShaderResource *new_resource);
};

} // namespace crown
59 changes: 59 additions & 0 deletions src/world/render_world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,27 @@ void RenderWorld::unit_destroyed_callback(UnitId unit)
}
}

void RenderWorld::reload_materials(const MaterialResource *old_resource, const MaterialResource *new_resource)
{
#if CROWN_CAN_RELOAD
for (u32 i = 0; i < _mesh_manager._data.size; ++i) {
if (_mesh_manager._data.material_resource[i] == old_resource) {
_mesh_manager._data.material[i] = _material_manager->get(new_resource);
_mesh_manager._data.material_resource[i] = new_resource;
}
}

for (u32 i = 0; i < _sprite_manager._data.size; ++i) {
if (_sprite_manager._data.material_resource[i] == old_resource) {
_sprite_manager._data.material[i] = _material_manager->get(new_resource);
_sprite_manager._data.material_resource[i] = new_resource;
}
}
#else
CE_UNUSED_2(old_resource, new_resource);
#endif
}

void RenderWorld::MeshManager::allocate(u32 num)
{
CE_ENSURE(num > _data.size);
Expand All @@ -495,6 +516,10 @@ void RenderWorld::MeshManager::allocate(u32 num)
+ num*sizeof(Material *) + alignof(Material *)
+ num*sizeof(Matrix4x4) + alignof(Matrix4x4)
+ num*sizeof(OBB) + alignof(OBB)
#if CROWN_CAN_RELOAD
+ num*sizeof(MaterialResource *) + alignof(MaterialResource *)
#endif
+ 0
;

MeshInstanceData new_data;
Expand All @@ -510,6 +535,9 @@ void RenderWorld::MeshManager::allocate(u32 num)
new_data.material = (Material ** )memory::align_top(new_data.mesh + num, alignof(Material *));
new_data.world = (Matrix4x4 * )memory::align_top(new_data.material + num, alignof(Matrix4x4));
new_data.obb = (OBB * )memory::align_top(new_data.world + num, alignof(OBB));
#if CROWN_CAN_RELOAD
new_data.material_resource = (const MaterialResource **)memory::align_top(new_data.obb + num, alignof(MaterialResource *));
#endif

memcpy(new_data.unit, _data.unit, _data.size * sizeof(UnitId));
memcpy(new_data.resource, _data.resource, _data.size * sizeof(MeshResource *));
Expand All @@ -518,6 +546,9 @@ void RenderWorld::MeshManager::allocate(u32 num)
memcpy(new_data.material, _data.material, _data.size * sizeof(Material *));
memcpy(new_data.world, _data.world, _data.size * sizeof(Matrix4x4));
memcpy(new_data.obb, _data.obb, _data.size * sizeof(OBB));
#if CROWN_CAN_RELOAD
memcpy(new_data.material_resource, _data.material_resource, _data.size * sizeof(MaterialResource *));
#endif

_allocator->deallocate(_data.buffer);
_data = new_data;
Expand Down Expand Up @@ -548,6 +579,9 @@ MeshInstance RenderWorld::MeshManager::create(UnitId unit, const MeshResource *m
_data.material[last] = _render_world->_material_manager->get(mat_res);
_data.world[last] = tr;
_data.obb[last] = mg->obb;
#if CROWN_CAN_RELOAD
_data.material_resource[last] = mat_res;
#endif

hash_map::set(_map, unit, last);
++_data.size;
Expand Down Expand Up @@ -582,6 +616,9 @@ void RenderWorld::MeshManager::destroy(MeshInstance inst)
_data.material[inst.i] = _data.material[last];
_data.world[inst.i] = _data.world[last];
_data.obb[inst.i] = _data.obb[last];
#if CROWN_CAN_RELOAD
_data.material_resource[inst.i] = _data.material_resource[last];
#endif

hash_map::set(_map, last_u, inst.i);
hash_map::remove(_map, u);
Expand Down Expand Up @@ -613,6 +650,9 @@ void RenderWorld::MeshManager::swap(u32 inst_a, u32 inst_b)
exchange(_data.material[inst_a], _data.material[inst_b]);
exchange(_data.world[inst_a], _data.world[inst_b]);
exchange(_data.obb[inst_a], _data.obb[inst_b]);
#if CROWN_CAN_RELOAD
exchange(_data.material_resource[inst_a], _data.material_resource[inst_b]);
#endif

hash_map::set(_map, unit_a, inst_b);
hash_map::set(_map, unit_b, inst_a);
Expand Down Expand Up @@ -691,6 +731,10 @@ void RenderWorld::SpriteManager::allocate(u32 num)
+ num*sizeof(bool) + alignof(bool)
+ num*sizeof(u32) + alignof(u32)
+ num*sizeof(u32) + alignof(u32)
#if CROWN_CAN_RELOAD
+ num*sizeof(MaterialResource **) + alignof(MaterialResource *)
#endif
+ 0
;

SpriteInstanceData new_data;
Expand All @@ -709,6 +753,9 @@ void RenderWorld::SpriteManager::allocate(u32 num)
new_data.flip_y = (bool * )memory::align_top(new_data.flip_x + num, alignof(bool));
new_data.layer = (u32 * )memory::align_top(new_data.flip_y + num, alignof(u32));
new_data.depth = (u32 * )memory::align_top(new_data.layer + num, alignof(u32));
#if CROWN_CAN_RELOAD
new_data.material_resource = (const MaterialResource **)memory::align_top(new_data.depth + num, alignof(MaterialResource *));
#endif

memcpy(new_data.unit, _data.unit, _data.size * sizeof(UnitId));
memcpy(new_data.resource, _data.resource, _data.size * sizeof(SpriteResource**));
Expand All @@ -720,6 +767,9 @@ void RenderWorld::SpriteManager::allocate(u32 num)
memcpy(new_data.flip_y, _data.flip_y, _data.size * sizeof(bool));
memcpy(new_data.layer, _data.layer, _data.size * sizeof(u32));
memcpy(new_data.depth, _data.depth, _data.size * sizeof(u32));
#if CROWN_CAN_RELOAD
memcpy(new_data.material_resource, _data.material_resource, _data.size * sizeof(MaterialResource *));
#endif

_allocator->deallocate(_data.buffer);
_data = new_data;
Expand Down Expand Up @@ -751,6 +801,9 @@ SpriteInstance RenderWorld::SpriteManager::create(UnitId unit, const SpriteResou
_data.flip_y[last] = false;
_data.layer[last] = srd.layer;
_data.depth[last] = srd.depth;
#if CROWN_CAN_RELOAD
_data.material_resource[last] = mat_res;
#endif

hash_map::set(_map, unit, last);
++_data.size;
Expand Down Expand Up @@ -787,6 +840,9 @@ void RenderWorld::SpriteManager::destroy(SpriteInstance inst)
_data.flip_y[inst.i] = _data.flip_y[last];
_data.layer[inst.i] = _data.layer[last];
_data.depth[inst.i] = _data.depth[last];
#if CROWN_CAN_RELOAD
_data.material_resource[inst.i] = _data.material_resource[last];
#endif

hash_map::set(_map, last_u, inst.i);
hash_map::remove(_map, u);
Expand Down Expand Up @@ -821,6 +877,9 @@ void RenderWorld::SpriteManager::swap(u32 inst_a, u32 inst_b)
exchange(_data.flip_y[inst_a], _data.flip_y[inst_b]);
exchange(_data.layer[inst_a], _data.layer[inst_b]);
exchange(_data.depth[inst_a], _data.depth[inst_b]);
#if CROWN_CAN_RELOAD
exchange(_data.material_resource[inst_a], _data.material_resource[inst_b]);
#endif

hash_map::set(_map, unit_a, inst_b);
hash_map::set(_map, unit_b, inst_a);
Expand Down
9 changes: 9 additions & 0 deletions src/world/render_world.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ struct RenderWorld
///
void unit_destroyed_callback(UnitId unit);

///
void reload_materials(const MaterialResource *old_resource, const MaterialResource *new_resource);

/// Callback to customize drawing of objects.
typedef void (*DrawOverride)(UnitId unit_id, RenderWorld *rw);

Expand Down Expand Up @@ -190,6 +193,9 @@ struct RenderWorld
Material **material;
Matrix4x4 *world;
OBB *obb;
#if CROWN_CAN_RELOAD
const MaterialResource **material_resource;
#endif
};

Allocator *_allocator;
Expand Down Expand Up @@ -267,6 +273,9 @@ struct RenderWorld
bool *flip_y;
u32 *layer;
u32 *depth;
#if CROWN_CAN_RELOAD
const MaterialResource **material_resource;
#endif
};

Allocator *_allocator;
Expand Down
8 changes: 7 additions & 1 deletion src/world/shader_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ static ShaderData SHADER_DATA_INVALID =
0u, BGFX_INVALID_HANDLE
}
},
BGFX_INVALID_HANDLE
BGFX_INVALID_HANDLE,
#if CROWN_CAN_RELOAD
NULL
#endif
};

ShaderManager::ShaderManager(Allocator &a)
Expand Down Expand Up @@ -111,6 +114,9 @@ void ShaderManager::online(StringId64 id, ResourceManager &rm)
sd.state = data.state;
memcpy(sd.samplers, data.samplers, sizeof(sd.samplers));
sd.program = program;
#if CROWN_CAN_RELOAD
sd.resource = shader;
#endif
hash_map::set(_shader_map, data.name, sd);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/world/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,16 @@ void World::disable_unit_callbacks()
#endif
}

void World::reload_materials(const MaterialResource *old_resource, const MaterialResource *new_resource)
{
#if CROWN_CAN_RELOAD
_render_world->reload_materials(old_resource, new_resource);
#else
CE_UNUSED_2(old_resource, new_resource);
CE_NOOP();
#endif
}

void spawn_units(World &w, const UnitResource *ur, const Vector3 &pos, const Quaternion &rot, const Vector3 &scl, const UnitId *unit_lookup)
{
SceneGraph *scene_graph = w._scene_graph;
Expand Down
3 changes: 3 additions & 0 deletions src/world/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ struct World
/// other callback. This is used only by the editor to prevent user logic to
/// interfere with editor's assumptions.
void disable_unit_callbacks();

///
void reload_materials(const MaterialResource *old_resource, const MaterialResource *new_resource);
};

void spawn_units(World &w, const UnitResource *ur, const Vector3 &pos, const Quaternion &rot, const Vector3 &scl, const UnitId *unit_lookup);
Expand Down

0 comments on commit 9faa820

Please sign in to comment.