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