Silicon
A realtime platform for creating interactive media.
VulkanRenderer.cpp
Go to the documentation of this file.
1// BSD 2-Clause License
2//
3// Copyright (c) 2022, Matthew McCall
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this
10// list of conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice,
13// this list of conditions and the following disclaimer in the documentation
14// and/or other materials provided with the distribution.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27//
28// Created by Matthew McCall on 11/20/22.
29//
30
31#include "Silicon/Event.hpp"
32#include "Silicon/Log.hpp"
33#include "Silicon/Types.hpp"
36
37#include "Silicon/Event.hpp"
38#include "Silicon/Window.hpp"
39
40#include "Buffer.hpp"
41#include "CommandPool.hpp"
42#include "FrameData.hpp"
43#include "Framebuffer.hpp"
44#include "Pipeline.hpp"
45#include "Semaphore.hpp"
46
48{
49public:
51 : Si::Renderer()
52 , m_window(window)
53 , m_surface(s_instance, window)
54 , m_physicalDevice(*Si::Vulkan::PhysicalDevice::getBest(
55 s_instance,
56 m_surface,
57 {
58 { "VK_KHR_portability_subset", false },
59 { VK_KHR_SWAPCHAIN_EXTENSION_NAME }
60 }))
61 , m_device(m_physicalDevice)
62 , m_swapChain(m_device, window, m_surface)
63 , m_renderPass(m_device)
64 , m_pipeline(m_renderPass)
65 , m_commandPool(m_device)
66 , m_vertexBuffer(s_instance, m_device, sizeof(Si::Vertex) * 3)
67 , m_resizeHandler([this](const Si::Event::WindowResize &event) {
68 resize = true;
69 })
70 {
71 m_pipeline.create();
72 m_swapChain.create();
73
74 Si::Vector<Si::Vulkan::ImageView>& imageViews = m_swapChain.getImageViews();
75 m_maxFrames = imageViews.size();
76
77 vk::CommandBufferAllocateInfo commandBufferAllocateInfo { *m_commandPool, vk::CommandBufferLevel::ePrimary, static_cast<uint32_t>(m_maxFrames) };
78 m_commandBuffers = m_device->allocateCommandBuffers<Si::Allocator<vk::CommandBuffer>>(commandBufferAllocateInfo);
79
80 m_fences.reserve(m_maxFrames);
81 m_framebuffers.reserve(m_maxFrames);
82 m_imageAvailableSemaphores.reserve(m_maxFrames);
83 m_renderFinishedSemaphores.reserve(m_maxFrames);
84
85 for (unsigned i = 0; i < m_maxFrames; i++) {
86 m_fences.emplace_back(m_device);
87 m_fences.back().create();
88
89 m_framebuffers.emplace_back(m_renderPass, imageViews[i], m_swapChain);
90 m_framebuffers.back().create();
91
92 m_imageAvailableSemaphores.emplace_back(m_device);
93 m_imageAvailableSemaphores.back().create();
94
95 m_renderFinishedSemaphores.emplace_back(m_device);
96 m_renderFinishedSemaphores.back().create();
97 }
98 }
99
100 bool Draw() override
101 {
102 std::array<vk::Fence, 1> fences = { *m_fences[m_frameIndex] };
103 auto waitResult = m_device->waitForFences(fences, VK_TRUE, std::numeric_limits<std::uint64_t>::max());
104
105 auto [result, imageIndex] = m_device->acquireNextImageKHR(*m_swapChain, std::numeric_limits<std::uint64_t>::max(), *m_imageAvailableSemaphores[m_frameIndex], VK_NULL_HANDLE);
106
107 if (result == vk::Result::eErrorOutOfDateKHR) {
108 OnResize();
109 return false;
110 } else if ((result != vk::Result::eSuccess) && (result != vk::Result::eSuboptimalKHR)) {
111 Si::Engine::Error("Failed to get next image!");
112 return false;
113 }
114
115 m_device->resetFences(fences);
116
117 vk::CommandBuffer commandBuffer = m_commandBuffers[m_frameIndex];
118
119 vk::CommandBufferBeginInfo commandBufferBeginInfo { vk::CommandBufferUsageFlagBits::eOneTimeSubmit };
120 commandBuffer.begin(commandBufferBeginInfo);
121
122 vk::Viewport viewport {
123 0,
124 0,
125 static_cast<float>(m_swapChain.getExtent().width),
126 static_cast<float>(m_swapChain.getExtent().height),
127 0,
128 1
129 };
130
131 vk::Rect2D scissor { { 0, 0 }, m_swapChain.getExtent() };
132
133 commandBuffer.setViewport(0, { viewport });
134 commandBuffer.setScissor(0, { scissor });
135
136 vk::ClearValue clearValue { vk::ClearColorValue().setFloat32({ 0, 0, 0, 0 }) };
137 vk::RenderPassBeginInfo renderPassBeginInfo { *m_renderPass, *(m_framebuffers[imageIndex]), { { 0, 0 }, m_swapChain.getExtent() }, clearValue };
138 commandBuffer.beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline);
139
140 commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, *m_pipeline);
141
142 std::array<vk::Buffer, 1> vertexBuffers { *m_vertexBuffer };
143 std::array<vk::DeviceSize, 1> vertexOffsets { 0 };
144
145 commandBuffer.bindVertexBuffers(0, vertexBuffers, vertexOffsets);
146
147 commandBuffer.draw(m_vertices.size(), 1, 0, 0);
148
149 commandBuffer.endRenderPass();
150 commandBuffer.end();
151
152 std::array<vk::Semaphore, 1> waitSemaphores { *m_imageAvailableSemaphores[m_frameIndex] };
153 std::array<vk::Semaphore, 1> signalSemaphores { *m_renderFinishedSemaphores[m_frameIndex] };
154 std::array<vk::PipelineStageFlags, 1> waitStages { vk::PipelineStageFlagBits::eColorAttachmentOutput };
155
156 std::array<vk::CommandBuffer, 1> commandBuffers = { commandBuffer };
157
158 vk::SubmitInfo submitInfo { waitSemaphores, waitStages, commandBuffers, signalSemaphores };
159
160 m_device.getGraphicsQueue().submit({ submitInfo }, *m_fences[m_frameIndex]);
161
162 std::array<vk::SwapchainKHR, 1> swapChains { *m_swapChain };
163 std::array<std::uint32_t, 1> imageIndices { imageIndex };
164
165 vk::PresentInfoKHR presentInfo { signalSemaphores, swapChains, imageIndices };
166
167 auto presentResult = m_device.getPresentQueue().presentKHR(presentInfo);
168
169 if ((presentResult == vk::Result::eErrorOutOfDateKHR) || (presentResult == vk::Result::eSuboptimalKHR) || resize) {
170 resize = false;
171 OnResize();
172 } else if (presentResult != vk::Result::eSuccess) {
173 Si::Engine::Error("Failed to get next image!");
174 return false;
175 }
176
177 ++m_frameIndex %= m_maxFrames;
178
179 return true;
180 }
181
182 void OnResize() override
183 {
184 m_device->waitIdle();
185 m_swapChain.create();
186 m_renderPass.create();
187 }
188
189private:
190
191 Si::Window &m_window;
192
193 static Si::Vulkan::Instance s_instance;
194
195 Si::Vulkan::Surface m_surface;
196 Si::Vulkan::PhysicalDevice m_physicalDevice;
197 Si::Vulkan::Device m_device;
198 Si::Vulkan::SwapChain m_swapChain;
199 Si::Vulkan::RenderPass m_renderPass;
200 Si::Vulkan::Pipeline m_pipeline;
201 Si::Vulkan::CommandPool m_commandPool;
202 Si::Vulkan::Buffer m_vertexBuffer;
203
206 Si::Vector<Si::Vulkan::Semaphore> m_imageAvailableSemaphores, m_renderFinishedSemaphores;
207 Si::Vector<Si::Vulkan::Shader> m_defaultShaders;
208
209 Si::Vector<vk::CommandBuffer> m_commandBuffers;
210
211 Si::Sub<Si::Event::WindowResize> m_resizeHandler;
212
213 std::uint32_t m_frameIndex = 0;
214 std::uint32_t m_maxFrames = 0;
215
216 bool resize = false;
217};
218
219Si::Vulkan::Instance VulkanRendererImpl::s_instance {};
220
222
224{
225}
226
228{
229}
230
231void Create(Window &window)
232{
233 Renderer::RegisterRenderer("Si::Vulkan", std::make_unique<VulkanRendererImpl>(window));
234}
235
236}
The Renderer class is the base class for all Renderer implementations.
Definition: Renderer.hpp:46
Si::Vector< Si::Vertex > m_vertices
Definition: Renderer.hpp:64
static void RegisterRenderer(const std::string &name, std::unique_ptr< Renderer > renderer)
Registers a renderer implementation with the renderer.
Definition: Renderer.cpp:42
Handle wrapper for Vulkan Command Pool.
Definition: CommandPool.hpp:47
A class representing a GPU device.
Definition: Device.hpp:64
vk::Queue getGraphicsQueue() const
Gets the graphics queue of the device.
Definition: Device.cpp:90
vk::Queue getPresentQueue() const
Gets the present queue of the device.
Definition: Device.cpp:100
void create()
Creates a handle.
Definition: Handle.cpp:44
A handle for Vulkan instances.
Definition: Instance.hpp:54
A class representing properties about a physical GPU.
Handle wrapper for Vulkan Pipeline.
Definition: Pipeline.hpp:50
Handle wrapper for Vulkan Render Pass.
Definition: RenderPass.hpp:47
Handle wrapper for Vulkan Surface.
Definition: Surface.hpp:49
Handle wrapper for Vulkan Swapchain.
Definition: SwapChain.hpp:50
Vector< ImageView > & getImageViews()
Definition: SwapChain.cpp:142
const vk::Extent2D & getExtent() const
Definition: SwapChain.cpp:127
void OnResize() override
VulkanRendererImpl(Si::Window &window)
bool Draw() override
T end(T... args)
void Error(Args &&... args)
Definition: Log.hpp:103
void Create(Window &window)
Definition: Allocator.hpp:36
boost::pool_allocator< T > Allocator
Definition: Allocator.hpp:38
T size(T... args)