Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit daac1ac

Browse files
committed
Refactor ResourcePool
- Use `IResourcePool` abstract interface for public API - Move individual pool classes to 'src', under `namespace detail` - Concretize in 'engine.cpp' This hides unecessary implementation details from users, and reduces includes in the public API.
1 parent d9ef64a commit daac1ac

File tree

9 files changed

+201
-165
lines changed

9 files changed

+201
-165
lines changed

lib/include/tkge/engine.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace tkge
4545
kvf::RenderDevice _renderDevice;
4646
kvf::RenderPass _renderPass;
4747

48-
graphics::ResourcePool _resourcePool;
48+
std::unique_ptr<graphics::IResourcePool> _resourcePool{};
4949

5050
vk::CommandBuffer _cmd{};
5151
AssetLoader _assetLoader;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
#include <vulkan/vulkan.hpp>
3+
4+
namespace tkge::graphics
5+
{
6+
/// \brief Description of a unique Pipeline's fixed state.
7+
struct PipelineFixedState
8+
{
9+
/// \brief Colour image format of target Render Pass.
10+
vk::Format colourFormat{};
11+
12+
vk::PrimitiveTopology topology{vk::PrimitiveTopology::eTriangleList};
13+
vk::PolygonMode polygonMode{vk::PolygonMode::eFill};
14+
};
15+
} // namespace tkge::graphics

lib/include/tkge/graphics/renderer.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace tkge::graphics
1515

1616
Renderer() = default;
1717

18-
explicit Renderer(kvf::RenderPass* renderPass, ResourcePool* resourcePool, vk::CommandBuffer commandBuffer, glm::ivec2 framebufferSize);
18+
explicit Renderer(kvf::RenderPass* renderPass, IResourcePool* resourcePool, vk::CommandBuffer commandBuffer, glm::ivec2 framebufferSize);
1919
~Renderer() { EndRender(); }
2020

2121
[[nodiscard]] bool IsRendering() const { return _renderPass != nullptr; }
@@ -33,7 +33,7 @@ namespace tkge::graphics
3333
void BindVboAndDraw(const Primitive& primitive) const;
3434

3535
kvf::RenderPass* _renderPass{};
36-
ResourcePool* _resourcePool{};
36+
IResourcePool* _resourcePool{};
3737

3838
const Shader* _shader{};
3939
vk::Pipeline _pipeline{};
Lines changed: 7 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,24 @@
11
#pragma once
2-
#include <kvf/render_device.hpp>
32
#include <kvf/vma.hpp>
3+
#include <tkge/graphics/pipeline_fixed_state.hpp>
44
#include <tkge/graphics/shader.hpp>
5-
#include <unordered_map>
6-
#include <vector>
75

86
namespace tkge::graphics
97
{
10-
/// \brief Cached storage for Vulkan Graphics Pipelines.
11-
class PipelinePool
8+
using Buffer = kvf::vma::Buffer;
9+
10+
class IResourcePool : public klib::Polymorphic
1211
{
1312
public:
14-
/// \brief Description of a unique Pipeline's fixed state.
15-
struct State
16-
{
17-
/// \brief Colour image format of target Render Pass.
18-
vk::Format colourFormat{};
19-
20-
vk::PrimitiveTopology topology{vk::PrimitiveTopology::eTriangleList};
21-
vk::PolygonMode polygonMode{vk::PolygonMode::eFill};
22-
};
23-
24-
/// \param renderDevice Pointer to valid RenderDevice.
25-
/// \param framebufferSamples Sample count of target Render Pass' colour attachment.
26-
explicit PipelinePool(gsl::not_null<const kvf::RenderDevice*> renderDevice, vk::SampleCountFlagBits framebufferSamples);
27-
28-
[[nodiscard]] vk::PipelineLayout PipelineLayout() const { return *_pipelineLayout; }
13+
[[nodiscard]] virtual vk::PipelineLayout PipelineLayout() const = 0;
2914

3015
/// \brief Get the Pipeline identified by the input parameters.
3116
/// \param shader Shader that will be used in draw calls (dynamic Pipeline state).
3217
/// \param state Fixed Pipeline state.
3318
/// \returns Existing Pipeline if already cached, otherwise a newly created one (unless creation fails).
34-
[[nodiscard]] vk::Pipeline GetPipeline(const Shader& shader, const State& state);
35-
36-
private:
37-
gsl::not_null<const kvf::RenderDevice*> _renderDevice;
38-
vk::SampleCountFlagBits _framebufferSamples;
39-
40-
vk::UniquePipelineLayout _pipelineLayout{};
41-
std::unordered_map<std::size_t, vk::UniquePipeline> _pipelines{};
42-
};
43-
44-
/// \brief Cached storage for Vulkan Host Buffers.
45-
class BufferPool
46-
{
47-
public:
48-
explicit BufferPool(gsl::not_null<kvf::RenderDevice*> renderDevice) : _renderDevice(renderDevice) {}
19+
[[nodiscard]] virtual vk::Pipeline GetPipeline(const Shader& shader, const PipelineFixedState& state) = 0;
4920

5021
/// \brief Allocate a Buffer for given usage and of given size.
51-
[[nodiscard]] kvf::vma::Buffer& Allocate(vk::BufferUsageFlags usage, vk::DeviceSize size);
52-
53-
/// \brief Make all buffers for the current frame available for use.
54-
void NextFrame();
55-
56-
private:
57-
struct Pool
58-
{
59-
std::vector<kvf::vma::Buffer> buffers{}; // buffer pool for a specific usage
60-
std::size_t next{}; // index of next available buffer
61-
};
62-
63-
using PoolMap = std::unordered_map<std::size_t, Pool>; // map of hash(usage) => Pool
64-
65-
gsl::not_null<kvf::RenderDevice*> _renderDevice;
66-
67-
kvf::Buffered<PoolMap> _poolMaps{}; // double/triple/etc buffered pools
68-
};
69-
70-
class ResourcePool
71-
{
72-
public:
73-
explicit ResourcePool(gsl::not_null<kvf::RenderDevice*> renderDevice, vk::SampleCountFlagBits framebufferSamples)
74-
: pipelinePool(renderDevice, framebufferSamples), bufferPool(renderDevice)
75-
{
76-
}
77-
78-
void NextFrame();
79-
80-
PipelinePool pipelinePool;
81-
BufferPool bufferPool;
22+
[[nodiscard]] virtual Buffer& AllocateBuffer(vk::BufferUsageFlags usage, vk::DeviceSize size) = 0;
8223
};
8324
} // namespace tkge::graphics

lib/src/detail/buffer_pool.hpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
#include <kvf/render_device.hpp>
3+
#include <kvf/vma.hpp>
4+
#include <vulkan/vulkan_hash.hpp>
5+
6+
namespace tkge::detail
7+
{
8+
/// \brief Cached storage for Vulkan Host Buffers.
9+
class BufferPool
10+
{
11+
public:
12+
explicit BufferPool(gsl::not_null<kvf::RenderDevice*> renderDevice) : _renderDevice(renderDevice) {}
13+
14+
// Allocate a Buffer for given usage and of given size.
15+
[[nodiscard]] kvf::vma::Buffer& Allocate(vk::BufferUsageFlags usage, vk::DeviceSize size)
16+
{
17+
const auto frameIndex = _renderDevice->get_frame_index();
18+
auto& poolMap = _poolMaps.at(std::size_t(frameIndex));
19+
auto& pool = poolMap[std::hash<vk::BufferUsageFlags>{}(usage)];
20+
if (pool.next < pool.buffers.size())
21+
{
22+
auto& ret = pool.buffers.at(pool.next++);
23+
ret.resize(size);
24+
return ret;
25+
}
26+
const auto createInfo = kvf::vma::BufferCreateInfo{
27+
.usage = usage,
28+
.type = kvf::vma::BufferType::Host,
29+
};
30+
pool.next = pool.buffers.size() + 1;
31+
return pool.buffers.emplace_back(_renderDevice, createInfo, size);
32+
}
33+
34+
// Make all buffers for the current frame available for use.
35+
void NextFrame()
36+
{
37+
// GPU has finished rendering the current frame, these resources can now be reused.
38+
// (Other frames may still be being rendered, hence the multiple buffering.)
39+
const auto frameIndex = _renderDevice->get_frame_index();
40+
auto& poolMap = _poolMaps.at(std::size_t(frameIndex));
41+
for (auto& [_, pool] : poolMap) { pool.next = 0; }
42+
}
43+
44+
private:
45+
struct Pool
46+
{
47+
std::vector<kvf::vma::Buffer> buffers{}; // buffer pool for a specific usage
48+
std::size_t next{}; // index of next available buffer
49+
};
50+
51+
using PoolMap = std::unordered_map<std::size_t, Pool>; // map of hash(usage) => Pool
52+
53+
gsl::not_null<kvf::RenderDevice*> _renderDevice;
54+
55+
kvf::Buffered<PoolMap> _poolMaps{}; // double/triple/etc buffered pools
56+
};
57+
} // namespace tkge::detail

lib/src/detail/pipeline_pool.hpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#pragma once
2+
#include <klib/hash_combine.hpp>
3+
#include <kvf/render_device.hpp>
4+
#include <tkge/graphics/pipeline_fixed_state.hpp>
5+
#include <tkge/graphics/shader.hpp>
6+
#include <tkge/graphics/vertex.hpp>
7+
#include <unordered_map>
8+
9+
namespace tkge::detail
10+
{
11+
/// \brief Cached storage for Vulkan Graphics Pipelines.
12+
class PipelinePool
13+
{
14+
public:
15+
using FixedState = graphics::PipelineFixedState;
16+
17+
/// \param renderDevice Pointer to valid RenderDevice.
18+
/// \param framebufferSamples Sample count of target Render Pass' colour attachment.
19+
explicit PipelinePool(gsl::not_null<const kvf::RenderDevice*> renderDevice, vk::SampleCountFlagBits framebufferSamples)
20+
: _renderDevice(renderDevice), _framebufferSamples(framebufferSamples)
21+
{
22+
// TODO: descriptor set layouts
23+
_pipelineLayout = renderDevice->get_device().createPipelineLayoutUnique({});
24+
}
25+
26+
[[nodiscard]] vk::PipelineLayout PipelineLayout() const { return *_pipelineLayout; }
27+
28+
/// \brief Get the Pipeline identified by the input parameters.
29+
/// \param shader Shader that will be used in draw calls (dynamic Pipeline state).
30+
/// \param state Fixed Pipeline state.
31+
/// \returns Existing Pipeline if already cached, otherwise a newly created one (unless creation fails).
32+
[[nodiscard]] vk::Pipeline GetPipeline(const graphics::Shader& shader, const FixedState& state)
33+
{
34+
using graphics::Vertex;
35+
36+
// A single Vertex Buffer is bound during draw (index 0), containing vertices.
37+
static constexpr auto VertexBindings = std::array{
38+
vk::VertexInputBindingDescription{0, sizeof(Vertex)},
39+
};
40+
41+
// Attributes for Vertex Buffer at index 0: list of interleaved vertices (span<Vertex>).
42+
static constexpr auto VertexAttributes = std::array{
43+
vk::VertexInputAttributeDescription{0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, position)},
44+
vk::VertexInputAttributeDescription{1, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(Vertex, colour)},
45+
vk::VertexInputAttributeDescription{2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, uv)},
46+
};
47+
48+
const auto key = klib::make_combined_hash(shader.GetHash(), state.colourFormat, state.polygonMode, state.topology);
49+
auto it = _pipelines.find(key);
50+
if (it != _pipelines.end()) { return *it->second; }
51+
52+
const auto pipelineState = kvf::PipelineState{
53+
.vertex_bindings = VertexBindings,
54+
.vertex_attributes = VertexAttributes,
55+
.vertex_shader = shader.VertexModule(),
56+
.fragment_shader = shader.FragmentModule(),
57+
.topology = state.topology,
58+
.polygon_mode = state.polygonMode,
59+
};
60+
const auto pipelineFormat = kvf::PipelineFormat{
61+
.samples = _framebufferSamples,
62+
.color = state.colourFormat,
63+
};
64+
auto ret = _renderDevice->create_pipeline(*_pipelineLayout, pipelineState, pipelineFormat);
65+
if (!ret) { return {}; }
66+
67+
it = _pipelines.insert({key, std::move(ret)}).first;
68+
return *it->second;
69+
}
70+
71+
private:
72+
gsl::not_null<const kvf::RenderDevice*> _renderDevice;
73+
vk::SampleCountFlagBits _framebufferSamples;
74+
75+
vk::UniquePipelineLayout _pipelineLayout{};
76+
std::unordered_map<std::size_t, vk::UniquePipeline> _pipelines{};
77+
};
78+
} // namespace tkge::detail

lib/src/engine.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include <detail/buffer_pool.hpp>
2+
#include <detail/pipeline_pool.hpp>
13
#include <klib/assert.hpp>
24
#include <kvf/is_positive.hpp>
35
#include <kvf/util.hpp>
@@ -6,8 +8,39 @@
68

79
namespace tkge
810
{
11+
namespace
12+
{
13+
class ResourcePool : public graphics::IResourcePool
14+
{
15+
public:
16+
explicit ResourcePool(gsl::not_null<kvf::RenderDevice*> renderDevice, vk::SampleCountFlagBits framebufferSamples)
17+
: _pipelinePool(renderDevice, framebufferSamples), _bufferPool(renderDevice)
18+
{
19+
}
20+
21+
[[nodiscard]] vk::PipelineLayout PipelineLayout() const final { return _pipelinePool.PipelineLayout(); }
22+
23+
[[nodiscard]] vk::Pipeline GetPipeline(const graphics::Shader& shader, const graphics::PipelineFixedState& state) final
24+
{
25+
return _pipelinePool.GetPipeline(shader, state);
26+
}
27+
28+
[[nodiscard]] graphics::Buffer& AllocateBuffer(const vk::BufferUsageFlags usage, const vk::DeviceSize size) final
29+
{
30+
return _bufferPool.Allocate(usage, size);
31+
}
32+
33+
void NextFrame() { _bufferPool.NextFrame(); }
34+
35+
private:
36+
detail::PipelinePool _pipelinePool;
37+
detail::BufferPool _bufferPool;
38+
};
39+
} // namespace
40+
941
Engine::Engine(const WindowSurface& surface, const vk::SampleCountFlagBits aa)
10-
: _window(CreateWindow(surface)), _renderDevice(_window.get()), _renderPass(&_renderDevice, aa), _resourcePool(&_renderDevice, aa)
42+
: _window(CreateWindow(surface)), _renderDevice(_window.get()), _renderPass(&_renderDevice, aa),
43+
_resourcePool(std::make_unique<ResourcePool>(&_renderDevice, aa))
1144
{
1245
_renderPass.set_color_target();
1346
}
@@ -19,7 +52,7 @@ namespace tkge
1952
vk::CommandBuffer Engine::NextFrame()
2053
{
2154
_cmd = _renderDevice.next_frame();
22-
_resourcePool.NextFrame();
55+
static_cast<ResourcePool*>(_resourcePool.get())->NextFrame(); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
2356
return _cmd;
2457
}
2558

@@ -41,7 +74,7 @@ namespace tkge
4174
if (!kvf::is_positive(framebufferSize)) { return {}; }
4275

4376
_renderPass.clear_color = clear.to_linear();
44-
return graphics::Renderer{&_renderPass, &_resourcePool, _cmd, framebufferSize};
77+
return graphics::Renderer{&_renderPass, _resourcePool.get(), _cmd, framebufferSize};
4578
}
4679

4780
void Engine::Present()

lib/src/graphics/renderer.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
#include <kvf/render_device.hpp>
12
#include <kvf/util.hpp>
23
#include <tkge/graphics/renderer.hpp>
34
#include <algorithm>
45

56
namespace tkge::graphics
67
{
7-
Renderer::Renderer(kvf::RenderPass* renderPass, ResourcePool* resourcePool, const vk::CommandBuffer commandBuffer, const glm::ivec2 framebufferSize)
8+
Renderer::Renderer(kvf::RenderPass* renderPass, IResourcePool* resourcePool, const vk::CommandBuffer commandBuffer, const glm::ivec2 framebufferSize)
89
: _renderPass(renderPass), _resourcePool(resourcePool)
910
{
1011
_renderPass->begin_render(commandBuffer, kvf::util::to_vk_extent(framebufferSize));
@@ -37,12 +38,12 @@ namespace tkge::graphics
3738
{
3839
if (!IsRendering() || _shader == nullptr || primitive.vertices.empty()) { return; }
3940

40-
const auto pipelineState = PipelinePool::State{
41+
const auto fixedState = PipelineFixedState{
4142
.colourFormat = _renderPass->get_color_format(),
4243
.topology = primitive.topology,
4344
.polygonMode = _polygonMode,
4445
};
45-
const auto pipeline = _resourcePool->pipelinePool.GetPipeline(*_shader, pipelineState);
46+
const auto pipeline = _resourcePool->GetPipeline(*_shader, fixedState);
4647
if (!pipeline) { return; }
4748

4849
if (_pipeline != pipeline)
@@ -58,7 +59,7 @@ namespace tkge::graphics
5859
{
5960
const auto vertSize = primitive.vertices.size_bytes();
6061
const auto vboSize = vertSize + primitive.indices.size_bytes();
61-
auto& vertexBuffer = _resourcePool->bufferPool.Allocate(vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer, vboSize);
62+
auto& vertexBuffer = _resourcePool->AllocateBuffer(vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer, vboSize);
6263
kvf::util::overwrite(vertexBuffer, primitive.vertices);
6364
if (!primitive.indices.empty()) { kvf::util::overwrite(vertexBuffer, primitive.indices, vertSize); }
6465

0 commit comments

Comments
 (0)