source: opengl-game/vulkan-game-ref.cpp@ fba08f2

feature/imgui-sdl points-test
Last change on this file since fba08f2 was fba08f2, checked in by Dmitry Portnoy <dmp1488@…>, 5 years ago

Update vulkan-game.cpp to support texturing in the shader

  • Property mode set to 100644
File size: 56.4 KB
Line 
1#define GLFW_INCLUDE_VULKAN
2#include <GLFW/glfw3.h>
3
4#define GLM_FORCE_RADIANS
5#include <glm/glm.hpp>
6#include <glm/gtc/matrix_transform.hpp>
7
8#define STB_IMAGE_IMPLEMENTATION
9#include <stb_image.h>
10
11#include <iostream>
12#include <fstream>
13#include <stdexcept>
14#include <algorithm>
15#include <chrono>
16#include <vector>
17#include <cstring>
18#include <cstdlib>
19#include <array>
20#include <optional>
21#include <set>
22
23const int WIDTH = 800;
24const int HEIGHT = 600;
25
26const int MAX_FRAMES_IN_FLIGHT = 2;
27
28const std::vector<const char*> validationLayers = {
29 "VK_LAYER_KHRONOS_validation"
30};
31
32const std::vector<const char*> deviceExtensions = {
33 VK_KHR_SWAPCHAIN_EXTENSION_NAME
34};
35
36#ifdef NDEBUG
37const bool enableValidationLayers = false;
38#else
39const bool enableValidationLayers = true;
40#endif
41
42VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
43 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
44 if (func != nullptr) {
45 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
46 }
47 else {
48 return VK_ERROR_EXTENSION_NOT_PRESENT;
49 }
50}
51
52void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
53 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
54 if (func != nullptr) {
55 func(instance, debugMessenger, pAllocator);
56 }
57}
58
59struct QueueFamilyIndices {
60 std::optional<uint32_t> graphicsFamily;
61 std::optional<uint32_t> presentFamily;
62
63 bool isComplete() {
64 return graphicsFamily.has_value() && presentFamily.has_value();
65 }
66};
67
68struct SwapChainSupportDetails {
69 VkSurfaceCapabilitiesKHR capabilities;
70 std::vector<VkSurfaceFormatKHR> formats;
71 std::vector<VkPresentModeKHR> presentModes;
72};
73
74struct Vertex {
75 glm::vec2 pos;
76 glm::vec3 color;
77 glm::vec2 texCoord;
78
79 static VkVertexInputBindingDescription getBindingDescription() {
80 VkVertexInputBindingDescription bindingDescription = {};
81 bindingDescription.binding = 0;
82 bindingDescription.stride = sizeof(Vertex);
83 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
84
85 return bindingDescription;
86 }
87
88 static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
89 std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions = {};
90
91 attributeDescriptions[0].binding = 0;
92 attributeDescriptions[0].location = 0;
93 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
94 attributeDescriptions[0].offset = offsetof(Vertex, pos);
95
96 attributeDescriptions[1].binding = 0;
97 attributeDescriptions[1].location = 1;
98 attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
99 attributeDescriptions[1].offset = offsetof(Vertex, color);
100
101 attributeDescriptions[2].binding = 0;
102 attributeDescriptions[2].location = 2;
103 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
104 attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
105
106 return attributeDescriptions;
107 }
108};
109
110struct UniformBufferObject {
111 alignas(16) glm::mat4 model;
112 alignas(16) glm::mat4 view;
113 alignas(16) glm::mat4 proj;
114};
115
116const std::vector<Vertex> vertices = {
117 {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
118 {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
119 {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
120 {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}
121};
122
123const std::vector<uint16_t> indices = {
124 0, 1, 2, 2, 3, 0
125};
126
127class HelloTriangleApplication {
128public:
129 void run() {
130 initWindow();
131 initVulkan();
132 mainLoop();
133 cleanup();
134 }
135
136private:
137 GLFWwindow* window;
138
139 VkInstance instance;
140 VkDebugUtilsMessengerEXT debugMessenger;
141 VkSurfaceKHR surface;
142
143 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
144 VkDevice device;
145
146 VkQueue graphicsQueue;
147 VkQueue presentQueue;
148
149 VkSwapchainKHR swapChain;
150 std::vector<VkImage> swapChainImages;
151 VkFormat swapChainImageFormat;
152 VkExtent2D swapChainExtent;
153 std::vector<VkImageView> swapChainImageViews;
154 std::vector<VkFramebuffer> swapChainFramebuffers;
155
156 VkRenderPass renderPass;
157 VkDescriptorSetLayout descriptorSetLayout;
158 VkPipelineLayout pipelineLayout;
159 VkPipeline graphicsPipeline;
160
161 VkCommandPool commandPool;
162
163 VkImage textureImage;
164 VkDeviceMemory textureImageMemory;
165 VkImageView textureImageView;
166 VkSampler textureSampler;
167
168 VkBuffer vertexBuffer;
169 VkDeviceMemory vertexBufferMemory;
170 VkBuffer indexBuffer;
171 VkDeviceMemory indexBufferMemory;
172
173 std::vector<VkBuffer> uniformBuffers;
174 std::vector<VkDeviceMemory> uniformBuffersMemory;
175
176 VkDescriptorPool descriptorPool;
177 std::vector<VkDescriptorSet> descriptorSets;
178
179 std::vector<VkCommandBuffer> commandBuffers;
180
181 std::vector<VkSemaphore> imageAvailableSemaphores;
182 std::vector<VkSemaphore> renderFinishedSemaphores;
183 std::vector<VkFence> inFlightFences;
184 size_t currentFrame = 0;
185
186 bool framebufferResized = false;
187
188 void initWindow() {
189 glfwInit();
190
191 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
192
193 window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
194 glfwSetWindowUserPointer(window, this);
195 glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
196 }
197
198 static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
199 auto app = reinterpret_cast<HelloTriangleApplication*>(glfwGetWindowUserPointer(window));
200 app->framebufferResized = true;
201 }
202
203 void initVulkan() {
204 createInstance();
205 setupDebugMessenger();
206 createSurface();
207 pickPhysicalDevice();
208 createLogicalDevice();
209 createSwapChain();
210 createImageViews();
211 createRenderPass();
212 createDescriptorSetLayout();
213 createGraphicsPipeline();
214 createFramebuffers();
215 createCommandPool();
216 createTextureImage();
217 createTextureImageView();
218 createTextureSampler();
219 createVertexBuffer();
220 createIndexBuffer();
221 createUniformBuffers();
222 createDescriptorPool();
223 createDescriptorSets();
224 createCommandBuffers();
225 createSyncObjects();
226 }
227
228 void mainLoop() {
229 while (!glfwWindowShouldClose(window)) {
230 glfwPollEvents();
231 drawFrame();
232 }
233
234 vkDeviceWaitIdle(device);
235 }
236
237 void cleanupSwapChain() {
238 for (auto framebuffer : swapChainFramebuffers) {
239 vkDestroyFramebuffer(device, framebuffer, nullptr);
240 }
241
242 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
243
244 vkDestroyPipeline(device, graphicsPipeline, nullptr);
245 vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
246 vkDestroyRenderPass(device, renderPass, nullptr);
247
248 for (auto imageView : swapChainImageViews) {
249 vkDestroyImageView(device, imageView, nullptr);
250 }
251
252 vkDestroySwapchainKHR(device, swapChain, nullptr);
253
254 for (size_t i = 0; i < swapChainImages.size(); i++) {
255 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
256 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
257 }
258
259 vkDestroyDescriptorPool(device, descriptorPool, nullptr);
260 }
261
262 void cleanup() {
263 cleanupSwapChain();
264
265 vkDestroySampler(device, textureSampler, nullptr);
266 vkDestroyImageView(device, textureImageView, nullptr);
267
268 vkDestroyImage(device, textureImage, nullptr);
269 vkFreeMemory(device, textureImageMemory, nullptr);
270
271 vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
272
273 vkDestroyBuffer(device, indexBuffer, nullptr);
274 vkFreeMemory(device, indexBufferMemory, nullptr);
275
276 vkDestroyBuffer(device, vertexBuffer, nullptr);
277 vkFreeMemory(device, vertexBufferMemory, nullptr);
278
279 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
280 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
281 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
282 vkDestroyFence(device, inFlightFences[i], nullptr);
283 }
284
285 vkDestroyCommandPool(device, commandPool, nullptr);
286
287 vkDestroyDevice(device, nullptr);
288
289 if (enableValidationLayers) {
290 DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
291 }
292
293 vkDestroySurfaceKHR(instance, surface, nullptr);
294 vkDestroyInstance(instance, nullptr);
295
296 glfwDestroyWindow(window);
297
298 glfwTerminate();
299 }
300
301 void recreateSwapChain() {
302 int width = 0, height = 0;
303 while (width == 0 || height == 0) {
304 glfwGetFramebufferSize(window, &width, &height);
305 glfwWaitEvents();
306 }
307
308 vkDeviceWaitIdle(device);
309
310 cleanupSwapChain();
311
312 createSwapChain();
313 createImageViews();
314 createRenderPass();
315 createGraphicsPipeline();
316 createFramebuffers();
317 createUniformBuffers();
318 createDescriptorPool();
319 createDescriptorSets();
320 createCommandBuffers();
321 }
322
323 void createInstance() {
324 if (enableValidationLayers && !checkValidationLayerSupport()) {
325 throw std::runtime_error("validation layers requested, but not available!");
326 }
327
328 VkApplicationInfo appInfo = {};
329 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
330 appInfo.pApplicationName = "Hello Triangle";
331 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
332 appInfo.pEngineName = "No Engine";
333 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
334 appInfo.apiVersion = VK_API_VERSION_1_0;
335
336 VkInstanceCreateInfo createInfo = {};
337 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
338 createInfo.pApplicationInfo = &appInfo;
339
340 auto extensions = getRequiredExtensions();
341 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
342 createInfo.ppEnabledExtensionNames = extensions.data();
343
344 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
345 if (enableValidationLayers) {
346 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
347 createInfo.ppEnabledLayerNames = validationLayers.data();
348
349 populateDebugMessengerCreateInfo(debugCreateInfo);
350 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)& debugCreateInfo;
351 }
352 else {
353 createInfo.enabledLayerCount = 0;
354
355 createInfo.pNext = nullptr;
356 }
357
358 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
359 throw std::runtime_error("failed to create instance!");
360 }
361 }
362
363 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
364 createInfo = {};
365 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
366 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
367 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
368 createInfo.pfnUserCallback = debugCallback;
369 }
370
371 void setupDebugMessenger() {
372 if (!enableValidationLayers) return;
373
374 VkDebugUtilsMessengerCreateInfoEXT createInfo;
375 populateDebugMessengerCreateInfo(createInfo);
376
377 if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
378 throw std::runtime_error("failed to set up debug messenger!");
379 }
380 }
381
382 void createSurface() {
383 if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
384 throw std::runtime_error("failed to create window surface!");
385 }
386 }
387
388 void pickPhysicalDevice() {
389 uint32_t deviceCount = 0;
390 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
391
392 if (deviceCount == 0) {
393 throw std::runtime_error("failed to find GPUs with Vulkan support!");
394 }
395
396 std::vector<VkPhysicalDevice> devices(deviceCount);
397 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
398
399 for (const auto& device : devices) {
400 if (isDeviceSuitable(device)) {
401 physicalDevice = device;
402 break;
403 }
404 }
405
406 if (physicalDevice == VK_NULL_HANDLE) {
407 throw std::runtime_error("failed to find a suitable GPU!");
408 }
409 }
410
411 void createLogicalDevice() {
412 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
413
414 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
415 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
416
417 float queuePriority = 1.0f;
418 for (uint32_t queueFamily : uniqueQueueFamilies) {
419 VkDeviceQueueCreateInfo queueCreateInfo = {};
420 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
421 queueCreateInfo.queueFamilyIndex = queueFamily;
422 queueCreateInfo.queueCount = 1;
423 queueCreateInfo.pQueuePriorities = &queuePriority;
424 queueCreateInfos.push_back(queueCreateInfo);
425 }
426
427 VkPhysicalDeviceFeatures deviceFeatures = {};
428 deviceFeatures.samplerAnisotropy = VK_TRUE;
429
430 VkDeviceCreateInfo createInfo = {};
431 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
432
433 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
434 createInfo.pQueueCreateInfos = queueCreateInfos.data();
435
436 createInfo.pEnabledFeatures = &deviceFeatures;
437
438 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
439 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
440
441 if (enableValidationLayers) {
442 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
443 createInfo.ppEnabledLayerNames = validationLayers.data();
444 }
445 else {
446 createInfo.enabledLayerCount = 0;
447 }
448
449 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
450 throw std::runtime_error("failed to create logical device!");
451 }
452
453 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
454 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
455 }
456
457 void createSwapChain() {
458 SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
459
460 VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
461 VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
462 VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
463
464 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
465 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
466 imageCount = swapChainSupport.capabilities.maxImageCount;
467 }
468
469 VkSwapchainCreateInfoKHR createInfo = {};
470 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
471 createInfo.surface = surface;
472
473 createInfo.minImageCount = imageCount;
474 createInfo.imageFormat = surfaceFormat.format;
475 createInfo.imageColorSpace = surfaceFormat.colorSpace;
476 createInfo.imageExtent = extent;
477 createInfo.imageArrayLayers = 1;
478 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
479
480 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
481 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
482
483 if (indices.graphicsFamily != indices.presentFamily) {
484 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
485 createInfo.queueFamilyIndexCount = 2;
486 createInfo.pQueueFamilyIndices = queueFamilyIndices;
487 }
488 else {
489 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
490 }
491
492 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
493 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
494 createInfo.presentMode = presentMode;
495 createInfo.clipped = VK_TRUE;
496
497 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
498 throw std::runtime_error("failed to create swap chain!");
499 }
500
501 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
502 swapChainImages.resize(imageCount);
503 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
504
505 swapChainImageFormat = surfaceFormat.format;
506 swapChainExtent = extent;
507 }
508
509 void createImageViews() {
510 swapChainImageViews.resize(swapChainImages.size());
511
512 for (size_t i = 0; i < swapChainImages.size(); i++) {
513 swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
514 }
515 }
516
517 void createRenderPass() {
518 VkAttachmentDescription colorAttachment = {};
519 colorAttachment.format = swapChainImageFormat;
520 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
521 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
522 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
523 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
524 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
525 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
526 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
527
528 VkAttachmentReference colorAttachmentRef = {};
529 colorAttachmentRef.attachment = 0;
530 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
531
532 VkSubpassDescription subpass = {};
533 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
534 subpass.colorAttachmentCount = 1;
535 subpass.pColorAttachments = &colorAttachmentRef;
536
537 VkSubpassDependency dependency = {};
538 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
539 dependency.dstSubpass = 0;
540 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
541 dependency.srcAccessMask = 0;
542 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
543 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
544
545 VkRenderPassCreateInfo renderPassInfo = {};
546 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
547 renderPassInfo.attachmentCount = 1;
548 renderPassInfo.pAttachments = &colorAttachment;
549 renderPassInfo.subpassCount = 1;
550 renderPassInfo.pSubpasses = &subpass;
551 renderPassInfo.dependencyCount = 1;
552 renderPassInfo.pDependencies = &dependency;
553
554 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
555 throw std::runtime_error("failed to create render pass!");
556 }
557 }
558
559 void createDescriptorSetLayout() {
560 VkDescriptorSetLayoutBinding uboLayoutBinding = {};
561 uboLayoutBinding.binding = 0;
562 uboLayoutBinding.descriptorCount = 1;
563 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
564 uboLayoutBinding.pImmutableSamplers = nullptr;
565 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
566
567 VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
568 samplerLayoutBinding.binding = 1;
569 samplerLayoutBinding.descriptorCount = 1;
570 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
571 samplerLayoutBinding.pImmutableSamplers = nullptr;
572 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
573
574 std::array<VkDescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
575 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
576 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
577 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
578 layoutInfo.pBindings = bindings.data();
579
580 if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
581 throw std::runtime_error("failed to create descriptor set layout!");
582 }
583 }
584
585 void createGraphicsPipeline() {
586 auto vertShaderCode = readFile("shaders/vert.spv");
587 auto fragShaderCode = readFile("shaders/frag.spv");
588
589 VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
590 VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
591
592 VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
593 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
594 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
595 vertShaderStageInfo.module = vertShaderModule;
596 vertShaderStageInfo.pName = "main";
597
598 VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
599 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
600 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
601 fragShaderStageInfo.module = fragShaderModule;
602 fragShaderStageInfo.pName = "main";
603
604 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
605
606 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
607 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
608
609 auto bindingDescription = Vertex::getBindingDescription();
610 auto attributeDescriptions = Vertex::getAttributeDescriptions();
611
612 vertexInputInfo.vertexBindingDescriptionCount = 1;
613 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
614 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
615 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
616
617 VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
618 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
619 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
620 inputAssembly.primitiveRestartEnable = VK_FALSE;
621
622 VkViewport viewport = {};
623 viewport.x = 0.0f;
624 viewport.y = 0.0f;
625 viewport.width = (float)swapChainExtent.width;
626 viewport.height = (float)swapChainExtent.height;
627 viewport.minDepth = 0.0f;
628 viewport.maxDepth = 1.0f;
629
630 VkRect2D scissor = {};
631 scissor.offset = { 0, 0 };
632 scissor.extent = swapChainExtent;
633
634 VkPipelineViewportStateCreateInfo viewportState = {};
635 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
636 viewportState.viewportCount = 1;
637 viewportState.pViewports = &viewport;
638 viewportState.scissorCount = 1;
639 viewportState.pScissors = &scissor;
640
641 VkPipelineRasterizationStateCreateInfo rasterizer = {};
642 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
643 rasterizer.depthClampEnable = VK_FALSE;
644 rasterizer.rasterizerDiscardEnable = VK_FALSE;
645 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
646 rasterizer.lineWidth = 1.0f;
647 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
648 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
649 rasterizer.depthBiasEnable = VK_FALSE;
650
651 VkPipelineMultisampleStateCreateInfo multisampling = {};
652 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
653 multisampling.sampleShadingEnable = VK_FALSE;
654 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
655
656 VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
657 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
658 colorBlendAttachment.blendEnable = VK_FALSE;
659
660 VkPipelineColorBlendStateCreateInfo colorBlending = {};
661 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
662 colorBlending.logicOpEnable = VK_FALSE;
663 colorBlending.logicOp = VK_LOGIC_OP_COPY;
664 colorBlending.attachmentCount = 1;
665 colorBlending.pAttachments = &colorBlendAttachment;
666 colorBlending.blendConstants[0] = 0.0f;
667 colorBlending.blendConstants[1] = 0.0f;
668 colorBlending.blendConstants[2] = 0.0f;
669 colorBlending.blendConstants[3] = 0.0f;
670
671 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
672 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
673 pipelineLayoutInfo.setLayoutCount = 1;
674 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
675
676 if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
677 throw std::runtime_error("failed to create pipeline layout!");
678 }
679
680 VkGraphicsPipelineCreateInfo pipelineInfo = {};
681 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
682 pipelineInfo.stageCount = 2;
683 pipelineInfo.pStages = shaderStages;
684 pipelineInfo.pVertexInputState = &vertexInputInfo;
685 pipelineInfo.pInputAssemblyState = &inputAssembly;
686 pipelineInfo.pViewportState = &viewportState;
687 pipelineInfo.pRasterizationState = &rasterizer;
688 pipelineInfo.pMultisampleState = &multisampling;
689 pipelineInfo.pColorBlendState = &colorBlending;
690 pipelineInfo.layout = pipelineLayout;
691 pipelineInfo.renderPass = renderPass;
692 pipelineInfo.subpass = 0;
693 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
694
695 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
696 throw std::runtime_error("failed to create graphics pipeline!");
697 }
698
699 vkDestroyShaderModule(device, fragShaderModule, nullptr);
700 vkDestroyShaderModule(device, vertShaderModule, nullptr);
701 }
702
703 void createFramebuffers() {
704 swapChainFramebuffers.resize(swapChainImageViews.size());
705
706 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
707 VkImageView attachments[] = {
708 swapChainImageViews[i]
709 };
710
711 VkFramebufferCreateInfo framebufferInfo = {};
712 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
713 framebufferInfo.renderPass = renderPass;
714 framebufferInfo.attachmentCount = 1;
715 framebufferInfo.pAttachments = attachments;
716 framebufferInfo.width = swapChainExtent.width;
717 framebufferInfo.height = swapChainExtent.height;
718 framebufferInfo.layers = 1;
719
720 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
721 throw std::runtime_error("failed to create framebuffer!");
722 }
723 }
724 }
725
726 void createCommandPool() {
727 QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
728
729 VkCommandPoolCreateInfo poolInfo = {};
730 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
731 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
732
733 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
734 throw std::runtime_error("failed to create graphics command pool!");
735 }
736 }
737
738 void createTextureImage() {
739 int texWidth, texHeight, texChannels;
740 stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
741 VkDeviceSize imageSize = texWidth * texHeight * 4;
742
743 if (!pixels) {
744 throw std::runtime_error("failed to load texture image!");
745 }
746
747 VkBuffer stagingBuffer;
748 VkDeviceMemory stagingBufferMemory;
749 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
750
751 void* data;
752 vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);
753 memcpy(data, pixels, static_cast<size_t>(imageSize));
754 vkUnmapMemory(device, stagingBufferMemory);
755
756 stbi_image_free(pixels);
757
758 createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
759
760 transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
761 copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
762 transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
763
764 vkDestroyBuffer(device, stagingBuffer, nullptr);
765 vkFreeMemory(device, stagingBufferMemory, nullptr);
766 }
767
768 void createTextureImageView() {
769 textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM);
770 }
771
772 void createTextureSampler() {
773 VkSamplerCreateInfo samplerInfo = {};
774 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
775 samplerInfo.magFilter = VK_FILTER_LINEAR;
776 samplerInfo.minFilter = VK_FILTER_LINEAR;
777 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
778 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
779 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
780 samplerInfo.anisotropyEnable = VK_TRUE;
781 samplerInfo.maxAnisotropy = 16;
782 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
783 samplerInfo.unnormalizedCoordinates = VK_FALSE;
784 samplerInfo.compareEnable = VK_FALSE;
785 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
786 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
787
788 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
789 throw std::runtime_error("failed to create texture sampler!");
790 }
791 }
792
793 VkImageView createImageView(VkImage image, VkFormat format) {
794 VkImageViewCreateInfo viewInfo = {};
795 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
796 viewInfo.image = image;
797 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
798 viewInfo.format = format;
799 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
800 viewInfo.subresourceRange.baseMipLevel = 0;
801 viewInfo.subresourceRange.levelCount = 1;
802 viewInfo.subresourceRange.baseArrayLayer = 0;
803 viewInfo.subresourceRange.layerCount = 1;
804
805 VkImageView imageView;
806 if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
807 throw std::runtime_error("failed to create texture image view!");
808 }
809
810 return imageView;
811 }
812
813 void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
814 VkImageCreateInfo imageInfo = {};
815 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
816 imageInfo.imageType = VK_IMAGE_TYPE_2D;
817 imageInfo.extent.width = width;
818 imageInfo.extent.height = height;
819 imageInfo.extent.depth = 1;
820 imageInfo.mipLevels = 1;
821 imageInfo.arrayLayers = 1;
822 imageInfo.format = format;
823 imageInfo.tiling = tiling;
824 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
825 imageInfo.usage = usage;
826 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
827 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
828
829 if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
830 throw std::runtime_error("failed to create image!");
831 }
832
833 VkMemoryRequirements memRequirements;
834 vkGetImageMemoryRequirements(device, image, &memRequirements);
835
836 VkMemoryAllocateInfo allocInfo = {};
837 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
838 allocInfo.allocationSize = memRequirements.size;
839 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
840
841 if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
842 throw std::runtime_error("failed to allocate image memory!");
843 }
844
845 vkBindImageMemory(device, image, imageMemory, 0);
846 }
847
848 void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
849 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
850
851 VkImageMemoryBarrier barrier = {};
852 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
853 barrier.oldLayout = oldLayout;
854 barrier.newLayout = newLayout;
855 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
856 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
857 barrier.image = image;
858 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
859 barrier.subresourceRange.baseMipLevel = 0;
860 barrier.subresourceRange.levelCount = 1;
861 barrier.subresourceRange.baseArrayLayer = 0;
862 barrier.subresourceRange.layerCount = 1;
863
864 VkPipelineStageFlags sourceStage;
865 VkPipelineStageFlags destinationStage;
866
867 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
868 barrier.srcAccessMask = 0;
869 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
870
871 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
872 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
873 }
874 else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
875 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
876 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
877
878 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
879 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
880 }
881 else {
882 throw std::invalid_argument("unsupported layout transition!");
883 }
884
885 vkCmdPipelineBarrier(
886 commandBuffer,
887 sourceStage, destinationStage,
888 0,
889 0, nullptr,
890 0, nullptr,
891 1, &barrier
892 );
893
894 endSingleTimeCommands(commandBuffer);
895 }
896
897 void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
898 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
899
900 VkBufferImageCopy region = {};
901 region.bufferOffset = 0;
902 region.bufferRowLength = 0;
903 region.bufferImageHeight = 0;
904 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
905 region.imageSubresource.mipLevel = 0;
906 region.imageSubresource.baseArrayLayer = 0;
907 region.imageSubresource.layerCount = 1;
908 region.imageOffset = { 0, 0, 0 };
909 region.imageExtent = {
910 width,
911 height,
912 1
913 };
914
915 vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
916
917 endSingleTimeCommands(commandBuffer);
918 }
919
920 void createVertexBuffer() {
921 VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
922
923 VkBuffer stagingBuffer;
924 VkDeviceMemory stagingBufferMemory;
925 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
926
927 void* data;
928 vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
929 memcpy(data, vertices.data(), (size_t)bufferSize);
930 vkUnmapMemory(device, stagingBufferMemory);
931
932 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
933
934 copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
935
936 vkDestroyBuffer(device, stagingBuffer, nullptr);
937 vkFreeMemory(device, stagingBufferMemory, nullptr);
938 }
939
940 void createIndexBuffer() {
941 VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
942
943 VkBuffer stagingBuffer;
944 VkDeviceMemory stagingBufferMemory;
945 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
946
947 void* data;
948 vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
949 memcpy(data, indices.data(), (size_t)bufferSize);
950 vkUnmapMemory(device, stagingBufferMemory);
951
952 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
953
954 copyBuffer(stagingBuffer, indexBuffer, bufferSize);
955
956 vkDestroyBuffer(device, stagingBuffer, nullptr);
957 vkFreeMemory(device, stagingBufferMemory, nullptr);
958 }
959
960 void createUniformBuffers() {
961 VkDeviceSize bufferSize = sizeof(UniformBufferObject);
962
963 uniformBuffers.resize(swapChainImages.size());
964 uniformBuffersMemory.resize(swapChainImages.size());
965
966 for (size_t i = 0; i < swapChainImages.size(); i++) {
967 createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]);
968 }
969 }
970
971 void createDescriptorPool() {
972 std::array<VkDescriptorPoolSize, 2> poolSizes = {};
973 poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
974 poolSizes[0].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
975 poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
976 poolSizes[1].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
977
978 VkDescriptorPoolCreateInfo poolInfo = {};
979 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
980 poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
981 poolInfo.pPoolSizes = poolSizes.data();
982 poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());
983
984 if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
985 throw std::runtime_error("failed to create descriptor pool!");
986 }
987 }
988
989 void createDescriptorSets() {
990 std::vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), descriptorSetLayout);
991 VkDescriptorSetAllocateInfo allocInfo = {};
992 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
993 allocInfo.descriptorPool = descriptorPool;
994 allocInfo.descriptorSetCount = static_cast<uint32_t>(swapChainImages.size());
995 allocInfo.pSetLayouts = layouts.data();
996
997 descriptorSets.resize(swapChainImages.size());
998 if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {
999 throw std::runtime_error("failed to allocate descriptor sets!");
1000 }
1001
1002 for (size_t i = 0; i < swapChainImages.size(); i++) {
1003 VkDescriptorBufferInfo bufferInfo = {};
1004 bufferInfo.buffer = uniformBuffers[i];
1005 bufferInfo.offset = 0;
1006 bufferInfo.range = sizeof(UniformBufferObject);
1007
1008 VkDescriptorImageInfo imageInfo = {};
1009 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1010 imageInfo.imageView = textureImageView;
1011 imageInfo.sampler = textureSampler;
1012
1013 std::array<VkWriteDescriptorSet, 2> descriptorWrites = {};
1014
1015 descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1016 descriptorWrites[0].dstSet = descriptorSets[i];
1017 descriptorWrites[0].dstBinding = 0;
1018 descriptorWrites[0].dstArrayElement = 0;
1019 descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1020 descriptorWrites[0].descriptorCount = 1;
1021 descriptorWrites[0].pBufferInfo = &bufferInfo;
1022
1023 descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1024 descriptorWrites[1].dstSet = descriptorSets[i];
1025 descriptorWrites[1].dstBinding = 1;
1026 descriptorWrites[1].dstArrayElement = 0;
1027 descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1028 descriptorWrites[1].descriptorCount = 1;
1029 descriptorWrites[1].pImageInfo = &imageInfo;
1030
1031 vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
1032 }
1033 }
1034
1035 void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
1036 VkBufferCreateInfo bufferInfo = {};
1037 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1038 bufferInfo.size = size;
1039 bufferInfo.usage = usage;
1040 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1041
1042 if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
1043 throw std::runtime_error("failed to create buffer!");
1044 }
1045
1046 VkMemoryRequirements memRequirements;
1047 vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
1048
1049 VkMemoryAllocateInfo allocInfo = {};
1050 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1051 allocInfo.allocationSize = memRequirements.size;
1052 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
1053
1054 if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
1055 throw std::runtime_error("failed to allocate buffer memory!");
1056 }
1057
1058 vkBindBufferMemory(device, buffer, bufferMemory, 0);
1059 }
1060
1061 VkCommandBuffer beginSingleTimeCommands() {
1062 VkCommandBufferAllocateInfo allocInfo = {};
1063 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1064 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1065 allocInfo.commandPool = commandPool;
1066 allocInfo.commandBufferCount = 1;
1067
1068 VkCommandBuffer commandBuffer;
1069 vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
1070
1071 VkCommandBufferBeginInfo beginInfo = {};
1072 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1073 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1074
1075 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1076
1077 return commandBuffer;
1078 }
1079
1080 void endSingleTimeCommands(VkCommandBuffer commandBuffer) {
1081 vkEndCommandBuffer(commandBuffer);
1082
1083 VkSubmitInfo submitInfo = {};
1084 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1085 submitInfo.commandBufferCount = 1;
1086 submitInfo.pCommandBuffers = &commandBuffer;
1087
1088 vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1089 vkQueueWaitIdle(graphicsQueue);
1090
1091 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1092 }
1093
1094 void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
1095 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
1096
1097 VkBufferCopy copyRegion = {};
1098 copyRegion.size = size;
1099 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
1100
1101 endSingleTimeCommands(commandBuffer);
1102 }
1103
1104 uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
1105 VkPhysicalDeviceMemoryProperties memProperties;
1106 vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
1107
1108 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
1109 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
1110 return i;
1111 }
1112 }
1113
1114 throw std::runtime_error("failed to find suitable memory type!");
1115 }
1116
1117 void createCommandBuffers() {
1118 commandBuffers.resize(swapChainFramebuffers.size());
1119
1120 VkCommandBufferAllocateInfo allocInfo = {};
1121 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1122 allocInfo.commandPool = commandPool;
1123 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1124 allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();
1125
1126 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
1127 throw std::runtime_error("failed to allocate command buffers!");
1128 }
1129
1130 for (size_t i = 0; i < commandBuffers.size(); i++) {
1131 VkCommandBufferBeginInfo beginInfo = {};
1132 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1133 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
1134
1135 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
1136 throw std::runtime_error("failed to begin recording command buffer!");
1137 }
1138
1139 VkRenderPassBeginInfo renderPassInfo = {};
1140 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1141 renderPassInfo.renderPass = renderPass;
1142 renderPassInfo.framebuffer = swapChainFramebuffers[i];
1143 renderPassInfo.renderArea.offset = { 0, 0 };
1144 renderPassInfo.renderArea.extent = swapChainExtent;
1145
1146 VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
1147 renderPassInfo.clearValueCount = 1;
1148 renderPassInfo.pClearValues = &clearColor;
1149
1150 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1151
1152 vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
1153
1154 VkBuffer vertexBuffers[] = { vertexBuffer };
1155 VkDeviceSize offsets[] = { 0 };
1156 vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
1157
1158 vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);
1159
1160 vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i], 0, nullptr);
1161
1162 vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
1163
1164 vkCmdEndRenderPass(commandBuffers[i]);
1165
1166 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
1167 throw std::runtime_error("failed to record command buffer!");
1168 }
1169 }
1170 }
1171
1172 void createSyncObjects() {
1173 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1174 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1175 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1176
1177 VkSemaphoreCreateInfo semaphoreInfo = {};
1178 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1179
1180 VkFenceCreateInfo fenceInfo = {};
1181 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1182 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1183
1184 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1185 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1186 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
1187 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1188 throw std::runtime_error("failed to create synchronization objects for a frame!");
1189 }
1190 }
1191 }
1192
1193 void updateUniformBuffer(uint32_t currentImage) {
1194 static auto startTime = std::chrono::high_resolution_clock::now();
1195
1196 auto currentTime = std::chrono::high_resolution_clock::now();
1197 float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
1198
1199 UniformBufferObject ubo = {};
1200 ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
1201 ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
1202 ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f);
1203 ubo.proj[1][1] *= -1;
1204
1205 void* data;
1206 vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, &data);
1207 memcpy(data, &ubo, sizeof(ubo));
1208 vkUnmapMemory(device, uniformBuffersMemory[currentImage]);
1209 }
1210
1211 void drawFrame() {
1212 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());
1213
1214 uint32_t imageIndex;
1215 VkResult result = vkAcquireNextImageKHR(device, swapChain, std::numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
1216
1217 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1218 recreateSwapChain();
1219 return;
1220 }
1221 else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
1222 throw std::runtime_error("failed to acquire swap chain image!");
1223 }
1224
1225 updateUniformBuffer(imageIndex);
1226
1227 VkSubmitInfo submitInfo = {};
1228 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1229
1230 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
1231 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1232 submitInfo.waitSemaphoreCount = 1;
1233 submitInfo.pWaitSemaphores = waitSemaphores;
1234 submitInfo.pWaitDstStageMask = waitStages;
1235
1236 submitInfo.commandBufferCount = 1;
1237 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
1238
1239 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
1240 submitInfo.signalSemaphoreCount = 1;
1241 submitInfo.pSignalSemaphores = signalSemaphores;
1242
1243 vkResetFences(device, 1, &inFlightFences[currentFrame]);
1244
1245 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
1246 throw std::runtime_error("failed to submit draw command buffer!");
1247 }
1248
1249 VkPresentInfoKHR presentInfo = {};
1250 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1251
1252 presentInfo.waitSemaphoreCount = 1;
1253 presentInfo.pWaitSemaphores = signalSemaphores;
1254
1255 VkSwapchainKHR swapChains[] = { swapChain };
1256 presentInfo.swapchainCount = 1;
1257 presentInfo.pSwapchains = swapChains;
1258
1259 presentInfo.pImageIndices = &imageIndex;
1260
1261 result = vkQueuePresentKHR(presentQueue, &presentInfo);
1262
1263 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
1264 framebufferResized = false;
1265 recreateSwapChain();
1266 }
1267 else if (result != VK_SUCCESS) {
1268 throw std::runtime_error("failed to present swap chain image!");
1269 }
1270
1271 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1272 }
1273
1274 VkShaderModule createShaderModule(const std::vector<char>& code) {
1275 VkShaderModuleCreateInfo createInfo = {};
1276 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1277 createInfo.codeSize = code.size();
1278 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
1279
1280 VkShaderModule shaderModule;
1281 if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
1282 throw std::runtime_error("failed to create shader module!");
1283 }
1284
1285 return shaderModule;
1286 }
1287
1288 VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
1289 for (const auto& availableFormat : availableFormats) {
1290 if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
1291 return availableFormat;
1292 }
1293 }
1294
1295 return availableFormats[0];
1296 }
1297
1298 VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
1299 VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
1300
1301 for (const auto& availablePresentMode : availablePresentModes) {
1302 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
1303 return availablePresentMode;
1304 }
1305 else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
1306 bestMode = availablePresentMode;
1307 }
1308 }
1309
1310 return bestMode;
1311 }
1312
1313 VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
1314 if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
1315 return capabilities.currentExtent;
1316 }
1317 else {
1318 int width, height;
1319 glfwGetFramebufferSize(window, &width, &height);
1320
1321 VkExtent2D actualExtent = {
1322 static_cast<uint32_t>(width),
1323 static_cast<uint32_t>(height)
1324 };
1325
1326 actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));
1327 actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height));
1328
1329 return actualExtent;
1330 }
1331 }
1332
1333 SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
1334 SwapChainSupportDetails details;
1335
1336 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
1337
1338 uint32_t formatCount;
1339 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
1340
1341 if (formatCount != 0) {
1342 details.formats.resize(formatCount);
1343 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
1344 }
1345
1346 uint32_t presentModeCount;
1347 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
1348
1349 if (presentModeCount != 0) {
1350 details.presentModes.resize(presentModeCount);
1351 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
1352 }
1353
1354 return details;
1355 }
1356
1357 bool isDeviceSuitable(VkPhysicalDevice device) {
1358 QueueFamilyIndices indices = findQueueFamilies(device);
1359
1360 bool extensionsSupported = checkDeviceExtensionSupport(device);
1361
1362 bool swapChainAdequate = false;
1363 if (extensionsSupported) {
1364 SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
1365 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
1366 }
1367
1368 VkPhysicalDeviceFeatures supportedFeatures;
1369 vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
1370
1371 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
1372 }
1373
1374 bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
1375 uint32_t extensionCount;
1376 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
1377
1378 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
1379 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
1380
1381 std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
1382
1383 for (const auto& extension : availableExtensions) {
1384 requiredExtensions.erase(extension.extensionName);
1385 }
1386
1387 return requiredExtensions.empty();
1388 }
1389
1390 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
1391 QueueFamilyIndices indices;
1392
1393 uint32_t queueFamilyCount = 0;
1394 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
1395
1396 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
1397 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
1398
1399 int i = 0;
1400 for (const auto& queueFamily : queueFamilies) {
1401 if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
1402 indices.graphicsFamily = i;
1403 }
1404
1405 VkBool32 presentSupport = false;
1406 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
1407
1408 if (queueFamily.queueCount > 0 && presentSupport) {
1409 indices.presentFamily = i;
1410 }
1411
1412 if (indices.isComplete()) {
1413 break;
1414 }
1415
1416 i++;
1417 }
1418
1419 return indices;
1420 }
1421
1422 std::vector<const char*> getRequiredExtensions() {
1423 uint32_t glfwExtensionCount = 0;
1424 const char** glfwExtensions;
1425 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
1426
1427 std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
1428
1429 if (enableValidationLayers) {
1430 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
1431 }
1432
1433 return extensions;
1434 }
1435
1436 bool checkValidationLayerSupport() {
1437 uint32_t layerCount;
1438 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
1439
1440 std::vector<VkLayerProperties> availableLayers(layerCount);
1441 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
1442
1443 for (const char* layerName : validationLayers) {
1444 bool layerFound = false;
1445
1446 for (const auto& layerProperties : availableLayers) {
1447 if (strcmp(layerName, layerProperties.layerName) == 0) {
1448 layerFound = true;
1449 break;
1450 }
1451 }
1452
1453 if (!layerFound) {
1454 return false;
1455 }
1456 }
1457
1458 return true;
1459 }
1460
1461 static std::vector<char> readFile(const std::string& filename) {
1462 std::ifstream file(filename, std::ios::ate | std::ios::binary);
1463
1464 if (!file.is_open()) {
1465 throw std::runtime_error("failed to open file!");
1466 }
1467
1468 size_t fileSize = (size_t)file.tellg();
1469 std::vector<char> buffer(fileSize);
1470
1471 file.seekg(0);
1472 file.read(buffer.data(), fileSize);
1473
1474 file.close();
1475
1476 return buffer;
1477 }
1478
1479 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
1480 std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
1481
1482 return VK_FALSE;
1483 }
1484};
1485
1486int main() {
1487 HelloTriangleApplication app;
1488
1489 try {
1490 app.run();
1491 }
1492 catch (const std::exception& e) {
1493 std::cerr << e.what() << std::endl;
1494 return EXIT_FAILURE;
1495 }
1496
1497 return EXIT_SUCCESS;
1498}
Note: See TracBrowser for help on using the repository browser.