Skip to content

Commit e74c4a5

Browse files
authored
Refactor renderers (#75)
* Store all scene rendering resources in SceneRenderer - Remove `Drawer`. * Improve instance buffers - Add `BufferView::count`, defaulted to 1. - Add `Buffer::Type::eInstance` for vertex buffers with instance data. - Make `Buffer::write()` strongly typed and take a span to store the count. - Add `StaticMesh::draw()`. - Update gltf2cpp. * Add MorphWeights, refactor Animation * Move Fps and Stats to Engine - Remove `Renderer::draw()`, store its stats in `SceneRenderer` + `Engine`. * Use SceneRenderer to bind instance buffers - Remove MeshView. * Incorporate primitive topology * Add RenderMode - Replace `Scene::pipeline_state` with `render_mode`. - Remove `Pipeline::State::line_width`. - Reflect `StaticMesh::topology()` in inspector.
1 parent d79435f commit e74c4a5

30 files changed

+372
-268
lines changed

lib/engine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ endif()
3939
target_sources(${PROJECT_NAME} PRIVATE
4040
include/${target_prefix}/engine/engine.hpp
4141
include/${target_prefix}/engine/scene_renderer.hpp
42+
include/${target_prefix}/engine/stats.hpp
4243

4344
include/${target_prefix}/engine/editor/browse_file.hpp
4445
include/${target_prefix}/engine/editor/common.hpp

lib/engine/include/facade/engine/engine.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22
#include <facade/build_version.hpp>
3+
#include <facade/engine/stats.hpp>
34
#include <facade/glfw/glfw.hpp>
45
#include <facade/scene/load_status.hpp>
56
#include <facade/scene/scene.hpp>
@@ -101,6 +102,9 @@ class Engine {
101102
glm::uvec2 window_extent() const;
102103
glm::uvec2 framebuffer_extent() const;
103104

105+
Stats const& stats() const;
106+
std::string_view gpu_name() const;
107+
104108
Scene& scene() const;
105109
State const& state() const;
106110
Input const& input() const;

lib/engine/include/facade/engine/scene_renderer.hpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,44 @@ class Skybox;
88

99
class SceneRenderer {
1010
public:
11+
struct Info {
12+
std::uint32_t triangles_drawn{};
13+
std::uint32_t draw_calls{};
14+
};
15+
1116
explicit SceneRenderer(Gfx const& gfx);
1217

18+
Info const& info() const { return m_info; }
1319
void render(Scene const& scene, Ptr<Skybox const> skybox, Renderer& renderer, vk::CommandBuffer cb);
1420

1521
private:
1622
void write_view(glm::vec2 const extent);
1723
void update_view(Pipeline& out_pipeline) const;
18-
std::span<glm::mat4x4 const> make_instances(Node const& node, glm::mat4x4 const& parent) const;
24+
std::span<glm::mat4x4 const> make_instance_mats(Node const& node, glm::mat4x4 const& parent);
25+
26+
void render(Renderer& renderer, vk::CommandBuffer cb, Skybox const& skybox);
27+
void render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 parent = matrix_identity_v);
28+
void draw(vk::CommandBuffer cb, StaticMesh const& static_mesh, std::span<glm::mat4x4 const> mats);
29+
30+
BufferView next_instances(std::span<glm::mat4x4 const> mats);
31+
32+
struct Instances {
33+
std::vector<Buffer> buffers{};
34+
std::size_t index{};
1935

20-
void render(Renderer& renderer, vk::CommandBuffer cb, Skybox const& skybox) const;
21-
void render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 parent = matrix_identity_v) const;
36+
void rotate();
37+
};
2238

2339
Gfx m_gfx;
40+
Instances m_instances{};
2441
Sampler m_sampler;
2542
Buffer m_view_proj;
2643
Buffer m_dir_lights;
2744
Texture m_white;
2845
Texture m_black;
46+
Info m_info{};
2947

30-
mutable std::vector<glm::mat4x4> m_instance_mats{};
48+
std::vector<glm::mat4x4> m_instance_mats{};
3149
Scene const* m_scene{};
3250
};
3351
} // namespace facade
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
#include <vulkan/vulkan.hpp>
3+
#include <string_view>
4+
5+
namespace facade {
6+
///
7+
/// \brief Stores various statistics.
8+
///
9+
struct Stats {
10+
///
11+
/// \brief Total frames so far.
12+
///
13+
std::uint64_t frame_counter{};
14+
15+
///
16+
/// \brief Triangles drawn in previous frame.
17+
///
18+
std::uint64_t triangles{};
19+
20+
///
21+
/// \brief Draw calls in previous frame.
22+
///
23+
std::uint32_t draw_calls{};
24+
25+
///
26+
/// \brief Framerate (until previous frame).
27+
///
28+
std::uint32_t fps{};
29+
30+
///
31+
/// \brief Current present mode.
32+
///
33+
vk::PresentModeKHR mode{};
34+
35+
///
36+
/// \brief Multi-sampled anti-aliasing level.
37+
///
38+
vk::SampleCountFlagBits current_msaa{};
39+
///
40+
/// \brief Supported anti-aliasing levels.
41+
///
42+
vk::SampleCountFlags supported_msaa{};
43+
};
44+
} // namespace facade

lib/engine/src/editor/inspector.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ void edit_material(SceneResources const& resources, UnlitMaterial& unlit) {
3131
if (unlit.texture) { name = FixedString<128>{"{} ({})", resources.textures[*unlit.texture].name(), *unlit.texture}; }
3232
make_id_slot(unlit.texture, "Texture", name.c_str(), {true});
3333
}
34+
35+
constexpr std::string_view to_str(vk::PrimitiveTopology const topo) {
36+
switch (topo) {
37+
case vk::PrimitiveTopology::ePointList: return "Point list";
38+
case vk::PrimitiveTopology::eLineList: return "Line list";
39+
case vk::PrimitiveTopology::eLineStrip: return "Line strip";
40+
case vk::PrimitiveTopology::eTriangleList: return "Triangle list";
41+
case vk::PrimitiveTopology::eTriangleStrip: return "Triangle strip";
42+
case vk::PrimitiveTopology::eTriangleFan: return "Triangle fan";
43+
default: return "(Unsupported)";
44+
}
45+
}
3446
} // namespace
3547

3648
void ResourceInspector::view(Texture const& texture, Id<Texture> id) const {
@@ -51,8 +63,10 @@ void ResourceInspector::view(StaticMesh const& mesh, Id<StaticMesh> id) const {
5163
auto tn = TreeNode{name.c_str()};
5264
drag_payload(id, name.c_str());
5365
if (tn) {
54-
ImGui::Text("%s", FixedString{"Vertices: {}", mesh.view().vertex_count}.c_str());
55-
ImGui::Text("%s", FixedString{"Indices: {}", mesh.view().index_count}.c_str());
66+
auto const info = mesh.info();
67+
ImGui::Text("%s", FixedString{"Vertices: {}", info.vertices}.c_str());
68+
ImGui::Text("%s", FixedString{"Indices: {}", info.indices}.c_str());
69+
ImGui::Text("%s", FixedString{"Topology: {}", to_str(mesh.topology())}.c_str());
5670
}
5771
}
5872

lib/engine/src/engine.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,21 @@ struct LoadRequest {
225225
AtomicLoadStatus status{};
226226
float start_time{};
227227
};
228+
229+
struct Fps {
230+
std::uint32_t frames{};
231+
std::uint32_t fps{};
232+
float start{time::since_start()};
233+
234+
std::uint32_t next_frame() {
235+
++frames;
236+
if (auto const now = time::since_start(); now - start >= 1.0f) {
237+
start = now;
238+
fps = std::exchange(frames, 0);
239+
}
240+
return fps;
241+
}
242+
};
228243
} // namespace
229244

230245
struct Engine::Impl {
@@ -237,6 +252,8 @@ struct Engine::Impl {
237252

238253
ThreadPool thread_pool{};
239254
std::mutex mutex{};
255+
Stats stats{};
256+
Fps fps{};
240257

241258
struct {
242259
LoadRequest request{};
@@ -293,6 +310,8 @@ auto Engine::poll() -> State const& {
293310
m_impl->window.window.get().glfw->poll_events();
294311
auto const& ret = m_impl.get()->window.window.get().state();
295312
m_impl->scene.tick(ret.dt);
313+
m_impl->stats.fps = m_impl->fps.next_frame();
314+
++m_impl->stats.frame_counter;
296315
return ret;
297316
}
298317

@@ -302,6 +321,17 @@ void Engine::render() {
302321
if (m_impl->window.renderer.next_frame({&cb, 1})) { m_impl->renderer.render(scene(), &m_impl->skybox, renderer(), cb); }
303322
m_impl->window.gui->end_frame();
304323
m_impl->window.renderer.render();
324+
{
325+
auto const& info = m_impl->renderer.info();
326+
m_impl->stats.draw_calls = info.draw_calls;
327+
m_impl->stats.triangles = info.triangles_drawn;
328+
}
329+
{
330+
auto info = m_impl->window.renderer.info();
331+
m_impl->stats.mode = info.present_mode;
332+
m_impl->stats.current_msaa = info.current_msaa;
333+
m_impl->stats.supported_msaa = info.supported_msaa;
334+
}
305335
}
306336

307337
void Engine::request_stop() { glfwSetWindowShouldClose(window(), GLFW_TRUE); }
@@ -310,6 +340,9 @@ glm::ivec2 Engine::window_position() const { return m_impl->window.window.get().
310340
glm::uvec2 Engine::window_extent() const { return m_impl->window.window.get().window_extent(); }
311341
glm::uvec2 Engine::framebuffer_extent() const { return m_impl->window.window.get().framebuffer_extent(); }
312342

343+
Stats const& Engine::stats() const { return m_impl->stats; }
344+
std::string_view Engine::gpu_name() const { return m_impl->window.renderer.gpu_name(); }
345+
313346
bool Engine::load_async(std::string json_path, UniqueTask<void()> on_loaded) {
314347
if (!fs::is_regular_file(json_path)) {
315348
// early return if file will fail to load anyway

lib/engine/src/scene_renderer.cpp

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,43 @@ struct DirLightSSBO {
1919
};
2020
} // namespace
2121

22+
void SceneRenderer::Instances::rotate() {
23+
for (auto& buffer : buffers) { buffer.rotate(); }
24+
index = 0;
25+
}
26+
2227
SceneRenderer::SceneRenderer(Gfx const& gfx)
2328
: m_gfx(gfx), m_sampler(gfx), m_view_proj(gfx, Buffer::Type::eUniform), m_dir_lights(gfx, Buffer::Type::eStorage),
2429
m_white(gfx, m_sampler.sampler(), Bmp1x1{0xff_B, 0xff_B, 0xff_B, 0xff_B}.view(), Texture::CreateInfo{.mip_mapped = false}),
2530
m_black(gfx, m_sampler.sampler(), Bmp1x1{0x0_B, 0x0_B, 0x0_B, 0xff_B}.view(), Texture::CreateInfo{.mip_mapped = false}) {}
2631

2732
void SceneRenderer::render(Scene const& scene, Ptr<Skybox const> skybox, Renderer& renderer, vk::CommandBuffer cb) {
2833
m_scene = &scene;
34+
m_info = {};
2935
write_view(renderer.framebuffer_extent());
3036
if (skybox) { render(renderer, cb, *skybox); }
3137
for (auto const& node : m_scene->roots()) { render(renderer, cb, m_scene->resources().nodes[node]); }
38+
m_instances.rotate();
3239
}
3340

3441
void SceneRenderer::write_view(glm::vec2 const extent) {
3542
auto const& cam_node = m_scene->camera();
3643
auto const cam_id = cam_node.find<Camera>();
3744
assert(cam_id);
3845
auto const& cam = m_scene->resources().cameras[*cam_id];
39-
struct {
46+
struct ViewSSBO {
4047
glm::mat4x4 mat_v;
4148
glm::mat4x4 mat_p;
4249
glm::vec4 vpos_exposure;
43-
} vp{
50+
} view{
4451
.mat_v = cam.view(cam_node.transform),
4552
.mat_p = cam.projection(extent),
4653
.vpos_exposure = {cam_node.transform.position(), cam.exposure},
4754
};
48-
m_view_proj.write(&vp, sizeof(vp));
55+
m_view_proj.write<ViewSSBO>({&view, 1});
4956
auto dir_lights = FlexArray<DirLightSSBO, 4>{};
5057
for (auto const& light : m_scene->lights.dir_lights.span()) { dir_lights.insert(DirLightSSBO::make(light)); }
51-
m_dir_lights.write(dir_lights.span().data(), dir_lights.span().size_bytes());
58+
m_dir_lights.write(dir_lights.span());
5259
}
5360

5461
void SceneRenderer::update_view(Pipeline& out_pipeline) const {
@@ -58,7 +65,7 @@ void SceneRenderer::update_view(Pipeline& out_pipeline) const {
5865
out_pipeline.bind(set0);
5966
}
6067

61-
std::span<glm::mat4x4 const> SceneRenderer::make_instances(Node const& node, glm::mat4x4 const& parent) const {
68+
std::span<glm::mat4x4 const> SceneRenderer::make_instance_mats(Node const& node, glm::mat4x4 const& parent) {
6269
m_instance_mats.clear();
6370
if (node.instances.empty()) {
6471
m_instance_mats.reserve(1);
@@ -70,40 +77,63 @@ std::span<glm::mat4x4 const> SceneRenderer::make_instances(Node const& node, glm
7077
return m_instance_mats;
7178
}
7279

73-
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Skybox const& skybox) const {
74-
auto state = m_scene->pipeline_state;
75-
state.depth_test = false;
76-
auto pipeline = renderer.bind_pipeline(cb, state, "skybox");
80+
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Skybox const& skybox) {
81+
auto pipeline = renderer.bind_pipeline(cb, {.depth_test = false}, "skybox");
7782
pipeline.set_line_width(1.0f);
7883
update_view(pipeline);
7984
auto& set1 = pipeline.next_set(1);
8085
set1.update(0, skybox.cubemap().descriptor_image());
8186
pipeline.bind(set1);
82-
auto const instance = glm::translate(matrix_identity_v, m_scene->camera().transform.position());
83-
renderer.draw(pipeline, skybox.static_mesh(), {&instance, 1});
87+
auto const mat = glm::translate(matrix_identity_v, m_scene->camera().transform.position());
88+
draw(cb, skybox.static_mesh(), {&mat, 1u});
8489
}
8590

86-
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 parent) const {
91+
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 parent) {
8792
auto const& resources = m_scene->resources();
8893
if (auto mesh_id = node.find<Mesh>()) {
8994
static auto const s_default_material = Material{std::make_unique<LitMaterial>()};
9095
auto const& mesh = resources.meshes[*mesh_id];
9196
for (auto const& primitive : mesh.primitives) {
9297
auto const& material = primitive.material ? resources.materials[primitive.material->value()] : static_cast<Material const&>(s_default_material);
93-
auto pipeline = renderer.bind_pipeline(cb, m_scene->pipeline_state, material.shader_id());
94-
pipeline.set_line_width(m_scene->pipeline_state.line_width);
98+
auto const& static_mesh = resources.static_meshes[primitive.static_mesh];
99+
100+
auto const mode = m_scene->render_mode.type == RenderMode::Type::eWireframe ? vk::PolygonMode::eLine : vk::PolygonMode::eFill;
101+
auto pipeline = renderer.bind_pipeline(cb, {.mode = mode, .topology = static_mesh.topology()}, material.shader_id());
102+
pipeline.set_line_width(m_scene->render_mode.line_width);
95103

96104
update_view(pipeline);
97105
auto const store = TextureStore{resources.textures, m_white, m_black};
98106
material.write_sets(pipeline, store);
99107

100-
auto const& static_mesh = resources.static_meshes[primitive.static_mesh];
101-
auto const instances = make_instances(node, parent);
102-
renderer.draw(pipeline, static_mesh, instances);
108+
auto const mats = make_instance_mats(node, parent);
109+
draw(cb, static_mesh, mats);
103110
}
104111
}
105112

106113
parent = parent * node.transform.matrix();
107114
for (auto const& id : node.children) { render(renderer, cb, m_scene->resources().nodes[id], parent); }
108115
}
116+
117+
void SceneRenderer::draw(vk::CommandBuffer cb, StaticMesh const& static_mesh, std::span<glm::mat4x4 const> mats) {
118+
auto const instances = next_instances(mats);
119+
cb.bindVertexBuffers(1u, instances.buffer, vk::DeviceSize{0});
120+
static_mesh.draw(cb, mats.size());
121+
m_info.triangles_drawn += static_mesh.info().vertices / 3;
122+
++m_info.draw_calls;
123+
}
124+
125+
BufferView SceneRenderer::next_instances(std::span<glm::mat4x4 const> mats) {
126+
auto& buffer = [&]() -> Buffer& {
127+
auto ret = Ptr<Buffer>{};
128+
if (m_instances.index < m_instances.buffers.size()) {
129+
ret = &m_instances.buffers[m_instances.index++];
130+
} else {
131+
++m_instances.index;
132+
ret = &m_instances.buffers.emplace_back(m_gfx, Buffer::Type::eInstance);
133+
}
134+
return *ret;
135+
}();
136+
buffer.write(mats);
137+
return buffer.view();
138+
}
109139
} // namespace facade

lib/ext/src.zip

5.71 KB
Binary file not shown.

0 commit comments

Comments
 (0)