diff --git a/libopenage/gamestate/game_entity.cpp b/libopenage/gamestate/game_entity.cpp index a4e18a29e3..421c2847e7 100644 --- a/libopenage/gamestate/game_entity.cpp +++ b/libopenage/gamestate/game_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "game_entity.h" @@ -30,7 +30,7 @@ entity_id_t GameEntity::get_id() const { return this->id; } -void GameEntity::set_render_entity(const std::shared_ptr &entity) { +void GameEntity::set_render_entity(const std::shared_ptr &entity) { // TODO: Transfer state from old render entity to new one? this->render_entity = entity; diff --git a/libopenage/gamestate/game_entity.h b/libopenage/gamestate/game_entity.h index 9b30e8ec76..923aed2eab 100644 --- a/libopenage/gamestate/game_entity.h +++ b/libopenage/gamestate/game_entity.h @@ -14,7 +14,7 @@ namespace openage { namespace renderer::world { -class WorldRenderEntity; +class RenderEntity; } namespace gamestate { @@ -62,7 +62,7 @@ class GameEntity { * * @param entity New render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Set the event manager of this entity. @@ -142,7 +142,7 @@ class GameEntity { /** * Render entity for pushing updates to the renderer. Can be \p nullptr. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * Event manager. diff --git a/libopenage/gamestate/terrain_chunk.cpp b/libopenage/gamestate/terrain_chunk.cpp index e988260333..e7dadbaf09 100644 --- a/libopenage/gamestate/terrain_chunk.cpp +++ b/libopenage/gamestate/terrain_chunk.cpp @@ -18,7 +18,7 @@ TerrainChunk::TerrainChunk(const util::Vector2s size, } } -void TerrainChunk::set_render_entity(const std::shared_ptr &entity) { +void TerrainChunk::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; } diff --git a/libopenage/gamestate/terrain_chunk.h b/libopenage/gamestate/terrain_chunk.h index 684d3af357..7939476adc 100644 --- a/libopenage/gamestate/terrain_chunk.h +++ b/libopenage/gamestate/terrain_chunk.h @@ -32,7 +32,7 @@ class TerrainChunk { * * @param entity New render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Get the size of this terrain chunk. @@ -78,7 +78,7 @@ class TerrainChunk { /** * Render entity for pushing updates to the renderer. Can be \p nullptr. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; }; } // namespace openage::gamestate diff --git a/libopenage/input/controller/hud/controller.cpp b/libopenage/input/controller/hud/controller.cpp index b423ef79ae..af89923d1b 100644 --- a/libopenage/input/controller/hud/controller.cpp +++ b/libopenage/input/controller/hud/controller.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "controller.h" @@ -27,11 +27,11 @@ bool Controller::process(const event_arguments &ev_args, return true; } -void Controller::set_drag_entity(const std::shared_ptr &entity) { +void Controller::set_drag_entity(const std::shared_ptr &entity) { this->drag_entity = entity; } -const std::shared_ptr &Controller::get_drag_entity() const { +const std::shared_ptr &Controller::get_drag_entity() const { return this->drag_entity; } @@ -39,7 +39,7 @@ void setup_defaults(const std::shared_ptr &ctx, const std::shared_ptr &hud_renderer) { binding_func_t drag_selection_init{[&](const event_arguments &args, const std::shared_ptr controller) { - auto render_entity = std::make_shared(args.mouse); + auto render_entity = std::make_shared(args.mouse); hud_renderer->add_drag_entity(render_entity); controller->set_drag_entity(render_entity); }}; diff --git a/libopenage/input/controller/hud/controller.h b/libopenage/input/controller/hud/controller.h index 60e9ff2bb8..44d64ee833 100644 --- a/libopenage/input/controller/hud/controller.h +++ b/libopenage/input/controller/hud/controller.h @@ -10,7 +10,7 @@ namespace openage { namespace renderer::hud { -class HudDragRenderEntity; +class DragRenderEntity; class HudRenderStage; } // namespace renderer::hud @@ -42,20 +42,20 @@ class Controller : public std::enable_shared_from_this { * * @param entity New render entity. */ - void set_drag_entity(const std::shared_ptr &entity); + void set_drag_entity(const std::shared_ptr &entity); /** * Get the render entity for the selection box. * * @return Render entity for the selection box. */ - const std::shared_ptr &get_drag_entity() const; + const std::shared_ptr &get_drag_entity() const; private: /** * Render entity for the selection box. */ - std::shared_ptr drag_entity; + std::shared_ptr drag_entity; }; /** diff --git a/libopenage/renderer/demo/demo_3.cpp b/libopenage/renderer/demo/demo_3.cpp index 408e699196..9c294285f9 100644 --- a/libopenage/renderer/demo/demo_3.cpp +++ b/libopenage/renderer/demo/demo_3.cpp @@ -123,7 +123,7 @@ void renderer_demo_3(const util::Path &path) { // Fill a 10x10 terrain grid with height values auto terrain_size = util::Vector2s{10, 10}; - std::vector> tiles{}; + std::vector> tiles{}; tiles.reserve(terrain_size[0] * terrain_size[1]); for (size_t i = 0; i < terrain_size[0] * terrain_size[1]; ++i) { tiles.emplace_back(0.0f, "./textures/test_terrain.terrain"); diff --git a/libopenage/renderer/demo/stresstest_0.cpp b/libopenage/renderer/demo/stresstest_0.cpp index 76c53d7b21..eae208c5d3 100644 --- a/libopenage/renderer/demo/stresstest_0.cpp +++ b/libopenage/renderer/demo/stresstest_0.cpp @@ -133,7 +133,7 @@ void renderer_stresstest_0(const util::Path &path) { // Fill a 10x10 terrain grid with height values auto terrain_size = util::Vector2s{10, 10}; - std::vector> tiles{}; + std::vector> tiles{}; tiles.reserve(terrain_size[0] * terrain_size[1]); for (size_t i = 0; i < terrain_size[0] * terrain_size[1]; ++i) { tiles.emplace_back(0.0f, "./textures/test_terrain.terrain"); @@ -147,7 +147,7 @@ void renderer_stresstest_0(const util::Path &path) { terrain0->update(terrain_size, tiles); // World entities - std::vector> render_entities{}; + std::vector> render_entities{}; auto add_world_entity = [&](const coord::phys3 initial_pos, const time::time_t time) { const auto animation_path = "./textures/test_tank_mirrored.sprite"; diff --git a/libopenage/renderer/demo/stresstest_1.cpp b/libopenage/renderer/demo/stresstest_1.cpp index ac6177f819..9d2d43968b 100644 --- a/libopenage/renderer/demo/stresstest_1.cpp +++ b/libopenage/renderer/demo/stresstest_1.cpp @@ -138,7 +138,7 @@ void renderer_stresstest_1(const util::Path &path) { // Fill a 10x10 terrain grid with height values auto terrain_size = util::Vector2s{10, 10}; - std::vector> tiles{}; + std::vector> tiles{}; tiles.reserve(terrain_size[0] * terrain_size[1]); for (size_t i = 0; i < terrain_size[0] * terrain_size[1]; ++i) { tiles.emplace_back(0.0f, "./textures/test_terrain.terrain"); @@ -151,7 +151,7 @@ void renderer_stresstest_1(const util::Path &path) { // send the terrain data to the terrain renderer terrain0->update(terrain_size, tiles); - std::vector> render_entities{}; + std::vector> render_entities{}; auto add_world_entity = [&](const coord::phys3 initial_pos, const time::time_t time) { const auto animation_path = "./textures/test_tank_mirrored.sprite"; diff --git a/libopenage/renderer/render_factory.cpp b/libopenage/renderer/render_factory.cpp index 6d5e276479..3dee6dd6ae 100644 --- a/libopenage/renderer/render_factory.cpp +++ b/libopenage/renderer/render_factory.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "render_factory.h" @@ -15,16 +15,16 @@ RenderFactory::RenderFactory(const std::shared_ptr world_renderer{world_renderer} { } -std::shared_ptr RenderFactory::add_terrain_render_entity(const util::Vector2s chunk_size, - const coord::tile_delta chunk_offset) { - auto entity = std::make_shared(); +std::shared_ptr RenderFactory::add_terrain_render_entity(const util::Vector2s chunk_size, + const coord::tile_delta chunk_offset) { + auto entity = std::make_shared(); this->terrain_renderer->add_render_entity(entity, chunk_size, chunk_offset.to_phys2().to_scene2()); return entity; } -std::shared_ptr RenderFactory::add_world_render_entity() { - auto entity = std::make_shared(); +std::shared_ptr RenderFactory::add_world_render_entity() { + auto entity = std::make_shared(); this->world_renderer->add_render_entity(entity); return entity; diff --git a/libopenage/renderer/render_factory.h b/libopenage/renderer/render_factory.h index 26601bcf7f..a211e3edf4 100644 --- a/libopenage/renderer/render_factory.h +++ b/libopenage/renderer/render_factory.h @@ -11,12 +11,12 @@ namespace openage::renderer { namespace terrain { class TerrainRenderStage; -class TerrainRenderEntity; +class RenderEntity; } // namespace terrain namespace world { class WorldRenderStage; -class WorldRenderEntity; +class RenderEntity; } // namespace world /** @@ -47,15 +47,15 @@ class RenderFactory { * * @return Render entity for pushing terrain updates. */ - std::shared_ptr add_terrain_render_entity(const util::Vector2s chunk_size, - const coord::tile_delta chunk_offset); + std::shared_ptr add_terrain_render_entity(const util::Vector2s chunk_size, + const coord::tile_delta chunk_offset); /** * Create a new world render entity and register it at the world renderer. * * @return Render entity for pushing terrain updates. */ - std::shared_ptr add_world_render_entity(); + std::shared_ptr add_world_render_entity(); private: /** diff --git a/libopenage/renderer/stages/CMakeLists.txt b/libopenage/renderer/stages/CMakeLists.txt index c2ae5bf781..77d6faa091 100644 --- a/libopenage/renderer/stages/CMakeLists.txt +++ b/libopenage/renderer/stages/CMakeLists.txt @@ -1,3 +1,7 @@ +add_sources(libopenage + render_entity.cpp +) + add_subdirectory(camera/) add_subdirectory(hud/) add_subdirectory(screen/) diff --git a/libopenage/renderer/stages/hud/object.cpp b/libopenage/renderer/stages/hud/object.cpp index 2b53bd1b3b..6e23b6663a 100644 --- a/libopenage/renderer/stages/hud/object.cpp +++ b/libopenage/renderer/stages/hud/object.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "object.h" @@ -21,7 +21,7 @@ HudDragObject::HudDragObject(const std::shared_ptr &entity) { +void HudDragObject::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; this->fetch_updates(); } @@ -38,8 +38,15 @@ void HudDragObject::fetch_updates(const time::time_t &time) { // Get data from render entity this->drag_start = this->render_entity->get_drag_start(); + + // Thread-safe access to curves needs a lock on the render entity's mutex + auto read_lock = this->render_entity->get_read_lock(); + this->drag_pos.sync(this->render_entity->get_drag_pos() /* , this->last_update */); + // Unlock the render entity mutex + read_lock.unlock(); + // Set self to changed so that world renderer can update the renderable this->changed = true; this->render_entity->clear_changed_flag(); diff --git a/libopenage/renderer/stages/hud/object.h b/libopenage/renderer/stages/hud/object.h index fac062f18d..d81e51ed8d 100644 --- a/libopenage/renderer/stages/hud/object.h +++ b/libopenage/renderer/stages/hud/object.h @@ -26,7 +26,7 @@ class Animation2dInfo; } // namespace resources namespace hud { -class HudDragRenderEntity; +class DragRenderEntity; /** * Stores the state of a renderable object in the HUD render stage. @@ -46,7 +46,7 @@ class HudDragObject { * * @param entity New world render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Set the current camera of the scene. @@ -147,7 +147,7 @@ class HudDragObject { /** * Source for positional and texture data. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * Position of the dragged corner. diff --git a/libopenage/renderer/stages/hud/render_entity.cpp b/libopenage/renderer/stages/hud/render_entity.cpp index f8cf19e692..2ff3f75649 100644 --- a/libopenage/renderer/stages/hud/render_entity.cpp +++ b/libopenage/renderer/stages/hud/render_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "render_entity.h" @@ -7,15 +7,14 @@ namespace openage::renderer::hud { -HudDragRenderEntity::HudDragRenderEntity(const coord::input drag_start) : - changed{false}, - last_update{0.0}, +DragRenderEntity::DragRenderEntity(const coord::input drag_start) : + renderer::RenderEntity{}, drag_pos{nullptr, 0, "", nullptr, drag_start}, drag_start{drag_start} { } -void HudDragRenderEntity::update(const coord::input drag_pos, - const time::time_t time) { +void DragRenderEntity::update(const coord::input drag_pos, + const time::time_t time) { std::unique_lock lock{this->mutex}; this->drag_pos.set_insert(time, drag_pos); @@ -24,30 +23,16 @@ void HudDragRenderEntity::update(const coord::input drag_pos, this->changed = true; } -time::time_t HudDragRenderEntity::get_update_time() { +const curve::Continuous &DragRenderEntity::get_drag_pos() { std::shared_lock lock{this->mutex}; - return this->last_update; -} - -const curve::Continuous &HudDragRenderEntity::get_drag_pos() { return this->drag_pos; } -const coord::input &HudDragRenderEntity::get_drag_start() { - return this->drag_start; -} - -bool HudDragRenderEntity::is_changed() { +const coord::input DragRenderEntity::get_drag_start() { std::shared_lock lock{this->mutex}; - return this->changed; -} - -void HudDragRenderEntity::clear_changed_flag() { - std::unique_lock lock{this->mutex}; - - this->changed = false; + return this->drag_start; } } // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_entity.h b/libopenage/renderer/stages/hud/render_entity.h index e2e2da65fa..9d15204386 100644 --- a/libopenage/renderer/stages/hud/render_entity.h +++ b/libopenage/renderer/stages/hud/render_entity.h @@ -3,12 +3,10 @@ #pragma once #include -#include -#include #include "coord/pixel.h" #include "curve/continuous.h" -#include "time/time.h" +#include "renderer/stages/render_entity.h" namespace openage::renderer::hud { @@ -16,36 +14,34 @@ namespace openage::renderer::hud { /** * Render entity for pushing drag selection updates to the HUD renderer. */ -class HudDragRenderEntity { +class DragRenderEntity final : public renderer::RenderEntity { public: /** * Create a new render entity for drag selection in the HUD. * * @param drag_start Position of the start corner. */ - HudDragRenderEntity(const coord::input drag_start); - ~HudDragRenderEntity() = default; + DragRenderEntity(const coord::input drag_start); + ~DragRenderEntity() = default; /** * Update the render entity with information from the gamestate * or input system. * + * Updating the render entity with this method is thread-safe. + * * @param drag_pos Position of the dragged corner. * @param time Current simulation time. */ void update(const coord::input drag_pos, const time::time_t time = 0.0); - /** - * Get the time of the last update. - * - * @return Time of last update. - */ - time::time_t get_update_time(); - /** * Get the position of the dragged corner. * + * Accessing the drag position curve REQUIRES a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Coordinates of the dragged corner. */ const curve::Continuous &get_drag_pos(); @@ -53,35 +49,13 @@ class HudDragRenderEntity { /** * Get the position of the start corner. * - * @return Coordinates of the start corner. - */ - const coord::input &get_drag_start(); - - /** - * Check whether the render entity has received new updates. + * Accessing the drag start is thread-safe. * - * @return true if updates have been received, else false. - */ - bool is_changed(); - - /** - * Clear the update flag by setting it to false. + * @return Coordinates of the start corner. */ - void clear_changed_flag(); + const coord::input get_drag_start(); private: - /** - * Flag for determining if the render entity has been updated by the - * corresponding gamestate entity. Set to true every time \p update() - * is called. - */ - bool changed; - - /** - * Time of the last update call. - */ - time::time_t last_update; - /** * Position of the dragged corner. */ @@ -91,10 +65,5 @@ class HudDragRenderEntity { * Position of the start corner. */ coord::input drag_start; - - /** - * Mutex for protecting threaded access. - */ - std::shared_mutex mutex; }; } // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_stage.cpp b/libopenage/renderer/stages/hud/render_stage.cpp index a55751ec5f..d5d7c5b83f 100644 --- a/libopenage/renderer/stages/hud/render_stage.cpp +++ b/libopenage/renderer/stages/hud/render_stage.cpp @@ -45,7 +45,7 @@ std::shared_ptr HudRenderStage::get_render_pass() { return this->render_pass; } -void HudRenderStage::add_drag_entity(const std::shared_ptr entity) { +void HudRenderStage::add_drag_entity(const std::shared_ptr entity) { std::unique_lock lock{this->mutex}; auto hud_object = std::make_shared(this->asset_manager); diff --git a/libopenage/renderer/stages/hud/render_stage.h b/libopenage/renderer/stages/hud/render_stage.h index f63662aa40..047aac6349 100644 --- a/libopenage/renderer/stages/hud/render_stage.h +++ b/libopenage/renderer/stages/hud/render_stage.h @@ -31,7 +31,7 @@ class AssetManager; namespace hud { class HudDragObject; -class HudDragRenderEntity; +class DragRenderEntity; /** * Renderer for the "Heads-Up Display" (HUD). @@ -71,7 +71,7 @@ class HudRenderStage { * * @param render_entity New render entity. */ - void add_drag_entity(const std::shared_ptr entity); + void add_drag_entity(const std::shared_ptr entity); /** * Remove the render object for drag selection. diff --git a/libopenage/renderer/stages/render_entity.cpp b/libopenage/renderer/stages/render_entity.cpp new file mode 100644 index 0000000000..dc95de9a6d --- /dev/null +++ b/libopenage/renderer/stages/render_entity.cpp @@ -0,0 +1,37 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#include "render_entity.h" + +#include + + +namespace openage::renderer { + +RenderEntity::RenderEntity() : + changed{false}, + last_update{time::time_t::zero()} { +} + +time::time_t RenderEntity::get_update_time() { + std::shared_lock lock{this->mutex}; + + return this->last_update; +} + +bool RenderEntity::is_changed() { + std::shared_lock lock{this->mutex}; + + return this->changed; +} + +void RenderEntity::clear_changed_flag() { + std::unique_lock lock{this->mutex}; + + this->changed = false; +} + +std::shared_lock RenderEntity::get_read_lock() { + return std::shared_lock{this->mutex}; +} + +} // namespace openage::renderer diff --git a/libopenage/renderer/stages/render_entity.h b/libopenage/renderer/stages/render_entity.h new file mode 100644 index 0000000000..f441452968 --- /dev/null +++ b/libopenage/renderer/stages/render_entity.h @@ -0,0 +1,83 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include + +#include "time/time.h" + + +namespace openage::renderer { + +/** + * Interface for render entities that allow pushing updates from game simulation + * to renderer. + */ +class RenderEntity { +public: + ~RenderEntity() = default; + + /** + * Get the time of the last update. + * + * Accessing the update time is thread-safe. + * + * @return Time of last update. + */ + time::time_t get_update_time(); + + /** + * Check whether the render entity has received new updates from the + * gamestate. + * + * @return true if updates have been received, else false. + */ + bool is_changed(); + + /** + * Clear the update flag by setting it to false. + */ + void clear_changed_flag(); + + /** + * Get a shared lock for thread-safe reading from the render entity. + * + * The caller is responsible for unlocking the mutex after reading. + * + * @return Lock for the render entity. + */ + std::shared_lock get_read_lock(); + +protected: + /** + * Create a new render entity. + * + * Members are initialized to these values by default: + * - \p changed: false + * - \p last_update: time::time_t::zero() + * + * Declared as protected to prevent direct instantiation of this class. + */ + RenderEntity(); + + /** + * Flag for determining if the render entity has been updated by the + * corresponding gamestate entity. Set to true every time \p update() + * is called. + */ + bool changed; + + /** + * Time of the last update call. + */ + time::time_t last_update; + + /** + * Mutex for protecting threaded access. + */ + std::shared_mutex mutex; +}; +} // namespace openage::renderer diff --git a/libopenage/renderer/stages/terrain/chunk.cpp b/libopenage/renderer/stages/terrain/chunk.cpp index 12b207bdc9..58a95d2a95 100644 --- a/libopenage/renderer/stages/terrain/chunk.cpp +++ b/libopenage/renderer/stages/terrain/chunk.cpp @@ -17,7 +17,7 @@ TerrainChunk::TerrainChunk(const std::shared_ptr &entity) { +void TerrainChunk::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; } @@ -31,11 +31,19 @@ void TerrainChunk::fetch_updates(const time::time_t & /* time */) { if (not this->render_entity->is_changed()) { return; } + + // Get the terrain data from the render entity + auto terrain_size = this->render_entity->get_size(); + auto terrain_paths = this->render_entity->get_terrain_paths(); + auto tiles = this->render_entity->get_tiles(); + auto heightmap_verts = this->render_entity->get_vertices(); + + // Recreate the mesh data // TODO: Change mesh instead of recreating it // TODO: Multiple meshes this->meshes.clear(); - for (const auto &terrain_path : this->render_entity->get_terrain_paths()) { - auto new_mesh = this->create_mesh(terrain_path); + for (const auto &terrain_path : terrain_paths) { + auto new_mesh = this->create_mesh(terrain_size, tiles, heightmap_verts, terrain_path); new_mesh->create_model_matrix(this->offset); this->meshes.push_back(new_mesh); } @@ -59,13 +67,12 @@ const std::vector> &TerrainChunk::get_meshes( return this->meshes; } -std::shared_ptr TerrainChunk::create_mesh(const std::string &texture_path) { - auto size = this->render_entity->get_size(); - auto v_width = size[0]; - auto v_height = size[1]; - - auto tiles = this->render_entity->get_tiles(); - auto heightmap_verts = this->render_entity->get_vertices(); +std::shared_ptr TerrainChunk::create_mesh(const util::Vector2s vert_size, + const RenderEntity::tiles_t &tiles, + const std::vector &heightmap_verts, + const std::string &texture_path) { + auto v_width = vert_size[0]; + auto v_height = vert_size[1]; // vertex data for the mesh std::vector mesh_verts{}; diff --git a/libopenage/renderer/stages/terrain/chunk.h b/libopenage/renderer/stages/terrain/chunk.h index b64133e5b6..7d3d9fcc70 100644 --- a/libopenage/renderer/stages/terrain/chunk.h +++ b/libopenage/renderer/stages/terrain/chunk.h @@ -7,6 +7,7 @@ #include #include "coord/scene.h" +#include "renderer/stages/terrain/render_entity.h" #include "time/time.h" #include "util/vector.h" @@ -19,7 +20,6 @@ class AssetManager; namespace terrain { class TerrainRenderMesh; -class TerrainRenderEntity; /** * Stores the state of a terrain chunk in the terrain render stage. @@ -44,7 +44,7 @@ class TerrainChunk { * @param size Size of the chunk in tiles. * @param offset Offset of the chunk from origin in tiles. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Fetch updates from the render entity. @@ -85,9 +85,17 @@ class TerrainChunk { /** * Create a terrain mesh from the data provided by the render entity. * + * @param vert_size Size of the terrain in vertices. + * @param tiles Data for each tile (elevation, terrain path). + * @param heightmap_verts Position of each vertex in the chunk. + * @param texture_path Path to the texture for the terrain. + * * @return New terrain mesh. */ - std::shared_ptr create_mesh(const std::string &texture_path); + std::shared_ptr create_mesh(const util::Vector2s vert_size, + const RenderEntity::tiles_t &tiles, + const std::vector &heightmap_verts, + const std::string &texture_path); /** * Size of the chunk in tiles (width x height). @@ -114,7 +122,7 @@ class TerrainChunk { * Source for ingame terrain coordinates. These coordinates are translated into * our render vertex mesh when \p update() is called. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; }; diff --git a/libopenage/renderer/stages/terrain/model.cpp b/libopenage/renderer/stages/terrain/model.cpp index 8d8cc4c727..30e235c1bd 100644 --- a/libopenage/renderer/stages/terrain/model.cpp +++ b/libopenage/renderer/stages/terrain/model.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "model.h" @@ -26,7 +26,7 @@ TerrainRenderModel::TerrainRenderModel(const std::shared_ptr &entity, +void TerrainRenderModel::add_chunk(const std::shared_ptr &entity, const util::Vector2s size, const coord::scene2_delta offset) { auto chunk = std::make_shared(this->asset_manager, size, offset); diff --git a/libopenage/renderer/stages/terrain/model.h b/libopenage/renderer/stages/terrain/model.h index c06ea351c0..951599855d 100644 --- a/libopenage/renderer/stages/terrain/model.h +++ b/libopenage/renderer/stages/terrain/model.h @@ -21,7 +21,7 @@ class AssetManager; } namespace terrain { -class TerrainRenderEntity; +class RenderEntity; class TerrainRenderMesh; class TerrainChunk; @@ -46,7 +46,7 @@ class TerrainRenderModel { * @param chunk_size Size of the chunk in tiles. * @param chunk_offset Offset of the chunk from origin in tiles. */ - void add_chunk(const std::shared_ptr &entity, + void add_chunk(const std::shared_ptr &entity, const util::Vector2s chunk_size, const coord::scene2_delta chunk_offset); diff --git a/libopenage/renderer/stages/terrain/render_entity.cpp b/libopenage/renderer/stages/terrain/render_entity.cpp index 7f2e6d825e..2316f5dab7 100644 --- a/libopenage/renderer/stages/terrain/render_entity.cpp +++ b/libopenage/renderer/stages/terrain/render_entity.cpp @@ -9,22 +9,19 @@ namespace openage::renderer::terrain { -TerrainRenderEntity::TerrainRenderEntity() : - changed{false}, +RenderEntity::RenderEntity() : + renderer::RenderEntity{}, size{0, 0}, tiles{}, - last_update{0.0}, terrain_paths{}, - vertices{} -// terrain_path{nullptr, 0}, -{ + vertices{} { } -void TerrainRenderEntity::update_tile(const util::Vector2s size, - const coord::tile &pos, - const terrain_elevation_t elevation, - const std::string terrain_path, - const time::time_t time) { +void RenderEntity::update_tile(const util::Vector2s size, + const coord::tile &pos, + const terrain_elevation_t elevation, + const std::string terrain_path, + const time::time_t time) { std::unique_lock lock{this->mutex}; if (this->vertices.empty()) { @@ -52,9 +49,9 @@ void TerrainRenderEntity::update_tile(const util::Vector2s size, this->changed = true; } -void TerrainRenderEntity::update(const util::Vector2s size, - const tiles_t tiles, - const time::time_t time) { +void RenderEntity::update(const util::Vector2s size, + const tiles_t tiles, + const time::time_t time) { std::unique_lock lock{this->mutex}; // increase by 1 in every dimension because tiles @@ -108,40 +105,28 @@ void TerrainRenderEntity::update(const util::Vector2s size, this->changed = true; } -const std::vector &TerrainRenderEntity::get_vertices() { +const std::vector RenderEntity::get_vertices() { std::shared_lock lock{this->mutex}; return this->vertices; } -const TerrainRenderEntity::tiles_t &TerrainRenderEntity::get_tiles() { +const RenderEntity::tiles_t RenderEntity::get_tiles() { std::shared_lock lock{this->mutex}; return this->tiles; } -const std::unordered_set &TerrainRenderEntity::get_terrain_paths() { +const std::unordered_set RenderEntity::get_terrain_paths() { std::shared_lock lock{this->mutex}; return this->terrain_paths; } -const util::Vector2s &TerrainRenderEntity::get_size() { +const util::Vector2s RenderEntity::get_size() { std::shared_lock lock{this->mutex}; return this->size; } -bool TerrainRenderEntity::is_changed() { - std::shared_lock lock{this->mutex}; - - return this->changed; -} - -void TerrainRenderEntity::clear_changed_flag() { - std::unique_lock lock{this->mutex}; - - this->changed = false; -} - } // namespace openage::renderer::terrain diff --git a/libopenage/renderer/stages/terrain/render_entity.h b/libopenage/renderer/stages/terrain/render_entity.h index 4136c7c81a..0f726a2351 100644 --- a/libopenage/renderer/stages/terrain/render_entity.h +++ b/libopenage/renderer/stages/terrain/render_entity.h @@ -2,8 +2,6 @@ #pragma once -#include -#include #include #include #include @@ -11,7 +9,7 @@ #include "coord/scene.h" #include "coord/tile.h" #include "curve/discrete.h" -#include "time/time.h" +#include "renderer/stages/render_entity.h" #include "util/vector.h" @@ -20,10 +18,10 @@ namespace openage::renderer::terrain { /** * Render entity for pushing updates to the Terrain renderer. */ -class TerrainRenderEntity { +class RenderEntity final : public renderer::RenderEntity { public: - TerrainRenderEntity(); - ~TerrainRenderEntity() = default; + RenderEntity(); + ~RenderEntity() = default; using terrain_elevation_t = util::FixedPoint; using tiles_t = std::vector>; @@ -32,6 +30,8 @@ class TerrainRenderEntity { * Update a single tile of the displayed terrain (chunk) with information from the * gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param size Size of the terrain in tiles (width x length) * @param pos Position of the tile in the chunk. * @param elevation Height of terrain tile. @@ -48,6 +48,8 @@ class TerrainRenderEntity { * Update the full grid of the displayed terrain (chunk) with information from the * gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param size Size of the terrain in tiles (width x length) * @param tiles Animation data for each tile (elevation, terrain path). * @param time Simulation time of the update. @@ -59,52 +61,40 @@ class TerrainRenderEntity { /** * Get the vertices of the terrain. * + * Accessing the terrain vertices is thread-safe. + * * @return Vector of vertex coordinates. */ - const std::vector &get_vertices(); + const std::vector get_vertices(); /** * Get the tiles of the terrain. * + * Accessing the terrain tiles is thread-safe. + * * @return Terrain tiles. */ - const tiles_t &get_tiles(); + const tiles_t get_tiles(); /** * Get the terrain paths used in the terrain. * + * Accessing the terrain paths is thread-safe. + * * @return Terrain paths. */ - const std::unordered_set &get_terrain_paths(); + const std::unordered_set get_terrain_paths(); /** * Get the number of vertices on each side of the terrain. * - * @return Vector with width as first element and height as second element. - */ - const util::Vector2s &get_size(); - - /** - * Check whether the render entity has received new updates from the - * gamestate. + * Accessing the vertices size is thread-safe. * - * @return true if updates have been received, else false. - */ - bool is_changed(); - - /** - * Clear the update flag by setting it to false. + * @return Vector with width as first element and height as second element. */ - void clear_changed_flag(); + const util::Vector2s get_size(); private: - /** - * Flag for determining if the render entity has been updated by the - * corresponding gamestate entity. Set to true every time \p update() - * is called. - */ - bool changed; - /** * Chunk dimensions (width x height). */ @@ -115,11 +105,6 @@ class TerrainRenderEntity { */ tiles_t tiles; - /** - * Time of the last update call. - */ - time::time_t last_update; - /** * Terrain texture paths used in \p tiles . */ @@ -129,10 +114,5 @@ class TerrainRenderEntity { * Terrain vertices (ingame coordinates). */ std::vector vertices; - - /** - * Mutex for protecting threaded access. - */ - std::shared_mutex mutex; }; } // namespace openage::renderer::terrain diff --git a/libopenage/renderer/stages/terrain/render_stage.cpp b/libopenage/renderer/stages/terrain/render_stage.cpp index 740fe260f6..01376d886b 100644 --- a/libopenage/renderer/stages/terrain/render_stage.cpp +++ b/libopenage/renderer/stages/terrain/render_stage.cpp @@ -48,7 +48,7 @@ std::shared_ptr TerrainRenderStage::get_render_pass() { return this->render_pass; } -void TerrainRenderStage::add_render_entity(const std::shared_ptr entity, +void TerrainRenderStage::add_render_entity(const std::shared_ptr entity, const util::Vector2s chunk_size, const coord::scene2_delta chunk_offset) { std::unique_lock lock{this->mutex}; diff --git a/libopenage/renderer/stages/terrain/render_stage.h b/libopenage/renderer/stages/terrain/render_stage.h index 40caf2b309..6066dc90aa 100644 --- a/libopenage/renderer/stages/terrain/render_stage.h +++ b/libopenage/renderer/stages/terrain/render_stage.h @@ -32,7 +32,7 @@ class AssetManager; } namespace terrain { -class TerrainRenderEntity; +class RenderEntity; class TerrainRenderMesh; class TerrainRenderModel; @@ -73,7 +73,7 @@ class TerrainRenderStage { * * @param render_entity New render entity. */ - void add_render_entity(const std::shared_ptr entity, + void add_render_entity(const std::shared_ptr entity, const util::Vector2s chunk_size, const coord::scene2_delta chunk_offset); @@ -119,7 +119,7 @@ class TerrainRenderStage { /** * Engine interface for updating terrain draw information. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * 3D model of the terrain. diff --git a/libopenage/renderer/stages/world/object.cpp b/libopenage/renderer/stages/world/object.cpp index d4629f866b..0e98a10d1f 100644 --- a/libopenage/renderer/stages/world/object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -43,7 +43,7 @@ WorldObject::WorldObject(const std::shared_ptr &entity) { +void WorldObject::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; this->fetch_updates(); } @@ -63,6 +63,9 @@ void WorldObject::fetch_updates(const time::time_t &time) { // Get data from render entity this->ref_id = this->render_entity->get_id(); + + // Thread-safe access to curves needs a lock on the render entity's mutex + auto read_lock = this->render_entity->get_read_lock(); this->position.sync(this->render_entity->get_position()); this->animation_info.sync(this->render_entity->get_animation_path(), std::function(const std::string &)>( @@ -79,6 +82,9 @@ void WorldObject::fetch_updates(const time::time_t &time) { this->last_update); this->angle.sync(this->render_entity->get_angle(), this->last_update); + // Unlock mutex of the render entity + read_lock.unlock(); + // Set self to changed so that world renderer can update the renderable this->changed = true; this->render_entity->clear_changed_flag(); diff --git a/libopenage/renderer/stages/world/object.h b/libopenage/renderer/stages/world/object.h index 1302733954..c5579f62a0 100644 --- a/libopenage/renderer/stages/world/object.h +++ b/libopenage/renderer/stages/world/object.h @@ -29,7 +29,7 @@ class Animation2dInfo; } // namespace resources namespace world { -class WorldRenderEntity; +class RenderEntity; /** * Stores the state of a renderable object in the World render stage. @@ -49,7 +49,7 @@ class WorldObject { * * @param entity New world render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Fetch updates from the render entity. @@ -177,7 +177,7 @@ class WorldObject { * Entity that gets updates from the gamestate, e.g. the position and * requested animation data. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * Reference ID for passing interaction with the graphic (e.g. mouse clicks) back to diff --git a/libopenage/renderer/stages/world/render_entity.cpp b/libopenage/renderer/stages/world/render_entity.cpp index 6909a6554a..7a8e145741 100644 --- a/libopenage/renderer/stages/world/render_entity.cpp +++ b/libopenage/renderer/stages/world/render_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "render_entity.h" @@ -10,20 +10,19 @@ namespace openage::renderer::world { -WorldRenderEntity::WorldRenderEntity() : - changed{false}, +RenderEntity::RenderEntity() : + renderer::RenderEntity{}, ref_id{0}, position{nullptr, 0, "", nullptr, SCENE_ORIGIN}, angle{nullptr, 0, "", nullptr, 0}, - animation_path{nullptr, 0}, - last_update{0.0} { + animation_path{nullptr, 0} { } -void WorldRenderEntity::update(const uint32_t ref_id, - const curve::Continuous &position, - const curve::Segmented &angle, - const std::string animation_path, - const time::time_t time) { +void RenderEntity::update(const uint32_t ref_id, + const curve::Continuous &position, + const curve::Segmented &angle, + const std::string animation_path, + const time::time_t time) { std::unique_lock lock{this->mutex}; this->ref_id = ref_id; @@ -41,10 +40,10 @@ void WorldRenderEntity::update(const uint32_t ref_id, this->last_update = time; } -void WorldRenderEntity::update(const uint32_t ref_id, - const coord::phys3 position, - const std::string animation_path, - const time::time_t time) { +void RenderEntity::update(const uint32_t ref_id, + const coord::phys3 position, + const std::string animation_path, + const time::time_t time) { std::unique_lock lock{this->mutex}; this->ref_id = ref_id; @@ -54,46 +53,28 @@ void WorldRenderEntity::update(const uint32_t ref_id, this->last_update = time; } -uint32_t WorldRenderEntity::get_id() { +uint32_t RenderEntity::get_id() { std::shared_lock lock{this->mutex}; return this->ref_id; } -const curve::Continuous &WorldRenderEntity::get_position() { +const curve::Continuous &RenderEntity::get_position() { std::shared_lock lock{this->mutex}; return this->position; } -const curve::Segmented &WorldRenderEntity::get_angle() { +const curve::Segmented &RenderEntity::get_angle() { std::shared_lock lock{this->mutex}; return this->angle; } -const curve::Discrete &WorldRenderEntity::get_animation_path() { +const curve::Discrete &RenderEntity::get_animation_path() { std::shared_lock lock{this->mutex}; return this->animation_path; } -time::time_t WorldRenderEntity::get_update_time() { - std::shared_lock lock{this->mutex}; - - return this->last_update; -} - -bool WorldRenderEntity::is_changed() { - std::shared_lock lock{this->mutex}; - - return this->changed; -} - -void WorldRenderEntity::clear_changed_flag() { - std::unique_lock lock{this->mutex}; - - this->changed = false; -} - } // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/render_entity.h b/libopenage/renderer/stages/world/render_entity.h index 7c56635f89..ed9011b8a4 100644 --- a/libopenage/renderer/stages/world/render_entity.h +++ b/libopenage/renderer/stages/world/render_entity.h @@ -3,18 +3,14 @@ #pragma once #include -#include -#include #include -#include - #include "coord/phys.h" #include "coord/scene.h" #include "curve/continuous.h" #include "curve/discrete.h" #include "curve/segmented.h" -#include "time/time.h" +#include "renderer/stages/render_entity.h" namespace openage::renderer::world { @@ -22,14 +18,16 @@ namespace openage::renderer::world { /** * Render entity for pushing updates to the World renderer. */ -class WorldRenderEntity { +class RenderEntity final : public renderer::RenderEntity { public: - WorldRenderEntity(); - ~WorldRenderEntity() = default; + RenderEntity(); + ~RenderEntity() = default; /** * Update the render entity with information from the gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param ref_id Game entity ID. * @param position Position of the game entity inside the game world. * @param angle Angle of the game entity inside the game world. @@ -47,6 +45,8 @@ class WorldRenderEntity { * * Update the render entity with information from the gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param ref_id Game entity ID. * @param position Position of the game entity inside the game world. * @param animation_path Path to the animation definition. @@ -60,6 +60,8 @@ class WorldRenderEntity { /** * Get the ID of the corresponding game entity. * + * Accessing the game entity ID is thread-safe. + * * @return Game entity ID. */ uint32_t get_id(); @@ -67,6 +69,9 @@ class WorldRenderEntity { /** * Get the position of the entity inside the game world. * + * Accessing the position curve REQUIRES a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Position curve of the entity. */ const curve::Continuous &get_position(); @@ -74,6 +79,9 @@ class WorldRenderEntity { /** * Get the angle of the entity inside the game world. * + * Accessing the angle curve REQUIRES a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Angle curve of the entity. */ const curve::Segmented &get_angle(); @@ -81,38 +89,14 @@ class WorldRenderEntity { /** * Get the animation definition path. * + * Accessing the animation path curve requires a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Path to the animation definition file. */ const curve::Discrete &get_animation_path(); - /** - * Get the time of the last update. - * - * @return Time of last update. - */ - time::time_t get_update_time(); - - /** - * Check whether the render entity has received new updates from the - * gamestate. - * - * @return true if updates have been received, else false. - */ - bool is_changed(); - - /** - * Clear the update flag by setting it to false. - */ - void clear_changed_flag(); - private: - /** - * Flag for determining if the render entity has been updated by the - * corresponding gamestate entity. Set to true every time \p update() - * is called. - */ - bool changed; - /** * ID of the game entity in the gamestate. */ @@ -132,15 +116,5 @@ class WorldRenderEntity { * Path to the animation definition file. */ curve::Discrete animation_path; - - /** - * Time of the last update call. - */ - time::time_t last_update; - - /** - * Mutex for protecting threaded access. - */ - std::shared_mutex mutex; }; } // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/render_stage.cpp b/libopenage/renderer/stages/world/render_stage.cpp index 0434f3cf07..d8d93279af 100644 --- a/libopenage/renderer/stages/world/render_stage.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -50,7 +50,7 @@ std::shared_ptr WorldRenderStage::get_render_pass() { return this->render_pass; } -void WorldRenderStage::add_render_entity(const std::shared_ptr entity) { +void WorldRenderStage::add_render_entity(const std::shared_ptr entity) { std::unique_lock lock{this->mutex}; auto world_object = std::make_shared(this->asset_manager); diff --git a/libopenage/renderer/stages/world/render_stage.h b/libopenage/renderer/stages/world/render_stage.h index e6ae39c094..f1256f3b57 100644 --- a/libopenage/renderer/stages/world/render_stage.h +++ b/libopenage/renderer/stages/world/render_stage.h @@ -31,7 +31,7 @@ class AssetManager; } namespace world { -class WorldRenderEntity; +class RenderEntity; class WorldObject; /** @@ -74,7 +74,7 @@ class WorldRenderStage { * * @param render_entity New render entity. */ - void add_render_entity(const std::shared_ptr entity); + void add_render_entity(const std::shared_ptr entity); /** * Update the render entities and render positions.