3#include "spark/base/Exception.h"
4#include "spark/imgui/ImGui.h"
5#include "spark/lib/Pointers.h"
6#include "spark/log/Logger.h"
7#include "spark/path/Paths.h"
8#include "spark/render/Buffer.h"
9#include "spark/render/DepthStencilState.h"
10#include "spark/render/Format.h"
11#include "spark/render/GraphicsFactory.h"
12#include "spark/render/IndexBuffer.h"
13#include "spark/render/InputAssembler.h"
14#include "spark/render/Rasterizer.h"
15#include "spark/render/RenderTarget.h"
16#include "spark/render/ShaderStages.h"
18#include "glm/matrix.hpp"
19#include "glm/gtc/matrix_transform.hpp"
23 template <
typename Backend>
25 std::function<
typename surface_type::handle_type(
const typename backend_type::handle_type&)> surface_factory,
26 std::span<std::string> required_extensions)
29 std::vector<std::string> layers;
31 layers.emplace_back(
"VK_LAYER_KHRONOS_validation");
34 log::info(
"Creating 2D renderer with a render area of {}x{}", render_area.x, render_area.y);
37 m_renderBackend = std::make_unique<backend_type>(required_extensions, layers);
42 const adapter_type* selected_adapter = m_renderBackend->findAdapter(std::nullopt);
43 if (!selected_adapter)
47 auto surface = m_renderBackend->createSurface(surface_factory);
48 m_device = m_renderBackend->template createDevice<backend_type>(
"Default",
51 render::Format::B8G8R8A8_UNORM,
52 m_viewport->rectangle().extent.castTo<
unsigned>(),
56 auto vertex_buffer_layout = std::make_unique<vertex_buffer_layout_type>(
sizeof(glm::vec3), 0);
57 vertex_buffer_layout->addAttribute(
render::BufferAttribute(0, 0, render::BufferFormat::XYZ32F, render::AttributeSemantic::Position, 0));
59 std::vector<std::unique_ptr<vertex_buffer_layout_type>> vertex_buffer_layouts;
60 vertex_buffer_layouts.push_back(std::move(vertex_buffer_layout));
62 auto index_buffer_layout = std::make_unique<index_buffer_layout_type>(render::IndexType::UInt16);
65 m_inputAssembler = std::make_shared<input_assembler_type>(std::move(vertex_buffer_layouts), std::move(index_buffer_layout), render::PrimitiveTopology::TriangleList);
68 std::vector<render::RenderTarget> render_targets;
69 render_targets.emplace_back(
"Color Target",
71 render::RenderTargetType::Present,
72 render::Format::B8G8R8A8_UNORM,
77 render_targets.emplace_back(
"Depth/Stencil Target",
79 render::RenderTargetType::DepthStencil,
80 render::Format::D32_SFLOAT,
86 auto render_pass = std::make_unique<render_pass_type>(*m_device,
"Opaque", render_targets);
89 std::vector<std::unique_ptr<shader_module_type>> modules;
90 modules.push_back(std::make_unique<shader_module_type>(*m_device, render::ShaderStage::Vertex, spark::path::engine_assets_path() /
"shaders" /
"2d_vert.spv"));
91 modules.push_back(std::make_unique<shader_module_type>(*m_device, render::ShaderStage::Fragment, spark::path::engine_assets_path() /
"shaders" /
"2d_frag.spv"));
93 auto shader_program = std::make_shared<shader_program_type>(*m_device, std::move(modules));
99 auto rasterizer = std::make_shared<rasterizer_type>(render::PolygonMode::Solid, render::CullMode::BackFaces, render::CullOrder::CounterClockWise, 1.f, depth_stencil_state);
102 auto render_pipeline = std::make_unique<render_pipeline_type>(*render_pass,
104 std::static_pointer_cast<pipeline_layout_type>(shader_program->reflectPipelineLayout()),
111 m_device->state().add(std::move(render_pass));
112 m_device->state().add(std::move(render_pipeline));
118 template <
typename Backend>
121 m_renderBackend->releaseDevice(
"Default");
124 template <
typename Backend>
131 const auto surface_format = m_device->swapChain().surfaceFormat();
132 m_device->swapChain().reset(surface_format, new_size, 3);
136 m_device->state().renderPass(
"Opaque").resizeFrameBuffers(new_size);
143 template <
typename Backend>
146 auto& render_pipeline = m_device->state().pipeline(
"Geometry");
147 auto command_buffer = m_device->transferQueue().createCommandBuffer(
true,
false);
150 auto staged_vertex_buffer = m_device->factory().createVertexBuffer(m_inputAssembler->vertexBufferLayout(0),
151 render::BufferUsage::Staging,
152 static_cast<unsigned>(s_rectangleVertices.size()));
154 auto vertex_buffer = m_device->factory().createVertexBuffer(
"Vertex Buffer",
155 m_inputAssembler->vertexBufferLayout(0),
156 render::BufferUsage::Resource,
157 static_cast<unsigned>(s_rectangleVertices.size()));
159 staged_vertex_buffer->map(s_rectangleVertices.data(), s_rectangleVertices.size() *
sizeof(glm::vec3), 0);
160 command_buffer->transfer(std::move(staged_vertex_buffer), *vertex_buffer, 0, 0,
static_cast<unsigned>(s_rectangleVertices.size()));
163 auto staged_indices_buffer = m_device->factory().createIndexBuffer(m_inputAssembler->indexBufferLayout(),
164 render::BufferUsage::Staging,
165 static_cast<unsigned>(s_rectangleIndices.size()));
167 auto index_buffer = m_device->factory().createIndexBuffer(
"Index Buffer",
168 m_inputAssembler->indexBufferLayout(),
169 render::BufferUsage::Resource,
170 static_cast<unsigned>(s_rectangleIndices.size()));
172 staged_indices_buffer->map(s_rectangleIndices.data(), s_rectangleIndices.size() *
sizeof(uint16_t), 0);
173 command_buffer->transfer(std::move(staged_indices_buffer), *index_buffer, 0, 0,
static_cast<unsigned>(s_rectangleIndices.size()));
176 auto& instance_binding_layout = render_pipeline.layout()->descriptorSet(0);
179 auto staged_instance_buffer = lib::dynamic_unique_pointer_cast<buffer_type>(
dynamic_cast<const render::IGraphicsFactory&
>(m_device->factory()).
180 createBuffer(
"Instance Staging Buffer",
181 instance_binding_layout,
183 render::BufferUsage::Staging,
184 sizeof(InstanceBuffer),
188 instance_binding_layout,
190 render::BufferUsage::Resource,
191 sizeof(InstanceBuffer),
194 auto instance_binding = instance_binding_layout.allocate(s_maxInstances, {{0, *instance_buffer}});
196 m_transferFences.push_back(m_device->transferQueue().submit(command_buffer));
198 m_device->state().add(lib::static_unique_pointer_cast<render::IVertexBuffer>(std::move(vertex_buffer)));
199 m_device->state().add(lib::static_unique_pointer_cast<render::IIndexBuffer>(std::move(index_buffer)));
200 m_device->state().add(std::move(staged_instance_buffer));
201 m_device->state().add(std::move(instance_buffer));
202 m_device->state().add(
"Instance Binding", std::move(instance_binding));
205 template <
typename Backend>
206 void Renderer2D<Backend>::updateCamera(
const render::ICommandBuffer& command_buffer)
209 const glm::mat4 view = glm::ortho(0.f, m_viewport->rectangle().extent.x, m_viewport->rectangle().extent.y, 0.f, -1.f, 1.f);
211 auto& pipeline =
dynamic_cast<render_pipeline_type&
>(m_device->state().pipeline(
"Geometry"));
212 command_buffer.pushConstants(*pipeline.layout()->pushConstants(), &view);
215 template <
typename Backend>
216 void Renderer2D<Backend>::upload()
218 if (m_instanceData.empty())
222 auto command_buffer = m_device->transferQueue().createCommandBuffer(
true,
false);
225 auto& staged_instance_buffer =
dynamic_cast<buffer_type&
>(m_device->state().buffer(
"Instance Staging Buffer"));
226 staged_instance_buffer.map(
static_cast<const void*
>(m_instanceData.data()), m_instanceData.size() *
sizeof(InstanceBuffer), 0);
228 command_buffer->transfer(staged_instance_buffer,
229 dynamic_cast<buffer_type&
>(m_device->state().buffer(
"Instance Buffer")),
232 static_cast<unsigned>(m_instanceData.size()));
235 m_transferFences.push_back(m_device->transferQueue().submit(command_buffer));
238 template <
typename Backend>
242 const auto back_buffer = m_device->swapChain().swapBackBuffer();
245 auto& render_pass = m_device->state().renderPass(
"Opaque");
246 const auto& geometry_pipeline = m_device->state().pipeline(
"Geometry");
247 const auto& instance_binding = m_device->state().descriptorSet(
"Instance Binding");
248 const auto& vertex_buffer = m_device->state().vertexBuffer(
"Vertex Buffer");
249 const auto& index_buffer = m_device->state().indexBuffer(
"Index Buffer");
255 render_pass.begin(back_buffer);
258 for (
const auto& fence : m_transferFences)
259 m_device->transferQueue().waitFor(fence);
260 m_transferFences.clear();
262 const auto command_buffer = render_pass.activeFrameBuffer().commandBuffer(0);
263 command_buffer->use(geometry_pipeline);
264 command_buffer->setViewport(m_viewport.get());
265 command_buffer->setScissor(m_scissor.get());
268 updateCamera(*command_buffer);
271 command_buffer->bind(instance_binding);
272 command_buffer->bind(vertex_buffer);
273 command_buffer->bind(index_buffer);
276 command_buffer->drawIndexed(index_buffer.elements(),
static_cast<unsigned>(m_instanceData.size()));
278 imgui::render(*command_buffer);
282 m_instanceData.clear();
285 template <
typename Backend>
288 m_instanceData.emplace_back(InstanceBuffer {
289 .transform = transform_matrix,
290 .color = glm::vec4(color.x, color.y, color.z, color.w),
294 template <
typename Backend>
297 m_instanceData.emplace_back(InstanceBuffer {
298 .transform = transform_matrix,
299 .color = glm::vec4(color.x, color.y, color.z, color.w),
Exception thrown when a pointer is null and should't be.
Definition Exception.h:66
An object that can render 2D graphics on a surface.
Definition Renderer2D.h:22
void render()
Draws the current frame.
Definition Renderer2D.h:239
void drawQuad(const glm::mat4 &transform_matrix, const spark::math::Vector4< float > &color={1.f, 1.f, 1.f, 1.f})
Draws a 1x1 quad with the given transformation_matrix.
Definition Renderer2D.h:286
void drawCircle(const glm::mat4 &transform_matrix, float radius, const spark::math::Vector4< float > &color={1.f, 1.f, 1.f, 1.f})
Draws a circle with the given transformation_matrix and radius.
Definition Renderer2D.h:295
void recreateSwapChain(const math::Vector2< unsigned > &new_size)
Recreates the swap chain with frame buffers of the new size.
Definition Renderer2D.h:125
Renderer2D(const math::Vector2< unsigned > &render_area, std::function< typename surface_type::handle_type(const typename backend_type::handle_type &)> surface_factory, std::span< std::string > required_extensions)
Creates a new 2D renderer.
Definition Renderer2D.h:24
Represents a rectangle in 2D space.
Definition Rectangle.h:15
A vector with two components.
Definition Vector2.h:13
constexpr Vector2< To > castTo() const noexcept
Casts all components of the Vector2 to the type To.
Definition Vector2.h:45
A vector with four components.
Definition Vector4.h:13
Stores metadata about a buffer attribute, member or field of a descriptor or buffer.
Definition Buffer.h:193
Stores the depth/stencil state of a see IRasterizer.
Definition DepthStencilState.h:75
Definition GraphicsFactory.h:16
std::unique_ptr< IBuffer > createBuffer(BufferType type, BufferUsage usage, std::size_t element_size, unsigned elements=1, bool allow_write=false) const
Creates a IBuffer of type type.
Definition GraphicsFactory.h:29
Describes the rasterizer depth bias.
Definition DepthStencilState.h:101
Describes the depth test state.
Definition DepthStencilState.h:81
CompareOperation operation
The compare operation used to pass the depth test (default: CompareOperation::Always).
Definition DepthStencilState.h:90
Describes the rasterizer stencil state.
Definition DepthStencilState.h:139