GCC Code Coverage Report


Directory: ./
File: src/tide/engine/vk_engine.cpp
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 0 696 0.0%
Functions: 0 61 0.0%
Branches: 0 890 0.0%

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, &copy);
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