Line |
Branch |
Exec |
Source |
1 |
|
|
|
2 |
|
|
#include "vk_engine.h" |
3 |
|
|
|
4 |
|
|
#include <SDL.h> |
5 |
|
|
#include <SDL_vulkan.h> |
6 |
|
|
|
7 |
|
|
#include <vk_initializers.h> |
8 |
|
|
#include <vk_types.h> |
9 |
|
|
|
10 |
|
|
#include "VkBootstrap.h" |
11 |
|
|
|
12 |
|
|
#include <fstream> |
13 |
|
|
#include <iostream> |
14 |
|
|
|
15 |
|
|
#include "vk_textures.h" |
16 |
|
|
|
17 |
|
|
#define VMA_IMPLEMENTATION |
18 |
|
|
#include "vk_mem_alloc.h" |
19 |
|
|
|
20 |
|
|
#include "imgui_impl_sdl2.h" |
21 |
|
|
#include "imgui_impl_vulkan.h" |
22 |
|
|
|
23 |
|
|
constexpr bool bUseValidationLayers = true; |
24 |
|
|
|
25 |
|
|
VulkanEngine* _engine = nullptr; |
26 |
|
|
|
27 |
|
|
VulkanEngine::VulkanEngine() { _engine = this; }; |
28 |
|
|
|
29 |
|
|
VulkanEngine& VulkanEngine::instance() { return *_engine; } |
30 |
|
|
|
31 |
|
|
// we want to immediately abort when there is an error. In normal engines this would give an error |
32 |
|
|
// message to the user, or perform a dump of state. |
33 |
|
|
using namespace std; |
34 |
|
|
#define VK_CHECK(x) \ |
35 |
|
|
do { \ |
36 |
|
|
VkResult err = x; \ |
37 |
|
|
if (err) { \ |
38 |
|
|
std::cout << "Detected Vulkan error: " << err << std::endl; \ |
39 |
|
|
abort(); \ |
40 |
|
|
} \ |
41 |
|
|
} while (0) |
42 |
|
|
|
43 |
|
|
void VulkanEngine::init() { |
44 |
|
|
// We initialize SDL and create a window with it. |
45 |
|
✗ |
SDL_Init(SDL_INIT_VIDEO); |
46 |
|
|
|
47 |
|
✗ |
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN); |
48 |
|
|
|
49 |
|
✗ |
assert(_window == nullptr); |
50 |
|
✗ |
_window = SDL_CreateWindow("Vulkan Engine", |
51 |
|
|
SDL_WINDOWPOS_UNDEFINED, |
52 |
|
|
SDL_WINDOWPOS_UNDEFINED, |
53 |
|
✗ |
_windowExtent.width, |
54 |
|
✗ |
_windowExtent.height, |
55 |
|
|
window_flags); |
56 |
|
|
|
57 |
|
✗ |
if (_window == nullptr) { |
58 |
|
✗ |
throw std::runtime_error(SDL_GetError()); |
59 |
|
|
} |
60 |
|
|
|
61 |
|
✗ |
init_vulkan(); |
62 |
|
|
|
63 |
|
✗ |
init_swapchain(); |
64 |
|
|
|
65 |
|
✗ |
init_default_renderpass(); |
66 |
|
|
|
67 |
|
✗ |
init_framebuffers(); |
68 |
|
|
|
69 |
|
✗ |
init_commands(); |
70 |
|
|
|
71 |
|
✗ |
init_sync_structures(); |
72 |
|
|
|
73 |
|
✗ |
init_descriptors(); |
74 |
|
|
|
75 |
|
✗ |
init_imgui(); |
76 |
|
|
|
77 |
|
|
// init_pipelines(); |
78 |
|
|
// load_images(); |
79 |
|
|
// load_meshes(); |
80 |
|
|
// init_scene(); |
81 |
|
|
|
82 |
|
|
// everything went fine |
83 |
|
✗ |
_isInitialized = true; |
84 |
|
✗ |
} |
85 |
|
|
void VulkanEngine::cleanup() { |
86 |
|
✗ |
if (_isInitialized) { |
87 |
|
|
|
88 |
|
|
// make sure the gpu has stopped doing its things |
89 |
|
✗ |
vkDeviceWaitIdle(_device); |
90 |
|
|
|
91 |
|
✗ |
_mainDeletionQueue.flush(); |
92 |
|
|
|
93 |
|
✗ |
vkDestroySurfaceKHR(_instance, _surface, nullptr); |
94 |
|
|
|
95 |
|
✗ |
vkDestroyDevice(_device, nullptr); |
96 |
|
✗ |
vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); |
97 |
|
✗ |
vkDestroyInstance(_instance, nullptr); |
98 |
|
|
|
99 |
|
✗ |
SDL_DestroyWindow(_window); |
100 |
|
|
} |
101 |
|
✗ |
} |
102 |
|
|
|
103 |
|
|
void VulkanEngine::start() {} |
104 |
|
|
void VulkanEngine::handle_event(SDL_Event const& event) {} |
105 |
|
|
void VulkanEngine::tick(float dt) {} |
106 |
|
|
void VulkanEngine::end() {} |
107 |
|
|
|
108 |
|
|
void VulkanEngine::draw(float dt) { |
109 |
|
|
|
110 |
|
|
// check if window is minimized and skip drawing |
111 |
|
✗ |
if (SDL_GetWindowFlags(_window) & SDL_WINDOW_MINIMIZED) |
112 |
|
✗ |
return; |
113 |
|
|
|
114 |
|
✗ |
ImGui_ImplVulkan_NewFrame(); |
115 |
|
✗ |
ImGui_ImplSDL2_NewFrame(_window); |
116 |
|
✗ |
ImGui::NewFrame(); |
117 |
|
|
|
118 |
|
|
// wait until the gpu has finished rendering the last frame. Timeout of 1 second |
119 |
|
✗ |
VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); |
120 |
|
✗ |
VK_CHECK(vkResetFences(_device, 1, &get_current_frame()._renderFence)); |
121 |
|
|
|
122 |
|
|
// now that we are sure that the commands finished executing, we can safely reset the command |
123 |
|
|
// buffer to begin recording again. |
124 |
|
✗ |
VK_CHECK(vkResetCommandBuffer(get_current_frame()._mainCommandBuffer, 0)); |
125 |
|
|
|
126 |
|
|
// request image from the swapchain |
127 |
|
|
uint32_t swapchainImageIndex; |
128 |
|
✗ |
VK_CHECK(vkAcquireNextImageKHR(_device, |
129 |
|
|
_swapchain, |
130 |
|
|
1000000000, |
131 |
|
|
get_current_frame()._presentSemaphore, |
132 |
|
|
nullptr, |
133 |
|
|
&swapchainImageIndex)); |
134 |
|
|
|
135 |
|
|
// naming it cmd for shorter writing |
136 |
|
✗ |
VkCommandBuffer cmd = get_current_frame()._mainCommandBuffer; |
137 |
|
|
|
138 |
|
|
// begin the command buffer recording. We will use this command buffer exactly once, so we want |
139 |
|
|
// to let vulkan know that |
140 |
|
|
VkCommandBufferBeginInfo cmdBeginInfo = |
141 |
|
✗ |
vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); |
142 |
|
|
|
143 |
|
✗ |
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); |
144 |
|
|
|
145 |
|
|
// make a clear-color from frame number. This will flash with a 120 frame period. |
146 |
|
|
VkClearValue clearValue; |
147 |
|
✗ |
float flash = abs(sin(_frameNumber / 120.f)); |
148 |
|
✗ |
clearValue.color = {{0.0f, 0.0f, flash, 1.0f}}; |
149 |
|
|
|
150 |
|
|
// clear depth at 1 |
151 |
|
|
VkClearValue depthClear; |
152 |
|
✗ |
depthClear.depthStencil.depth = 1.f; |
153 |
|
|
|
154 |
|
|
// start the main renderpass. |
155 |
|
|
// We will use the clear color from above, and the framebuffer of the index the swapchain gave |
156 |
|
|
// us |
157 |
|
✗ |
VkRenderPassBeginInfo rpInfo = vkinit::renderpass_begin_info( |
158 |
|
✗ |
_renderPass, _windowExtent, _framebuffers[swapchainImageIndex]); |
159 |
|
|
|
160 |
|
|
// connect clear values |
161 |
|
✗ |
rpInfo.clearValueCount = 2; |
162 |
|
|
|
163 |
|
✗ |
VkClearValue clearValues[] = {clearValue, depthClear}; |
164 |
|
|
|
165 |
|
✗ |
rpInfo.pClearValues = &clearValues[0]; |
166 |
|
|
|
167 |
|
✗ |
vkCmdBeginRenderPass(cmd, &rpInfo, VK_SUBPASS_CONTENTS_INLINE); |
168 |
|
|
|
169 |
|
|
// draw_objects(cmd, _renderables.data(), _renderables.size()); |
170 |
|
✗ |
tick(dt); |
171 |
|
|
|
172 |
|
✗ |
ImGui::EndFrame(); |
173 |
|
✗ |
ImGui::Render(); |
174 |
|
|
|
175 |
|
✗ |
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); |
176 |
|
|
// finalize the render pass |
177 |
|
✗ |
vkCmdEndRenderPass(cmd); |
178 |
|
|
// finalize the command buffer (we can no longer add commands, but it can now be executed) |
179 |
|
✗ |
VK_CHECK(vkEndCommandBuffer(cmd)); |
180 |
|
|
|
181 |
|
|
// prepare the submission to the queue. |
182 |
|
|
// we want to wait on the _presentSemaphore, as that semaphore is signaled when the swapchain is |
183 |
|
|
// ready we will signal the _renderSemaphore, to signal that rendering has finished |
184 |
|
|
|
185 |
|
✗ |
VkSubmitInfo submit = vkinit::submit_info(&cmd); |
186 |
|
✗ |
VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
187 |
|
|
|
188 |
|
✗ |
submit.pWaitDstStageMask = &waitStage; |
189 |
|
|
|
190 |
|
✗ |
submit.waitSemaphoreCount = 1; |
191 |
|
✗ |
submit.pWaitSemaphores = &get_current_frame()._presentSemaphore; |
192 |
|
|
|
193 |
|
✗ |
submit.signalSemaphoreCount = 1; |
194 |
|
✗ |
submit.pSignalSemaphores = &get_current_frame()._renderSemaphore; |
195 |
|
|
|
196 |
|
|
// submit command buffer to the queue and execute it. |
197 |
|
|
// _renderFence will now block until the graphic commands finish execution |
198 |
|
✗ |
VK_CHECK(vkQueueSubmit(_graphicsQueue, 1, &submit, get_current_frame()._renderFence)); |
199 |
|
|
|
200 |
|
|
// prepare present |
201 |
|
|
// this will put the image we just rendered to into the visible window. |
202 |
|
|
// we want to wait on the _renderSemaphore for that, |
203 |
|
|
// as its necessary that drawing commands have finished before the image is displayed to the |
204 |
|
|
// user |
205 |
|
✗ |
VkPresentInfoKHR presentInfo = vkinit::present_info(); |
206 |
|
|
|
207 |
|
✗ |
presentInfo.pSwapchains = &_swapchain; |
208 |
|
✗ |
presentInfo.swapchainCount = 1; |
209 |
|
|
|
210 |
|
✗ |
presentInfo.pWaitSemaphores = &get_current_frame()._renderSemaphore; |
211 |
|
✗ |
presentInfo.waitSemaphoreCount = 1; |
212 |
|
|
|
213 |
|
✗ |
presentInfo.pImageIndices = &swapchainImageIndex; |
214 |
|
|
|
215 |
|
✗ |
VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo)); |
216 |
|
|
|
217 |
|
|
// increase the number of frames drawn |
218 |
|
✗ |
_frameNumber++; |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
void VulkanEngine::run() { |
222 |
|
|
SDL_Event e; |
223 |
|
✗ |
bool bQuit = false; |
224 |
|
|
|
225 |
|
|
using Clock = std::chrono::high_resolution_clock; |
226 |
|
|
using TimePoint = Clock::time_point; |
227 |
|
|
using TimeDelta = std::chrono::duration<float, std::ratio<1, 1>>; |
228 |
|
|
|
229 |
|
✗ |
TimePoint now = Clock::now(); |
230 |
|
✗ |
TimePoint prev = now; |
231 |
|
|
|
232 |
|
✗ |
start(); |
233 |
|
|
|
234 |
|
|
// main loop |
235 |
|
✗ |
while (!bQuit) { |
236 |
|
|
// Handle events on queue |
237 |
|
✗ |
while (SDL_PollEvent(&e) != 0) { |
238 |
|
✗ |
if (!ImGui_ImplSDL2_ProcessEvent(&e)) { |
239 |
|
|
// Standard Event handling |
240 |
|
✗ |
handle_event(e); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
// close the window when user alt-f4s or clicks the X button |
244 |
|
✗ |
if (e.type == SDL_QUIT) { |
245 |
|
✗ |
bQuit = true; |
246 |
|
✗ |
} else if (e.type == SDL_KEYDOWN) { |
247 |
|
✗ |
if (e.key.keysym.sym == SDLK_SPACE) { |
248 |
|
✗ |
_selectedShader += 1; |
249 |
|
✗ |
if (_selectedShader > 1) { |
250 |
|
✗ |
_selectedShader = 0; |
251 |
|
|
} |
252 |
|
|
} |
253 |
|
|
} |
254 |
|
|
} |
255 |
|
|
|
256 |
|
✗ |
now = Clock::now(); |
257 |
|
✗ |
float dt = TimeDelta(now - prev).count(); |
258 |
|
✗ |
draw(dt); |
259 |
|
✗ |
prev = now; |
260 |
|
|
} |
261 |
|
✗ |
} |
262 |
|
|
|
263 |
|
|
FrameData& VulkanEngine::get_current_frame() { return _frames[_frameNumber % FRAME_OVERLAP]; } |
264 |
|
|
|
265 |
|
|
FrameData& VulkanEngine::get_last_frame() { return _frames[(_frameNumber - 1) % 2]; } |
266 |
|
|
|
267 |
|
|
void VulkanEngine::init_vulkan() { |
268 |
|
✗ |
vkb::InstanceBuilder builder; |
269 |
|
|
|
270 |
|
|
// make the vulkan instance, with basic debug features |
271 |
|
✗ |
auto inst_ret = builder.set_app_name("Example Vulkan Application") |
272 |
|
✗ |
.request_validation_layers(bUseValidationLayers) |
273 |
|
✗ |
.use_default_debug_messenger() |
274 |
|
✗ |
.require_api_version(1, 1, 0) |
275 |
|
✗ |
.desire_api_version(1, 3, 0) |
276 |
|
✗ |
.build(); |
277 |
|
|
|
278 |
|
✗ |
vkb::Instance vkb_inst = inst_ret.value(); |
279 |
|
|
|
280 |
|
|
// grab the instance |
281 |
|
✗ |
_instance = vkb_inst.instance; |
282 |
|
✗ |
_debug_messenger = vkb_inst.debug_messenger; |
283 |
|
|
|
284 |
|
✗ |
if (SDL_Vulkan_CreateSurface(_window, _instance, &_surface) == SDL_FALSE) { |
285 |
|
✗ |
const char* msg = SDL_GetError(); |
286 |
|
✗ |
throw std::runtime_error(std::string("Surface creation failed: ") + std::string(msg)); |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
// use vkbootstrap to select a gpu. |
290 |
|
|
// We want a gpu that can write to the SDL surface and supports vulkan 1.2 |
291 |
|
✗ |
vkb::PhysicalDeviceSelector selector{vkb_inst}; |
292 |
|
✗ |
auto physicalDeviceResult = selector.set_minimum_version(1, 1).set_surface(_surface).select(); |
293 |
|
✗ |
if (!physicalDeviceResult) { |
294 |
|
✗ |
throw std::runtime_error("Physical device failed"); |
295 |
|
|
} |
296 |
|
✗ |
vkb::PhysicalDevice physicalDevice = physicalDeviceResult.value(); |
297 |
|
|
// --- |
298 |
|
|
|
299 |
|
|
// create the final vulkan device |
300 |
|
✗ |
vkb::DeviceBuilder deviceBuilder{physicalDevice}; |
301 |
|
✗ |
auto vkbDeviceResult = deviceBuilder.build(); |
302 |
|
✗ |
if (!vkbDeviceResult) { |
303 |
|
✗ |
throw std::runtime_error("Device failed"); |
304 |
|
|
} |
305 |
|
✗ |
vkb::Device vkbDevice = vkbDeviceResult.value(); |
306 |
|
|
// --- |
307 |
|
|
|
308 |
|
|
// Get the VkDevice handle used in the rest of a vulkan application |
309 |
|
✗ |
_device = vkbDevice.device; |
310 |
|
✗ |
_chosenGPU = physicalDevice.physical_device; |
311 |
|
|
|
312 |
|
|
// use vkbootstrap to get a Graphics queue |
313 |
|
✗ |
auto graphicsQueueResult = vkbDevice.get_queue(vkb::QueueType::graphics); |
314 |
|
✗ |
if (!graphicsQueueResult) { |
315 |
|
✗ |
throw std::runtime_error("Graphic Queue not found"); |
316 |
|
|
} |
317 |
|
✗ |
_graphicsQueue = graphicsQueueResult.value(); |
318 |
|
|
// --- |
319 |
|
|
|
320 |
|
✗ |
auto graphicsQueueFamilyResult = vkbDevice.get_queue_index(vkb::QueueType::graphics); |
321 |
|
✗ |
if (!graphicsQueueFamilyResult) { |
322 |
|
✗ |
throw std::runtime_error("Graphic Queue family not found"); |
323 |
|
|
} |
324 |
|
✗ |
_graphicsQueueFamily = graphicsQueueFamilyResult.value(); |
325 |
|
|
// --- |
326 |
|
|
|
327 |
|
|
// initialize the memory allocator |
328 |
|
✗ |
VmaAllocatorCreateInfo allocatorInfo = {}; |
329 |
|
✗ |
allocatorInfo.physicalDevice = _chosenGPU; |
330 |
|
✗ |
allocatorInfo.device = _device; |
331 |
|
✗ |
allocatorInfo.instance = _instance; |
332 |
|
✗ |
vmaCreateAllocator(&allocatorInfo, &_allocator); |
333 |
|
|
|
334 |
|
|
_mainDeletionQueue.push_function([&]() { vmaDestroyAllocator(_allocator); }); |
335 |
|
|
|
336 |
|
✗ |
vkGetPhysicalDeviceProperties(_chosenGPU, &_gpuProperties); |
337 |
|
|
|
338 |
|
✗ |
std::cout << "The gpu has a minimum buffer alignement of " |
339 |
|
✗ |
<< _gpuProperties.limits.minUniformBufferOffsetAlignment << std::endl; |
340 |
|
✗ |
} |
341 |
|
|
|
342 |
|
|
void VulkanEngine::init_swapchain() { |
343 |
|
✗ |
vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, _device, _surface}; |
344 |
|
|
|
345 |
|
|
vkb::Swapchain vkbSwapchain = swapchainBuilder |
346 |
|
✗ |
.use_default_format_selection() |
347 |
|
|
// use vsync present mode |
348 |
|
✗ |
.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR) |
349 |
|
✗ |
.set_desired_extent(_windowExtent.width, _windowExtent.height) |
350 |
|
✗ |
.build() |
351 |
|
✗ |
.value(); |
352 |
|
|
|
353 |
|
|
// store swapchain and its related images |
354 |
|
✗ |
_swapchain = vkbSwapchain.swapchain; |
355 |
|
✗ |
_swapchainImages = vkbSwapchain.get_images().value(); |
356 |
|
✗ |
_swapchainImageViews = vkbSwapchain.get_image_views().value(); |
357 |
|
|
|
358 |
|
✗ |
_swachainImageFormat = vkbSwapchain.image_format; |
359 |
|
|
|
360 |
|
✗ |
_mainDeletionQueue.push_function( |
361 |
|
|
[=]() { vkDestroySwapchainKHR(_device, _swapchain, nullptr); }); |
362 |
|
|
|
363 |
|
|
// depth image size will match the window |
364 |
|
✗ |
VkExtent3D depthImageExtent = {_windowExtent.width, _windowExtent.height, 1}; |
365 |
|
|
|
366 |
|
|
// hardcoding the depth format to 32 bit float |
367 |
|
✗ |
_depthFormat = VK_FORMAT_D32_SFLOAT; |
368 |
|
|
|
369 |
|
|
// the depth image will be a image with the format we selected and Depth Attachment usage flag |
370 |
|
✗ |
VkImageCreateInfo dimg_info = vkinit::image_create_info( |
371 |
|
|
_depthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depthImageExtent); |
372 |
|
|
|
373 |
|
|
// for the depth image, we want to allocate it from gpu local memory |
374 |
|
✗ |
VmaAllocationCreateInfo dimg_allocinfo = {}; |
375 |
|
✗ |
dimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
376 |
|
✗ |
dimg_allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); |
377 |
|
|
|
378 |
|
|
// allocate and create the image |
379 |
|
✗ |
vmaCreateImage(_allocator, |
380 |
|
|
&dimg_info, |
381 |
|
|
&dimg_allocinfo, |
382 |
|
|
&_depthImage._image, |
383 |
|
|
&_depthImage._allocation, |
384 |
|
|
nullptr); |
385 |
|
|
|
386 |
|
|
// build a image-view for the depth image to use for rendering |
387 |
|
|
VkImageViewCreateInfo dview_info = |
388 |
|
✗ |
vkinit::imageview_create_info(_depthFormat, _depthImage._image, VK_IMAGE_ASPECT_DEPTH_BIT); |
389 |
|
|
; |
390 |
|
|
|
391 |
|
✗ |
VK_CHECK(vkCreateImageView(_device, &dview_info, nullptr, &_depthImageView)); |
392 |
|
|
|
393 |
|
|
// add to deletion queues |
394 |
|
|
_mainDeletionQueue.push_function([=]() { |
395 |
|
✗ |
vkDestroyImageView(_device, _depthImageView, nullptr); |
396 |
|
✗ |
vmaDestroyImage(_allocator, _depthImage._image, _depthImage._allocation); |
397 |
|
✗ |
}); |
398 |
|
✗ |
} |
399 |
|
|
|
400 |
|
|
void VulkanEngine::init_imgui() { |
401 |
|
|
// 1: create descriptor pool for IMGUI |
402 |
|
|
// the size of the pool is very oversize, but it's copied from imgui demo itself. |
403 |
|
✗ |
VkDescriptorPoolSize pool_sizes[] = {{VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, |
404 |
|
|
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, |
405 |
|
|
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, |
406 |
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, |
407 |
|
|
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, |
408 |
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, |
409 |
|
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, |
410 |
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, |
411 |
|
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, |
412 |
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, |
413 |
|
|
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}}; |
414 |
|
|
|
415 |
|
✗ |
VkDescriptorPoolCreateInfo pool_info = {}; |
416 |
|
✗ |
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; |
417 |
|
✗ |
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; |
418 |
|
✗ |
pool_info.maxSets = 1000; |
419 |
|
✗ |
pool_info.poolSizeCount = uint32_t(std::size(pool_sizes)); |
420 |
|
✗ |
pool_info.pPoolSizes = pool_sizes; |
421 |
|
|
|
422 |
|
|
VkDescriptorPool imguiPool; |
423 |
|
✗ |
VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool)); |
424 |
|
|
|
425 |
|
|
// 2: initialize imgui library |
426 |
|
|
|
427 |
|
|
// this initializes the core structures of imgui |
428 |
|
✗ |
ImGui::CreateContext(); |
429 |
|
|
|
430 |
|
|
// this initializes imgui for SDL |
431 |
|
✗ |
ImGui_ImplSDL2_InitForVulkan(_window); |
432 |
|
|
|
433 |
|
|
// this initializes imgui for Vulkan |
434 |
|
✗ |
ImGui_ImplVulkan_InitInfo init_info = {}; |
435 |
|
✗ |
init_info.Instance = _instance; |
436 |
|
✗ |
init_info.PhysicalDevice = _chosenGPU; |
437 |
|
✗ |
init_info.Device = _device; |
438 |
|
✗ |
init_info.Queue = _graphicsQueue; |
439 |
|
✗ |
init_info.DescriptorPool = imguiPool; |
440 |
|
✗ |
init_info.MinImageCount = 3; |
441 |
|
✗ |
init_info.ImageCount = 3; |
442 |
|
✗ |
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; |
443 |
|
|
|
444 |
|
✗ |
ImGui_ImplVulkan_Init(&init_info, _renderPass); |
445 |
|
|
|
446 |
|
|
// execute a gpu command to upload imgui font textures |
447 |
|
|
immediate_submit([&](VkCommandBuffer cmd) { ImGui_ImplVulkan_CreateFontsTexture(cmd); }); |
448 |
|
|
|
449 |
|
|
// clear font textures from cpu data |
450 |
|
✗ |
ImGui_ImplVulkan_DestroyFontUploadObjects(); |
451 |
|
|
|
452 |
|
|
// add the destroy the imgui created structures |
453 |
|
|
_mainDeletionQueue.push_function([=]() { |
454 |
|
✗ |
vkDestroyDescriptorPool(_device, imguiPool, nullptr); |
455 |
|
✗ |
ImGui_ImplVulkan_Shutdown(); |
456 |
|
✗ |
}); |
457 |
|
✗ |
} |
458 |
|
|
|
459 |
|
|
void VulkanEngine::init_default_renderpass() { |
460 |
|
|
// we define an attachment description for our main color image |
461 |
|
|
// the attachment is loaded as "clear" when renderpass start |
462 |
|
|
// the attachment is stored when renderpass ends |
463 |
|
|
// the attachment layout starts as "undefined", and transitions to "Present" so its possible to |
464 |
|
|
// display it we dont care about stencil, and dont use multisampling |
465 |
|
|
|
466 |
|
✗ |
VkAttachmentDescription color_attachment = {}; |
467 |
|
✗ |
color_attachment.format = _swachainImageFormat; |
468 |
|
✗ |
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
469 |
|
✗ |
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
470 |
|
✗ |
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
471 |
|
✗ |
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
472 |
|
✗ |
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
473 |
|
✗ |
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
474 |
|
✗ |
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
475 |
|
|
|
476 |
|
✗ |
VkAttachmentReference color_attachment_ref = {}; |
477 |
|
✗ |
color_attachment_ref.attachment = 0; |
478 |
|
✗ |
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
479 |
|
|
|
480 |
|
✗ |
VkAttachmentDescription depth_attachment = {}; |
481 |
|
|
// Depth attachment |
482 |
|
✗ |
depth_attachment.flags = 0; |
483 |
|
✗ |
depth_attachment.format = _depthFormat; |
484 |
|
✗ |
depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT; |
485 |
|
✗ |
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
486 |
|
✗ |
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
487 |
|
✗ |
depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
488 |
|
✗ |
depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
489 |
|
✗ |
depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
490 |
|
✗ |
depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
491 |
|
|
|
492 |
|
✗ |
VkAttachmentReference depth_attachment_ref = {}; |
493 |
|
✗ |
depth_attachment_ref.attachment = 1; |
494 |
|
✗ |
depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
495 |
|
|
|
496 |
|
|
// we are going to create 1 subpass, which is the minimum you can do |
497 |
|
✗ |
VkSubpassDescription subpass = {}; |
498 |
|
✗ |
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
499 |
|
✗ |
subpass.colorAttachmentCount = 1; |
500 |
|
✗ |
subpass.pColorAttachments = &color_attachment_ref; |
501 |
|
|
// hook the depth attachment into the subpass |
502 |
|
✗ |
subpass.pDepthStencilAttachment = &depth_attachment_ref; |
503 |
|
|
|
504 |
|
|
// 1 dependency, which is from "outside" into the subpass. And we can read or write color |
505 |
|
✗ |
VkSubpassDependency dependency = {}; |
506 |
|
✗ |
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; |
507 |
|
✗ |
dependency.dstSubpass = 0; |
508 |
|
✗ |
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
509 |
|
✗ |
dependency.srcAccessMask = 0; |
510 |
|
✗ |
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
511 |
|
✗ |
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
512 |
|
|
|
513 |
|
|
// dependency from outside to the subpass, making this subpass dependent on the previous |
514 |
|
|
// renderpasses |
515 |
|
✗ |
VkSubpassDependency depth_dependency = {}; |
516 |
|
✗ |
depth_dependency.srcSubpass = VK_SUBPASS_EXTERNAL; |
517 |
|
✗ |
depth_dependency.dstSubpass = 0; |
518 |
|
✗ |
depth_dependency.srcStageMask = |
519 |
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; |
520 |
|
✗ |
depth_dependency.srcAccessMask = 0; |
521 |
|
✗ |
depth_dependency.dstStageMask = |
522 |
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; |
523 |
|
✗ |
depth_dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
524 |
|
|
|
525 |
|
|
// array of 2 dependencies, one for color, two for depth |
526 |
|
✗ |
VkSubpassDependency dependencies[2] = {dependency, depth_dependency}; |
527 |
|
|
|
528 |
|
|
// array of 2 attachments, one for the color, and other for depth |
529 |
|
✗ |
VkAttachmentDescription attachments[2] = {color_attachment, depth_attachment}; |
530 |
|
|
|
531 |
|
✗ |
VkRenderPassCreateInfo render_pass_info = {}; |
532 |
|
✗ |
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
533 |
|
|
// 2 attachments from attachment array |
534 |
|
✗ |
render_pass_info.attachmentCount = 2; |
535 |
|
✗ |
render_pass_info.pAttachments = &attachments[0]; |
536 |
|
✗ |
render_pass_info.subpassCount = 1; |
537 |
|
✗ |
render_pass_info.pSubpasses = &subpass; |
538 |
|
|
// 2 dependencies from dependency array |
539 |
|
✗ |
render_pass_info.dependencyCount = 2; |
540 |
|
✗ |
render_pass_info.pDependencies = &dependencies[0]; |
541 |
|
|
|
542 |
|
✗ |
VK_CHECK(vkCreateRenderPass(_device, &render_pass_info, nullptr, &_renderPass)); |
543 |
|
|
|
544 |
|
|
_mainDeletionQueue.push_function([=]() { vkDestroyRenderPass(_device, _renderPass, nullptr); }); |
545 |
|
✗ |
} |
546 |
|
|
|
547 |
|
|
void VulkanEngine::init_framebuffers() { |
548 |
|
|
// create the framebuffers for the swapchain images. This will connect the render-pass to the |
549 |
|
|
// images for rendering |
550 |
|
✗ |
VkFramebufferCreateInfo fb_info = vkinit::framebuffer_create_info(_renderPass, _windowExtent); |
551 |
|
|
|
552 |
|
✗ |
const std::size_t swapchain_imagecount = _swapchainImages.size(); |
553 |
|
✗ |
_framebuffers = std::vector<VkFramebuffer>(swapchain_imagecount); |
554 |
|
|
|
555 |
|
✗ |
for (std::size_t i = 0; i < swapchain_imagecount; i++) { |
556 |
|
|
|
557 |
|
|
VkImageView attachments[2]; |
558 |
|
✗ |
attachments[0] = _swapchainImageViews[i]; |
559 |
|
✗ |
attachments[1] = _depthImageView; |
560 |
|
|
|
561 |
|
✗ |
fb_info.pAttachments = attachments; |
562 |
|
✗ |
fb_info.attachmentCount = 2; |
563 |
|
✗ |
VK_CHECK(vkCreateFramebuffer(_device, &fb_info, nullptr, &_framebuffers[i])); |
564 |
|
|
|
565 |
|
|
_mainDeletionQueue.push_function([=]() { |
566 |
|
✗ |
vkDestroyFramebuffer(_device, _framebuffers[i], nullptr); |
567 |
|
✗ |
vkDestroyImageView(_device, _swapchainImageViews[i], nullptr); |
568 |
|
✗ |
}); |
569 |
|
|
} |
570 |
|
✗ |
} |
571 |
|
|
|
572 |
|
|
void VulkanEngine::init_commands() { |
573 |
|
|
// create a command pool for commands submitted to the graphics queue. |
574 |
|
|
// we also want the pool to allow for resetting of individual command buffers |
575 |
|
✗ |
VkCommandPoolCreateInfo commandPoolInfo = vkinit::command_pool_create_info( |
576 |
|
|
_graphicsQueueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); |
577 |
|
|
|
578 |
|
✗ |
for (int i = 0; i < FRAME_OVERLAP; i++) { |
579 |
|
|
|
580 |
|
✗ |
VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, &_frames[i]._commandPool)); |
581 |
|
|
|
582 |
|
|
// allocate the default command buffer that we will use for rendering |
583 |
|
|
VkCommandBufferAllocateInfo cmdAllocInfo = |
584 |
|
✗ |
vkinit::command_buffer_allocate_info(_frames[i]._commandPool, 1); |
585 |
|
|
|
586 |
|
✗ |
VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &_frames[i]._mainCommandBuffer)); |
587 |
|
|
|
588 |
|
✗ |
_mainDeletionQueue.push_function( |
589 |
|
|
[=]() { vkDestroyCommandPool(_device, _frames[i]._commandPool, nullptr); }); |
590 |
|
|
} |
591 |
|
|
|
592 |
|
|
VkCommandPoolCreateInfo uploadCommandPoolInfo = |
593 |
|
✗ |
vkinit::command_pool_create_info(_graphicsQueueFamily); |
594 |
|
|
// create pool for upload context |
595 |
|
✗ |
VK_CHECK(vkCreateCommandPool( |
596 |
|
|
_device, &uploadCommandPoolInfo, nullptr, &_uploadContext._commandPool)); |
597 |
|
|
|
598 |
|
✗ |
_mainDeletionQueue.push_function( |
599 |
|
|
[=]() { vkDestroyCommandPool(_device, _uploadContext._commandPool, nullptr); }); |
600 |
|
|
|
601 |
|
|
// allocate the default command buffer that we will use for rendering |
602 |
|
|
VkCommandBufferAllocateInfo cmdAllocInfo = |
603 |
|
✗ |
vkinit::command_buffer_allocate_info(_uploadContext._commandPool, 1); |
604 |
|
|
|
605 |
|
✗ |
VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &_uploadContext._commandBuffer)); |
606 |
|
✗ |
} |
607 |
|
|
|
608 |
|
|
void VulkanEngine::init_sync_structures() { |
609 |
|
|
// create syncronization structures |
610 |
|
|
// one fence to control when the gpu has finished rendering the frame, |
611 |
|
|
// and 2 semaphores to syncronize rendering with swapchain |
612 |
|
|
// we want the fence to start signalled so we can wait on it on the first frame |
613 |
|
✗ |
VkFenceCreateInfo fenceCreateInfo = vkinit::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT); |
614 |
|
|
|
615 |
|
✗ |
VkSemaphoreCreateInfo semaphoreCreateInfo = vkinit::semaphore_create_info(); |
616 |
|
|
|
617 |
|
✗ |
for (int i = 0; i < FRAME_OVERLAP; i++) { |
618 |
|
|
|
619 |
|
✗ |
VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_frames[i]._renderFence)); |
620 |
|
|
|
621 |
|
|
// enqueue the destruction of the fence |
622 |
|
✗ |
_mainDeletionQueue.push_function( |
623 |
|
|
[=]() { vkDestroyFence(_device, _frames[i]._renderFence, nullptr); }); |
624 |
|
|
|
625 |
|
✗ |
VK_CHECK(vkCreateSemaphore( |
626 |
|
|
_device, &semaphoreCreateInfo, nullptr, &_frames[i]._presentSemaphore)); |
627 |
|
✗ |
VK_CHECK(vkCreateSemaphore( |
628 |
|
|
_device, &semaphoreCreateInfo, nullptr, &_frames[i]._renderSemaphore)); |
629 |
|
|
|
630 |
|
|
// enqueue the destruction of semaphores |
631 |
|
|
_mainDeletionQueue.push_function([=]() { |
632 |
|
✗ |
vkDestroySemaphore(_device, _frames[i]._presentSemaphore, nullptr); |
633 |
|
✗ |
vkDestroySemaphore(_device, _frames[i]._renderSemaphore, nullptr); |
634 |
|
✗ |
}); |
635 |
|
|
} |
636 |
|
|
|
637 |
|
✗ |
VkFenceCreateInfo uploadFenceCreateInfo = vkinit::fence_create_info(); |
638 |
|
|
|
639 |
|
✗ |
VK_CHECK(vkCreateFence(_device, &uploadFenceCreateInfo, nullptr, &_uploadContext._uploadFence)); |
640 |
|
✗ |
_mainDeletionQueue.push_function( |
641 |
|
|
[=]() { vkDestroyFence(_device, _uploadContext._uploadFence, nullptr); }); |
642 |
|
✗ |
} |
643 |
|
|
|
644 |
|
|
void VulkanEngine::init_pipelines() { |
645 |
|
|
VkShaderModule colorMeshShader; |
646 |
|
✗ |
std::string p = "E:/work/lython/src/tide/shaders/"; |
647 |
|
✗ |
p = "E:/work/lython/build/bin/Debug/shaders/"; |
648 |
|
|
|
649 |
|
✗ |
if (!load_shader_module(p + std::string("default_lit.frag.spv"), &colorMeshShader)) { |
650 |
|
✗ |
std::cout << "Error when building the colored mesh shader" << std::endl; |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
VkShaderModule texturedMeshShader; |
654 |
|
✗ |
if (!load_shader_module(p + std::string("textured_lit.frag.spv"), &texturedMeshShader)) { |
655 |
|
✗ |
std::cout << "Error when building the colored mesh shader" << std::endl; |
656 |
|
|
} |
657 |
|
|
|
658 |
|
|
VkShaderModule meshVertShader; |
659 |
|
✗ |
if (!load_shader_module(p + std::string("tri_mesh_ssbo.vert.spv"), &meshVertShader)) { |
660 |
|
✗ |
std::cout << "Error when building the mesh vertex shader module" << std::endl; |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
// build the stage-create-info for both vertex and fragment stages. This lets the pipeline know |
664 |
|
|
// the shader modules per stage |
665 |
|
✗ |
PipelineBuilder pipelineBuilder; |
666 |
|
|
|
667 |
|
✗ |
pipelineBuilder._shaderStages.push_back( |
668 |
|
✗ |
vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_VERTEX_BIT, meshVertShader)); |
669 |
|
|
|
670 |
|
✗ |
pipelineBuilder._shaderStages.push_back( |
671 |
|
✗ |
vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_FRAGMENT_BIT, colorMeshShader)); |
672 |
|
|
|
673 |
|
|
// we start from just the default empty pipeline layout info |
674 |
|
✗ |
VkPipelineLayoutCreateInfo mesh_pipeline_layout_info = vkinit::pipeline_layout_create_info(); |
675 |
|
|
|
676 |
|
|
// setup push constants |
677 |
|
|
VkPushConstantRange push_constant; |
678 |
|
|
// offset 0 |
679 |
|
✗ |
push_constant.offset = 0; |
680 |
|
|
// size of a MeshPushConstant struct |
681 |
|
✗ |
push_constant.size = sizeof(MeshPushConstants); |
682 |
|
|
// for the vertex shader |
683 |
|
✗ |
push_constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; |
684 |
|
|
|
685 |
|
✗ |
mesh_pipeline_layout_info.pPushConstantRanges = &push_constant; |
686 |
|
✗ |
mesh_pipeline_layout_info.pushConstantRangeCount = 1; |
687 |
|
|
|
688 |
|
✗ |
VkDescriptorSetLayout setLayouts[] = {_globalSetLayout, _objectSetLayout}; |
689 |
|
|
|
690 |
|
✗ |
mesh_pipeline_layout_info.setLayoutCount = 2; |
691 |
|
✗ |
mesh_pipeline_layout_info.pSetLayouts = setLayouts; |
692 |
|
|
|
693 |
|
|
VkPipelineLayout meshPipLayout; |
694 |
|
✗ |
VK_CHECK(vkCreatePipelineLayout(_device, &mesh_pipeline_layout_info, nullptr, &meshPipLayout)); |
695 |
|
|
|
696 |
|
|
// we start from the normal mesh layout |
697 |
|
✗ |
VkPipelineLayoutCreateInfo textured_pipeline_layout_info = mesh_pipeline_layout_info; |
698 |
|
|
|
699 |
|
|
VkDescriptorSetLayout texturedSetLayouts[] = { |
700 |
|
✗ |
_globalSetLayout, _objectSetLayout, _singleTextureSetLayout}; |
701 |
|
|
|
702 |
|
✗ |
textured_pipeline_layout_info.setLayoutCount = 3; |
703 |
|
✗ |
textured_pipeline_layout_info.pSetLayouts = texturedSetLayouts; |
704 |
|
|
|
705 |
|
|
VkPipelineLayout texturedPipeLayout; |
706 |
|
✗ |
VK_CHECK(vkCreatePipelineLayout( |
707 |
|
|
_device, &textured_pipeline_layout_info, nullptr, &texturedPipeLayout)); |
708 |
|
|
|
709 |
|
|
// hook the push constants layout |
710 |
|
✗ |
pipelineBuilder._pipelineLayout = meshPipLayout; |
711 |
|
|
|
712 |
|
|
// vertex input controls how to read vertices from vertex buffers. We arent using it yet |
713 |
|
✗ |
pipelineBuilder._vertexInputInfo = vkinit::vertex_input_state_create_info(); |
714 |
|
|
|
715 |
|
|
// input assembly is the configuration for drawing triangle lists, strips, or individual points. |
716 |
|
|
// we are just going to draw triangle list |
717 |
|
|
pipelineBuilder._inputAssembly = |
718 |
|
✗ |
vkinit::input_assembly_create_info(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); |
719 |
|
|
|
720 |
|
|
// build viewport and scissor from the swapchain extents |
721 |
|
✗ |
pipelineBuilder._viewport.x = 0.0f; |
722 |
|
✗ |
pipelineBuilder._viewport.y = 0.0f; |
723 |
|
✗ |
pipelineBuilder._viewport.width = (float)_windowExtent.width; |
724 |
|
✗ |
pipelineBuilder._viewport.height = (float)_windowExtent.height; |
725 |
|
✗ |
pipelineBuilder._viewport.minDepth = 0.0f; |
726 |
|
✗ |
pipelineBuilder._viewport.maxDepth = 1.0f; |
727 |
|
|
|
728 |
|
✗ |
pipelineBuilder._scissor.offset = {0, 0}; |
729 |
|
✗ |
pipelineBuilder._scissor.extent = _windowExtent; |
730 |
|
|
|
731 |
|
|
// configure the rasterizer to draw filled triangles |
732 |
|
✗ |
pipelineBuilder._rasterizer = vkinit::rasterization_state_create_info(VK_POLYGON_MODE_FILL); |
733 |
|
|
|
734 |
|
|
// we dont use multisampling, so just run the default one |
735 |
|
✗ |
pipelineBuilder._multisampling = vkinit::multisampling_state_create_info(); |
736 |
|
|
|
737 |
|
|
// a single blend attachment with no blending and writing to RGBA |
738 |
|
✗ |
pipelineBuilder._colorBlendAttachment = vkinit::color_blend_attachment_state(); |
739 |
|
|
|
740 |
|
|
// default depthtesting |
741 |
|
|
pipelineBuilder._depthStencil = |
742 |
|
✗ |
vkinit::depth_stencil_create_info(true, true, VK_COMPARE_OP_LESS_OR_EQUAL); |
743 |
|
|
|
744 |
|
|
// build the mesh pipeline |
745 |
|
|
|
746 |
|
✗ |
VertexInputDescription vertexDescription = Vertex::get_vertex_description(); |
747 |
|
|
|
748 |
|
|
// connect the pipeline builder vertex input info to the one we get from Vertex |
749 |
|
✗ |
pipelineBuilder._vertexInputInfo.pVertexAttributeDescriptions = |
750 |
|
✗ |
vertexDescription.attributes.data(); |
751 |
|
✗ |
pipelineBuilder._vertexInputInfo.vertexAttributeDescriptionCount = |
752 |
|
✗ |
uint32_t(vertexDescription.attributes.size()); |
753 |
|
|
|
754 |
|
✗ |
pipelineBuilder._vertexInputInfo.pVertexBindingDescriptions = vertexDescription.bindings.data(); |
755 |
|
✗ |
pipelineBuilder._vertexInputInfo.vertexBindingDescriptionCount = |
756 |
|
✗ |
uint32_t(vertexDescription.bindings.size()); |
757 |
|
|
|
758 |
|
|
// build the mesh triangle pipeline |
759 |
|
✗ |
VkPipeline meshPipeline = pipelineBuilder.build_pipeline(_device, _renderPass); |
760 |
|
|
|
761 |
|
✗ |
create_material(meshPipeline, meshPipLayout, "defaultmesh"); |
762 |
|
|
|
763 |
|
✗ |
pipelineBuilder._shaderStages.clear(); |
764 |
|
✗ |
pipelineBuilder._shaderStages.push_back( |
765 |
|
✗ |
vkinit::pipeline_shader_stage_create_info(VK_SHADER_STAGE_VERTEX_BIT, meshVertShader)); |
766 |
|
|
|
767 |
|
✗ |
pipelineBuilder._shaderStages.push_back(vkinit::pipeline_shader_stage_create_info( |
768 |
|
|
VK_SHADER_STAGE_FRAGMENT_BIT, texturedMeshShader)); |
769 |
|
|
|
770 |
|
✗ |
pipelineBuilder._pipelineLayout = texturedPipeLayout; |
771 |
|
✗ |
VkPipeline texPipeline = pipelineBuilder.build_pipeline(_device, _renderPass); |
772 |
|
✗ |
create_material(texPipeline, texturedPipeLayout, "texturedmesh"); |
773 |
|
|
|
774 |
|
✗ |
vkDestroyShaderModule(_device, meshVertShader, nullptr); |
775 |
|
✗ |
vkDestroyShaderModule(_device, colorMeshShader, nullptr); |
776 |
|
✗ |
vkDestroyShaderModule(_device, texturedMeshShader, nullptr); |
777 |
|
|
|
778 |
|
|
_mainDeletionQueue.push_function([=]() { |
779 |
|
✗ |
vkDestroyPipeline(_device, meshPipeline, nullptr); |
780 |
|
✗ |
vkDestroyPipeline(_device, texPipeline, nullptr); |
781 |
|
|
|
782 |
|
✗ |
vkDestroyPipelineLayout(_device, meshPipLayout, nullptr); |
783 |
|
✗ |
vkDestroyPipelineLayout(_device, texturedPipeLayout, nullptr); |
784 |
|
✗ |
}); |
785 |
|
✗ |
} |
786 |
|
|
|
787 |
|
|
bool VulkanEngine::load_shader_module(std::string const& filePath, |
788 |
|
|
VkShaderModule* outShaderModule) { |
789 |
|
|
// open the file. With cursor at the end |
790 |
|
✗ |
std::ifstream file(filePath.c_str(), std::ios::ate | std::ios::binary); |
791 |
|
|
|
792 |
|
✗ |
if (!file.is_open()) { |
793 |
|
✗ |
std::cout << "FAIL"; |
794 |
|
✗ |
return false; |
795 |
|
|
} |
796 |
|
|
|
797 |
|
|
// find what the size of the file is by looking up the location of the cursor |
798 |
|
|
// because the cursor is at the end, it gives the size directly in bytes |
799 |
|
✗ |
size_t fileSize = (size_t)file.tellg(); |
800 |
|
|
|
801 |
|
|
// spirv expects the buffer to be on uint32, so make sure to reserve a int vector big enough for |
802 |
|
|
// the entire file |
803 |
|
✗ |
std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t)); |
804 |
|
|
|
805 |
|
|
// put file cursor at beggining |
806 |
|
✗ |
file.seekg(0); |
807 |
|
|
|
808 |
|
|
// load the entire file into the buffer |
809 |
|
✗ |
file.read((char*)buffer.data(), fileSize); |
810 |
|
|
|
811 |
|
|
// now that the file is loaded into the buffer, we can close it |
812 |
|
✗ |
file.close(); |
813 |
|
|
|
814 |
|
|
// create a new shader module, using the buffer we loaded |
815 |
|
✗ |
VkShaderModuleCreateInfo createInfo = {}; |
816 |
|
✗ |
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
817 |
|
✗ |
createInfo.pNext = nullptr; |
818 |
|
|
|
819 |
|
|
// codeSize has to be in bytes, so multply the ints in the buffer by size of int to know the |
820 |
|
|
// real size of the buffer |
821 |
|
✗ |
createInfo.codeSize = buffer.size() * sizeof(uint32_t); |
822 |
|
✗ |
createInfo.pCode = buffer.data(); |
823 |
|
|
|
824 |
|
|
// check that the creation goes well. |
825 |
|
|
VkShaderModule shaderModule; |
826 |
|
✗ |
if (vkCreateShaderModule(_device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { |
827 |
|
✗ |
return false; |
828 |
|
|
} |
829 |
|
✗ |
*outShaderModule = shaderModule; |
830 |
|
✗ |
return true; |
831 |
|
✗ |
} |
832 |
|
|
|
833 |
|
|
VkPipeline PipelineBuilder::build_pipeline(VkDevice device, VkRenderPass pass) { |
834 |
|
|
// make viewport state from our stored viewport and scissor. |
835 |
|
|
// at the moment we wont support multiple viewports or scissors |
836 |
|
✗ |
VkPipelineViewportStateCreateInfo viewportState = {}; |
837 |
|
✗ |
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
838 |
|
✗ |
viewportState.pNext = nullptr; |
839 |
|
|
|
840 |
|
✗ |
viewportState.viewportCount = 1; |
841 |
|
✗ |
viewportState.pViewports = &_viewport; |
842 |
|
✗ |
viewportState.scissorCount = 1; |
843 |
|
✗ |
viewportState.pScissors = &_scissor; |
844 |
|
|
|
845 |
|
|
// setup dummy color blending. We arent using transparent objects yet |
846 |
|
|
// the blending is just "no blend", but we do write to the color attachment |
847 |
|
✗ |
VkPipelineColorBlendStateCreateInfo colorBlending = {}; |
848 |
|
✗ |
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
849 |
|
✗ |
colorBlending.pNext = nullptr; |
850 |
|
|
|
851 |
|
✗ |
colorBlending.logicOpEnable = VK_FALSE; |
852 |
|
✗ |
colorBlending.logicOp = VK_LOGIC_OP_COPY; |
853 |
|
✗ |
colorBlending.attachmentCount = 1; |
854 |
|
✗ |
colorBlending.pAttachments = &_colorBlendAttachment; |
855 |
|
|
|
856 |
|
|
// build the actual pipeline |
857 |
|
|
// we now use all of the info structs we have been writing into into this one to create the |
858 |
|
|
// pipeline |
859 |
|
✗ |
VkGraphicsPipelineCreateInfo pipelineInfo = {}; |
860 |
|
✗ |
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
861 |
|
✗ |
pipelineInfo.pNext = nullptr; |
862 |
|
|
|
863 |
|
✗ |
pipelineInfo.stageCount = uint32_t(_shaderStages.size()); |
864 |
|
✗ |
pipelineInfo.pStages = _shaderStages.data(); |
865 |
|
✗ |
pipelineInfo.pVertexInputState = &_vertexInputInfo; |
866 |
|
✗ |
pipelineInfo.pInputAssemblyState = &_inputAssembly; |
867 |
|
✗ |
pipelineInfo.pViewportState = &viewportState; |
868 |
|
✗ |
pipelineInfo.pRasterizationState = &_rasterizer; |
869 |
|
✗ |
pipelineInfo.pMultisampleState = &_multisampling; |
870 |
|
✗ |
pipelineInfo.pColorBlendState = &colorBlending; |
871 |
|
✗ |
pipelineInfo.pDepthStencilState = &_depthStencil; |
872 |
|
✗ |
pipelineInfo.layout = _pipelineLayout; |
873 |
|
✗ |
pipelineInfo.renderPass = pass; |
874 |
|
✗ |
pipelineInfo.subpass = 0; |
875 |
|
✗ |
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; |
876 |
|
|
|
877 |
|
|
// its easy to error out on create graphics pipeline, so we handle it a bit better than the |
878 |
|
|
// common VK_CHECK case |
879 |
|
|
VkPipeline newPipeline; |
880 |
|
✗ |
if (vkCreateGraphicsPipelines( |
881 |
|
✗ |
device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &newPipeline) != VK_SUCCESS) { |
882 |
|
✗ |
std::cout << "failed to create pipline\n"; |
883 |
|
✗ |
return VK_NULL_HANDLE; // failed to create graphics pipeline |
884 |
|
|
} else { |
885 |
|
✗ |
return newPipeline; |
886 |
|
|
} |
887 |
|
|
} |
888 |
|
|
|
889 |
|
|
void VulkanEngine::load_meshes() { |
890 |
|
✗ |
Mesh triMesh{}; |
891 |
|
|
// make the array 3 vertices long |
892 |
|
✗ |
triMesh._vertices.resize(3); |
893 |
|
|
|
894 |
|
|
// vertex positions |
895 |
|
✗ |
triMesh._vertices[0].position = {1.f, 1.f, 0.0f}; |
896 |
|
✗ |
triMesh._vertices[1].position = {-1.f, 1.f, 0.0f}; |
897 |
|
✗ |
triMesh._vertices[2].position = {0.f, -1.f, 0.0f}; |
898 |
|
|
|
899 |
|
|
// vertex colors, all green |
900 |
|
✗ |
triMesh._vertices[0].color = {0.f, 1.f, 0.0f}; // pure green |
901 |
|
✗ |
triMesh._vertices[1].color = {0.f, 1.f, 0.0f}; // pure green |
902 |
|
✗ |
triMesh._vertices[2].color = {0.f, 1.f, 0.0f}; // pure green |
903 |
|
|
// we dont care about the vertex normals |
904 |
|
|
|
905 |
|
|
// load the monkey |
906 |
|
✗ |
Mesh monkeyMesh{}; |
907 |
|
✗ |
std::string p = "E:/work/lython/build/bin/Debug/assets/"; |
908 |
|
✗ |
monkeyMesh.load_from_obj((p + std::string("monkey_smooth.obj")).c_str()); |
909 |
|
|
|
910 |
|
✗ |
Mesh lostEmpire{}; |
911 |
|
✗ |
lostEmpire.load_from_obj((p + std::string("lost_empire.obj")).c_str()); |
912 |
|
|
|
913 |
|
✗ |
upload_mesh(triMesh); |
914 |
|
✗ |
upload_mesh(monkeyMesh); |
915 |
|
✗ |
upload_mesh(lostEmpire); |
916 |
|
|
|
917 |
|
✗ |
_meshes["monkey"] = monkeyMesh; |
918 |
|
✗ |
_meshes["triangle"] = triMesh; |
919 |
|
✗ |
_meshes["empire"] = lostEmpire; |
920 |
|
✗ |
} |
921 |
|
|
|
922 |
|
|
VkSampler VulkanEngine::new_texture_sampler() { |
923 |
|
✗ |
VkSamplerCreateInfo samplerInfo{}; |
924 |
|
✗ |
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; |
925 |
|
✗ |
samplerInfo.magFilter = VK_FILTER_LINEAR; |
926 |
|
✗ |
samplerInfo.minFilter = VK_FILTER_LINEAR; |
927 |
|
|
|
928 |
|
✗ |
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; |
929 |
|
✗ |
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; |
930 |
|
✗ |
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; |
931 |
|
|
|
932 |
|
✗ |
samplerInfo.anisotropyEnable = VK_TRUE; |
933 |
|
✗ |
samplerInfo.maxAnisotropy = 16.0f; |
934 |
|
✗ |
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; |
935 |
|
✗ |
samplerInfo.unnormalizedCoordinates = VK_FALSE; |
936 |
|
✗ |
samplerInfo.compareEnable = VK_FALSE; |
937 |
|
✗ |
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; |
938 |
|
|
|
939 |
|
✗ |
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; |
940 |
|
✗ |
samplerInfo.mipLodBias = 0.0f; |
941 |
|
✗ |
samplerInfo.minLod = 0.0f; |
942 |
|
✗ |
samplerInfo.maxLod = 0.0f; |
943 |
|
|
|
944 |
|
|
VkSampler textureSampler; |
945 |
|
✗ |
if (vkCreateSampler(_device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { |
946 |
|
✗ |
throw std::runtime_error("failed to create texture sampler!"); |
947 |
|
|
} |
948 |
|
✗ |
return textureSampler; |
949 |
|
|
} |
950 |
|
|
|
951 |
|
|
ImTextureID VulkanEngine::load_imtexture(const char* name) { |
952 |
|
✗ |
Texture tex = load_texture(name); |
953 |
|
✗ |
VkDescriptorSet set = ImGui_ImplVulkan_AddTexture( |
954 |
|
✗ |
get_sampler(), tex.imageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
955 |
|
|
|
956 |
|
|
_mainDeletionQueue.push_function([=]() { ImGui_ImplVulkan_RemoveTexture(set); }); |
957 |
|
✗ |
_lookup[set] = tex; |
958 |
|
✗ |
return (ImTextureID)set; |
959 |
|
|
} |
960 |
|
|
|
961 |
|
|
int VulkanEngine::get_height(ImTextureID texture) { |
962 |
|
✗ |
return _lookup[(VkDescriptorSet)(texture)].height; |
963 |
|
|
} |
964 |
|
|
int VulkanEngine::get_width(ImTextureID texture) { |
965 |
|
✗ |
return _lookup[(VkDescriptorSet)(texture)].width; |
966 |
|
|
} |
967 |
|
|
|
968 |
|
|
VkSampler VulkanEngine::get_sampler() { |
969 |
|
✗ |
if (_defaultSampler == nullptr) { |
970 |
|
✗ |
_defaultSampler = new_texture_sampler(); |
971 |
|
✗ |
_mainDeletionQueue.push_function( |
972 |
|
|
[=]() { vkDestroySampler(_device, _defaultSampler, nullptr); }); |
973 |
|
|
} |
974 |
|
✗ |
return _defaultSampler; |
975 |
|
|
} |
976 |
|
|
|
977 |
|
|
Texture VulkanEngine::load_texture(const char* file) { |
978 |
|
✗ |
Texture tex = _loadedTextures[file]; |
979 |
|
✗ |
if (tex.image._image == nullptr) { |
980 |
|
✗ |
vkutil::load_image_from_file(*this, file, tex); |
981 |
|
✗ |
_loadedTextures[file] = tex; |
982 |
|
|
|
983 |
|
✗ |
VkImageViewCreateInfo imageinfo = vkinit::imageview_create_info( |
984 |
|
|
VK_FORMAT_R8G8B8A8_SRGB, tex.image._image, VK_IMAGE_ASPECT_COLOR_BIT); |
985 |
|
✗ |
vkCreateImageView(_device, &imageinfo, nullptr, &tex.imageView); |
986 |
|
|
|
987 |
|
✗ |
_mainDeletionQueue.push_function( |
988 |
|
|
[=]() { vkDestroyImageView(_device, tex.imageView, nullptr); }); |
989 |
|
|
} |
990 |
|
|
|
991 |
|
✗ |
VkDescriptorSet set = ImGui_ImplVulkan_AddTexture( |
992 |
|
|
get_sampler(), tex.imageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
993 |
|
|
|
994 |
|
✗ |
return tex; |
995 |
|
|
} |
996 |
|
|
|
997 |
|
|
void VulkanEngine::load_images() { |
998 |
|
|
Texture lostEmpire; |
999 |
|
|
|
1000 |
|
✗ |
std::string p = "E:/work/lython/build/bin/Debug/assets/"; |
1001 |
|
✗ |
vkutil::load_image_from_file( |
1002 |
|
✗ |
*this, (p + std::string("lost_empire-RGBA.png")).c_str(), lostEmpire); |
1003 |
|
|
|
1004 |
|
✗ |
VkImageViewCreateInfo imageinfo = vkinit::imageview_create_info( |
1005 |
|
|
VK_FORMAT_R8G8B8A8_SRGB, lostEmpire.image._image, VK_IMAGE_ASPECT_COLOR_BIT); |
1006 |
|
✗ |
vkCreateImageView(_device, &imageinfo, nullptr, &lostEmpire.imageView); |
1007 |
|
|
|
1008 |
|
✗ |
_mainDeletionQueue.push_function( |
1009 |
|
|
[=]() { vkDestroyImageView(_device, lostEmpire.imageView, nullptr); }); |
1010 |
|
|
|
1011 |
|
✗ |
_loadedTextures["empire_diffuse"] = lostEmpire; |
1012 |
|
✗ |
} |
1013 |
|
|
|
1014 |
|
|
void VulkanEngine::upload_mesh(Mesh& mesh) { |
1015 |
|
✗ |
const size_t bufferSize = mesh._vertices.size() * sizeof(Vertex); |
1016 |
|
|
// allocate vertex buffer |
1017 |
|
✗ |
VkBufferCreateInfo stagingBufferInfo = {}; |
1018 |
|
✗ |
stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
1019 |
|
✗ |
stagingBufferInfo.pNext = nullptr; |
1020 |
|
|
// this is the total size, in bytes, of the buffer we are allocating |
1021 |
|
✗ |
stagingBufferInfo.size = bufferSize; |
1022 |
|
|
// this buffer is going to be used as a Vertex Buffer |
1023 |
|
✗ |
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; |
1024 |
|
|
|
1025 |
|
|
// let the VMA library know that this data should be writeable by CPU, but also readable by GPU |
1026 |
|
✗ |
VmaAllocationCreateInfo vmaallocInfo = {}; |
1027 |
|
✗ |
vmaallocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; |
1028 |
|
|
|
1029 |
|
|
AllocatedBuffer stagingBuffer; |
1030 |
|
|
|
1031 |
|
|
// allocate the buffer |
1032 |
|
✗ |
VK_CHECK(vmaCreateBuffer(_allocator, |
1033 |
|
|
&stagingBufferInfo, |
1034 |
|
|
&vmaallocInfo, |
1035 |
|
|
&stagingBuffer._buffer, |
1036 |
|
|
&stagingBuffer._allocation, |
1037 |
|
|
nullptr)); |
1038 |
|
|
|
1039 |
|
|
// copy vertex data |
1040 |
|
|
void* data; |
1041 |
|
✗ |
vmaMapMemory(_allocator, stagingBuffer._allocation, &data); |
1042 |
|
|
|
1043 |
|
✗ |
memcpy(data, mesh._vertices.data(), mesh._vertices.size() * sizeof(Vertex)); |
1044 |
|
|
|
1045 |
|
✗ |
vmaUnmapMemory(_allocator, stagingBuffer._allocation); |
1046 |
|
|
|
1047 |
|
|
// allocate vertex buffer |
1048 |
|
✗ |
VkBufferCreateInfo vertexBufferInfo = {}; |
1049 |
|
✗ |
vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
1050 |
|
✗ |
vertexBufferInfo.pNext = nullptr; |
1051 |
|
|
// this is the total size, in bytes, of the buffer we are allocating |
1052 |
|
✗ |
vertexBufferInfo.size = bufferSize; |
1053 |
|
|
// this buffer is going to be used as a Vertex Buffer |
1054 |
|
✗ |
vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
1055 |
|
|
|
1056 |
|
|
// let the VMA library know that this data should be gpu native |
1057 |
|
✗ |
vmaallocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; |
1058 |
|
|
|
1059 |
|
|
// allocate the buffer |
1060 |
|
✗ |
VK_CHECK(vmaCreateBuffer(_allocator, |
1061 |
|
|
&vertexBufferInfo, |
1062 |
|
|
&vmaallocInfo, |
1063 |
|
|
&mesh._vertexBuffer._buffer, |
1064 |
|
|
&mesh._vertexBuffer._allocation, |
1065 |
|
|
nullptr)); |
1066 |
|
|
// add the destruction of triangle mesh buffer to the deletion queue |
1067 |
|
|
_mainDeletionQueue.push_function([=]() { |
1068 |
|
✗ |
vmaDestroyBuffer(_allocator, mesh._vertexBuffer._buffer, mesh._vertexBuffer._allocation); |
1069 |
|
✗ |
}); |
1070 |
|
|
|
1071 |
|
|
immediate_submit([=](VkCommandBuffer cmd) { |
1072 |
|
|
VkBufferCopy copy; |
1073 |
|
✗ |
copy.dstOffset = 0; |
1074 |
|
✗ |
copy.srcOffset = 0; |
1075 |
|
✗ |
copy.size = bufferSize; |
1076 |
|
✗ |
vkCmdCopyBuffer(cmd, stagingBuffer._buffer, mesh._vertexBuffer._buffer, 1, ©); |
1077 |
|
✗ |
}); |
1078 |
|
|
|
1079 |
|
✗ |
vmaDestroyBuffer(_allocator, stagingBuffer._buffer, stagingBuffer._allocation); |
1080 |
|
✗ |
} |
1081 |
|
|
|
1082 |
|
|
Material* VulkanEngine::create_material(VkPipeline pipeline, |
1083 |
|
|
VkPipelineLayout layout, |
1084 |
|
|
const std::string& name) { |
1085 |
|
✗ |
Material mat; |
1086 |
|
✗ |
mat.pipeline = pipeline; |
1087 |
|
✗ |
mat.pipelineLayout = layout; |
1088 |
|
✗ |
_materials[name] = mat; |
1089 |
|
✗ |
return &_materials[name]; |
1090 |
|
|
} |
1091 |
|
|
|
1092 |
|
|
Material* VulkanEngine::get_material(const std::string& name) { |
1093 |
|
|
// search for the object, and return nullpointer if not found |
1094 |
|
✗ |
auto it = _materials.find(name); |
1095 |
|
✗ |
if (it == _materials.end()) { |
1096 |
|
✗ |
return nullptr; |
1097 |
|
|
} else { |
1098 |
|
✗ |
return &(*it).second; |
1099 |
|
|
} |
1100 |
|
|
} |
1101 |
|
|
|
1102 |
|
|
Mesh* VulkanEngine::get_mesh(const std::string& name) { |
1103 |
|
✗ |
auto it = _meshes.find(name); |
1104 |
|
✗ |
if (it == _meshes.end()) { |
1105 |
|
✗ |
return nullptr; |
1106 |
|
|
} else { |
1107 |
|
✗ |
return &(*it).second; |
1108 |
|
|
} |
1109 |
|
|
} |
1110 |
|
|
|
1111 |
|
|
void VulkanEngine::draw_objects(VkCommandBuffer cmd, RenderObject* first, int count) { |
1112 |
|
|
// make a model view matrix for rendering the object |
1113 |
|
|
// camera view |
1114 |
|
✗ |
glm::vec3 camPos = {0.f, -6.f, -10.f}; |
1115 |
|
|
|
1116 |
|
✗ |
glm::mat4 view = glm::translate(glm::mat4(1.f), camPos); |
1117 |
|
|
// camera projection |
1118 |
|
✗ |
glm::mat4 projection = glm::perspective(glm::radians(70.f), 1700.f / 900.f, 0.1f, 200.0f); |
1119 |
|
✗ |
projection[1][1] *= -1; |
1120 |
|
|
|
1121 |
|
|
GPUCameraData camData; |
1122 |
|
✗ |
camData.proj = projection; |
1123 |
|
✗ |
camData.view = view; |
1124 |
|
✗ |
camData.viewproj = projection * view; |
1125 |
|
|
|
1126 |
|
|
void* data; |
1127 |
|
✗ |
vmaMapMemory(_allocator, get_current_frame().cameraBuffer._allocation, &data); |
1128 |
|
|
|
1129 |
|
✗ |
memcpy(data, &camData, sizeof(GPUCameraData)); |
1130 |
|
|
|
1131 |
|
✗ |
vmaUnmapMemory(_allocator, get_current_frame().cameraBuffer._allocation); |
1132 |
|
|
|
1133 |
|
✗ |
float framed = (_frameNumber / 120.f); |
1134 |
|
|
|
1135 |
|
✗ |
_sceneParameters.ambientColor = {sin(framed), 0, cos(framed), 1}; |
1136 |
|
|
|
1137 |
|
|
char* sceneData; |
1138 |
|
✗ |
vmaMapMemory(_allocator, _sceneParameterBuffer._allocation, (void**)&sceneData); |
1139 |
|
|
|
1140 |
|
✗ |
std::size_t frameIndex = _frameNumber % FRAME_OVERLAP; |
1141 |
|
|
|
1142 |
|
✗ |
sceneData += pad_uniform_buffer_size(sizeof(GPUSceneData)) * frameIndex; |
1143 |
|
|
|
1144 |
|
✗ |
memcpy(sceneData, &_sceneParameters, sizeof(GPUSceneData)); |
1145 |
|
|
|
1146 |
|
✗ |
vmaUnmapMemory(_allocator, _sceneParameterBuffer._allocation); |
1147 |
|
|
|
1148 |
|
|
void* objectData; |
1149 |
|
✗ |
vmaMapMemory(_allocator, get_current_frame().objectBuffer._allocation, &objectData); |
1150 |
|
|
|
1151 |
|
✗ |
GPUObjectData* objectSSBO = (GPUObjectData*)objectData; |
1152 |
|
|
|
1153 |
|
✗ |
for (int i = 0; i < count; i++) { |
1154 |
|
✗ |
RenderObject& object = first[i]; |
1155 |
|
✗ |
objectSSBO[i].modelMatrix = object.transformMatrix; |
1156 |
|
|
} |
1157 |
|
|
|
1158 |
|
✗ |
vmaUnmapMemory(_allocator, get_current_frame().objectBuffer._allocation); |
1159 |
|
|
|
1160 |
|
✗ |
Mesh* lastMesh = nullptr; |
1161 |
|
✗ |
Material* lastMaterial = nullptr; |
1162 |
|
|
|
1163 |
|
✗ |
for (int i = 0; i < count; i++) { |
1164 |
|
✗ |
RenderObject& object = first[i]; |
1165 |
|
|
|
1166 |
|
|
// only bind the pipeline if it doesnt match with the already bound one |
1167 |
|
✗ |
if (object.material != lastMaterial) { |
1168 |
|
|
|
1169 |
|
✗ |
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, object.material->pipeline); |
1170 |
|
✗ |
lastMaterial = object.material; |
1171 |
|
|
|
1172 |
|
|
uint32_t uniform_offset = |
1173 |
|
✗ |
uint32_t(pad_uniform_buffer_size(sizeof(GPUSceneData)) * frameIndex); |
1174 |
|
✗ |
vkCmdBindDescriptorSets(cmd, |
1175 |
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS, |
1176 |
|
✗ |
object.material->pipelineLayout, |
1177 |
|
|
0, |
1178 |
|
|
1, |
1179 |
|
✗ |
&get_current_frame().globalDescriptor, |
1180 |
|
|
1, |
1181 |
|
|
&uniform_offset); |
1182 |
|
|
|
1183 |
|
|
// object data descriptor |
1184 |
|
✗ |
vkCmdBindDescriptorSets(cmd, |
1185 |
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS, |
1186 |
|
✗ |
object.material->pipelineLayout, |
1187 |
|
|
1, |
1188 |
|
|
1, |
1189 |
|
✗ |
&get_current_frame().objectDescriptor, |
1190 |
|
|
0, |
1191 |
|
|
nullptr); |
1192 |
|
|
|
1193 |
|
✗ |
if (object.material->textureSet != VK_NULL_HANDLE) { |
1194 |
|
|
// texture descriptor |
1195 |
|
✗ |
vkCmdBindDescriptorSets(cmd, |
1196 |
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS, |
1197 |
|
✗ |
object.material->pipelineLayout, |
1198 |
|
|
2, |
1199 |
|
|
1, |
1200 |
|
✗ |
&object.material->textureSet, |
1201 |
|
|
0, |
1202 |
|
|
nullptr); |
1203 |
|
|
} |
1204 |
|
|
} |
1205 |
|
|
|
1206 |
|
✗ |
glm::mat4 model = object.transformMatrix; |
1207 |
|
|
// final render matrix, that we are calculating on the cpu |
1208 |
|
✗ |
glm::mat4 mesh_matrix = model; |
1209 |
|
|
|
1210 |
|
|
MeshPushConstants constants; |
1211 |
|
✗ |
constants.render_matrix = mesh_matrix; |
1212 |
|
|
|
1213 |
|
|
// upload the mesh to the gpu via pushconstants |
1214 |
|
✗ |
vkCmdPushConstants(cmd, |
1215 |
|
✗ |
object.material->pipelineLayout, |
1216 |
|
|
VK_SHADER_STAGE_VERTEX_BIT, |
1217 |
|
|
0, |
1218 |
|
|
sizeof(MeshPushConstants), |
1219 |
|
|
&constants); |
1220 |
|
|
|
1221 |
|
|
// only bind the mesh if its a different one from last bind |
1222 |
|
✗ |
if (object.mesh != lastMesh) { |
1223 |
|
|
// bind the mesh vertex buffer with offset 0 |
1224 |
|
✗ |
VkDeviceSize offset = 0; |
1225 |
|
✗ |
vkCmdBindVertexBuffers(cmd, 0, 1, &object.mesh->_vertexBuffer._buffer, &offset); |
1226 |
|
✗ |
lastMesh = object.mesh; |
1227 |
|
|
} |
1228 |
|
|
// we can now draw |
1229 |
|
✗ |
vkCmdDraw(cmd, uint32_t(object.mesh->_vertices.size()), 1, 0, uint32_t(i)); |
1230 |
|
|
} |
1231 |
|
✗ |
} |
1232 |
|
|
|
1233 |
|
|
void VulkanEngine::init_scene() { |
1234 |
|
|
RenderObject monkey; |
1235 |
|
✗ |
monkey.mesh = get_mesh("monkey"); |
1236 |
|
✗ |
monkey.material = get_material("defaultmesh"); |
1237 |
|
✗ |
monkey.transformMatrix = glm::mat4{1.0f}; |
1238 |
|
|
|
1239 |
|
✗ |
_renderables.push_back(monkey); |
1240 |
|
|
|
1241 |
|
|
RenderObject map; |
1242 |
|
✗ |
map.mesh = get_mesh("empire"); |
1243 |
|
✗ |
map.material = get_material("texturedmesh"); |
1244 |
|
✗ |
map.transformMatrix = glm::translate(glm::vec3{5, -10, 0}); // glm::mat4{ 1.0f }; |
1245 |
|
|
|
1246 |
|
✗ |
_renderables.push_back(map); |
1247 |
|
|
|
1248 |
|
✗ |
for (int x = -20; x <= 20; x++) { |
1249 |
|
✗ |
for (int y = -20; y <= 20; y++) { |
1250 |
|
|
|
1251 |
|
|
RenderObject tri; |
1252 |
|
✗ |
tri.mesh = get_mesh("triangle"); |
1253 |
|
✗ |
tri.material = get_material("defaultmesh"); |
1254 |
|
✗ |
glm::mat4 translation = glm::translate(glm::mat4{1.0}, glm::vec3(x, 0, y)); |
1255 |
|
✗ |
glm::mat4 scale = glm::scale(glm::mat4{1.0}, glm::vec3(0.2, 0.2, 0.2)); |
1256 |
|
✗ |
tri.transformMatrix = translation * scale; |
1257 |
|
|
|
1258 |
|
✗ |
_renderables.push_back(tri); |
1259 |
|
|
} |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
✗ |
Material* texturedMat = get_material("texturedmesh"); |
1263 |
|
|
|
1264 |
|
✗ |
VkDescriptorSetAllocateInfo allocInfo = {}; |
1265 |
|
✗ |
allocInfo.pNext = nullptr; |
1266 |
|
✗ |
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; |
1267 |
|
✗ |
allocInfo.descriptorPool = _descriptorPool; |
1268 |
|
✗ |
allocInfo.descriptorSetCount = 1; |
1269 |
|
✗ |
allocInfo.pSetLayouts = &_singleTextureSetLayout; |
1270 |
|
|
|
1271 |
|
✗ |
vkAllocateDescriptorSets(_device, &allocInfo, &texturedMat->textureSet); |
1272 |
|
|
|
1273 |
|
✗ |
VkSamplerCreateInfo samplerInfo = vkinit::sampler_create_info(VK_FILTER_NEAREST); |
1274 |
|
|
|
1275 |
|
|
VkSampler blockySampler; |
1276 |
|
✗ |
vkCreateSampler(_device, &samplerInfo, nullptr, &blockySampler); |
1277 |
|
|
|
1278 |
|
|
_mainDeletionQueue.push_function([=]() { vkDestroySampler(_device, blockySampler, nullptr); }); |
1279 |
|
|
|
1280 |
|
|
VkDescriptorImageInfo imageBufferInfo; |
1281 |
|
✗ |
imageBufferInfo.sampler = blockySampler; |
1282 |
|
✗ |
imageBufferInfo.imageView = _loadedTextures["empire_diffuse"].imageView; |
1283 |
|
✗ |
imageBufferInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
1284 |
|
|
|
1285 |
|
✗ |
VkWriteDescriptorSet texture1 = vkinit::write_descriptor_image( |
1286 |
|
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, texturedMat->textureSet, &imageBufferInfo, 0); |
1287 |
|
|
|
1288 |
|
✗ |
vkUpdateDescriptorSets(_device, 1, &texture1, 0, nullptr); |
1289 |
|
✗ |
} |
1290 |
|
|
|
1291 |
|
|
AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, |
1292 |
|
|
VkBufferUsageFlags usage, |
1293 |
|
|
VmaMemoryUsage memoryUsage) { |
1294 |
|
|
// allocate vertex buffer |
1295 |
|
✗ |
VkBufferCreateInfo bufferInfo = {}; |
1296 |
|
✗ |
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
1297 |
|
✗ |
bufferInfo.pNext = nullptr; |
1298 |
|
✗ |
bufferInfo.size = allocSize; |
1299 |
|
|
|
1300 |
|
✗ |
bufferInfo.usage = usage; |
1301 |
|
|
|
1302 |
|
|
// let the VMA library know that this data should be writeable by CPU, but also readable by GPU |
1303 |
|
✗ |
VmaAllocationCreateInfo vmaallocInfo = {}; |
1304 |
|
✗ |
vmaallocInfo.usage = memoryUsage; |
1305 |
|
|
|
1306 |
|
|
AllocatedBuffer newBuffer; |
1307 |
|
|
|
1308 |
|
|
// allocate the buffer |
1309 |
|
✗ |
VK_CHECK(vmaCreateBuffer(_allocator, |
1310 |
|
|
&bufferInfo, |
1311 |
|
|
&vmaallocInfo, |
1312 |
|
|
&newBuffer._buffer, |
1313 |
|
|
&newBuffer._allocation, |
1314 |
|
|
nullptr)); |
1315 |
|
|
|
1316 |
|
✗ |
return newBuffer; |
1317 |
|
|
} |
1318 |
|
|
|
1319 |
|
|
size_t VulkanEngine::pad_uniform_buffer_size(size_t originalSize) { |
1320 |
|
|
// Calculate required alignment based on minimum device offset alignment |
1321 |
|
✗ |
size_t minUboAlignment = _gpuProperties.limits.minUniformBufferOffsetAlignment; |
1322 |
|
✗ |
size_t alignedSize = originalSize; |
1323 |
|
✗ |
if (minUboAlignment > 0) { |
1324 |
|
✗ |
alignedSize = (alignedSize + minUboAlignment - 1) & ~(minUboAlignment - 1); |
1325 |
|
|
} |
1326 |
|
✗ |
return alignedSize; |
1327 |
|
|
} |
1328 |
|
|
|
1329 |
|
|
void VulkanEngine::immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function) { |
1330 |
|
✗ |
VkCommandBuffer cmd = _uploadContext._commandBuffer; |
1331 |
|
|
// begin the command buffer recording. We will use this command buffer exactly once, so we want |
1332 |
|
|
// to let vulkan know that |
1333 |
|
|
VkCommandBufferBeginInfo cmdBeginInfo = |
1334 |
|
✗ |
vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); |
1335 |
|
|
|
1336 |
|
✗ |
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); |
1337 |
|
|
|
1338 |
|
✗ |
function(cmd); |
1339 |
|
|
|
1340 |
|
✗ |
VK_CHECK(vkEndCommandBuffer(cmd)); |
1341 |
|
|
|
1342 |
|
✗ |
VkSubmitInfo submit = vkinit::submit_info(&cmd); |
1343 |
|
|
|
1344 |
|
|
// submit command buffer to the queue and execute it. |
1345 |
|
|
// _renderFence will now block until the graphic commands finish execution |
1346 |
|
✗ |
VK_CHECK(vkQueueSubmit(_graphicsQueue, 1, &submit, _uploadContext._uploadFence)); |
1347 |
|
|
|
1348 |
|
✗ |
vkWaitForFences(_device, 1, &_uploadContext._uploadFence, true, 9999999999); |
1349 |
|
✗ |
vkResetFences(_device, 1, &_uploadContext._uploadFence); |
1350 |
|
|
|
1351 |
|
✗ |
vkResetCommandPool(_device, _uploadContext._commandPool, 0); |
1352 |
|
✗ |
} |
1353 |
|
|
|
1354 |
|
|
void VulkanEngine::init_descriptors() { |
1355 |
|
|
|
1356 |
|
|
// create a descriptor pool that will hold 10 uniform buffers |
1357 |
|
|
std::vector<VkDescriptorPoolSize> sizes = {{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 10}, |
1358 |
|
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 10}, |
1359 |
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 10}, |
1360 |
|
✗ |
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 10}}; |
1361 |
|
|
|
1362 |
|
✗ |
VkDescriptorPoolCreateInfo pool_info = {}; |
1363 |
|
✗ |
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; |
1364 |
|
✗ |
pool_info.flags = 0; |
1365 |
|
✗ |
pool_info.maxSets = 10; |
1366 |
|
✗ |
pool_info.poolSizeCount = (uint32_t)sizes.size(); |
1367 |
|
✗ |
pool_info.pPoolSizes = sizes.data(); |
1368 |
|
|
|
1369 |
|
✗ |
vkCreateDescriptorPool(_device, &pool_info, nullptr, &_descriptorPool); |
1370 |
|
|
|
1371 |
|
✗ |
VkDescriptorSetLayoutBinding cameraBind = vkinit::descriptorset_layout_binding( |
1372 |
|
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0); |
1373 |
|
✗ |
VkDescriptorSetLayoutBinding sceneBind = vkinit::descriptorset_layout_binding( |
1374 |
|
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, |
1375 |
|
|
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, |
1376 |
|
|
1); |
1377 |
|
|
|
1378 |
|
✗ |
VkDescriptorSetLayoutBinding bindings[] = {cameraBind, sceneBind}; |
1379 |
|
|
|
1380 |
|
✗ |
VkDescriptorSetLayoutCreateInfo setinfo = {}; |
1381 |
|
✗ |
setinfo.bindingCount = 2; |
1382 |
|
✗ |
setinfo.flags = 0; |
1383 |
|
✗ |
setinfo.pNext = nullptr; |
1384 |
|
✗ |
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; |
1385 |
|
✗ |
setinfo.pBindings = bindings; |
1386 |
|
|
|
1387 |
|
✗ |
vkCreateDescriptorSetLayout(_device, &setinfo, nullptr, &_globalSetLayout); |
1388 |
|
|
|
1389 |
|
✗ |
VkDescriptorSetLayoutBinding objectBind = vkinit::descriptorset_layout_binding( |
1390 |
|
|
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0); |
1391 |
|
|
|
1392 |
|
✗ |
VkDescriptorSetLayoutCreateInfo set2info = {}; |
1393 |
|
✗ |
set2info.bindingCount = 1; |
1394 |
|
✗ |
set2info.flags = 0; |
1395 |
|
✗ |
set2info.pNext = nullptr; |
1396 |
|
✗ |
set2info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; |
1397 |
|
✗ |
set2info.pBindings = &objectBind; |
1398 |
|
|
|
1399 |
|
✗ |
vkCreateDescriptorSetLayout(_device, &set2info, nullptr, &_objectSetLayout); |
1400 |
|
|
|
1401 |
|
✗ |
VkDescriptorSetLayoutBinding textureBind = vkinit::descriptorset_layout_binding( |
1402 |
|
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0); |
1403 |
|
|
|
1404 |
|
✗ |
VkDescriptorSetLayoutCreateInfo set3info = {}; |
1405 |
|
✗ |
set3info.bindingCount = 1; |
1406 |
|
✗ |
set3info.flags = 0; |
1407 |
|
✗ |
set3info.pNext = nullptr; |
1408 |
|
✗ |
set3info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; |
1409 |
|
✗ |
set3info.pBindings = &textureBind; |
1410 |
|
|
|
1411 |
|
✗ |
vkCreateDescriptorSetLayout(_device, &set3info, nullptr, &_singleTextureSetLayout); |
1412 |
|
|
|
1413 |
|
|
const size_t sceneParamBufferSize = |
1414 |
|
✗ |
FRAME_OVERLAP * pad_uniform_buffer_size(sizeof(GPUSceneData)); |
1415 |
|
|
|
1416 |
|
✗ |
_sceneParameterBuffer = create_buffer( |
1417 |
|
|
sceneParamBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); |
1418 |
|
|
|
1419 |
|
✗ |
for (int i = 0; i < FRAME_OVERLAP; i++) { |
1420 |
|
✗ |
_frames[i].cameraBuffer = create_buffer( |
1421 |
|
|
sizeof(GPUCameraData), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); |
1422 |
|
|
|
1423 |
|
✗ |
const int MAX_OBJECTS = 10000; |
1424 |
|
✗ |
_frames[i].objectBuffer = create_buffer(sizeof(GPUObjectData) * MAX_OBJECTS, |
1425 |
|
|
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, |
1426 |
|
|
VMA_MEMORY_USAGE_CPU_TO_GPU); |
1427 |
|
|
|
1428 |
|
✗ |
VkDescriptorSetAllocateInfo allocInfo = {}; |
1429 |
|
✗ |
allocInfo.pNext = nullptr; |
1430 |
|
✗ |
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; |
1431 |
|
✗ |
allocInfo.descriptorPool = _descriptorPool; |
1432 |
|
✗ |
allocInfo.descriptorSetCount = 1; |
1433 |
|
✗ |
allocInfo.pSetLayouts = &_globalSetLayout; |
1434 |
|
|
|
1435 |
|
✗ |
vkAllocateDescriptorSets(_device, &allocInfo, &_frames[i].globalDescriptor); |
1436 |
|
|
|
1437 |
|
✗ |
VkDescriptorSetAllocateInfo objectSetAlloc = {}; |
1438 |
|
✗ |
objectSetAlloc.pNext = nullptr; |
1439 |
|
✗ |
objectSetAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; |
1440 |
|
✗ |
objectSetAlloc.descriptorPool = _descriptorPool; |
1441 |
|
✗ |
objectSetAlloc.descriptorSetCount = 1; |
1442 |
|
✗ |
objectSetAlloc.pSetLayouts = &_objectSetLayout; |
1443 |
|
|
|
1444 |
|
✗ |
vkAllocateDescriptorSets(_device, &objectSetAlloc, &_frames[i].objectDescriptor); |
1445 |
|
|
|
1446 |
|
|
VkDescriptorBufferInfo cameraInfo; |
1447 |
|
✗ |
cameraInfo.buffer = _frames[i].cameraBuffer._buffer; |
1448 |
|
✗ |
cameraInfo.offset = 0; |
1449 |
|
✗ |
cameraInfo.range = sizeof(GPUCameraData); |
1450 |
|
|
|
1451 |
|
|
VkDescriptorBufferInfo sceneInfo; |
1452 |
|
✗ |
sceneInfo.buffer = _sceneParameterBuffer._buffer; |
1453 |
|
✗ |
sceneInfo.offset = 0; |
1454 |
|
✗ |
sceneInfo.range = sizeof(GPUSceneData); |
1455 |
|
|
|
1456 |
|
|
VkDescriptorBufferInfo objectBufferInfo; |
1457 |
|
✗ |
objectBufferInfo.buffer = _frames[i].objectBuffer._buffer; |
1458 |
|
✗ |
objectBufferInfo.offset = 0; |
1459 |
|
✗ |
objectBufferInfo.range = sizeof(GPUObjectData) * MAX_OBJECTS; |
1460 |
|
|
|
1461 |
|
✗ |
VkWriteDescriptorSet cameraWrite = vkinit::write_descriptor_buffer( |
1462 |
|
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, _frames[i].globalDescriptor, &cameraInfo, 0); |
1463 |
|
|
|
1464 |
|
✗ |
VkWriteDescriptorSet sceneWrite = vkinit::write_descriptor_buffer( |
1465 |
|
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, _frames[i].globalDescriptor, &sceneInfo, 1); |
1466 |
|
|
|
1467 |
|
✗ |
VkWriteDescriptorSet objectWrite = vkinit::write_descriptor_buffer( |
1468 |
|
|
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, _frames[i].objectDescriptor, &objectBufferInfo, 0); |
1469 |
|
|
|
1470 |
|
✗ |
VkWriteDescriptorSet setWrites[] = {cameraWrite, sceneWrite, objectWrite}; |
1471 |
|
|
|
1472 |
|
✗ |
vkUpdateDescriptorSets(_device, 3, setWrites, 0, nullptr); |
1473 |
|
|
} |
1474 |
|
|
|
1475 |
|
|
_mainDeletionQueue.push_function([&]() { |
1476 |
|
✗ |
vmaDestroyBuffer( |
1477 |
|
✗ |
_allocator, _sceneParameterBuffer._buffer, _sceneParameterBuffer._allocation); |
1478 |
|
|
|
1479 |
|
✗ |
vkDestroyDescriptorSetLayout(_device, _objectSetLayout, nullptr); |
1480 |
|
✗ |
vkDestroyDescriptorSetLayout(_device, _globalSetLayout, nullptr); |
1481 |
|
✗ |
vkDestroyDescriptorSetLayout(_device, _singleTextureSetLayout, nullptr); |
1482 |
|
|
|
1483 |
|
✗ |
vkDestroyDescriptorPool(_device, _descriptorPool, nullptr); |
1484 |
|
|
|
1485 |
|
✗ |
for (int i = 0; i < FRAME_OVERLAP; i++) { |
1486 |
|
✗ |
vmaDestroyBuffer( |
1487 |
|
|
_allocator, _frames[i].cameraBuffer._buffer, _frames[i].cameraBuffer._allocation); |
1488 |
|
|
|
1489 |
|
✗ |
vmaDestroyBuffer( |
1490 |
|
|
_allocator, _frames[i].objectBuffer._buffer, _frames[i].objectBuffer._allocation); |
1491 |
|
|
} |
1492 |
|
✗ |
}); |
1493 |
|
✗ |
} |
1494 |
|
|
|