Skip to content

Commit

Permalink
Zephyr: Renderer: fix bugs in and optimize internal scene representation
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed Jun 5, 2024
1 parent cd32996 commit ec34612
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 174 deletions.
2 changes: 2 additions & 0 deletions zephyr/renderer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(SOURCES
src/engine/geometry_cache.cpp
src/vulkan/vulkan_instance.cpp
src/render_engine.cpp
src/render_scene.cpp
)

set(HEADERS
Expand All @@ -37,6 +38,7 @@ set(HEADERS_PUBLIC
include/zephyr/renderer/vulkan/vulkan_instance.hpp
include/zephyr/renderer/vulkan/vulkan_physical_device.hpp
include/zephyr/renderer/render_engine.hpp
include/zephyr/renderer/render_scene.hpp
)

find_package(SDL2 REQUIRED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace zephyr {

// Render Thread API:
void ProcessPendingUpdates();
RenderGeometry* GetCachedRenderGeometry(const Geometry* geometry);
RenderGeometry* GetCachedRenderGeometry(const Geometry* geometry) const;

private:
struct GeometryState {
Expand Down Expand Up @@ -49,7 +49,7 @@ namespace zephyr {

std::shared_ptr<RenderBackend> m_render_backend;
std::unordered_map<const Geometry*, GeometryState> m_geometry_state_table{};
std::unordered_map<const Geometry*, RenderGeometry*> m_render_geometry_table{};
mutable std::unordered_map<const Geometry*, RenderGeometry*> m_render_geometry_table{};
std::vector<UploadTask> m_upload_tasks{};
std::vector<DeleteTask> m_delete_tasks[2]{};
};
Expand Down
38 changes: 4 additions & 34 deletions zephyr/renderer/include/zephyr/renderer/render_engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <zephyr/renderer/backend/render_backend.hpp>
#include <zephyr/renderer/engine/geometry_cache.hpp>
#include <zephyr/renderer/resource/geometry.hpp>
#include <zephyr/renderer/render_scene.hpp>
#include <zephyr/scene/scene_graph.hpp>
#include <zephyr/scene/scene_node.hpp>
#include <EASTL/hash_map.h>
Expand All @@ -26,31 +27,6 @@ namespace zephyr {
void RenderScene();

private:
struct SceneNodeMeshData {
SceneNodeMeshData(const Matrix4& local_to_world, Geometry* geometry) : local_to_world{local_to_world}, geometry{geometry} {}
Matrix4 local_to_world;
Geometry* geometry;
};

struct SceneNodeData {
std::optional<size_t> mesh_data_id{};
std::optional<size_t> camera_data_id{};

[[nodiscard]] bool Empty() const {
return !mesh_data_id.has_value() &&
!camera_data_id.has_value();
}
};

// Methods used for translating the scene graph into our internal representation.
void RebuildScene();
void PatchScene();
void PatchNodeMounted(SceneNode* node);
void PatchNodeRemoved(SceneNode* node);
void PatchNodeComponentMounted(SceneNode* node, std::type_index component_type);
void PatchNodeComponentRemoved(SceneNode* node, std::type_index component_type);
void PatchNodeTransformChanged(SceneNode* node);

void CreateRenderThread();
void JoinRenderThread();
void RenderThreadMain();
Expand All @@ -61,18 +37,12 @@ namespace zephyr {
std::thread m_render_thread;
std::atomic_bool m_render_thread_running;
std::atomic_bool m_render_thread_is_waiting;
std::binary_semaphore m_caller_thread_semaphore{0}; //> Semaphore signalled by the calling thread
std::binary_semaphore m_render_thread_semaphore{1}; //> Semaphore signalled by the rendering thread
std::binary_semaphore m_caller_thread_semaphore{0}; //< Semaphore signalled by the calling thread
std::binary_semaphore m_render_thread_semaphore{1}; //< Semaphore signalled by the rendering thread

GeometryCache m_geometry_cache;

std::shared_ptr<SceneGraph> m_current_scene_graph{};
bool m_need_scene_rebuild{};

// Representation of the scene graph that is internal to the render engine.
std::vector<SceneNodeMeshData> m_scene_node_mesh_data{};
std::vector<RenderCamera> m_scene_node_camera_data{};
eastl::hash_map<const SceneNode*, SceneNodeData> m_scene_node_data{};
class RenderScene m_render_scene{}; //< Representation of the scene graph that is internal to the render engine.

std::vector<RenderObject> m_render_objects{};
RenderCamera m_render_camera{};
Expand Down
76 changes: 76 additions & 0 deletions zephyr/renderer/include/zephyr/renderer/render_scene.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

#pragma once

#include <zephyr/math/frustum.hpp>
#include <zephyr/math/matrix4.hpp>
#include <zephyr/renderer/backend/render_backend.hpp>
#include <zephyr/renderer/engine/geometry_cache.hpp>
#include <zephyr/renderer/resource/geometry.hpp>
#include <zephyr/scene/scene_graph.hpp>
#include <zephyr/integer.hpp>
#include <EASTL/hash_map.h>
#include <EASTL/hash_set.h>
#include <memory>
#include <vector>

namespace zephyr {

class RenderScene {
public:
void SetSceneGraph(std::shared_ptr<SceneGraph> scene_graph);
void Update();
void GetRenderObjects(std::vector<RenderObject>& out_render_objects, const GeometryCache& geometry_cache);
void GetRenderCamera(RenderCamera& out_render_camera);
void UpdateGeometries(GeometryCache& geometry_cache);

private:
using Entity = u32;
using EntityID = size_t;

enum ComponentFlag : Entity {
COMPONENT_FLAG_MESH = 1ul << 0,
COMPONENT_FLAG_CAMERA = 1ul << 1
};

struct Transform {
Matrix4 local_to_world;
};

struct Mesh {
const Geometry* geometry;
};

struct Camera {
Matrix4 projection;
Frustum frustum;
};

void RebuildScene();
void PatchScene();
void PatchNodeMounted(SceneNode* node);
void PatchNodeRemoved(SceneNode* node);
void PatchNodeComponentMounted(SceneNode* node, std::type_index component_type);
void PatchNodeComponentRemoved(SceneNode* node, std::type_index component_type);
void PatchNodeTransformChanged(SceneNode* node);

EntityID GetOrCreateEntityForNode(const SceneNode* node);

EntityID CreateEntity();
void DestroyEntity(EntityID entity_id);
void ResizeComponentStorage(size_t capacity);

std::shared_ptr<SceneGraph> m_current_scene_graph{};
eastl::hash_map<const SceneNode*, EntityID> m_node_entity_map{};
eastl::hash_set<const Geometry*> m_active_geometry_set{};
bool m_require_full_rebuild{};

std::vector<Entity> m_entities{};
std::vector<EntityID> m_free_entity_list{};
std::vector<Transform> m_components_transform{};
std::vector<Mesh> m_components_mesh{};
std::vector<Camera> m_components_camera{};
std::vector<EntityID> m_view_mesh{};
std::vector<EntityID> m_view_camera{};
};

} // namespace zephyr
2 changes: 1 addition & 1 deletion zephyr/renderer/src/engine/geometry_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ namespace zephyr {
ProcessPendingUploads();
}

RenderGeometry* GeometryCache::GetCachedRenderGeometry(const Geometry* geometry) {
RenderGeometry* GeometryCache::GetCachedRenderGeometry(const Geometry* geometry) const {
if(!m_render_geometry_table.contains(geometry)) {
ZEPHYR_PANIC("Bad attempt to retrieve cached render geometry of a geometry which isn't cached.")
}
Expand Down
142 changes: 5 additions & 137 deletions zephyr/renderer/src/render_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ namespace zephyr {
}

void RenderEngine::SetSceneGraph(std::shared_ptr<SceneGraph> scene_graph) {
if(m_current_scene_graph != scene_graph) {
m_current_scene_graph = std::move(scene_graph);
m_need_scene_rebuild = true;
}
m_render_scene.SetSceneGraph(std::move(scene_graph));
}

void RenderEngine::RenderScene() {
Expand All @@ -31,132 +28,15 @@ namespace zephyr {
m_geometry_cache.CommitPendingDeleteTaskList();

// Update the internal scene graph representation of the render engine based on changes in the scene graph.
if(m_need_scene_rebuild) {
RebuildScene();
m_need_scene_rebuild = false;
} else {
PatchScene();
}
m_render_scene.Update();

// Update all geometries which might be rendered in this frame.
for(const auto& game_thread_render_object : m_scene_node_mesh_data) {
m_geometry_cache.UpdateGeometry(game_thread_render_object.geometry);
}
m_render_scene.UpdateGeometries(m_geometry_cache);

// Signal to the render thread that the next frame is ready
m_caller_thread_semaphore.release();
}

void RenderEngine::RebuildScene() {
m_scene_node_data.clear();
m_scene_node_mesh_data.clear();
m_scene_node_camera_data.clear();

m_current_scene_graph->GetRoot()->Traverse([this](SceneNode* child_node) {
if(child_node->IsVisible()) {
PatchNodeMounted(child_node);
return true;
}
return false;
});
}

void RenderEngine::PatchScene() {
for(const ScenePatch& patch : m_current_scene_graph->GetScenePatches()) {
switch(patch.type) {
case ScenePatch::Type::NodeMounted: PatchNodeMounted(patch.node.get()); break;
case ScenePatch::Type::NodeRemoved: PatchNodeRemoved(patch.node.get()); break;
case ScenePatch::Type::ComponentMounted: PatchNodeComponentMounted(patch.node.get(), patch.component_type); break;
case ScenePatch::Type::ComponentRemoved: PatchNodeComponentRemoved(patch.node.get(), patch.component_type); break;
case ScenePatch::Type::NodeTransformChanged: PatchNodeTransformChanged(patch.node.get()); break;
default: ZEPHYR_PANIC("Unhandled scene patch type: {}", (int)patch.type);
}
}
}

void RenderEngine::PatchNodeMounted(SceneNode* node) {
for(auto& [component_type, _] : node->GetComponents()) {
PatchNodeComponentMounted(node, component_type);
}
}

void RenderEngine::PatchNodeRemoved(SceneNode* node) {
// TODO(fleroviux): this could be optimized, just unload everything.
for(auto& [component_type, _] : node->GetComponents()) {
PatchNodeComponentRemoved(node, component_type);
}
}

void RenderEngine::PatchNodeComponentMounted(SceneNode* node, std::type_index component_type) {
if(component_type == typeid(MeshComponent)) {
SceneNodeData& node_data = m_scene_node_data[node];
auto& mesh_component = node->GetComponent<MeshComponent>();

node_data.mesh_data_id = m_scene_node_mesh_data.size();
m_scene_node_mesh_data.emplace_back(node->GetTransform().GetWorld(), mesh_component.geometry.get());
}

if(component_type == typeid(PerspectiveCameraComponent)) {
SceneNodeData& node_data = m_scene_node_data[node];
auto& camera_component = node->GetComponent<PerspectiveCameraComponent>();

node_data.camera_data_id = m_scene_node_camera_data.size();
m_scene_node_camera_data.push_back({
.projection = camera_component.GetProjectionMatrix(),
.view = node->GetTransform().GetWorld().Inverse(),
.frustum = camera_component.GetFrustum()
});
}
}

void RenderEngine::PatchNodeComponentRemoved(SceneNode* node, std::type_index component_type) {
const auto node_data_match = m_scene_node_data.find(node);

if(node_data_match == m_scene_node_data.end()) {
return;
}

SceneNodeData& node_data = node_data_match->second;

if(component_type == typeid(MeshComponent)) {
if(node_data.mesh_data_id.has_value()) {
// TODO(fleroviux): this breaks indices in other SceneNodeData
m_scene_node_mesh_data.erase(m_scene_node_mesh_data.begin() + node_data.mesh_data_id.value());
node_data.mesh_data_id.reset();
}
}

if(component_type == typeid(PerspectiveCameraComponent)) {
if(node_data.camera_data_id.has_value()) {
// TODO(fleroviux): this breaks indices in other SceneNodeData
m_scene_node_camera_data.erase(m_scene_node_camera_data.begin() + node_data.camera_data_id.value());
node_data.camera_data_id.reset();
}
}

if(m_scene_node_data[node].Empty()) {
m_scene_node_data.erase(node);
}
}

void RenderEngine::PatchNodeTransformChanged(SceneNode* node) {
const auto node_data_match = m_scene_node_data.find(node);

if(node_data_match == m_scene_node_data.end()) {
return;
}

const SceneNodeData& node_data = node_data_match->second;

if(node_data.mesh_data_id.has_value()) {
m_scene_node_mesh_data[node_data.mesh_data_id.value()].local_to_world = node->GetTransform().GetWorld();
}

if(node_data.camera_data_id.has_value()) {
m_scene_node_camera_data[node_data.camera_data_id.value()].view = node->GetTransform().GetWorld().Inverse();
}
}

void RenderEngine::CreateRenderThread() {
m_render_thread_running = true;
m_render_thread_is_waiting = false;
Expand Down Expand Up @@ -191,20 +71,8 @@ namespace zephyr {
m_render_thread_is_waiting = false;

m_geometry_cache.ProcessPendingUpdates();

m_render_objects.clear();

for(const auto& scene_node_mesh_data : m_scene_node_mesh_data) {
m_render_objects.push_back({
.render_geometry = m_geometry_cache.GetCachedRenderGeometry(scene_node_mesh_data.geometry),
.local_to_world = scene_node_mesh_data.local_to_world
});
}

if(m_scene_node_camera_data.empty()) {
ZEPHYR_PANIC("Scene graph does not contain a camera to render with.");
}
m_render_camera = m_scene_node_camera_data[0];
m_render_scene.GetRenderObjects(m_render_objects, m_geometry_cache);
m_render_scene.GetRenderCamera(m_render_camera);

// Signal to the caller thread that we are done reading the internal render structures.
m_render_thread_semaphore.release();
Expand Down
Loading

0 comments on commit ec34612

Please sign in to comment.