Skip to content

Commit

Permalink
Zephyr: Renderer: do not recreate render bundles every frame
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed Jun 8, 2024
1 parent 5a69498 commit db6ef97
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 45 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,15 +95,15 @@ 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();
}
Expand Down Expand Up @@ -275,7 +275,7 @@ namespace zephyr {
};
std::copy_n(index_data, sizeof(index_data) / sizeof(u32), indices.begin());

const int grid_size = 37;
const int grid_size = 64;

for(int x = -grid_size / 2; x < grid_size / 2; x++) {
for(int y = -grid_size / 2; y < grid_size / 2; y++) {
Expand Down
32 changes: 32 additions & 0 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 @@ -63,6 +67,23 @@ namespace zephyr {

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 @@ -79,9 +100,20 @@ namespace zephyr {

/// 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;
}
};
26 changes: 26 additions & 0 deletions zephyr/renderer/include/zephyr/renderer/render_scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ namespace zephyr {
void GetRenderCamera(RenderCamera& out_render_camera);
void UpdateGeometries(GeometryCache& geometry_cache);

void UpdateRenderBundles(const GeometryCache& geometry_cache);

[[nodiscard]] const eastl::hash_map<RenderBackend::RenderBundleKey, std::vector<RenderBackend::RenderBundleItem>>& GetRenderBundles() {
return m_render_bundles;
}

private:
using Entity = u32;
using EntityID = size_t;
Expand All @@ -45,6 +51,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 @@ -71,6 +93,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
8 changes: 7 additions & 1 deletion 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 @@ -85,7 +87,7 @@ namespace zephyr {
}

void OpenGLRenderBackend::Render(const RenderCamera& render_camera, std::span<const RenderObject> render_objects) {
std::unordered_map<RenderBundleKey, std::vector<RenderBundleItem>> render_bundles;
eastl::hash_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.
Expand All @@ -96,6 +98,10 @@ namespace zephyr {
render_bundles[render_bundle_key].emplace_back(render_object.local_to_world, (u32)render_geometry->GetGeometryID(), (u32)0u);
}

Render(render_camera, render_bundles);
}

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
30 changes: 1 addition & 29 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 @@ -37,18 +26,11 @@ namespace zephyr {
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 +59,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
4 changes: 4 additions & 0 deletions zephyr/renderer/src/backend/vulkan/render_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ namespace zephyr {
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 {
// Complete command recording and mark the command buffer as ready for queue submission.
vkEndCommandBuffer(m_vk_command_buffer);
Expand Down
5 changes: 3 additions & 2 deletions zephyr/renderer/src/render_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ namespace zephyr {
while(m_render_thread_running) {
ReadyRenderThreadData();

m_render_backend->Render(m_render_camera, m_render_objects);
// m_render_backend->Render(m_render_camera, m_render_objects);
m_render_backend->Render(m_render_camera, m_render_scene.GetRenderBundles());
m_render_backend->SwapBuffers();
}

Expand All @@ -71,7 +72,7 @@ namespace zephyr {
m_render_thread_is_waiting = false;

m_geometry_cache.ProcessPendingUpdates();
m_render_scene.GetRenderObjects(m_render_objects, m_geometry_cache);
m_render_scene.UpdateRenderBundles(m_geometry_cache);
m_render_scene.GetRenderCamera(m_render_camera);

// Signal to the caller thread that we are done reading the internal render structures.
Expand Down
53 changes: 52 additions & 1 deletion zephyr/renderer/src/render_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,52 @@ namespace zephyr {
}
}

void RenderScene::UpdateRenderBundles(const GeometryCache& geometry_cache) {
for(const RenderScenePatch& render_scene_patch : m_render_scene_patches) {
switch(render_scene_patch.type) {
case RenderScenePatch::Type::MeshMounted: {
const Transform& entity_transform = m_components_transform[render_scene_patch.entity_id];
const Mesh& entity_mesh = m_components_mesh[render_scene_patch.entity_id];

// TODO(fleroviux): get rid of unsafe size_t to u32 conversion.
const RenderGeometry* const render_geometry = geometry_cache.GetCachedRenderGeometry(entity_mesh.geometry);
RenderBackend::RenderBundleKey render_bundle_key{};
render_bundle_key.uses_ibo = render_geometry->GetNumberOfIndices();
render_bundle_key.geometry_layout = render_geometry->GetLayout().key;

std::vector<RenderBackend::RenderBundleItem>& render_bundle = m_render_bundles[render_bundle_key];
render_bundle.emplace_back(entity_transform.local_to_world, (u32)render_geometry->GetGeometryID(), (u32)0u);

m_entity_to_render_item_location[render_scene_patch.entity_id] = { render_bundle_key, render_bundle.size() - 1u };
break;
}
case RenderScenePatch::Type::MeshRemoved: {
const auto match = m_entity_to_render_item_location.find(render_scene_patch.entity_id);
const RenderBundleItemLocation& location = match->second;

std::vector<RenderBackend::RenderBundleItem>& render_bundle = m_render_bundles[location.key];
render_bundle.erase(render_bundle.begin() + location.index);

m_entity_to_render_item_location.erase(match);
break;
}
case RenderScenePatch::Type::TransformChanged: {
const auto match = m_entity_to_render_item_location.find(render_scene_patch.entity_id);

if(match != m_entity_to_render_item_location.end()) {
const RenderBundleItemLocation& location = match->second;

m_render_bundles[location.key][location.index].local_to_world = m_components_transform[render_scene_patch.entity_id].local_to_world;
}
break;
}
default: ZEPHYR_PANIC("unhandled patch type: {}", (int)render_scene_patch.type);
}
}

m_render_scene_patches.clear();
}

void RenderScene::RebuildScene() {
m_node_entity_map.clear();
m_entities.clear();
Expand Down Expand Up @@ -106,6 +152,7 @@ namespace zephyr {
m_entities[entity_id] |= COMPONENT_FLAG_MESH;
m_view_mesh.push_back(entity_id);
m_active_geometry_set.insert(entity_mesh.geometry);
m_render_scene_patches.push_back({.type = RenderScenePatch::Type::MeshMounted, .entity_id = entity_id});
}

if(component_type == typeid(PerspectiveCameraComponent)) {
Expand All @@ -128,6 +175,7 @@ namespace zephyr {
m_entities[entity_id] &= ~COMPONENT_FLAG_MESH;
m_view_mesh.erase(std::ranges::find(m_view_mesh, entity_id));
m_active_geometry_set.erase(m_components_mesh[entity_id].geometry);
m_render_scene_patches.push_back({.type = RenderScenePatch::Type::MeshRemoved, .entity_id = entity_id});
did_remove_component = true;
}

Expand All @@ -153,8 +201,11 @@ namespace zephyr {
return;
}

Transform& entity_transform = m_components_transform[node_and_entity_id->second];
const EntityID entity_id = node_and_entity_id->second;

Transform& entity_transform = m_components_transform[entity_id];
entity_transform.local_to_world = node->GetTransform().GetWorld();
m_render_scene_patches.push_back({.type = RenderScenePatch::Type::TransformChanged, .entity_id = entity_id});
}

RenderScene::EntityID RenderScene::GetOrCreateEntityForNode(const SceneNode* node) {
Expand Down

0 comments on commit db6ef97

Please sign in to comment.