Skip to content

Commit

Permalink
Zephyr: Renderer: do not recreate render bundles every frame (#9)
Browse files Browse the repository at this point in the history
* Zephyr: Renderer: do not recreate render bundles every frame

* Zephyr: Renderer: rename RenderEngine::RenderScene() to RenderEngine::SubmitFrame()

* Zephyr: Renderer: remove unused RenderObject API

* Zephyr: Renderer: move geometry cache fully into RenderScene
  • Loading branch information
fleroviux authored Jun 11, 2024
1 parent 5a69498 commit 1394dac
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 140 deletions.
20 changes: 10 additions & 10 deletions app/next/src/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,23 @@ namespace zephyr {
camera_transform.SetPosition(camera_position);
camera_transform.SetRotation(extrinsic_xyz_angles_to_quaternion({euler_x, euler_y, 0.0f}));

for(SceneNode* cube : m_dynamic_cubes) {
Vector3 position = cube->GetTransform().GetPosition();
position.X() += 0.01;
cube->GetTransform().SetPosition(position);

Quaternion rotation = cube->GetTransform().GetRotation();
rotation = Quaternion::FromAxisAngle({0, 1, 0}, 0.01f) * rotation;
cube->GetTransform().SetRotation(rotation);
}
// for(SceneNode* cube : m_dynamic_cubes) {
// Vector3 position = cube->GetTransform().GetPosition();
// position.X() += 0.01;
// cube->GetTransform().SetPosition(position);
//
// Quaternion rotation = cube->GetTransform().GetRotation();
// rotation = Quaternion::FromAxisAngle({0, 1, 0}, 0.01f) * rotation;
// cube->GetTransform().SetRotation(rotation);
// }

RenderFrame();
}
}

void MainWindow::RenderFrame() {
m_scene_graph->UpdateTransforms();
m_render_engine->RenderScene();
m_render_engine->SubmitFrame();
m_scene_graph->ClearScenePatches();

m_frame++;
Expand Down
38 changes: 32 additions & 6 deletions zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#include <zephyr/math/frustum.hpp>
#include <zephyr/math/matrix4.hpp>
#include <zephyr/float.hpp>
#include <zephyr/hash.hpp>
#include <zephyr/integer.hpp>
#include <EASTL/hash_map.h>
#include <span>

namespace zephyr {
Expand Down Expand Up @@ -40,6 +42,8 @@ namespace zephyr {
public:
virtual ~RenderGeometry() = default;

[[nodiscard]] virtual RenderGeometryLayout GetLayout() const = 0;
[[nodiscard]] virtual size_t GetGeometryID() const = 0;
[[nodiscard]] virtual size_t GetNumberOfVertices() const = 0;
[[nodiscard]] virtual size_t GetNumberOfIndices() const = 0;
};
Expand All @@ -56,13 +60,25 @@ namespace zephyr {
Frustum frustum{};
};

struct RenderObject {
RenderGeometry* render_geometry{};
Matrix4 local_to_world{};
};

class RenderBackend {
public:
struct RenderBundleKey {
bool uses_ibo;
u32 geometry_layout;

[[nodiscard]] bool operator==(const RenderBundleKey& other) const {
return uses_ibo == other.uses_ibo && geometry_layout == other.geometry_layout;
}
};

struct RenderBundleItem {
RenderBundleItem(const Matrix4& local_to_world, u32 draw_command_id, u32 material_id) : local_to_world{local_to_world}, draw_command_id{draw_command_id}, material_id{material_id} {}
Matrix4 local_to_world;
u32 draw_command_id;
u32 material_id;
u32 padding[2]; // Padding for std430 buffer layout
};

virtual ~RenderBackend() = default;

/// Needs to be called from the render thread before performing any render operations.
Expand All @@ -78,10 +94,20 @@ namespace zephyr {
virtual void DestroyRenderGeometry(RenderGeometry* render_geometry) = 0;

/// Just a quick thing for testing the rendering.
virtual void Render(const RenderCamera& render_camera, std::span<const RenderObject> render_objects) = 0;
virtual void Render(const RenderCamera& render_camera, const eastl::hash_map<RenderBundleKey, std::vector<RenderBundleItem>>& render_bundles) = 0;

/// Start rendering the next frame.
virtual void SwapBuffers() = 0;
};

} // namespace zephyr

template<>
struct eastl::hash<zephyr::RenderBackend::RenderBundleKey> {
[[nodiscard]] std::size_t operator()(const zephyr::RenderBackend::RenderBundleKey& key) const noexcept {
size_t h = 0;
zephyr::hash_combine(h, key.uses_ibo);
zephyr::hash_combine(h, key.geometry_layout);
return h;
}
};
16 changes: 2 additions & 14 deletions zephyr/renderer/include/zephyr/renderer/render_engine.hpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@

#pragma once

#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/renderer/render_scene.hpp>
#include <zephyr/scene/scene_graph.hpp>
#include <zephyr/scene/scene_node.hpp>
#include <EASTL/hash_map.h>
#include <atomic>
#include <optional>
#include <semaphore>
#include <thread>
#include <typeindex>
#include <vector>

namespace zephyr {

Expand All @@ -24,7 +16,7 @@ namespace zephyr {
~RenderEngine();

void SetSceneGraph(std::shared_ptr<SceneGraph> scene_graph);
void RenderScene();
void SubmitFrame();

private:
void CreateRenderThread();
Expand All @@ -40,11 +32,7 @@ namespace zephyr {
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;

class RenderScene m_render_scene{}; //< Representation of the scene graph that is internal to the render engine.

std::vector<RenderObject> m_render_objects{};
RenderScene m_render_scene; //< Representation of the scene graph that is internal to the render engine.
RenderCamera m_render_camera{};
};

Expand Down
33 changes: 30 additions & 3 deletions zephyr/renderer/include/zephyr/renderer/render_scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ namespace zephyr {

class RenderScene {
public:
explicit RenderScene(std::shared_ptr<RenderBackend> render_backend);

// Game Thread API:
void SetSceneGraph(std::shared_ptr<SceneGraph> scene_graph);
void Update();
void GetRenderObjects(std::vector<RenderObject>& out_render_objects, const GeometryCache& geometry_cache);
void UpdateStage1();

// Render Thread API:
void UpdateStage2();
void GetRenderCamera(RenderCamera& out_render_camera);
void UpdateGeometries(GeometryCache& geometry_cache);
[[nodiscard]] const eastl::hash_map<RenderBackend::RenderBundleKey, std::vector<RenderBackend::RenderBundleItem>>& GetRenderBundles();

private:
using Entity = u32;
Expand All @@ -45,6 +50,22 @@ namespace zephyr {
Frustum frustum;
};

struct RenderScenePatch {
enum class Type : u8 {
MeshMounted,
MeshRemoved,
TransformChanged
};

Type type;
EntityID entity_id;
};

struct RenderBundleItemLocation {
RenderBackend::RenderBundleKey key;
size_t index;
};

void RebuildScene();
void PatchScene();
void PatchNodeMounted(SceneNode* node);
Expand All @@ -59,6 +80,8 @@ namespace zephyr {
void DestroyEntity(EntityID entity_id);
void ResizeComponentStorage(size_t capacity);

GeometryCache m_geometry_cache;

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{};
Expand All @@ -71,6 +94,10 @@ namespace zephyr {
std::vector<Camera> m_components_camera{};
std::vector<EntityID> m_view_mesh{};
std::vector<EntityID> m_view_camera{};

std::vector<RenderScenePatch> m_render_scene_patches{};
eastl::hash_map<EntityID, RenderBundleItemLocation> m_entity_to_render_item_location{};
eastl::hash_map<RenderBackend::RenderBundleKey, std::vector<RenderBackend::RenderBundleItem>> m_render_bundles{};
};

} // namespace zephyr
15 changes: 3 additions & 12 deletions zephyr/renderer/src/backend/opengl/render_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ namespace zephyr {
glEnable(GL_DEPTH_TEST);

m_render_geometry_manager = std::make_unique<OpenGLRenderGeometryManager>();

SDL_GL_SetSwapInterval(0);
}

void OpenGLRenderBackend::DestroyContext() {
Expand Down Expand Up @@ -84,18 +86,7 @@ namespace zephyr {
m_render_geometry_manager->DestroyRenderGeometry(render_geometry);
}

void OpenGLRenderBackend::Render(const RenderCamera& render_camera, std::span<const RenderObject> render_objects) {
std::unordered_map<RenderBundleKey, std::vector<RenderBundleItem>> render_bundles;

for(const RenderObject& render_object : render_objects) {
// TODO(fleroviux): get rid of unsafe size_t to u32 conversion.
auto render_geometry = dynamic_cast<OpenGLRenderGeometry*>(render_object.render_geometry);
RenderBundleKey render_bundle_key{};
render_bundle_key.uses_ibo = render_geometry->GetNumberOfIndices();
render_bundle_key.geometry_layout = render_geometry->GetLayout().key;
render_bundles[render_bundle_key].emplace_back(render_object.local_to_world, (u32)render_geometry->GetGeometryID(), (u32)0u);
}

void OpenGLRenderBackend::Render(const RenderCamera& render_camera, const eastl::hash_map<RenderBundleKey, std::vector<RenderBundleItem>>& render_bundles) {
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Expand Down
31 changes: 1 addition & 30 deletions zephyr/renderer/src/backend/opengl/render_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,18 @@
#pragma once

#include <zephyr/renderer/backend/render_backend.hpp>
#include <zephyr/hash.hpp>
#include <zephyr/panic.hpp>
#include <GL/glew.h>
#include <GL/gl.h>
#include <SDL.h>
#include <SDL_opengl.h>
#include <unordered_map>

#include "render_geometry/render_geometry_manager.hpp"

namespace zephyr {

class OpenGLRenderBackend final : public RenderBackend {
public:
struct RenderBundleKey {
bool uses_ibo;
u32 geometry_layout;

[[nodiscard]] bool operator==(const RenderBundleKey& other) const {
return uses_ibo == other.uses_ibo && geometry_layout == other.geometry_layout;
}
};

explicit OpenGLRenderBackend(SDL_Window* sdl2_window);

void InitializeContext() override;
Expand All @@ -36,19 +25,11 @@ namespace zephyr {
void UpdateRenderGeometryAABB(RenderGeometry* render_geometry, const Box3& aabb) override;
void DestroyRenderGeometry(RenderGeometry* render_geometry) override;

void Render(const RenderCamera& render_camera, std::span<const RenderObject> render_objects) override;
void Render(const RenderCamera& render_camera, const eastl::hash_map<RenderBundleKey, std::vector<RenderBundleItem>>& render_bundles) override;

void SwapBuffers() override;

private:
struct RenderBundleItem {
RenderBundleItem(const Matrix4& local_to_world, u32 draw_command_id, u32 material_id) : local_to_world{local_to_world}, draw_command_id{draw_command_id}, material_id{material_id} {}
Matrix4 local_to_world;
u32 draw_command_id;
u32 material_id;
u32 padding[2]; // Padding for std430 buffer layout
};

static constexpr u32 k_max_draws_per_draw_call = 16384;

void CreateDrawShaderProgram();
Expand Down Expand Up @@ -77,13 +58,3 @@ namespace zephyr {
}

} // namespace zephyr

template<>
struct std::hash<zephyr::OpenGLRenderBackend::RenderBundleKey> {
[[nodiscard]] std::size_t operator()(const zephyr::OpenGLRenderBackend::RenderBundleKey& key) const noexcept {
size_t h = 0;
zephyr::hash_combine(h, key.uses_ibo);
zephyr::hash_combine(h, key.geometry_layout);
return h;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ namespace zephyr {

~OpenGLRenderGeometry() override;

[[nodiscard]] RenderGeometryLayout GetLayout() const {
[[nodiscard]] RenderGeometryLayout GetLayout() const override {
return m_layout;
}

[[nodiscard]] size_t GetGeometryID() const {
[[nodiscard]] size_t GetGeometryID() const override {
return m_geometry_render_data_allocation.base_element;
}

Expand Down
33 changes: 2 additions & 31 deletions zephyr/renderer/src/backend/vulkan/render_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,37 +77,8 @@ namespace zephyr {
ZEPHYR_PANIC("unimplemented");
}

void Render(const RenderCamera& render_camera, std::span<const RenderObject> render_objects) override {
const VkClearValue clear_value{
.color = VkClearColorValue{
.float32 = {0.01f, 0.01f, 0.01f, 1.0f}
}
};

const VkRenderPassBeginInfo render_pass_begin_info{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = m_vk_render_pass,
.framebuffer = m_vk_swap_chain_fbs[m_vk_swap_chain_image_index],
.renderArea = VkRect2D{
.offset = VkOffset2D{0u, 0u},
.extent = VkExtent2D{1920u, 1080u}
},
.clearValueCount = 1u,
.pClearValues = &clear_value
};

const Matrix4 view_projection = render_camera.projection * render_camera.view;
vkCmdBeginRenderPass(m_vk_command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(m_vk_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vk_pipeline);
vkCmdPushConstants(m_vk_command_buffer, m_vk_pipeline_layout, VK_SHADER_STAGE_ALL, 0u, sizeof(Matrix4), &view_projection);

for(const RenderObject& render_object : render_objects) {
vkCmdPushConstants(m_vk_command_buffer, m_vk_pipeline_layout, VK_SHADER_STAGE_ALL, sizeof(Matrix4), sizeof(Matrix4), &render_object.local_to_world);
vkCmdDraw(m_vk_command_buffer, 3u, 1u, 0u, 0u);
}

vkCmdEndRenderPass(m_vk_command_buffer);
void Render(const RenderCamera& render_camera, const eastl::hash_map<RenderBundleKey, std::vector<RenderBundleItem>>& render_bundles) override {
ZEPHYR_PANIC("unimplemented");
}

void SwapBuffers() override {
Expand Down
Loading

0 comments on commit 1394dac

Please sign in to comment.