source: opengl-game/Vulkan.cpp@ c205c3a

feature/imgui-sdl
Last change on this file since c205c3a was c205c3a, checked in by Dmitry Portnoy <dportnoy@…>, 4 years ago

In VulkanSFMLReference, use the Vulkan SDK version of vulkan.h instead of the one from the SFML repo, switch to the newer debugUtilsMessengerEXT for debugging, and add resources the example code needs for rendering

  • Property mode set to 100644
File size: 91.8 KB
Line 
1#include <vulkan/vulkan.h>
2
3#include <iostream>
4#include <vector>
5#include <limits>
6#include <cstring>
7#include <cmath>
8
9#include <SFML/Window.hpp>
10#include <SFML/Graphics.hpp> // Include graphics because we use sf::Image for loading images
11
12#include "vulkan-utils-new.hpp"
13
14using namespace std;
15
16namespace {
17
18 #ifdef NDEBUG
19 const bool ENABLE_VALIDATION_LAYERS = false;
20 #else
21 const bool ENABLE_VALIDATION_LAYERS = true;
22 #endif
23
24 typedef float Vec3[3];
25 typedef float Matrix[4][4];
26
27 // Multiply 2 matrices
28 void matrixMultiply(Matrix& result, const Matrix& left, const Matrix& right) {
29 Matrix temp;
30
31 for (int i = 0; i < 4; i++)
32 {
33 for (int j = 0; j < 4; j++)
34 temp[i][j] = left[0][j] * right[i][0] + left[1][j] * right[i][1] + left[2][j] * right[i][2] + left[3][j] * right[i][3];
35 }
36
37 std::memcpy(result, temp, sizeof(Matrix));
38 }
39
40 // Rotate a matrix around the x-axis
41 void matrixRotateX(Matrix& result, float angle) {
42 Matrix matrix = {
43 {1.f, 0.f, 0.f, 0.f},
44 {0.f, std::cos(angle), std::sin(angle), 0.f},
45 {0.f, -std::sin(angle), std::cos(angle), 0.f},
46 {0.f, 0.f, 0.f, 1.f}
47 };
48
49 matrixMultiply(result, result, matrix);
50 }
51
52 // Rotate a matrix around the y-axis
53 void matrixRotateY(Matrix& result, float angle) {
54 Matrix matrix = {
55 { std::cos(angle), 0.f, std::sin(angle), 0.f},
56 { 0.f, 1.f, 0.f, 0.f},
57 {-std::sin(angle), 0.f, std::cos(angle), 0.f},
58 { 0.f, 0.f, 0.f, 1.f}
59 };
60
61 matrixMultiply(result, result, matrix);
62 }
63
64 // Rotate a matrix around the z-axis
65 void matrixRotateZ(Matrix& result, float angle) {
66 Matrix matrix = {
67 { std::cos(angle), std::sin(angle), 0.f, 0.f},
68 {-std::sin(angle), std::cos(angle), 0.f, 0.f},
69 { 0.f, 0.f, 1.f, 0.f},
70 { 0.f, 0.f, 0.f, 1.f}
71 };
72
73 matrixMultiply(result, result, matrix);
74 }
75
76 // Construct a lookat view matrix
77 void matrixLookAt(Matrix& result, const Vec3& eye, const Vec3& center, const Vec3& up) {
78 // Forward-looking vector
79 Vec3 forward = {
80 center[0] - eye[0],
81 center[1] - eye[1],
82 center[2] - eye[2]
83 };
84
85 // Normalize
86 float factor = 1.0f / std::sqrt(forward[0] * forward[0] + forward[1] * forward[1] + forward[2] * forward[2]);
87
88 for (int i = 0; i < 3; i++) {
89 forward[i] = forward[i] * factor;
90 }
91
92 // Side vector (Forward cross product Up)
93 Vec3 side = {
94 forward[1] * up[2] - forward[2] * up[1],
95 forward[2] * up[0] - forward[0] * up[2],
96 forward[0] * up[1] - forward[1] * up[0]
97 };
98
99 // Normalize
100 factor = 1.0f / std::sqrt(side[0] * side[0] + side[1] * side[1] + side[2] * side[2]);
101
102 for (int i = 0; i < 3; i++)
103 side[i] = side[i] * factor;
104
105 result[0][0] = side[0];
106 result[0][1] = side[1] * forward[2] - side[2] * forward[1];
107 result[0][2] = -forward[0];
108 result[0][3] = 0.f;
109
110 result[1][0] = side[1];
111 result[1][1] = side[2] * forward[0] - side[0] * forward[2];
112 result[1][2] = -forward[1];
113 result[1][3] = 0.f;
114
115 result[2][0] = side[2];
116 result[2][1] = side[0] * forward[1] - side[1] * forward[0];
117 result[2][2] = -forward[2];
118 result[2][3] = 0.f;
119
120 result[3][0] = (-eye[0]) * result[0][0] + (-eye[1]) * result[1][0] + (-eye[2]) * result[2][0];
121 result[3][1] = (-eye[0]) * result[0][1] + (-eye[1]) * result[1][1] + (-eye[2]) * result[2][1];
122 result[3][2] = (-eye[0]) * result[0][2] + (-eye[1]) * result[1][2] + (-eye[2]) * result[2][2];
123 result[3][3] = (-eye[0]) * result[0][3] + (-eye[1]) * result[1][3] + (-eye[2]) * result[2][3] + 1.0f;
124 }
125
126 // Construct a perspective projection matrix
127 void matrixPerspective(Matrix& result, float fov, float aspect, float nearPlane, float farPlane) {
128 const float a = 1.f / std::tan(fov / 2.f);
129
130 result[0][0] = a / aspect;
131 result[0][1] = 0.f;
132 result[0][2] = 0.f;
133 result[0][3] = 0.f;
134
135 result[1][0] = 0.f;
136 result[1][1] = -a;
137 result[1][2] = 0.f;
138 result[1][3] = 0.f;
139
140 result[2][0] = 0.f;
141 result[2][1] = 0.f;
142 result[2][2] = -((farPlane + nearPlane) / (farPlane - nearPlane));
143 result[2][3] = -1.f;
144
145 result[3][0] = 0.f;
146 result[3][1] = 0.f;
147 result[3][2] = -((2.f * farPlane * nearPlane) / (farPlane - nearPlane));
148 result[3][3] = 0.f;
149 }
150
151 // Clamp a value between low and high values
152 template<typename T>
153 T clamp(T value, T low, T high) {
154 return (value <= low) ? low : ((value >= high) ? high : value);
155 }
156
157 VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
158 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
159 VkDebugUtilsMessageTypeFlagsEXT messageType,
160 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
161 void* pUserData) {
162 cerr << "validation layer: " << pCallbackData->pMessage << endl;
163
164 return VK_FALSE;
165 }
166}
167
168
169class VulkanExample {
170public:
171 // Constructor
172 VulkanExample() :
173 window(sf::VideoMode(800, 600), "SFML window with Vulkan", sf::Style::Default),
174 vulkanAvailable(sf::Vulkan::isAvailable()),
175 maxFramesInFlight(2),
176 currentFrame(0),
177 swapchainOutOfDate(false),
178 instance(0),
179 surface(0),
180 gpu(0),
181 queueFamilyIndex(-1),
182 device(0),
183 queue(0),
184 swapchainFormat(),
185 swapchainExtent(),
186 swapchain(0),
187 depthFormat(VK_FORMAT_UNDEFINED),
188 depthImage(0),
189 depthImageMemory(0),
190 depthImageView(0),
191 vertexShaderModule(0),
192 fragmentShaderModule(0),
193 descriptorSetLayout(0),
194 pipelineLayout(0),
195 renderPass(0),
196 graphicsPipeline(0),
197 commandPool(0),
198 vertexBuffer(0),
199 vertexBufferMemory(0),
200 indexBuffer(0),
201 indexBufferMemory(0),
202 textureImage(0),
203 textureImageMemory(0),
204 textureImageView(0),
205 textureSampler(0),
206 descriptorPool(0)
207 {
208 const vector<const char*> validationLayers = {
209 "VK_LAYER_KHRONOS_validation",
210 "VK_LAYER_LUNARG_monitor" // This should show the FPS in the title bar
211 };
212
213 // Vulkan setup procedure
214 if (vulkanAvailable) setupInstance(validationLayers);
215 if (vulkanAvailable) setupDebugMessenger();
216 if (vulkanAvailable) setupSurface();
217 if (vulkanAvailable) setupPhysicalDevice();
218 if (vulkanAvailable) setupLogicalDevice();
219 if (vulkanAvailable) setupSwapchain();
220 if (vulkanAvailable) setupSwapchainImages();
221 if (vulkanAvailable) setupShaders();
222 if (vulkanAvailable) setupRenderpass();
223 if (vulkanAvailable) setupDescriptorSetLayout();
224 if (vulkanAvailable) setupPipelineLayout();
225 if (vulkanAvailable) setupPipeline();
226 if (vulkanAvailable) setupCommandPool();
227 if (vulkanAvailable) setupVertexBuffer();
228 if (vulkanAvailable) setupIndexBuffer();
229 if (vulkanAvailable) setupUniformBuffers();
230 if (vulkanAvailable) setupDepthImage();
231 if (vulkanAvailable) setupDepthImageView();
232 if (vulkanAvailable) setupTextureImage();
233 if (vulkanAvailable) setupTextureImageView();
234 if (vulkanAvailable) setupTextureSampler();
235 if (vulkanAvailable) setupFramebuffers();
236 if (vulkanAvailable) setupDescriptorPool();
237 if (vulkanAvailable) setupDescriptorSets();
238 if (vulkanAvailable) setupCommandBuffers();
239 if (vulkanAvailable) setupDraw();
240 if (vulkanAvailable) setupSemaphores();
241 if (vulkanAvailable) setupFences();
242
243 // If something went wrong, notify the user by setting the window title
244 if (!vulkanAvailable)
245 window.setTitle("SFML window with Vulkan (Vulkan not available)");
246 }
247
248
249 // Destructor
250 ~VulkanExample() {
251 // Wait until there are no pending frames
252 if (device) {
253 vkDeviceWaitIdle(device);
254 }
255
256 // Teardown swapchain
257 cleanupSwapchain();
258
259 // Vulkan teardown procedure
260 for (std::size_t i = 0; i < fences.size(); i++)
261 vkDestroyFence(device, fences[i], 0);
262
263 for (std::size_t i = 0; i < renderFinishedSemaphores.size(); i++)
264 vkDestroySemaphore(device, renderFinishedSemaphores[i], 0);
265
266 for (std::size_t i = 0; i < imageAvailableSemaphores.size(); i++)
267 vkDestroySemaphore(device, imageAvailableSemaphores[i], 0);
268
269 if (descriptorPool)
270 vkDestroyDescriptorPool(device, descriptorPool, 0);
271
272 for (std::size_t i = 0; i < uniformBuffersMemory.size(); i++)
273 vkFreeMemory(device, uniformBuffersMemory[i], 0);
274
275 for (std::size_t i = 0; i < uniformBuffers.size(); i++)
276 vkDestroyBuffer(device, uniformBuffers[i], 0);
277
278 if (textureSampler)
279 vkDestroySampler(device, textureSampler, 0);
280
281 if (textureImageView)
282 vkDestroyImageView(device, textureImageView, 0);
283
284 if (textureImageMemory)
285 vkFreeMemory(device, textureImageMemory, 0);
286
287 if (textureImage)
288 vkDestroyImage(device, textureImage, 0);
289
290 if (indexBufferMemory)
291 vkFreeMemory(device, indexBufferMemory, 0);
292
293 if (indexBuffer)
294 vkDestroyBuffer(device, indexBuffer, 0);
295
296 if (vertexBufferMemory)
297 vkFreeMemory(device, vertexBufferMemory, 0);
298
299 if (vertexBuffer)
300 vkDestroyBuffer(device, vertexBuffer, 0);
301
302 if (commandPool)
303 vkDestroyCommandPool(device, commandPool, 0);
304
305 if (descriptorSetLayout)
306 vkDestroyDescriptorSetLayout(device, descriptorSetLayout, 0);
307
308 if (fragmentShaderModule)
309 vkDestroyShaderModule(device, fragmentShaderModule, 0);
310
311 if (vertexShaderModule)
312 vkDestroyShaderModule(device, vertexShaderModule, 0);
313
314 if (device)
315 vkDestroyDevice(device, 0);
316
317 if (surface)
318 vkDestroySurfaceKHR(instance, surface, 0);
319
320 if (ENABLE_VALIDATION_LAYERS) {
321 VulkanUtilsNew::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
322 }
323
324 if (instance)
325 vkDestroyInstance(instance, 0);
326 }
327
328 // Cleanup swapchain
329 void cleanupSwapchain() {
330 // Swapchain teardown procedure
331 for (std::size_t i = 0; i < fences.size(); i++)
332 vkWaitForFences(device, 1, &fences[i], VK_TRUE, std::numeric_limits<uint64_t>::max());
333
334 if (commandBuffers.size())
335 vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), &commandBuffers[0]);
336
337 commandBuffers.clear();
338
339 for (std::size_t i = 0; i < swapchainFramebuffers.size(); i++)
340 vkDestroyFramebuffer(device, swapchainFramebuffers[i], 0);
341
342 swapchainFramebuffers.clear();
343
344 if (graphicsPipeline) {
345 vkDestroyPipeline(device, graphicsPipeline, 0);
346 }
347
348 if (renderPass)
349 vkDestroyRenderPass(device, renderPass, 0);
350
351 if (pipelineLayout)
352 vkDestroyPipelineLayout(device, pipelineLayout, 0);
353
354 if (depthImageView)
355 vkDestroyImageView(device, depthImageView, 0);
356
357 if (depthImageMemory)
358 vkFreeMemory(device, depthImageMemory, 0);
359
360 if (depthImage)
361 vkDestroyImage(device, depthImage, 0);
362
363 for (std::size_t i = 0; i < swapchainImageViews.size(); i++)
364 vkDestroyImageView(device, swapchainImageViews[i], 0);
365
366 swapchainImageViews.clear();
367
368 if (swapchain) {
369 vkDestroySwapchainKHR(device, swapchain, 0);
370 }
371 }
372
373 // Cleanup and recreate swapchain
374 void recreateSwapchain() {
375 cout << "Recreating pipeline..." << endl;
376
377 // Wait until there are no pending frames
378 vkDeviceWaitIdle(device);
379
380 // Cleanup swapchain
381 cleanupSwapchain();
382
383 // Swapchain setup procedure
384 if (vulkanAvailable) setupSwapchain();
385 if (vulkanAvailable) setupSwapchainImages();
386 if (vulkanAvailable) setupPipelineLayout();
387 if (vulkanAvailable) setupRenderpass();
388 if (vulkanAvailable) setupPipeline();
389 if (vulkanAvailable) setupDepthImage();
390 if (vulkanAvailable) setupDepthImageView();
391 if (vulkanAvailable) setupFramebuffers();
392 if (vulkanAvailable) setupCommandBuffers();
393 if (vulkanAvailable) setupDraw();
394 }
395
396 // Setup Vulkan instance
397 void setupInstance(const vector<const char*>& validationLayers) {
398 if (ENABLE_VALIDATION_LAYERS && !VulkanUtilsNew::checkValidationLayerSupport(validationLayers)) {
399 throw runtime_error("validation layers requested, but not available!");
400 }
401
402 // Retrieve the extensions we need to enable in order to use Vulkan with SFML
403 std::vector<const char*> requiredExtentions = sf::Vulkan::getGraphicsRequiredInstanceExtensions();
404 if (ENABLE_VALIDATION_LAYERS) {
405 requiredExtentions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
406 }
407
408 // Register our application information
409 VkApplicationInfo applicationInfo = VkApplicationInfo();
410 applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
411 applicationInfo.pApplicationName = "SFML Vulkan Test";
412 applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
413 applicationInfo.pEngineName = "SFML Vulkan Test Engine";
414 applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
415 applicationInfo.apiVersion = VK_API_VERSION_1_0;
416
417 VkInstanceCreateInfo instanceCreateInfo = VkInstanceCreateInfo();
418 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
419 instanceCreateInfo.pApplicationInfo = &applicationInfo;
420
421 instanceCreateInfo.enabledExtensionCount = requiredExtentions.size();
422 instanceCreateInfo.ppEnabledExtensionNames = requiredExtentions.data();
423
424 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
425 if (ENABLE_VALIDATION_LAYERS) {
426 instanceCreateInfo.enabledLayerCount = validationLayers.size();
427 instanceCreateInfo.ppEnabledLayerNames = validationLayers.data();
428
429 populateDebugMessengerCreateInfo(debugCreateInfo);
430 instanceCreateInfo.pNext = &debugCreateInfo;
431 } else {
432 instanceCreateInfo.enabledLayerCount = 0;
433
434 instanceCreateInfo.pNext = nullptr;
435 }
436
437 if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS) {
438 throw runtime_error("failed to create instance!");
439 }
440 }
441
442 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
443 createInfo = {};
444 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
445 // createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
446 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
447 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
448 createInfo.pfnUserCallback = debugCallback;
449 }
450
451 // Setup our debug callback function to be called by Vulkan
452 void setupDebugMessenger() {
453 if (!ENABLE_VALIDATION_LAYERS) return;
454
455 VkDebugUtilsMessengerCreateInfoEXT createInfo;
456 populateDebugMessengerCreateInfo(createInfo);
457
458 if (VulkanUtilsNew::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
459 throw runtime_error("failed to set up debug messenger!");
460 }
461 }
462
463 // Setup the SFML window Vulkan rendering surface
464 void setupSurface()
465 {
466 if (!window.createVulkanSurface(instance, surface))
467 vulkanAvailable = false;
468 }
469
470 // Select a GPU to use and query its capabilities
471 void setupPhysicalDevice()
472 {
473 // Last sanity check
474 if (!vkEnumeratePhysicalDevices || !vkCreateDevice || !vkGetPhysicalDeviceProperties)
475 {
476 vulkanAvailable = false;
477 return;
478 }
479
480 // Retrieve list of GPUs
481 uint32_t objectCount = 0;
482
483 std::vector<VkPhysicalDevice> devices;
484
485 if (vkEnumeratePhysicalDevices(instance, &objectCount, 0) != VK_SUCCESS)
486 {
487 vulkanAvailable = false;
488 return;
489 }
490
491 devices.resize(objectCount);
492
493 if (vkEnumeratePhysicalDevices(instance, &objectCount, &devices[0]) != VK_SUCCESS)
494 {
495 vulkanAvailable = false;
496 return;
497 }
498
499 // Look for a GPU that supports swapchains
500 for (std::size_t i = 0; i < devices.size(); i++)
501 {
502 VkPhysicalDeviceProperties deviceProperties;
503 vkGetPhysicalDeviceProperties(devices[i], &deviceProperties);
504
505 std::vector<VkExtensionProperties> extensions;
506
507 if (vkEnumerateDeviceExtensionProperties(devices[i], 0, &objectCount, 0) != VK_SUCCESS)
508 {
509 vulkanAvailable = false;
510 return;
511 }
512
513 extensions.resize(objectCount);
514
515 if (vkEnumerateDeviceExtensionProperties(devices[i], 0, &objectCount, &extensions[0]) != VK_SUCCESS)
516 {
517 vulkanAvailable = false;
518 return;
519 }
520
521 bool supportsSwapchain = false;
522
523 for (std::size_t j = 0; j < extensions.size(); j++)
524 {
525 if (!std::strcmp(extensions[j].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME))
526 {
527 supportsSwapchain = true;
528 break;
529 }
530 }
531
532 if (!supportsSwapchain)
533 continue;
534
535 // Prefer discrete over integrated GPUs if multiple are available
536 if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
537 {
538 gpu = devices[i];
539 break;
540 }
541 else if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
542 {
543 gpu = devices[i];
544 }
545 }
546
547 if (!gpu)
548 {
549 vulkanAvailable = false;
550 return;
551 }
552
553 // Check what depth formats are available and select one
554 VkFormatProperties formatProperties = VkFormatProperties();
555
556 vkGetPhysicalDeviceFormatProperties(gpu, VK_FORMAT_D24_UNORM_S8_UINT, &formatProperties);
557
558 if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
559 depthFormat = VK_FORMAT_D24_UNORM_S8_UINT;
560 }
561 else
562 {
563 vkGetPhysicalDeviceFormatProperties(gpu, VK_FORMAT_D32_SFLOAT_S8_UINT, &formatProperties);
564
565 if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
566 depthFormat = VK_FORMAT_D32_SFLOAT_S8_UINT;
567 }
568 else
569 {
570 vkGetPhysicalDeviceFormatProperties(gpu, VK_FORMAT_D32_SFLOAT, &formatProperties);
571
572 if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
573 depthFormat = VK_FORMAT_D32_SFLOAT;
574 }
575 else
576 {
577 vulkanAvailable = false;
578 return;
579 }
580 }
581 }
582 }
583
584 // Setup logical device and device queue
585 void setupLogicalDevice()
586 {
587 // Select a queue family that supports graphics operations and surface presentation
588 uint32_t objectCount = 0;
589
590 std::vector<VkQueueFamilyProperties> queueFamilyProperties;
591
592 vkGetPhysicalDeviceQueueFamilyProperties(gpu, &objectCount, 0);
593
594 queueFamilyProperties.resize(objectCount);
595
596 vkGetPhysicalDeviceQueueFamilyProperties(gpu, &objectCount, &queueFamilyProperties[0]);
597
598 for (std::size_t i = 0; i < queueFamilyProperties.size(); i++)
599 {
600 VkBool32 surfaceSupported = VK_FALSE;
601
602 vkGetPhysicalDeviceSurfaceSupportKHR(gpu, i, surface, &surfaceSupported);
603
604 if ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && (surfaceSupported == VK_TRUE))
605 {
606 queueFamilyIndex = i;
607 break;
608 }
609 }
610
611 if (queueFamilyIndex < 0)
612 {
613 vulkanAvailable = false;
614 return;
615 }
616
617 float queuePriority = 1.0f;
618
619 VkDeviceQueueCreateInfo deviceQueueCreateInfo = VkDeviceQueueCreateInfo();
620 deviceQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
621 deviceQueueCreateInfo.queueCount = 1;
622 deviceQueueCreateInfo.queueFamilyIndex = queueFamilyIndex;
623 deviceQueueCreateInfo.pQueuePriorities = &queuePriority;
624
625 // Enable the swapchain extension
626 const char* extentions[1] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
627
628 // Enable anisotropic filtering
629 VkPhysicalDeviceFeatures physicalDeviceFeatures = VkPhysicalDeviceFeatures();
630 physicalDeviceFeatures.samplerAnisotropy = VK_TRUE;
631
632 VkDeviceCreateInfo deviceCreateInfo = VkDeviceCreateInfo();
633 deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
634 deviceCreateInfo.enabledExtensionCount = 1;
635 deviceCreateInfo.ppEnabledExtensionNames = extentions;
636 deviceCreateInfo.queueCreateInfoCount = 1;
637 deviceCreateInfo.pQueueCreateInfos = &deviceQueueCreateInfo;
638 deviceCreateInfo.pEnabledFeatures = &physicalDeviceFeatures;
639
640 // Create our logical device
641 if (vkCreateDevice(gpu, &deviceCreateInfo, 0, &device) != VK_SUCCESS)
642 {
643 vulkanAvailable = false;
644 return;
645 }
646
647 // Retrieve a handle to the logical device command queue
648 vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue);
649 }
650
651 // Query surface formats and set up swapchain
652 void setupSwapchain() {
653 cout << "STARTED CALL" << endl;
654 // Select a surface format that supports RGBA color format
655 uint32_t objectCount = 0;
656
657 std::vector<VkSurfaceFormatKHR> surfaceFormats;
658
659 if (vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &objectCount, 0) != VK_SUCCESS) {
660 vulkanAvailable = false;
661 return;
662 }
663
664 surfaceFormats.resize(objectCount);
665
666 if (vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &objectCount, &surfaceFormats[0]) != VK_SUCCESS) {
667 vulkanAvailable = false;
668 return;
669 }
670
671 if ((surfaceFormats.size() == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) {
672 swapchainFormat.format = VK_FORMAT_B8G8R8A8_UNORM;
673 swapchainFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
674 } else if (!surfaceFormats.empty()) {
675 for (std::size_t i = 0; i < surfaceFormats.size(); i++) {
676 if ((surfaceFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM) && (surfaceFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)) {
677 swapchainFormat.format = VK_FORMAT_B8G8R8A8_UNORM;
678 swapchainFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
679
680 break;
681 }
682 }
683
684 if (swapchainFormat.format == VK_FORMAT_UNDEFINED) {
685 swapchainFormat = surfaceFormats[0];
686 }
687 } else {
688 vulkanAvailable = false;
689 return;
690 }
691
692 // Select a swapchain present mode
693 std::vector<VkPresentModeKHR> presentModes;
694
695 if (vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, surface, &objectCount, 0) != VK_SUCCESS) {
696 vulkanAvailable = false;
697 return;
698 }
699
700 presentModes.resize(objectCount);
701
702 if (vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, surface, &objectCount, &presentModes[0]) != VK_SUCCESS) {
703 vulkanAvailable = false;
704 return;
705 }
706
707 // Prefer mailbox over FIFO if it is available
708 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
709
710 for (std::size_t i = 0; i < presentModes.size(); i++) {
711 if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
712 presentMode = presentModes[i];
713 break;
714 }
715 }
716
717 // Determine size and count of swapchain images
718 VkSurfaceCapabilitiesKHR surfaceCapabilities;
719
720 if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, surface, &surfaceCapabilities) != VK_SUCCESS) {
721 vulkanAvailable = false;
722 return;
723 }
724
725 swapchainExtent.width = clamp<uint32_t>(window.getSize().x, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
726 swapchainExtent.height = clamp<uint32_t>(window.getSize().y, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
727
728 uint32_t imageCount = clamp<uint32_t>(2, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
729
730 VkSwapchainCreateInfoKHR swapchainCreateInfo = VkSwapchainCreateInfoKHR();
731 swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
732 swapchainCreateInfo.surface = surface;
733 swapchainCreateInfo.minImageCount = imageCount;
734 swapchainCreateInfo.imageFormat = swapchainFormat.format;
735 swapchainCreateInfo.imageColorSpace = swapchainFormat.colorSpace;
736 swapchainCreateInfo.imageExtent = swapchainExtent;
737 swapchainCreateInfo.imageArrayLayers = 1;
738 swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
739 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
740 swapchainCreateInfo.preTransform = surfaceCapabilities.currentTransform;
741 swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
742 swapchainCreateInfo.presentMode = presentMode;
743 swapchainCreateInfo.clipped = VK_TRUE;
744 swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
745
746 // Create the swapchain
747 if (vkCreateSwapchainKHR(device, &swapchainCreateInfo, 0, &swapchain) != VK_SUCCESS) {
748 vulkanAvailable = false;
749 return;
750 }
751 }
752
753 // Retrieve the swapchain images and create image views for them
754 void setupSwapchainImages()
755 {
756 // Retrieve swapchain images
757 uint32_t objectCount = 0;
758
759 if (vkGetSwapchainImagesKHR(device, swapchain, &objectCount, 0) != VK_SUCCESS)
760 {
761 vulkanAvailable = false;
762 return;
763 }
764
765 swapchainImages.resize(objectCount);
766 swapchainImageViews.resize(objectCount);
767
768 if (vkGetSwapchainImagesKHR(device, swapchain, &objectCount, &swapchainImages[0]) != VK_SUCCESS)
769 {
770 vulkanAvailable = false;
771 return;
772 }
773
774 VkImageViewCreateInfo imageViewCreateInfo = VkImageViewCreateInfo();
775 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
776 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
777 imageViewCreateInfo.format = swapchainFormat.format;
778 imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
779 imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
780 imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
781 imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
782 imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
783 imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
784 imageViewCreateInfo.subresourceRange.levelCount = 1;
785 imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
786 imageViewCreateInfo.subresourceRange.layerCount = 1;
787
788 // Create an image view for each swapchain image
789 for (std::size_t i = 0; i < swapchainImages.size(); i++)
790 {
791 imageViewCreateInfo.image = swapchainImages[i];
792
793 if (vkCreateImageView(device, &imageViewCreateInfo, 0, &swapchainImageViews[i]) != VK_SUCCESS)
794 {
795 vulkanAvailable = false;
796 return;
797 }
798 }
799 }
800
801 // Load vertex and fragment shader modules
802 void setupShaders()
803 {
804 VkShaderModuleCreateInfo shaderModuleCreateInfo = VkShaderModuleCreateInfo();
805 shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
806
807 // Use the vertex shader SPIR-V code to create a vertex shader module
808 {
809 sf::FileInputStream file;
810
811 if (!file.open("resources/shader.vert.spv"))
812 {
813 vulkanAvailable = false;
814 return;
815 }
816
817 std::vector<char> buffer(static_cast<std::size_t>(file.getSize()));
818
819 if (file.read(&buffer[0], file.getSize()) != file.getSize())
820 {
821 vulkanAvailable = false;
822 return;
823 }
824
825 shaderModuleCreateInfo.codeSize = buffer.size();
826 shaderModuleCreateInfo.pCode = reinterpret_cast<const uint32_t*>(&buffer[0]);
827
828 if (vkCreateShaderModule(device, &shaderModuleCreateInfo, 0, &vertexShaderModule) != VK_SUCCESS)
829 {
830 vulkanAvailable = false;
831 return;
832 }
833 }
834
835 // Use the fragment shader SPIR-V code to create a fragment shader module
836 {
837 sf::FileInputStream file;
838
839 if (!file.open("resources/shader.frag.spv"))
840 {
841 vulkanAvailable = false;
842 return;
843 }
844
845 std::vector<char> buffer(static_cast<std::size_t>(file.getSize()));
846
847 if (file.read(&buffer[0], file.getSize()) != file.getSize())
848 {
849 vulkanAvailable = false;
850 return;
851 }
852
853 shaderModuleCreateInfo.codeSize = buffer.size();
854 shaderModuleCreateInfo.pCode = reinterpret_cast<const uint32_t*>(&buffer[0]);
855
856 if (vkCreateShaderModule(device, &shaderModuleCreateInfo, 0, &fragmentShaderModule) != VK_SUCCESS)
857 {
858 vulkanAvailable = false;
859 return;
860 }
861 }
862
863 // Prepare the shader stage information for later pipeline creation
864 shaderStages[0] = VkPipelineShaderStageCreateInfo();
865 shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
866 shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
867 shaderStages[0].module = vertexShaderModule;
868 shaderStages[0].pName = "main";
869
870 shaderStages[1] = VkPipelineShaderStageCreateInfo();
871 shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
872 shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
873 shaderStages[1].module = fragmentShaderModule;
874 shaderStages[1].pName = "main";
875 }
876
877 // Setup renderpass and its subpass dependencies
878 void setupRenderpass()
879 {
880 VkAttachmentDescription attachmentDescriptions[2];
881
882 // Color attachment
883 attachmentDescriptions[0] = VkAttachmentDescription();
884 attachmentDescriptions[0].format = swapchainFormat.format;
885 attachmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT;
886 attachmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
887 attachmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
888 attachmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
889 attachmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
890 attachmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
891 attachmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
892
893 // Depth attachment
894 attachmentDescriptions[1] = VkAttachmentDescription();
895 attachmentDescriptions[1].format = depthFormat;
896 attachmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT;
897 attachmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
898 attachmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
899 attachmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
900 attachmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
901 attachmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
902 attachmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
903
904 VkAttachmentReference attachmentReferences[2];
905
906 attachmentReferences[0] = VkAttachmentReference();
907 attachmentReferences[0].attachment = 0;
908 attachmentReferences[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
909
910 attachmentReferences[1] = VkAttachmentReference();
911 attachmentReferences[1].attachment = 1;
912 attachmentReferences[1].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
913
914 // Set up the renderpass to depend on commands that execute before the renderpass begins
915 VkSubpassDescription subpassDescription = VkSubpassDescription();
916 subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
917 subpassDescription.colorAttachmentCount = 1;
918 subpassDescription.pColorAttachments = &attachmentReferences[0];
919 subpassDescription.pDepthStencilAttachment = &attachmentReferences[1];
920
921 VkSubpassDependency subpassDependency = VkSubpassDependency();
922 subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
923 subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
924 subpassDependency.srcAccessMask = 0;
925 subpassDependency.dstSubpass = 0;
926 subpassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
927 subpassDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
928
929 VkRenderPassCreateInfo renderPassCreateInfo = VkRenderPassCreateInfo();
930 renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
931 renderPassCreateInfo.attachmentCount = 2;
932 renderPassCreateInfo.pAttachments = attachmentDescriptions;
933 renderPassCreateInfo.subpassCount = 1;
934 renderPassCreateInfo.pSubpasses = &subpassDescription;
935 renderPassCreateInfo.dependencyCount = 1;
936 renderPassCreateInfo.pDependencies = &subpassDependency;
937
938 // Create the renderpass
939 if (vkCreateRenderPass(device, &renderPassCreateInfo, 0, &renderPass) != VK_SUCCESS)
940 {
941 vulkanAvailable = false;
942 return;
943 }
944 }
945
946 // Set up uniform buffer and texture sampler descriptor set layouts
947 void setupDescriptorSetLayout()
948 {
949 VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[2];
950
951 // Layout binding for uniform buffer
952 descriptorSetLayoutBindings[0] = VkDescriptorSetLayoutBinding();
953 descriptorSetLayoutBindings[0].binding = 0;
954 descriptorSetLayoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
955 descriptorSetLayoutBindings[0].descriptorCount = 1;
956 descriptorSetLayoutBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
957
958 // Layout binding for texture sampler
959 descriptorSetLayoutBindings[1] = VkDescriptorSetLayoutBinding();
960 descriptorSetLayoutBindings[1].binding = 1;
961 descriptorSetLayoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
962 descriptorSetLayoutBindings[1].descriptorCount = 1;
963 descriptorSetLayoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
964
965 VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = VkDescriptorSetLayoutCreateInfo();
966 descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
967 descriptorSetLayoutCreateInfo.bindingCount = 2;
968 descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
969
970 // Create descriptor set layout
971 if (vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, 0, &descriptorSetLayout) != VK_SUCCESS)
972 {
973 vulkanAvailable = false;
974 return;
975 }
976 }
977
978 // Set up pipeline layout
979 void setupPipelineLayout() {
980 VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = VkPipelineLayoutCreateInfo();
981 pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
982 pipelineLayoutCreateInfo.setLayoutCount = 1;
983 pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayout;
984
985 // Create pipeline layout
986 if (vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, 0, &pipelineLayout) != VK_SUCCESS) {
987 vulkanAvailable = false;
988 return;
989 }
990 }
991
992 // Set up rendering pipeline
993 void setupPipeline() {
994 // Set up how the vertex shader pulls data out of our vertex buffer
995 VkVertexInputBindingDescription vertexInputBindingDescription = VkVertexInputBindingDescription();
996 vertexInputBindingDescription.binding = 0;
997 vertexInputBindingDescription.stride = sizeof(float) * 9;
998 vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
999
1000 // Set up how the vertex buffer data is interpreted as attributes by the vertex shader
1001 VkVertexInputAttributeDescription vertexInputAttributeDescriptions[3];
1002
1003 // Position attribute
1004 vertexInputAttributeDescriptions[0] = VkVertexInputAttributeDescription();
1005 vertexInputAttributeDescriptions[0].binding = 0;
1006 vertexInputAttributeDescriptions[0].location = 0;
1007 vertexInputAttributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1008 vertexInputAttributeDescriptions[0].offset = sizeof(float) * 0;
1009
1010 // Color attribute
1011 vertexInputAttributeDescriptions[1] = VkVertexInputAttributeDescription();
1012 vertexInputAttributeDescriptions[1].binding = 0;
1013 vertexInputAttributeDescriptions[1].location = 1;
1014 vertexInputAttributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1015 vertexInputAttributeDescriptions[1].offset = sizeof(float) * 3;
1016
1017 // Texture coordinate attribute
1018 vertexInputAttributeDescriptions[2] = VkVertexInputAttributeDescription();
1019 vertexInputAttributeDescriptions[2].binding = 0;
1020 vertexInputAttributeDescriptions[2].location = 2;
1021 vertexInputAttributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1022 vertexInputAttributeDescriptions[2].offset = sizeof(float) * 7;
1023
1024 VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = VkPipelineVertexInputStateCreateInfo();
1025 vertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1026 vertexInputStateCreateInfo.vertexBindingDescriptionCount = 1;
1027 vertexInputStateCreateInfo.pVertexBindingDescriptions = &vertexInputBindingDescription;
1028 vertexInputStateCreateInfo.vertexAttributeDescriptionCount = 3;
1029 vertexInputStateCreateInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions;
1030
1031 // We want to generate a triangle list with our vertex data
1032 VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = VkPipelineInputAssemblyStateCreateInfo();
1033 inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1034 inputAssemblyStateCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1035 inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
1036
1037 // Set up the viewport
1038 VkViewport viewport = VkViewport();
1039 viewport.x = 0.0f;
1040 viewport.y = 0.0f;
1041 viewport.width = static_cast<float>(swapchainExtent.width);
1042 viewport.height = static_cast<float>(swapchainExtent.height);
1043 viewport.minDepth = 0.0f;
1044 viewport.maxDepth = 1.f;
1045
1046 // Set up the scissor region
1047 VkRect2D scissor = VkRect2D();
1048 scissor.offset.x = 0;
1049 scissor.offset.y = 0;
1050 scissor.extent = swapchainExtent;
1051
1052 VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo = VkPipelineViewportStateCreateInfo();
1053 pipelineViewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1054 pipelineViewportStateCreateInfo.viewportCount = 1;
1055 pipelineViewportStateCreateInfo.pViewports = &viewport;
1056 pipelineViewportStateCreateInfo.scissorCount = 1;
1057 pipelineViewportStateCreateInfo.pScissors = &scissor;
1058
1059 // Set up rasterization parameters: fill polygons, no backface culling, front face is counter-clockwise
1060 VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo = VkPipelineRasterizationStateCreateInfo();
1061 pipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1062 pipelineRasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
1063 pipelineRasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
1064 pipelineRasterizationStateCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
1065 pipelineRasterizationStateCreateInfo.lineWidth = 1.0f;
1066 pipelineRasterizationStateCreateInfo.cullMode = VK_CULL_MODE_NONE;
1067 pipelineRasterizationStateCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1068 pipelineRasterizationStateCreateInfo.depthBiasEnable = VK_FALSE;
1069
1070 // Enable depth testing and disable scissor testing
1071 VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo = VkPipelineDepthStencilStateCreateInfo();
1072 pipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1073 pipelineDepthStencilStateCreateInfo.depthTestEnable = VK_TRUE;
1074 pipelineDepthStencilStateCreateInfo.depthWriteEnable = VK_TRUE;
1075 pipelineDepthStencilStateCreateInfo.depthCompareOp = VK_COMPARE_OP_LESS;
1076 pipelineDepthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE;
1077 pipelineDepthStencilStateCreateInfo.stencilTestEnable = VK_FALSE;
1078
1079 // Enable multi-sampling
1080 VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = VkPipelineMultisampleStateCreateInfo();
1081 pipelineMultisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1082 pipelineMultisampleStateCreateInfo.sampleShadingEnable = VK_FALSE;
1083 pipelineMultisampleStateCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1084
1085 // Set up blending parameters
1086 VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = VkPipelineColorBlendAttachmentState();
1087 pipelineColorBlendAttachmentState.blendEnable = VK_TRUE;
1088 pipelineColorBlendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1089 pipelineColorBlendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1090 pipelineColorBlendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
1091 pipelineColorBlendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1092 pipelineColorBlendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1093 pipelineColorBlendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
1094 pipelineColorBlendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1095
1096 VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo = VkPipelineColorBlendStateCreateInfo();
1097 pipelineColorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1098 pipelineColorBlendStateCreateInfo.logicOpEnable = VK_FALSE;
1099 pipelineColorBlendStateCreateInfo.attachmentCount = 1;
1100 pipelineColorBlendStateCreateInfo.pAttachments = &pipelineColorBlendAttachmentState;
1101
1102 VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo = VkGraphicsPipelineCreateInfo();
1103 graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1104 graphicsPipelineCreateInfo.stageCount = 2;
1105 graphicsPipelineCreateInfo.pStages = shaderStages;
1106 graphicsPipelineCreateInfo.pVertexInputState = &vertexInputStateCreateInfo;
1107 graphicsPipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo;
1108 graphicsPipelineCreateInfo.pViewportState = &pipelineViewportStateCreateInfo;
1109 graphicsPipelineCreateInfo.pRasterizationState = &pipelineRasterizationStateCreateInfo;
1110 graphicsPipelineCreateInfo.pDepthStencilState = &pipelineDepthStencilStateCreateInfo;
1111 graphicsPipelineCreateInfo.pMultisampleState = &pipelineMultisampleStateCreateInfo;
1112 graphicsPipelineCreateInfo.pColorBlendState = &pipelineColorBlendStateCreateInfo;
1113 graphicsPipelineCreateInfo.layout = pipelineLayout;
1114 graphicsPipelineCreateInfo.renderPass = renderPass;
1115 graphicsPipelineCreateInfo.subpass = 0;
1116
1117 // Create our graphics pipeline
1118 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, 0, &graphicsPipeline) != VK_SUCCESS) {
1119 vulkanAvailable = false;
1120 }
1121 }
1122
1123 // Use our renderpass and swapchain images to create the corresponding framebuffers
1124 void setupFramebuffers() {
1125 swapchainFramebuffers.resize(swapchainImageViews.size());
1126
1127 VkFramebufferCreateInfo framebufferCreateInfo = VkFramebufferCreateInfo();
1128 framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1129 framebufferCreateInfo.renderPass = renderPass;
1130 framebufferCreateInfo.attachmentCount = 2;
1131 framebufferCreateInfo.width = swapchainExtent.width;
1132 framebufferCreateInfo.height = swapchainExtent.height;
1133 framebufferCreateInfo.layers = 1;
1134
1135 for (std::size_t i = 0; i < swapchainFramebuffers.size(); i++) {
1136 // Each framebuffer consists of a corresponding swapchain image and the shared depth image
1137 VkImageView attachments[] = { swapchainImageViews[i], depthImageView };
1138
1139 framebufferCreateInfo.pAttachments = attachments;
1140
1141 // Create the framebuffer
1142 if (vkCreateFramebuffer(device, &framebufferCreateInfo, 0, &swapchainFramebuffers[i]) != VK_SUCCESS) {
1143 vulkanAvailable = false;
1144 return;
1145 }
1146 }
1147 }
1148
1149 // Set up our command pool
1150 void setupCommandPool() {
1151 // We want to be able to reset command buffers after submitting them
1152 VkCommandPoolCreateInfo commandPoolCreateInfo = VkCommandPoolCreateInfo();
1153 commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1154 commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
1155 commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1156
1157 // Create our command pool
1158 if (vkCreateCommandPool(device, &commandPoolCreateInfo, 0, &commandPool) != VK_SUCCESS) {
1159 vulkanAvailable = false;
1160 return;
1161 }
1162 }
1163
1164 // Helper to create a generic buffer with the specified size, usage and memory flags
1165 bool createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& memory) {
1166 // We only have a single queue so we can request exclusive access
1167 VkBufferCreateInfo bufferCreateInfo = VkBufferCreateInfo();
1168 bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1169 bufferCreateInfo.size = size;
1170 bufferCreateInfo.usage = usage;
1171 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1172
1173 // Create the buffer, this does not allocate any memory for it yet
1174 if (vkCreateBuffer(device, &bufferCreateInfo, 0, &buffer) != VK_SUCCESS) {
1175 return false;
1176 }
1177
1178 // Check what kind of memory we need to request from the GPU
1179 VkMemoryRequirements memoryRequirements = VkMemoryRequirements();
1180 vkGetBufferMemoryRequirements(device, buffer, &memoryRequirements);
1181
1182 // Check what GPU memory type is available for us to allocate out of
1183 VkPhysicalDeviceMemoryProperties memoryProperties = VkPhysicalDeviceMemoryProperties();
1184 vkGetPhysicalDeviceMemoryProperties(gpu, &memoryProperties);
1185
1186 uint32_t memoryType = 0;
1187
1188 for (; memoryType < memoryProperties.memoryTypeCount; memoryType++) {
1189 if ((memoryRequirements.memoryTypeBits & (1 << memoryType)) &&
1190 ((memoryProperties.memoryTypes[memoryType].propertyFlags & properties) == properties)) {
1191 break;
1192 }
1193 }
1194
1195 if (memoryType == memoryProperties.memoryTypeCount) {
1196 return false;
1197 }
1198
1199 VkMemoryAllocateInfo memoryAllocateInfo = VkMemoryAllocateInfo();
1200 memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1201 memoryAllocateInfo.allocationSize = memoryRequirements.size;
1202 memoryAllocateInfo.memoryTypeIndex = memoryType;
1203
1204 // Allocate the memory out of the GPU pool for the required memory type
1205 if (vkAllocateMemory(device, &memoryAllocateInfo, 0, &memory) != VK_SUCCESS) {
1206 return false;
1207 }
1208
1209 // Bind the allocated memory to our buffer object
1210 if (vkBindBufferMemory(device, buffer, memory, 0) != VK_SUCCESS) {
1211 return false;
1212 }
1213
1214 return true;
1215 }
1216
1217 // Helper to copy the contents of one buffer to another buffer
1218 bool copyBuffer(VkBuffer dst, VkBuffer src, VkDeviceSize size) {
1219 // Allocate a primary command buffer out of our command pool
1220 VkCommandBufferAllocateInfo commandBufferAllocateInfo = VkCommandBufferAllocateInfo();
1221 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1222 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1223 commandBufferAllocateInfo.commandPool = commandPool;
1224 commandBufferAllocateInfo.commandBufferCount = 1;
1225
1226 VkCommandBuffer commandBuffer;
1227
1228 if (vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer) != VK_SUCCESS) {
1229 return false;
1230 }
1231
1232 // Begin the command buffer
1233 VkCommandBufferBeginInfo commandBufferBeginInfo = VkCommandBufferBeginInfo();
1234 commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1235 commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1236
1237 if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS) {
1238 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1239
1240 return false;
1241 }
1242
1243 // Add our buffer copy command
1244 VkBufferCopy bufferCopy = VkBufferCopy();
1245 bufferCopy.srcOffset = 0;
1246 bufferCopy.dstOffset = 0;
1247 bufferCopy.size = size;
1248
1249 vkCmdCopyBuffer(commandBuffer, src, dst, 1, &bufferCopy);
1250
1251 // End and submit the command buffer
1252 vkEndCommandBuffer(commandBuffer);
1253
1254 VkSubmitInfo submitInfo = VkSubmitInfo();
1255 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1256 submitInfo.commandBufferCount = 1;
1257 submitInfo.pCommandBuffers = &commandBuffer;
1258
1259 if (vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
1260 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1261
1262 return false;
1263 }
1264
1265 // Ensure the command buffer has been processed
1266 if (vkQueueWaitIdle(queue) != VK_SUCCESS) {
1267 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1268
1269 return false;
1270 }
1271
1272 // Free the command buffer
1273 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1274
1275 return true;
1276 }
1277
1278 // Create our vertex buffer and upload its data
1279 void setupVertexBuffer() {
1280 float vertexData[] = {
1281 // X Y Z R G B A U V
1282 -0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
1283 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1284 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
1285 -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1286
1287 -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1288 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
1289 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1290 -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
1291
1292 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
1293 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1294 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
1295 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1296
1297 -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1298 -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
1299 -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1300 -0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1301
1302 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
1303 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1304 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1305 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1306
1307 -0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1308 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1309 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
1310 -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f
1311 };
1312
1313 // Create a staging buffer that is writable by the CPU
1314 VkBuffer stagingBuffer = 0;
1315 VkDeviceMemory stagingBufferMemory = 0;
1316
1317 if (!createBuffer(
1318 sizeof(vertexData),
1319 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1320 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1321 stagingBuffer,
1322 stagingBufferMemory
1323 ))
1324 {
1325 vulkanAvailable = false;
1326 return;
1327 }
1328
1329 void* ptr;
1330
1331 // Map the buffer into our address space
1332 if (vkMapMemory(device, stagingBufferMemory, 0, sizeof(vertexData), 0, &ptr) != VK_SUCCESS)
1333 {
1334 vkFreeMemory(device, stagingBufferMemory, 0);
1335 vkDestroyBuffer(device, stagingBuffer, 0);
1336
1337 vulkanAvailable = false;
1338 return;
1339 }
1340
1341 // Copy the vertex data into the buffer
1342 std::memcpy(ptr, vertexData, sizeof(vertexData));
1343
1344 // Unmap the buffer
1345 vkUnmapMemory(device, stagingBufferMemory);
1346
1347 // Create the GPU local vertex buffer
1348 if (!createBuffer(
1349 sizeof(vertexData),
1350 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1351 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1352 vertexBuffer,
1353 vertexBufferMemory
1354 ))
1355 {
1356 vkFreeMemory(device, stagingBufferMemory, 0);
1357 vkDestroyBuffer(device, stagingBuffer, 0);
1358
1359 vulkanAvailable = false;
1360 return;
1361 }
1362
1363 // Copy the contents of the staging buffer into the GPU vertex buffer
1364 vulkanAvailable = copyBuffer(vertexBuffer, stagingBuffer, sizeof(vertexData));
1365
1366 // Free the staging buffer and its memory
1367 vkFreeMemory(device, stagingBufferMemory, 0);
1368 vkDestroyBuffer(device, stagingBuffer, 0);
1369 }
1370
1371 // Create our index buffer and upload its data
1372 void setupIndexBuffer()
1373 {
1374 uint16_t indexData[] = {
1375 0, 1, 2,
1376 2, 3, 0,
1377
1378 4, 5, 6,
1379 6, 7, 4,
1380
1381 8, 9, 10,
1382 10, 11, 8,
1383
1384 12, 13, 14,
1385 14, 15, 12,
1386
1387 16, 17, 18,
1388 18, 19, 16,
1389
1390 20, 21, 22,
1391 22, 23, 20
1392 };
1393
1394 // Create a staging buffer that is writable by the CPU
1395 VkBuffer stagingBuffer = 0;
1396 VkDeviceMemory stagingBufferMemory = 0;
1397
1398 if (!createBuffer(
1399 sizeof(indexData),
1400 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1401 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1402 stagingBuffer,
1403 stagingBufferMemory
1404 ))
1405 {
1406 vulkanAvailable = false;
1407 return;
1408 }
1409
1410 void* ptr;
1411
1412 // Map the buffer into our address space
1413 if (vkMapMemory(device, stagingBufferMemory, 0, sizeof(indexData), 0, &ptr) != VK_SUCCESS)
1414 {
1415 vkFreeMemory(device, stagingBufferMemory, 0);
1416 vkDestroyBuffer(device, stagingBuffer, 0);
1417
1418 vulkanAvailable = false;
1419 return;
1420 }
1421
1422 // Copy the index data into the buffer
1423 std::memcpy(ptr, indexData, sizeof(indexData));
1424
1425 // Unmap the buffer
1426 vkUnmapMemory(device, stagingBufferMemory);
1427
1428 // Create the GPU local index buffer
1429 if (!createBuffer(
1430 sizeof(indexData),
1431 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
1432 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1433 indexBuffer,
1434 indexBufferMemory
1435 ))
1436 {
1437 vkFreeMemory(device, stagingBufferMemory, 0);
1438 vkDestroyBuffer(device, stagingBuffer, 0);
1439
1440 vulkanAvailable = false;
1441 return;
1442 }
1443
1444 // Copy the contents of the staging buffer into the GPU index buffer
1445 vulkanAvailable = copyBuffer(indexBuffer, stagingBuffer, sizeof(indexData));
1446
1447 // Free the staging buffer and its memory
1448 vkFreeMemory(device, stagingBufferMemory, 0);
1449 vkDestroyBuffer(device, stagingBuffer, 0);
1450 }
1451
1452 // Create our uniform buffer but don't upload any data yet
1453 void setupUniformBuffers()
1454 {
1455 // Create a uniform buffer for every frame that might be in flight to prevent clobbering
1456 for (size_t i = 0; i < swapchainImages.size(); i++)
1457 {
1458 uniformBuffers.push_back(0);
1459 uniformBuffersMemory.push_back(0);
1460
1461 // The uniform buffer will be host visible and coherent since we use it for streaming data every frame
1462 if (!createBuffer(
1463 sizeof(Matrix) * 3,
1464 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1465 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1466 uniformBuffers[i],
1467 uniformBuffersMemory[i]
1468 ))
1469 {
1470 vulkanAvailable = false;
1471 return;
1472 }
1473 }
1474 }
1475
1476 // Helper to create a generic image with the specified size, format, usage and memory flags
1477 bool createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory)
1478 {
1479 // We only have a single queue so we can request exclusive access
1480 VkImageCreateInfo imageCreateInfo = VkImageCreateInfo();
1481 imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1482 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
1483 imageCreateInfo.extent.width = width;
1484 imageCreateInfo.extent.height = height;
1485 imageCreateInfo.extent.depth = 1;
1486 imageCreateInfo.mipLevels = 1;
1487 imageCreateInfo.arrayLayers = 1;
1488 imageCreateInfo.format = format;
1489 imageCreateInfo.tiling = tiling;
1490 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1491 imageCreateInfo.usage = usage;
1492 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1493 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1494
1495 // Create the image, this does not allocate any memory for it yet
1496 if (vkCreateImage(device, &imageCreateInfo, 0, &image) != VK_SUCCESS)
1497 return false;
1498
1499 // Check what kind of memory we need to request from the GPU
1500 VkMemoryRequirements memoryRequirements = VkMemoryRequirements();
1501 vkGetImageMemoryRequirements(device, image, &memoryRequirements);
1502
1503 // Check what GPU memory type is available for us to allocate out of
1504 VkPhysicalDeviceMemoryProperties memoryProperties = VkPhysicalDeviceMemoryProperties();
1505 vkGetPhysicalDeviceMemoryProperties(gpu, &memoryProperties);
1506
1507 uint32_t memoryType = 0;
1508
1509 for (; memoryType < memoryProperties.memoryTypeCount; memoryType++)
1510 {
1511 if ((memoryRequirements.memoryTypeBits & (1 << memoryType)) &&
1512 ((memoryProperties.memoryTypes[memoryType].propertyFlags & properties) == properties))
1513 break;
1514 }
1515
1516 if (memoryType == memoryProperties.memoryTypeCount)
1517 return false;
1518
1519 VkMemoryAllocateInfo memoryAllocateInfo = VkMemoryAllocateInfo();
1520 memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1521 memoryAllocateInfo.allocationSize = memoryRequirements.size;
1522 memoryAllocateInfo.memoryTypeIndex = memoryType;
1523
1524 // Allocate the memory out of the GPU pool for the required memory type
1525 if (vkAllocateMemory(device, &memoryAllocateInfo, 0, &imageMemory) != VK_SUCCESS)
1526 return false;
1527
1528 // Bind the allocated memory to our image object
1529 if (vkBindImageMemory(device, image, imageMemory, 0) != VK_SUCCESS)
1530 return false;
1531
1532 return true;
1533 }
1534
1535 // Create our depth image and transition it into the proper layout
1536 void setupDepthImage()
1537 {
1538 // Create our depth image
1539 if (!createImage(
1540 swapchainExtent.width,
1541 swapchainExtent.height,
1542 depthFormat,
1543 VK_IMAGE_TILING_OPTIMAL,
1544 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
1545 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1546 depthImage,
1547 depthImageMemory
1548 ))
1549 {
1550 vulkanAvailable = false;
1551 return;
1552 }
1553
1554 // Allocate a command buffer
1555 VkCommandBufferAllocateInfo commandBufferAllocateInfo = VkCommandBufferAllocateInfo();
1556 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1557 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1558 commandBufferAllocateInfo.commandPool = commandPool;
1559 commandBufferAllocateInfo.commandBufferCount = 1;
1560
1561 VkCommandBuffer commandBuffer;
1562
1563 if (vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer) != VK_SUCCESS)
1564 {
1565 vulkanAvailable = false;
1566 return;
1567 }
1568
1569 // Begin the command buffer
1570 VkCommandBufferBeginInfo commandBufferBeginInfo = VkCommandBufferBeginInfo();
1571 commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1572 commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1573
1574 VkSubmitInfo submitInfo = VkSubmitInfo();
1575 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1576 submitInfo.commandBufferCount = 1;
1577 submitInfo.pCommandBuffers = &commandBuffer;
1578
1579 if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS)
1580 {
1581 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1582
1583 vulkanAvailable = false;
1584 return;
1585 }
1586
1587 // Submit a barrier to transition the image layout to depth stencil optimal
1588 VkImageMemoryBarrier barrier = VkImageMemoryBarrier();
1589 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1590 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1591 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1592 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1593 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1594 barrier.image = depthImage;
1595 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | ((depthFormat == VK_FORMAT_D32_SFLOAT) ? 0 : VK_IMAGE_ASPECT_STENCIL_BIT);
1596 barrier.subresourceRange.baseMipLevel = 0;
1597 barrier.subresourceRange.levelCount = 1;
1598 barrier.subresourceRange.baseArrayLayer = 0;
1599 barrier.subresourceRange.layerCount = 1;
1600 barrier.srcAccessMask = 0;
1601 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1602
1603 vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, 0, 0, 0, 1, &barrier);
1604
1605 // End and submit the command buffer
1606 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1607 {
1608 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1609
1610 vulkanAvailable = false;
1611 return;
1612 }
1613
1614 if (vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS)
1615 {
1616 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1617
1618 vulkanAvailable = false;
1619 return;
1620 }
1621
1622 // Ensure the command buffer has been processed
1623 if (vkQueueWaitIdle(queue) != VK_SUCCESS)
1624 {
1625 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1626
1627 vulkanAvailable = false;
1628 return;
1629 }
1630
1631 // Free the command buffer
1632 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1633 }
1634
1635 // Create an image view for our depth image
1636 void setupDepthImageView()
1637 {
1638 VkImageViewCreateInfo imageViewCreateInfo = VkImageViewCreateInfo();
1639 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1640 imageViewCreateInfo.image = depthImage;
1641 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1642 imageViewCreateInfo.format = depthFormat;
1643 imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | ((depthFormat == VK_FORMAT_D32_SFLOAT) ? 0 : VK_IMAGE_ASPECT_STENCIL_BIT);
1644 imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
1645 imageViewCreateInfo.subresourceRange.levelCount = 1;
1646 imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
1647 imageViewCreateInfo.subresourceRange.layerCount = 1;
1648
1649 // Create the depth image view
1650 if (vkCreateImageView(device, &imageViewCreateInfo, 0, &depthImageView) != VK_SUCCESS)
1651 {
1652 vulkanAvailable = false;
1653 return;
1654 }
1655 }
1656
1657 // Create an image for our texture data
1658 void setupTextureImage()
1659 {
1660 // Load the image data
1661 sf::Image imageData;
1662
1663 if (!imageData.loadFromFile("resources/logo.png"))
1664 {
1665 vulkanAvailable = false;
1666 return;
1667 }
1668
1669 // Create a staging buffer to transfer the data with
1670 VkDeviceSize imageSize = imageData.getSize().x * imageData.getSize().y * 4;
1671
1672 VkBuffer stagingBuffer;
1673 VkDeviceMemory stagingBufferMemory;
1674 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
1675
1676 void* ptr;
1677
1678 // Map the buffer into our address space
1679 if (vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &ptr) != VK_SUCCESS)
1680 {
1681 vkFreeMemory(device, stagingBufferMemory, 0);
1682 vkDestroyBuffer(device, stagingBuffer, 0);
1683
1684 vulkanAvailable = false;
1685 return;
1686 }
1687
1688 // Copy the image data into the buffer
1689 std::memcpy(ptr, imageData.getPixelsPtr(), static_cast<size_t>(imageSize));
1690
1691 // Unmap the buffer
1692 vkUnmapMemory(device, stagingBufferMemory);
1693
1694 // Create a GPU local image
1695 if (!createImage(
1696 imageData.getSize().x,
1697 imageData.getSize().y,
1698 VK_FORMAT_R8G8B8A8_UNORM,
1699 VK_IMAGE_TILING_OPTIMAL,
1700 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
1701 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1702 textureImage,
1703 textureImageMemory
1704 ))
1705 {
1706 vkFreeMemory(device, stagingBufferMemory, 0);
1707 vkDestroyBuffer(device, stagingBuffer, 0);
1708
1709 vulkanAvailable = false;
1710 return;
1711 }
1712
1713 // Create a command buffer
1714 VkCommandBufferAllocateInfo commandBufferAllocateInfo = VkCommandBufferAllocateInfo();
1715 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1716 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1717 commandBufferAllocateInfo.commandPool = commandPool;
1718 commandBufferAllocateInfo.commandBufferCount = 1;
1719
1720 VkCommandBuffer commandBuffer;
1721
1722 if (vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer) != VK_SUCCESS)
1723 {
1724 vkFreeMemory(device, stagingBufferMemory, 0);
1725 vkDestroyBuffer(device, stagingBuffer, 0);
1726
1727 vulkanAvailable = false;
1728 return;
1729 }
1730
1731 // Begin the command buffer
1732 VkCommandBufferBeginInfo commandBufferBeginInfo = VkCommandBufferBeginInfo();
1733 commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1734 commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1735
1736 VkSubmitInfo submitInfo = VkSubmitInfo();
1737 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1738 submitInfo.commandBufferCount = 1;
1739 submitInfo.pCommandBuffers = &commandBuffer;
1740
1741 if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS)
1742 {
1743 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1744
1745 vkFreeMemory(device, stagingBufferMemory, 0);
1746 vkDestroyBuffer(device, stagingBuffer, 0);
1747
1748 vulkanAvailable = false;
1749 return;
1750 }
1751
1752 // Submit a barrier to transition the image layout to transfer destionation optimal
1753 VkImageMemoryBarrier barrier = VkImageMemoryBarrier();
1754 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1755 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1756 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1757 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1758 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1759 barrier.image = textureImage;
1760 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1761 barrier.subresourceRange.baseMipLevel = 0;
1762 barrier.subresourceRange.levelCount = 1;
1763 barrier.subresourceRange.baseArrayLayer = 0;
1764 barrier.subresourceRange.layerCount = 1;
1765 barrier.srcAccessMask = 0;
1766 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1767
1768 vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, 0, 0, 0, 1, &barrier);
1769
1770 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1771 {
1772 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1773
1774 vkFreeMemory(device, stagingBufferMemory, 0);
1775 vkDestroyBuffer(device, stagingBuffer, 0);
1776
1777 vulkanAvailable = false;
1778 return;
1779 }
1780
1781 if (vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS)
1782 {
1783 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1784
1785 vkFreeMemory(device, stagingBufferMemory, 0);
1786 vkDestroyBuffer(device, stagingBuffer, 0);
1787
1788 vulkanAvailable = false;
1789 return;
1790 }
1791
1792 // Ensure the command buffer has been processed
1793 if (vkQueueWaitIdle(queue) != VK_SUCCESS)
1794 {
1795 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1796
1797 vkFreeMemory(device, stagingBufferMemory, 0);
1798 vkDestroyBuffer(device, stagingBuffer, 0);
1799
1800 vulkanAvailable = false;
1801 return;
1802 }
1803
1804 // Begin the command buffer
1805 if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS)
1806 {
1807 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1808
1809 vkFreeMemory(device, stagingBufferMemory, 0);
1810 vkDestroyBuffer(device, stagingBuffer, 0);
1811
1812 vulkanAvailable = false;
1813 return;
1814 }
1815
1816 // Copy the staging buffer contents into the image
1817 VkBufferImageCopy bufferImageCopy = VkBufferImageCopy();
1818 bufferImageCopy.bufferOffset = 0;
1819 bufferImageCopy.bufferRowLength = 0;
1820 bufferImageCopy.bufferImageHeight = 0;
1821 bufferImageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1822 bufferImageCopy.imageSubresource.mipLevel = 0;
1823 bufferImageCopy.imageSubresource.baseArrayLayer = 0;
1824 bufferImageCopy.imageSubresource.layerCount = 1;
1825 bufferImageCopy.imageOffset.x = 0;
1826 bufferImageCopy.imageOffset.y = 0;
1827 bufferImageCopy.imageOffset.z = 0;
1828 bufferImageCopy.imageExtent.width = imageData.getSize().x;
1829 bufferImageCopy.imageExtent.height = imageData.getSize().y;
1830 bufferImageCopy.imageExtent.depth = 1;
1831
1832 vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopy);
1833
1834 // End and submit the command buffer
1835 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1836 {
1837 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1838
1839 vkFreeMemory(device, stagingBufferMemory, 0);
1840 vkDestroyBuffer(device, stagingBuffer, 0);
1841
1842 vulkanAvailable = false;
1843 return;
1844 }
1845
1846 if (vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS)
1847 {
1848 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1849
1850 vkFreeMemory(device, stagingBufferMemory, 0);
1851 vkDestroyBuffer(device, stagingBuffer, 0);
1852
1853 vulkanAvailable = false;
1854 return;
1855 }
1856
1857 // Ensure the command buffer has been processed
1858 if (vkQueueWaitIdle(queue) != VK_SUCCESS)
1859 {
1860 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1861
1862 vkFreeMemory(device, stagingBufferMemory, 0);
1863 vkDestroyBuffer(device, stagingBuffer, 0);
1864
1865 vulkanAvailable = false;
1866 return;
1867 }
1868
1869 // Begin the command buffer
1870 if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS)
1871 {
1872 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1873
1874 vkFreeMemory(device, stagingBufferMemory, 0);
1875 vkDestroyBuffer(device, stagingBuffer, 0);
1876
1877 vulkanAvailable = false;
1878 return;
1879 }
1880
1881 // Submit a barrier to transition the image layout from transfer destionation optimal to shader read-only optimal
1882 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1883 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1884 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1885 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1886
1887 vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, 0, 0, 0, 1, &barrier);
1888
1889 // End and submit the command buffer
1890 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1891 {
1892 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1893
1894 vkFreeMemory(device, stagingBufferMemory, 0);
1895 vkDestroyBuffer(device, stagingBuffer, 0);
1896
1897 vulkanAvailable = false;
1898 return;
1899 }
1900
1901 if (vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS)
1902 {
1903 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1904
1905 vkFreeMemory(device, stagingBufferMemory, 0);
1906 vkDestroyBuffer(device, stagingBuffer, 0);
1907
1908 vulkanAvailable = false;
1909 return;
1910 }
1911
1912 // Ensure the command buffer has been processed
1913 if (vkQueueWaitIdle(queue) != VK_SUCCESS)
1914 {
1915 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1916
1917 vkFreeMemory(device, stagingBufferMemory, 0);
1918 vkDestroyBuffer(device, stagingBuffer, 0);
1919
1920 vulkanAvailable = false;
1921 return;
1922 }
1923
1924 // Free the command buffer
1925 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1926
1927 vkFreeMemory(device, stagingBufferMemory, 0);
1928 vkDestroyBuffer(device, stagingBuffer, 0);
1929 }
1930
1931 // Create an image view for our texture
1932 void setupTextureImageView()
1933 {
1934 VkImageViewCreateInfo imageViewCreateInfo = VkImageViewCreateInfo();
1935 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1936 imageViewCreateInfo.image = textureImage;
1937 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1938 imageViewCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1939 imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1940 imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
1941 imageViewCreateInfo.subresourceRange.levelCount = 1;
1942 imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
1943 imageViewCreateInfo.subresourceRange.layerCount = 1;
1944
1945 // Create our texture image view
1946 if (vkCreateImageView(device, &imageViewCreateInfo, 0, &textureImageView) != VK_SUCCESS)
1947 {
1948 vulkanAvailable = false;
1949 return;
1950 }
1951 }
1952
1953 // Create a sampler for our texture
1954 void setupTextureSampler()
1955 {
1956 // Sampler parameters: linear min/mag filtering, 4x anisotropic
1957 VkSamplerCreateInfo samplerCreateInfo = VkSamplerCreateInfo();
1958 samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1959 samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
1960 samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
1961 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1962 samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1963 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1964 samplerCreateInfo.anisotropyEnable = VK_TRUE;
1965 samplerCreateInfo.maxAnisotropy = 4;
1966 samplerCreateInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1967 samplerCreateInfo.unnormalizedCoordinates = VK_FALSE;
1968 samplerCreateInfo.compareEnable = VK_FALSE;
1969 samplerCreateInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1970 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1971 samplerCreateInfo.mipLodBias = 0.0f;
1972 samplerCreateInfo.minLod = 0.0f;
1973 samplerCreateInfo.maxLod = 0.0f;
1974
1975 // Create our sampler
1976 if (vkCreateSampler(device, &samplerCreateInfo, 0, &textureSampler) != VK_SUCCESS)
1977 {
1978 vulkanAvailable = false;
1979 return;
1980 }
1981 }
1982
1983 // Set up our descriptor pool
1984 void setupDescriptorPool()
1985 {
1986 // We need to allocate as many descriptor sets as we have frames in flight
1987 VkDescriptorPoolSize descriptorPoolSizes[2];
1988
1989 descriptorPoolSizes[0] = VkDescriptorPoolSize();
1990 descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1991 descriptorPoolSizes[0].descriptorCount = static_cast<uint32_t>(swapchainImages.size());
1992
1993 descriptorPoolSizes[1] = VkDescriptorPoolSize();
1994 descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1995 descriptorPoolSizes[1].descriptorCount = static_cast<uint32_t>(swapchainImages.size());
1996
1997 VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = VkDescriptorPoolCreateInfo();
1998 descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1999 descriptorPoolCreateInfo.poolSizeCount = 2;
2000 descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes;
2001 descriptorPoolCreateInfo.maxSets = static_cast<uint32_t>(swapchainImages.size());
2002
2003 // Create the descriptor pool
2004 if (vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, 0, &descriptorPool) != VK_SUCCESS)
2005 {
2006 vulkanAvailable = false;
2007 return;
2008 }
2009 }
2010
2011 // Set up our descriptor sets
2012 void setupDescriptorSets()
2013 {
2014 // Allocate a descriptor set for each frame in flight
2015 std::vector<VkDescriptorSetLayout> descriptorSetLayouts(swapchainImages.size(), descriptorSetLayout);
2016
2017 VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = VkDescriptorSetAllocateInfo();
2018 descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
2019 descriptorSetAllocateInfo.descriptorPool = descriptorPool;
2020 descriptorSetAllocateInfo.descriptorSetCount = static_cast<uint32_t>(swapchainImages.size());
2021 descriptorSetAllocateInfo.pSetLayouts = &descriptorSetLayouts[0];
2022
2023 descriptorSets.resize(swapchainImages.size());
2024
2025 if (vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSets[0]) != VK_SUCCESS)
2026 {
2027 descriptorSets.clear();
2028
2029 vulkanAvailable = false;
2030 return;
2031 }
2032
2033 // For every descriptor set, set up the bindings to our uniform buffer and texture sampler
2034 for (std::size_t i = 0; i < descriptorSets.size(); i++)
2035 {
2036 VkWriteDescriptorSet writeDescriptorSets[2];
2037
2038 // Uniform buffer binding information
2039 VkDescriptorBufferInfo descriptorBufferInfo = VkDescriptorBufferInfo();
2040 descriptorBufferInfo.buffer = uniformBuffers[i];
2041 descriptorBufferInfo.offset = 0;
2042 descriptorBufferInfo.range = sizeof(Matrix) * 3;
2043
2044 writeDescriptorSets[0] = VkWriteDescriptorSet();
2045 writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
2046 writeDescriptorSets[0].dstSet = descriptorSets[i];
2047 writeDescriptorSets[0].dstBinding = 0;
2048 writeDescriptorSets[0].dstArrayElement = 0;
2049 writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
2050 writeDescriptorSets[0].descriptorCount = 1;
2051 writeDescriptorSets[0].pBufferInfo = &descriptorBufferInfo;
2052
2053 // Texture sampler binding information
2054 VkDescriptorImageInfo descriptorImageInfo = VkDescriptorImageInfo();
2055 descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2056 descriptorImageInfo.imageView = textureImageView;
2057 descriptorImageInfo.sampler = textureSampler;
2058
2059 writeDescriptorSets[1] = VkWriteDescriptorSet();
2060 writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
2061 writeDescriptorSets[1].dstSet = descriptorSets[i];
2062 writeDescriptorSets[1].dstBinding = 1;
2063 writeDescriptorSets[1].dstArrayElement = 0;
2064 writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2065 writeDescriptorSets[1].descriptorCount = 1;
2066 writeDescriptorSets[1].pImageInfo = &descriptorImageInfo;
2067
2068 // Update the desciptor set
2069 vkUpdateDescriptorSets(device, 2, writeDescriptorSets, 0, 0);
2070 }
2071 }
2072
2073 // Set up the command buffers we use for drawing each frame
2074 void setupCommandBuffers()
2075 {
2076 // We need a command buffer for every frame in flight
2077 commandBuffers.resize(swapchainFramebuffers.size());
2078
2079 // These are primary command buffers
2080 VkCommandBufferAllocateInfo commandBufferAllocateInfo = VkCommandBufferAllocateInfo();
2081 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2082 commandBufferAllocateInfo.commandPool = commandPool;
2083 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2084 commandBufferAllocateInfo.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
2085
2086 // Allocate the command buffers from our command pool
2087 if (vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffers[0]) != VK_SUCCESS)
2088 {
2089 commandBuffers.clear();
2090 vulkanAvailable = false;
2091 return;
2092 }
2093 }
2094
2095 // Set up the commands we need to issue to draw a frame
2096 void setupDraw()
2097 {
2098 // Set up our clear colors
2099 VkClearValue clearColors[2];
2100
2101 // Clear color buffer to opaque black
2102 clearColors[0] = VkClearValue();
2103 clearColors[0].color.float32[0] = 0.0f;
2104 clearColors[0].color.float32[1] = 0.0f;
2105 clearColors[0].color.float32[2] = 0.0f;
2106 clearColors[0].color.float32[3] = 0.0f;
2107
2108 // Clear depth to 1.0f
2109 clearColors[1] = VkClearValue();
2110 clearColors[1].depthStencil.depth = 1.0f;
2111 clearColors[1].depthStencil.stencil = 0;
2112
2113 VkRenderPassBeginInfo renderPassBeginInfo = VkRenderPassBeginInfo();
2114 renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2115 renderPassBeginInfo.renderPass = renderPass;
2116 renderPassBeginInfo.renderArea.offset.x = 0;
2117 renderPassBeginInfo.renderArea.offset.y = 0;
2118 renderPassBeginInfo.renderArea.extent = swapchainExtent;
2119 renderPassBeginInfo.clearValueCount = 2;
2120 renderPassBeginInfo.pClearValues = clearColors;
2121
2122 // Simultaneous use: this command buffer can be resubmitted to a queue before a previous submission is completed
2123 VkCommandBufferBeginInfo commandBufferBeginInfo = VkCommandBufferBeginInfo();
2124 commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2125 commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
2126
2127 // Set up the command buffers for each frame in flight
2128 for (std::size_t i = 0; i < commandBuffers.size(); i++)
2129 {
2130 // Begin the command buffer
2131 if (vkBeginCommandBuffer(commandBuffers[i], &commandBufferBeginInfo) != VK_SUCCESS)
2132 {
2133 vulkanAvailable = false;
2134 return;
2135 }
2136
2137 // Begin the renderpass
2138 renderPassBeginInfo.framebuffer = swapchainFramebuffers[i];
2139
2140 vkCmdBeginRenderPass(commandBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
2141
2142 // Bind our graphics pipeline
2143 vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
2144
2145 // Bind our vertex buffer
2146 VkDeviceSize offset = 0;
2147
2148 vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, &vertexBuffer, &offset);
2149
2150 // Bind our index buffer
2151 vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);
2152
2153 // Bind our descriptor sets
2154 vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i], 0, 0);
2155
2156 // Draw our primitives
2157 vkCmdDrawIndexed(commandBuffers[i], 36, 1, 0, 0, 0);
2158
2159 // End the renderpass
2160 vkCmdEndRenderPass(commandBuffers[i]);
2161
2162 // End the command buffer
2163 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS)
2164 {
2165 vulkanAvailable = false;
2166 return;
2167 }
2168 }
2169 }
2170
2171 // Set up the semaphores we use to synchronize frames among each other
2172 void setupSemaphores()
2173 {
2174 VkSemaphoreCreateInfo semaphoreCreateInfo = VkSemaphoreCreateInfo();
2175 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
2176
2177 // Create a semaphore to track when an swapchain image is available for each frame in flight
2178 for (int i = 0; i < maxFramesInFlight; i++)
2179 {
2180 imageAvailableSemaphores.push_back(0);
2181
2182 if (vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &imageAvailableSemaphores[i]) != VK_SUCCESS)
2183 {
2184 imageAvailableSemaphores.pop_back();
2185 vulkanAvailable = false;
2186 return;
2187 }
2188 }
2189
2190 // Create a semaphore to track when rendering is complete for each frame in flight
2191 for (int i = 0; i < maxFramesInFlight; i++)
2192 {
2193 renderFinishedSemaphores.push_back(0);
2194
2195 if (vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &renderFinishedSemaphores[i]) != VK_SUCCESS)
2196 {
2197 renderFinishedSemaphores.pop_back();
2198 vulkanAvailable = false;
2199 return;
2200 }
2201 }
2202 }
2203
2204 // Set up the fences we use to synchronize frames among each other
2205 void setupFences()
2206 {
2207 // Create the fences in the signaled state
2208 VkFenceCreateInfo fenceCreateInfo = VkFenceCreateInfo();
2209 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2210 fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
2211
2212 // Create a fence to track when queue submission is complete for each frame in flight
2213 for (int i = 0; i < maxFramesInFlight; i++)
2214 {
2215 fences.push_back(0);
2216
2217 if (vkCreateFence(device, &fenceCreateInfo, 0, &fences[i]) != VK_SUCCESS)
2218 {
2219 fences.pop_back();
2220 vulkanAvailable = false;
2221 return;
2222 }
2223 }
2224 }
2225
2226 // Update the matrices in our uniform buffer every frame
2227 void updateUniformBuffer(float elapsed)
2228 {
2229 const float pi = 3.14159265359f;
2230
2231 // Construct the model matrix
2232 Matrix model = {
2233 1.0f, 0.0f, 0.0f, 0.0f,
2234 0.0f, 1.0f, 0.0f, 0.0f,
2235 0.0f, 0.0f, 1.0f, 0.0f,
2236 0.0f, 0.0f, 0.0f, 1.0f
2237 };
2238
2239 matrixRotateX(model, elapsed * 59.0f * pi / 180.f);
2240 matrixRotateY(model, elapsed * 83.0f * pi / 180.f);
2241 matrixRotateZ(model, elapsed * 109.0f * pi / 180.f);
2242
2243 // Translate the model based on the mouse position
2244 float x = clamp(sf::Mouse::getPosition(window).x * 2.f / window.getSize().x - 1.f, -1.0f, 1.0f) * 2.0f;
2245 float y = clamp(-sf::Mouse::getPosition(window).y * 2.f / window.getSize().y + 1.f, -1.0f, 1.0f) * 1.5f;
2246
2247 model[3][0] -= x;
2248 model[3][2] += y;
2249
2250 // Construct the view matrix
2251 const Vec3 eye = { 0.0f, 4.0f, 0.0f };
2252 const Vec3 center = { 0.0f, 0.0f, 0.0f };
2253 const Vec3 up = { 0.0f, 0.0f, 1.0f };
2254
2255 Matrix view;
2256
2257 matrixLookAt(view, eye, center, up);
2258
2259 // Construct the projection matrix
2260 const float fov = 45.0f;
2261 const float aspect = static_cast<float>(swapchainExtent.width) / static_cast<float>(swapchainExtent.height);
2262 const float nearPlane = 0.1f;
2263 const float farPlane = 10.0f;
2264
2265 Matrix projection;
2266
2267 matrixPerspective(projection, fov * pi / 180.f, aspect, nearPlane, farPlane);
2268
2269 char* ptr;
2270
2271 // Map the current frame's uniform buffer into our address space
2272 if (vkMapMemory(device, uniformBuffersMemory[currentFrame], 0, sizeof(Matrix) * 3, 0, reinterpret_cast<void**>(&ptr)) != VK_SUCCESS)
2273 {
2274 vulkanAvailable = false;
2275 return;
2276 }
2277
2278 // Copy the matrix data into the current frame's uniform buffer
2279 std::memcpy(ptr + sizeof(Matrix) * 0, model, sizeof(Matrix));
2280 std::memcpy(ptr + sizeof(Matrix) * 1, view, sizeof(Matrix));
2281 std::memcpy(ptr + sizeof(Matrix) * 2, projection, sizeof(Matrix));
2282
2283 // Unmap the buffer
2284 vkUnmapMemory(device, uniformBuffersMemory[currentFrame]);
2285 }
2286
2287 void draw() {
2288 uint32_t imageIndex = 0;
2289
2290 // If the objects we need to submit this frame are still pending, wait here
2291 vkWaitForFences(device, 1, &fences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());
2292
2293 // Get the next image in the swapchain
2294 VkResult result = vkAcquireNextImageKHR(device, swapchain, std::numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
2295
2296 // Check if we need to re-create the swapchain (e.g. if the window was resized)
2297 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
2298 recreateSwapchain();
2299 swapchainOutOfDate = false;
2300 return;
2301 }
2302
2303 // && result != VK_TIMEOUT && result != VK_NOT_READY
2304 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
2305 throw runtime_error("failed to acquire swap chain image!");
2306 vulkanAvailable = false;
2307 return;
2308 }
2309
2310 // Wait for the swapchain image to be available in the color attachment stage before submitting the queue
2311 VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2312
2313 // Signal the render finished semaphore once the queue has been processed
2314 VkSubmitInfo submitInfo = VkSubmitInfo();
2315 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2316 submitInfo.waitSemaphoreCount = 1;
2317 submitInfo.pWaitSemaphores = &imageAvailableSemaphores[currentFrame];
2318 submitInfo.pWaitDstStageMask = &waitStages;
2319 submitInfo.commandBufferCount = 1;
2320 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
2321 submitInfo.signalSemaphoreCount = 1;
2322 submitInfo.pSignalSemaphores = &renderFinishedSemaphores[currentFrame];
2323
2324 vkResetFences(device, 1, &fences[currentFrame]);
2325
2326 if (vkQueueSubmit(queue, 1, &submitInfo, fences[currentFrame]) != VK_SUCCESS) {
2327 vulkanAvailable = false;
2328 return;
2329 }
2330
2331 // Wait for rendering to complete before presenting
2332 VkPresentInfoKHR presentInfo = VkPresentInfoKHR();
2333 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2334 presentInfo.waitSemaphoreCount = 1;
2335 presentInfo.pWaitSemaphores = &renderFinishedSemaphores[currentFrame];
2336 presentInfo.swapchainCount = 1;
2337 presentInfo.pSwapchains = &swapchain;
2338 presentInfo.pImageIndices = &imageIndex;
2339
2340 // Queue presentation
2341 result = vkQueuePresentKHR(queue, &presentInfo);
2342
2343 // Check if we need to re-create the swapchain (e.g. if the window was resized)
2344 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || swapchainOutOfDate) {
2345 recreateSwapchain();
2346 swapchainOutOfDate = false;
2347 } else if (result != VK_SUCCESS) {
2348 throw runtime_error("failed to present swap chain image!");
2349 vulkanAvailable = false;
2350 return;
2351 }
2352
2353 // Make sure to use the next frame's objects next frame
2354 currentFrame = (currentFrame + 1) % maxFramesInFlight;
2355 }
2356
2357 void run()
2358 {
2359 sf::Clock clock;
2360
2361 // Start game loop
2362 while (window.isOpen())
2363 {
2364 // Process events
2365 sf::Event event;
2366 while (window.pollEvent(event)) {
2367 // Close window: exit
2368 if (event.type == sf::Event::Closed)
2369 window.close();
2370
2371 // Escape key: exit
2372 if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
2373 window.close();
2374
2375 // Re-create the swapchain when the window is resized
2376 if (event.type == sf::Event::Resized)
2377 swapchainOutOfDate = true;
2378 }
2379
2380 if (vulkanAvailable) {
2381 // Update the uniform buffer (matrices)
2382 updateUniformBuffer(clock.getElapsedTime().asSeconds());
2383
2384 // Render the frame
2385 draw();
2386 }
2387 }
2388 }
2389
2390private:
2391 sf::WindowBase window;
2392
2393 bool vulkanAvailable;
2394
2395 const int maxFramesInFlight;
2396 int currentFrame;
2397 bool swapchainOutOfDate;
2398
2399 VkInstance instance;
2400 VkDebugUtilsMessengerEXT debugMessenger;
2401 VkSurfaceKHR surface;
2402 VkPhysicalDevice gpu;
2403 int queueFamilyIndex;
2404 VkDevice device;
2405 VkQueue queue;
2406 VkSurfaceFormatKHR swapchainFormat;
2407 VkExtent2D swapchainExtent;
2408 VkSwapchainKHR swapchain;
2409 std::vector<VkImage> swapchainImages;
2410 std::vector<VkImageView> swapchainImageViews;
2411 VkFormat depthFormat;
2412 VkImage depthImage;
2413 VkDeviceMemory depthImageMemory;
2414 VkImageView depthImageView;
2415 VkShaderModule vertexShaderModule;
2416 VkShaderModule fragmentShaderModule;
2417 VkPipelineShaderStageCreateInfo shaderStages[2];
2418 VkDescriptorSetLayout descriptorSetLayout;
2419 VkPipelineLayout pipelineLayout;
2420 VkRenderPass renderPass;
2421 VkPipeline graphicsPipeline;
2422 std::vector<VkFramebuffer> swapchainFramebuffers;
2423 VkCommandPool commandPool;
2424 VkBuffer vertexBuffer;
2425 VkDeviceMemory vertexBufferMemory;
2426 VkBuffer indexBuffer;
2427 VkDeviceMemory indexBufferMemory;
2428 std::vector<VkBuffer> uniformBuffers;
2429 std::vector<VkDeviceMemory> uniformBuffersMemory;
2430 VkImage textureImage;
2431 VkDeviceMemory textureImageMemory;
2432 VkImageView textureImageView;
2433 VkSampler textureSampler;
2434 VkDescriptorPool descriptorPool;
2435 std::vector<VkDescriptorSet> descriptorSets;
2436 std::vector<VkCommandBuffer> commandBuffers;
2437 std::vector<VkSemaphore> imageAvailableSemaphores;
2438 std::vector<VkSemaphore> renderFinishedSemaphores;
2439 std::vector<VkFence> fences;
2440};
2441
2442
2443////////////////////////////////////////////////////////////
2444/// Entry point of application
2445///
2446/// \return Application exit code
2447///
2448////////////////////////////////////////////////////////////
2449int main()
2450{
2451 VulkanExample example;
2452
2453 example.run();
2454
2455 return EXIT_SUCCESS;
2456}
Note: See TracBrowser for help on using the repository browser.