libmx2 2.2.1
C++20 multimedia and game-engine library (SDL2 / OpenGL / Vulkan)
Loading...
Searching...
No Matches
vk.cpp
Go to the documentation of this file.
1/**
2 * @file vk.cpp
3 * @brief Implementation of mx::VKWindow Vulkan window and Vulkan initialisation helpers.
4 */
5#include"vk.hpp"
6#include"loadpng.hpp"
7
8namespace mx {
9
10 VKWindow::VKWindow(const std::string &title, int width, int height, bool full, bool valid) {
11 enableValidation = valid;
12 initWindow(title, width, height, full);
13 }
14
15 void VKWindow::initWindow(const std::string &title, int width, int height, bool full) {
16 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
17 throw mx::Exception("SDL_Init: Failure: " + std::string(SDL_GetError()));
18 }
19 if(full)
20 window = SDL_CreateWindow(title.c_str(),SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,width, height,SDL_WINDOW_VULKAN | SDL_WINDOW_FULLSCREEN_DESKTOP);
21 else
22 window = SDL_CreateWindow(title.c_str(),SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,width, height,SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
23
24 if (!window) {
25 throw mx::Exception("failure to create window: " + std::string(SDL_GetError()));
26 }
27 SDL_Vulkan_GetDrawableSize(window, &w, &h);
28 std::cout << ">> [Window] Actual drawable size: " << w << "x" << h << std::endl;
29 }
30
32 active = false;
33 }
34
35 void VKWindow::setFont(const std::string &font, const int size) {
36 this->font = font;
37 this->font_size = size;
38 if (textRenderer) {
39 textRenderer->setFont(util.getFilePath(font), size);
40 }
41 }
42
45 return true;
46 }
47 const Uint32 controllerFlags = SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
48 if ((SDL_WasInit(controllerFlags) & controllerFlags) != controllerFlags) {
49 if (SDL_InitSubSystem(controllerFlags) != 0) {
50 std::cerr << "Warning: SDL controller subsystem init failed: " << SDL_GetError() << std::endl;
51 return false;
52 }
53 }
54 SDL_GameControllerEventState(SDL_ENABLE);
55 SDL_JoystickEventState(SDL_ENABLE);
57 return true;
58 }
59
82
83 try {
86 textRenderer->setDescriptorSetLayout(textDescriptorSetLayout);
89 } catch (const std::exception& e) {
90 std::cerr << "Warning: Text rendering initialization failed: " << e.what() << std::endl;
91 std::cerr << "Text rendering will be disabled." << std::endl;
92 textRenderer = nullptr;
93 }
94 }
95
96 void VKWindow::updateTexture(SDL_Surface* newSurface) {
97 VkDeviceSize imageSize = newSurface->w * newSurface->h * 4;
98 updateTexture(newSurface->pixels, imageSize);
99 }
100
102 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
103 samplerLayoutBinding.binding = 0;
104 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
105 samplerLayoutBinding.descriptorCount = 1;
106 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
107
108 VkDescriptorSetLayoutBinding uboLayoutBinding{};
109 uboLayoutBinding.binding = 1;
110 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
111 uboLayoutBinding.descriptorCount = 1;
112 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
113
114 std::array<VkDescriptorSetLayoutBinding, 2> bindings = {samplerLayoutBinding, uboLayoutBinding};
115
116 VkDescriptorSetLayoutCreateInfo layoutInfo{};
117 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
118 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
119 layoutInfo.pBindings = bindings.data();
120
121 if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
122 throw mx::Exception("Failed to create descriptor set layout!");
123 }
124
125 std::cout << ">> [DescriptorSetLayout] Created with 2 bindings:\n";
126 std::cout << " Binding 0: Combined Image Sampler (texture)\n";
127 std::cout << " Binding 1: Uniform Buffer (time, tint color)\n";
128 }
129
130 void VKWindow::setPath(const std::string &path) {
131 util.path = path;
132 }
133
134 bool VKWindow::isDeviceSuitable(VkPhysicalDevice device) {
136
137 bool extensionsSupported = false;
138 const std::vector<const char*> deviceExtensions = {
139 VK_KHR_SWAPCHAIN_EXTENSION_NAME
140 };
141
142 uint32_t extensionCount;
143 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
144 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
145 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
146
147 std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
148 for (const auto& extension : availableExtensions) {
149 requiredExtensions.erase(extension.extensionName);
150 }
151 extensionsSupported = requiredExtensions.empty();
152
153 bool swapChainAdequate = false;
154 if (extensionsSupported) {
156 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
157 }
158
159 return indices.isComplete() && extensionsSupported && swapChainAdequate;
160 }
161
162 VkShaderModule VKWindow::createShaderModule(const std::vector<char>& code) {
163 VkShaderModuleCreateInfo createInfo{};
164 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
165 createInfo.codeSize = code.size();
166 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
167 VkShaderModule shaderModule;
168 if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
169 throw mx::Exception("Failed to create shader module!");
170 }
171 return shaderModule;
172 }
173
175 QueueFamilyIndices indices;
176 uint32_t queueFamilyCount = 0;
177 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
178 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
179 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
180
181 int i = 0;
182 for (const auto& queueFamily : queueFamilies) {
183 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
184 indices.graphicsFamily = i;
185 }
186 VkBool32 presentSupport = false;
187 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
188 if (presentSupport) {
189 indices.presentFamily = i;
190 }
191 if (indices.isComplete()) {
192 break;
193 }
194 i++;
195 }
196 return indices;
197 }
198
199 VkSurfaceFormatKHR VKWindow::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
200 if (availableFormats.empty()) {
201 throw mx::Exception("No available surface formats!");
202 }
203 for (const auto& availableFormat : availableFormats) {
204 if (availableFormat.format == VK_FORMAT_R8G8B8A8_UNORM &&
205 availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
206 return availableFormat;
207 }
208 }
209 return availableFormats[0];
210 }
211
212 VkPresentModeKHR VKWindow::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
213 for (const auto& availablePresentMode : availablePresentModes) {
214 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
215 return availablePresentMode;
216 }
217 }
218 return VK_PRESENT_MODE_FIFO_KHR;
219 }
220
221 VkExtent2D VKWindow::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
222 if (capabilities.currentExtent.width != UINT32_MAX) {
223 return capabilities.currentExtent;
224 } else {
225 VkExtent2D actualExtent = { static_cast<uint32_t>(w), static_cast<uint32_t>(h) };
226 actualExtent.width = std::max(capabilities.minImageExtent.width,
227 std::min(capabilities.maxImageExtent.width, actualExtent.width));
228 actualExtent.height = std::max(capabilities.minImageExtent.height,
229 std::min(capabilities.maxImageExtent.height, actualExtent.height));
230 return actualExtent;
231 }
232 }
233
235 SDL_Event e;
236 while (active) {
237 while (SDL_PollEvent(&e)) {
238 event(e);
239 }
240 proc();
241 draw();
242 }
243 }
245
246 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
247 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
248 VkDebugUtilsMessageTypeFlagsEXT messageType,
249 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
250 void* pUserData) {
251 if (pCallbackData && pCallbackData->pMessage &&
252 std::strstr(pCallbackData->pMessage, "VK_LAYER_VKBASALT_post_processing") &&
253 std::strstr(pCallbackData->pMessage, "duplicate")) {
254 return VK_FALSE;
255 }
256 std::cerr << "[Vulkan Validation] " << pCallbackData->pMessage << std::endl;
257 return VK_FALSE;
258 }
259
261#ifndef WITH_MOLTEN
262 if (volkInitialize() != VK_SUCCESS) {
263 throw mx::Exception("Failed to initialize Volk!");
264 }
265#endif
266
267 VkApplicationInfo appInfo{};
268 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
269 appInfo.pApplicationName = "VulkanApp";
270 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
271 appInfo.apiVersion = VK_API_VERSION_1_3;
272
273 unsigned int sdlExtensionCount = 0;
274 SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, nullptr);
275 std::vector<const char*> extensions(sdlExtensionCount);
276 SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, extensions.data());
277
278 const std::vector<const char*> validationLayers = {
279 "VK_LAYER_KHRONOS_validation"
280 };
281 std::vector<const char*> debugExtensions = {
282 VK_EXT_DEBUG_UTILS_EXTENSION_NAME
283 };
284
285
286 if (enableValidation) {
287 extensions.insert(extensions.end(), debugExtensions.begin(), debugExtensions.end());
288 }
289
290#ifdef WITH_MOLTEN
291
292 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
293#endif
294
295 bool layersSupported = true;
296 uint32_t layerCount;
297 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
298 std::vector<VkLayerProperties> availableLayers(layerCount);
299 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
300
301 for (const char* layerName : validationLayers) {
302 bool layerFound = false;
303 for (const auto& layer : availableLayers) {
304 if (strcmp(layerName, layer.layerName) == 0) {
305 layerFound = true;
306 break;
307 }
308 }
309 if (!layerFound) {
310 layersSupported = false;
311 break;
312 }
313 }
314
315 VkInstanceCreateInfo createInfo{};
316 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
317 createInfo.pApplicationInfo = &appInfo;
318 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
319 createInfo.ppEnabledExtensionNames = extensions.data();
320
321#ifdef WITH_MOLTEN
322 createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
323#endif
324
325 if (layersSupported && enableValidation) {
326 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
327 createInfo.ppEnabledLayerNames = validationLayers.data();
328 }
329 else {
330 createInfo.enabledLayerCount = 0;
331 }
332
333 VkDebugUtilsMessengerCreateInfoEXT messengerInfo{};
334
335 if (enableValidation) {
336 messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
337 messengerInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
338 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
339 messengerInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
340 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
341 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
342 messengerInfo.pfnUserCallback = debugCallback;
343 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &messengerInfo;
344 }
345
346 VK_CHECK_RESULT(vkCreateInstance(&createInfo, nullptr, &instance));
347
348 uint32_t instanceVersion = 0;
349 vkEnumerateInstanceVersion(&instanceVersion);
350 std::cout << "Vulkan Instance Version: "
351 << VK_VERSION_MAJOR(instanceVersion) << "."
352 << VK_VERSION_MINOR(instanceVersion) << "."
353 << VK_VERSION_PATCH(instanceVersion) << "\n";
354
355#ifndef WITH_MOLTEN
356 volkLoadInstance(instance);
357#endif
358 if(enableValidation) {
359 if (vkCreateDebugUtilsMessengerEXT(instance, &messengerInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
360 throw mx::Exception("Failed to set up debug messenger!");
361 }
362 }
363 }
364
366 if (!SDL_Vulkan_CreateSurface(window, instance, &surface)) {
367 throw mx::Exception("Failed to create Vulkan surface!");
368 }
369 }
370
373 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
374
375 uint32_t formatCount = 0;
376 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
377 if (formatCount != 0) {
378 details.formats.resize(formatCount);
379 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
380 }
381
382 uint32_t presentModeCount = 0;
383 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
384 if (presentModeCount != 0) {
385 details.presentModes.resize(presentModeCount);
386 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
387 }
388 return details;
389 }
390
392 uint32_t deviceCount = 0;
393 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
394 if (deviceCount == 0) {
395 throw mx::Exception("Failed to find GPUs with Vulkan support!");
396 }
397 std::vector<VkPhysicalDevice> devices(deviceCount);
398 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
399
400 int bestScore = -1;
401 for (const auto& deviceCandidate : devices) {
402 if (!isDeviceSuitable(deviceCandidate))
403 continue;
404
405 VkPhysicalDeviceProperties props;
406 vkGetPhysicalDeviceProperties(deviceCandidate, &props);
407
408 int score = 0;
409 switch (props.deviceType) {
410 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: score = 4; break;
411 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: score = 3; break;
412 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: score = 2; break;
413 case VK_PHYSICAL_DEVICE_TYPE_OTHER: score = 1; break;
414 case VK_PHYSICAL_DEVICE_TYPE_CPU: score = 0; break;
415 default: score = 0; break;
416 }
417
418 std::cout << "Found Vulkan device: " << props.deviceName
419 << " (type=" << props.deviceType << ", score=" << score << ")\n";
420
421 if (score > bestScore) {
422 bestScore = score;
423 physicalDevice = deviceCandidate;
424 }
425 }
426
427 if (physicalDevice == VK_NULL_HANDLE) {
428 throw mx::Exception("Failed to find a suitable GPU!");
429 }
430 VkPhysicalDeviceProperties deviceProperties;
431 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
432 std::cout << "Selected device: " << deviceProperties.deviceName << std::endl;
433 }
434
437 std::set<uint32_t> uniqueQueueFamilies = {
438 indices.graphicsFamily.value(),
439 indices.presentFamily.value()
440 };
441 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
442 float queuePriority = 1.0f;
443 for (uint32_t queueFamily : uniqueQueueFamilies) {
444 VkDeviceQueueCreateInfo queueInfo{};
445 queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
446 queueInfo.queueFamilyIndex = queueFamily;
447 queueInfo.queueCount = 1;
448 queueInfo.pQueuePriorities = &queuePriority;
449 queueCreateInfos.push_back(queueInfo);
450 }
451
452 VkPhysicalDeviceFeatures supportedFeatures{};
453 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
454
455 VkPhysicalDeviceFeatures deviceFeatures{};
456 deviceFeatures.samplerAnisotropy = supportedFeatures.samplerAnisotropy;
457 supportsWireFrame = (supportedFeatures.fillModeNonSolid == VK_TRUE);
458 deviceFeatures.fillModeNonSolid = supportsWireFrame ? VK_TRUE : VK_FALSE;
459
460 if (!supportsWireFrame) {
461 std::cout << "[Vulkan] fillModeNonSolid not supported; wireframe pipeline will use fill mode fallback.\n";
462 }
463
464 std::vector<const char*> deviceExtensions = {
465 VK_KHR_SWAPCHAIN_EXTENSION_NAME
466 };
467
468#ifdef WITH_MOLTEN
469
470 uint32_t extensionCount;
471 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
472 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
473 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
474
475 for (const auto& ext : availableExtensions) {
476 if (strcmp(ext.extensionName, "VK_KHR_portability_subset") == 0) {
477 deviceExtensions.push_back("VK_KHR_portability_subset");
478 break;
479 }
480 }
481#endif
482
483 VkDeviceCreateInfo createInfo{};
484 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
485 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
486 createInfo.pQueueCreateInfos = queueCreateInfos.data();
487 createInfo.pEnabledFeatures = &deviceFeatures;
488
489
490 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
491 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
492
493 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
494 throw mx::Exception("Failed to create logical device!");
495 }
496
497 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
498 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
499#ifndef WITH_MOLTEN
500 volkLoadDevice(device);
501#endif
502
503 }
504
507 VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
508 VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
509 VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
510
511 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
512 if (swapChainSupport.capabilities.maxImageCount > 0 &&
513 imageCount > swapChainSupport.capabilities.maxImageCount) {
514 imageCount = swapChainSupport.capabilities.maxImageCount;
515 }
516
517 VkSwapchainCreateInfoKHR createInfo{};
518 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
519 createInfo.surface = surface;
520 createInfo.minImageCount = imageCount;
521 createInfo.imageFormat = surfaceFormat.format;
522 createInfo.imageColorSpace = surfaceFormat.colorSpace;
523 createInfo.imageExtent = extent;
524 createInfo.imageArrayLayers = 1;
525 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
526
528 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
529 if (indices.graphicsFamily != indices.presentFamily) {
530 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
531 createInfo.queueFamilyIndexCount = 2;
532 createInfo.pQueueFamilyIndices = queueFamilyIndices;
533 } else {
534 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
535 createInfo.queueFamilyIndexCount = 0;
536 createInfo.pQueueFamilyIndices = nullptr;
537 }
538
539 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
540 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
541 createInfo.presentMode = presentMode;
542 createInfo.clipped = VK_TRUE;
543 createInfo.oldSwapchain = VK_NULL_HANDLE;
544
545 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
546 throw mx::Exception("Failed to create swap chain!");
547 }
548
549 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
550 swapChainImages.resize(imageCount);
551 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
552
553 swapChainImageFormat = surfaceFormat.format;
554 swapChainExtent = extent;
555 w = static_cast<int>(swapChainExtent.width);
556 h = static_cast<int>(swapChainExtent.height);
557 }
558
561 for (size_t i = 0; i < swapChainImages.size(); i++) {
562 VkImageViewCreateInfo createInfo{};
563 createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
564 createInfo.image = swapChainImages[i];
565 createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
566 createInfo.format = swapChainImageFormat;
567 createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
568 createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
569 createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
570 createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
571 createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
572 createInfo.subresourceRange.baseMipLevel = 0;
573 createInfo.subresourceRange.levelCount = 1;
574 createInfo.subresourceRange.baseArrayLayer = 0;
575 createInfo.subresourceRange.layerCount = 1;
576 if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
577 throw mx::Exception("Failed to create image views!");
578 }
579 }
580 }
581
583 VkAttachmentDescription colorAttachment{};
584 colorAttachment.format = swapChainImageFormat;
585 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
586 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
587 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
588 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
589 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
590 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
591 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
592
593 VkAttachmentDescription depthAttachment{};
594 depthAttachment.format = depthFormat;
595 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
596 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
597 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
598 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
599 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
600 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
601 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
602
603 VkAttachmentReference colorAttachmentRef{};
604 colorAttachmentRef.attachment = 0;
605 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
606
607 VkAttachmentReference depthAttachmentRef{};
608 depthAttachmentRef.attachment = 1;
609 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
610
611 VkSubpassDescription subpass{};
612 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
613 subpass.colorAttachmentCount = 1;
614 subpass.pColorAttachments = &colorAttachmentRef;
615 subpass.pDepthStencilAttachment = &depthAttachmentRef;
616
617 VkSubpassDependency dependency{};
618 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
619 dependency.dstSubpass = 0;
620 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
621 dependency.srcAccessMask = 0;
622 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
623 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
624
625 std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
626 VkRenderPassCreateInfo renderPassInfo{};
627 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
628 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
629 renderPassInfo.pAttachments = attachments.data();
630 renderPassInfo.subpassCount = 1;
631 renderPassInfo.pSubpasses = &subpass;
632 renderPassInfo.dependencyCount = 1;
633 renderPassInfo.pDependencies = &dependency;
634
635 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
636 throw mx::Exception("Failed to create render pass!");
637 }
638 }
639
640
641
644 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
645 std::array<VkImageView, 2> attachments = {
648 };
649 VkFramebufferCreateInfo framebufferInfo{};
650 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
651 framebufferInfo.renderPass = renderPass;
652 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
653 framebufferInfo.pAttachments = attachments.data();
654 framebufferInfo.width = swapChainExtent.width;
655 framebufferInfo.height = swapChainExtent.height;
656 framebufferInfo.layers = 1;
657 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
658 throw mx::Exception("Failed to create framebuffer!");
659 }
660 }
661 }
662
665 VkCommandPoolCreateInfo poolInfo{};
666 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
667 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
668 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
669 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
670 throw mx::Exception("Failed to create command pool!");
671 }
672 }
673
676 VkCommandBufferAllocateInfo allocInfo{};
677 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
678 allocInfo.commandPool = commandPool;
679 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
680 allocInfo.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
681
682 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
683 throw mx::Exception("Failed to allocate command buffers!");
684 }
685
686 std::cout << ">> [CommandBuffers] Allocated " << commandBuffers.size() << " command buffers\n";
687 }
688
690 VkSemaphoreCreateInfo semaphoreInfo{};
691 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
692
693 VkFenceCreateInfo fenceInfo{};
694 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
695 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
696
697 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS ||
698 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS ||
699 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS) {
700 throw mx::Exception("Failed to create synchronization objects!");
701 }
702 }
703
705 std::cout << ">> [CleanupSwapChain] Releasing swapchain-dependent resources...\n";
706 std::cout << " Framebuffers: " << swapChainFramebuffers.size() << "\n";
707
708 for (auto framebuffer : swapChainFramebuffers) {
709 if (framebuffer != VK_NULL_HANDLE) {
710 vkDestroyFramebuffer(device, framebuffer, nullptr);
711 }
712 }
713 swapChainFramebuffers.clear();
714
715 if (!commandBuffers.empty()) {
716 std::cout << " Command buffers: " << commandBuffers.size() << "\n";
717 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
718 commandBuffers.clear();
719 }
720
721 if (graphicsPipeline != VK_NULL_HANDLE) {
722 std::cout << " Destroying graphics pipeline\n";
723 vkDestroyPipeline(device, graphicsPipeline, nullptr);
724 graphicsPipeline = VK_NULL_HANDLE;
725 }
726 if (graphicsPipelineMatrix != VK_NULL_HANDLE) {
727 std::cout << " Destroying matrix graphics pipeline\n";
728 vkDestroyPipeline(device, graphicsPipelineMatrix, nullptr);
729 graphicsPipelineMatrix = VK_NULL_HANDLE;
730 }
731 if (pipelineLayout != VK_NULL_HANDLE) {
732 std::cout << " Destroying pipeline layout\n";
733 vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
734 pipelineLayout = VK_NULL_HANDLE;
735 }
736 if (renderPass != VK_NULL_HANDLE) {
737 std::cout << " Destroying render pass\n";
738 vkDestroyRenderPass(device, renderPass, nullptr);
739 renderPass = VK_NULL_HANDLE;
740 }
741
742 for (auto imageView : swapChainImageViews) {
743 if (imageView != VK_NULL_HANDLE) {
744 vkDestroyImageView(device, imageView, nullptr);
745 }
746 }
747 swapChainImageViews.clear();
748
749 if (swapChain != VK_NULL_HANDLE) {
750 std::cout << " Destroying swapchain\n";
751 vkDestroySwapchainKHR(device, swapChain, nullptr);
752 swapChain = VK_NULL_HANDLE;
753 }
754
755 if (depthImageView != VK_NULL_HANDLE) {
756 vkDestroyImageView(device, depthImageView, nullptr);
757 depthImageView = VK_NULL_HANDLE;
758 }
759 if (depthImage != VK_NULL_HANDLE) {
760 vkDestroyImage(device, depthImage, nullptr);
761 depthImage = VK_NULL_HANDLE;
762 }
763 if (depthImageMemory != VK_NULL_HANDLE) {
764 vkFreeMemory(device, depthImageMemory, nullptr);
765 depthImageMemory = VK_NULL_HANDLE;
766 }
767
768 std::cout << ">> [CleanupSwapChain] Complete\n";
769 }
770
771 VkFormat VKWindow::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
772 for (VkFormat format : candidates) {
773 VkFormatProperties props;
774 vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
775 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
776 return format;
777 } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
778 return format;
779 }
780 }
781 throw mx::Exception("Failed to find supported format!");
782 }
783
785 return findSupportedFormat(
786 {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
787 VK_IMAGE_TILING_OPTIMAL,
788 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
789 );
790 }
791
794
796 swapChainExtent.width,
797 swapChainExtent.height,
799 VK_IMAGE_TILING_OPTIMAL,
800 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
801 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
804 );
805
806 depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
807
808 std::cout << ">> [DepthResources] Created depth buffer (" << swapChainExtent.width << "x" << swapChainExtent.height << ")\n";
809 }
810
812
813
814 std::vector<Vertex> vertices = {
815
816 {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
817 {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
818 {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
819 {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
820 };
821
822 std::vector<uint32_t> indices = {
823 0, 1, 2,
824 2, 3, 0
825 };
826
827 indexCount = static_cast<uint32_t>(indices.size());
828
829
830 VkDeviceSize vertexBufferSize = sizeof(Vertex) * vertices.size();
831
832 VkBuffer stagingBuffer;
833 VkDeviceMemory stagingBufferMemory;
834 createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
835 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
836 stagingBuffer, stagingBufferMemory);
837
838 void* data;
839 vkMapMemory(device, stagingBufferMemory, 0, vertexBufferSize, 0, &data);
840 memcpy(data, vertices.data(), vertexBufferSize);
841 vkUnmapMemory(device, stagingBufferMemory);
842
843 createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
844 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
845
846 copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize);
847
848 vkDestroyBuffer(device, stagingBuffer, nullptr);
849 vkFreeMemory(device, stagingBufferMemory, nullptr);
850
851
852 VkDeviceSize indexBufferSize = sizeof(uint32_t) * indices.size();
853
854 createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
855 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
856 stagingBuffer, stagingBufferMemory);
857
858 vkMapMemory(device, stagingBufferMemory, 0, indexBufferSize, 0, &data);
859 memcpy(data, indices.data(), indexBufferSize);
860 vkUnmapMemory(device, stagingBufferMemory);
861
862 createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
863 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
864
865 copyBuffer(stagingBuffer, indexBuffer, indexBufferSize);
866
867 vkDestroyBuffer(device, stagingBuffer, nullptr);
868 vkFreeMemory(device, stagingBufferMemory, nullptr);
869
870 std::cout << ">> [VertexBuffer] Created full-screen quad (4 vertices, 6 indices)\n";
871 }
872 void VKWindow::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
873 VkBufferCreateInfo bufferInfo{};
874 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
875 bufferInfo.size = size;
876 bufferInfo.usage = usage;
877 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
878 if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
879 throw mx::Exception("Failed to create buffer!");
880 }
881 VkMemoryRequirements memRequirements;
882 vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
883 VkMemoryAllocateInfo allocInfo{};
884 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
885 allocInfo.allocationSize = memRequirements.size;
886 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
887 if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
888 throw mx::Exception("Failed to allocate buffer memory!");
889 }
890 vkBindBufferMemory(device, buffer, bufferMemory, 0);
891 }
892
893 uint32_t VKWindow::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
894 VkPhysicalDeviceMemoryProperties memProperties;
895 vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
896 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
897 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
898 return i;
899 }
900 }
901 throw mx::Exception("Failed to find suitable memory type!");
902 }
903
904 void VKWindow::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
905 VkCommandBufferAllocateInfo allocInfo{};
906 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
907 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
908 allocInfo.commandPool = commandPool;
909 allocInfo.commandBufferCount = 1;
910
911 VkCommandBuffer commandBuffer;
912 VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer));
913
914 VkCommandBufferBeginInfo beginInfo{};
915 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
916 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
917
918 VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &beginInfo));
919
920 VkBufferCopy copyRegion{};
921 copyRegion.size = size;
922 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
923
924 VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));
925
926 VkSubmitInfo submitInfo{};
927 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
928 submitInfo.commandBufferCount = 1;
929 submitInfo.pCommandBuffers = &commandBuffer;
930
931 VK_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE));
932 VK_CHECK_RESULT(vkQueueWaitIdle(graphicsQueue));
933
934 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
935 }
936
937 void VKWindow::createTextureImage(SDL_Surface* surfacex) {
938 if (!surfacex) {
939 throw mx::Exception("SDL_Surface is null!");
940 }
941 if (textureImageView != VK_NULL_HANDLE) {
942 vkDestroyImageView(device, textureImageView, nullptr);
943 textureImageView = VK_NULL_HANDLE;
944 }
945 if (textureImage != VK_NULL_HANDLE) {
946 vkDestroyImage(device, textureImage, nullptr);
947 textureImage = VK_NULL_HANDLE;
948 textureImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
949 }
950 if (textureImageMemory != VK_NULL_HANDLE) {
951 vkFreeMemory(device, textureImageMemory, nullptr);
952 textureImageMemory = VK_NULL_HANDLE;
953 }
954 SDL_Surface* surface = SDL_ConvertSurfaceFormat(surfacex, SDL_PIXELFORMAT_RGBA8888, 0);
955 if (!surface) {
956 throw mx::Exception("SDL_ConvertSurfaceFormat failed!");
957 }
958 width = surface->w;
959 height = surface->h;
960 VkDeviceSize imageSize = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(height) * 4;
961 VkBuffer stagingBuffer;
962 VkDeviceMemory stagingBufferMemory;
963 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
964 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
965 stagingBuffer, stagingBufferMemory);
966 void* data = nullptr;
967 VK_CHECK_RESULT(vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data));
968 if (SDL_MUSTLOCK(surface)) {
969 SDL_LockSurface(surface);
970 }
971 uint8_t* dst = reinterpret_cast<uint8_t*>(data);
972 const uint8_t* src = reinterpret_cast<const uint8_t*>(surface->pixels);
973 const size_t rowBytes = static_cast<size_t>(width) * 4;
974 for (int y = 0; y < surface->h; ++y) {
975 memcpy(dst + static_cast<size_t>(y) * rowBytes, src + static_cast<size_t>(y) * static_cast<size_t>(surface->pitch), rowBytes);
976 }
977 if (SDL_MUSTLOCK(surface)) {
978 SDL_UnlockSurface(surface);
979 }
980 vkUnmapMemory(device, stagingBufferMemory);
981 SDL_FreeSurface(surface);
982 VkFormat textureFormat = VK_FORMAT_R8G8B8A8_UNORM;
983 createImage(static_cast<uint32_t>(width), static_cast<uint32_t>(height), textureFormat, VK_IMAGE_TILING_OPTIMAL,
984 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
985 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
986 transitionImageLayout(textureImage, textureFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
987 textureImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
988 copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(width), static_cast<uint32_t>(height));
989 transitionImageLayout(textureImage, textureFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
990 textureImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
991 vkDestroyBuffer(device, stagingBuffer, nullptr);
992 vkFreeMemory(device, stagingBufferMemory, nullptr);
993 }
994
995 void VKWindow::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
996 VkImageCreateInfo imageInfo{};
997 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
998 imageInfo.imageType = VK_IMAGE_TYPE_2D;
999 imageInfo.extent.width = width;
1000 imageInfo.extent.height = height;
1001 imageInfo.extent.depth = 1;
1002 imageInfo.mipLevels = 1;
1003 imageInfo.arrayLayers = 1;
1004 imageInfo.format = format;
1005 imageInfo.tiling = tiling;
1006 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1007 imageInfo.usage = usage;
1008 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1009 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1010 imageInfo.flags = 0;
1011 if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
1012 throw mx::Exception("Failed to create image!");
1013 }
1014 VkMemoryRequirements memRequirements;
1015 vkGetImageMemoryRequirements(device, image, &memRequirements);
1016 VkMemoryAllocateInfo allocInfo{};
1017 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1018 allocInfo.allocationSize = memRequirements.size;
1019 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
1020 if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
1021 throw mx::Exception("Failed to allocate image memory!");
1022 }
1023 vkBindImageMemory(device, image, imageMemory, 0);
1024 }
1025
1026 void VKWindow::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
1027 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
1028 VkImageMemoryBarrier barrier{};
1029 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1030 barrier.oldLayout = oldLayout;
1031 barrier.newLayout = newLayout;
1032 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1033 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1034 barrier.image = image;
1035 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1036 barrier.subresourceRange.baseMipLevel = 0;
1037 barrier.subresourceRange.levelCount = 1;
1038 barrier.subresourceRange.baseArrayLayer = 0;
1039 barrier.subresourceRange.layerCount = 1;
1040
1041 VkPipelineStageFlags sourceStage;
1042 VkPipelineStageFlags destinationStage;
1043
1044 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
1045 barrier.srcAccessMask = 0;
1046 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1047 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1048 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1049 }
1050 else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
1051 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1052 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1053 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1054 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
1055 }
1056 else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
1057 barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
1058 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1059 sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
1060 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1061 } else {
1062 throw mx::Exception ("Transition");
1063 }
1064 vkCmdPipelineBarrier(
1065 commandBuffer,
1066 sourceStage, destinationStage,
1067 0,
1068 0, nullptr,
1069 0, nullptr,
1070 1, &barrier
1071 );
1072
1073 endSingleTimeCommands(commandBuffer);
1074 }
1075 void VKWindow::setupTextureImage(uint32_t w, uint32_t h) {
1076 if (textureImage != VK_NULL_HANDLE) {
1077 vkDestroyImage(device, textureImage, nullptr);
1078 vkFreeMemory(device, textureImageMemory, nullptr);
1079 textureImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1080 }
1081 width = w;
1082 height = h;
1083 VkFormat textureFormat = VK_FORMAT_R8G8B8A8_UNORM;
1084 createImage(width, height, textureFormat, VK_IMAGE_TILING_OPTIMAL,
1085 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
1086 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
1087
1088 transitionImageLayout(textureImage, textureFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
1089 textureImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1090 }
1091
1092 void VKWindow::updateTexture(void* pixels, VkDeviceSize imageSize) {
1093 VkBuffer stagingBuffer;
1094 VkDeviceMemory stagingBufferMemory;
1095 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1096 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1097 stagingBuffer, stagingBufferMemory);
1098 void* data;
1099 VK_CHECK_RESULT(vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data));
1100 memcpy(data, pixels, static_cast<size_t>(imageSize));
1101 vkUnmapMemory(device, stagingBufferMemory);
1102 VkFormat textureFormat = VK_FORMAT_R8G8B8A8_UNORM;
1103 if (textureImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
1104 transitionImageLayout(textureImage, textureFormat, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
1105 textureImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1106 } else if (textureImageLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
1107 transitionImageLayout(textureImage, textureFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
1108 textureImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1109 }
1110 copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(width), static_cast<uint32_t>(height));
1111 transitionImageLayout(textureImage, textureFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
1112 textureImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1113 vkDestroyBuffer(device, stagingBuffer, nullptr);
1114 vkFreeMemory(device, stagingBufferMemory, nullptr);
1115 }
1116
1117 void VKWindow::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
1118 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
1119
1120 VkBufferImageCopy region{};
1121 region.bufferOffset = 0;
1122 region.bufferRowLength = 0;
1123 region.bufferImageHeight = 0;
1124 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1125 region.imageSubresource.mipLevel = 0;
1126 region.imageSubresource.baseArrayLayer = 0;
1127 region.imageSubresource.layerCount = 1;
1128 region.imageOffset = { 0, 0, 0 };
1129 region.imageExtent = {
1130 width,
1131 height,
1132 1
1133 };
1134
1135 vkCmdCopyBufferToImage(
1136 commandBuffer,
1137 buffer,
1138 image,
1139 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1140 1,
1141 &region
1142 );
1143
1144 endSingleTimeCommands(commandBuffer);
1145 }
1146
1148 VkCommandBufferAllocateInfo allocInfo{};
1149 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1150 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1151 allocInfo.commandPool = commandPool;
1152 allocInfo.commandBufferCount = 1;
1153
1154 VkCommandBuffer commandBuffer;
1155 VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer));
1156
1157 VkCommandBufferBeginInfo beginInfo{};
1158 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1159 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1160
1161 VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &beginInfo));
1162
1163 return commandBuffer;
1164 }
1165
1166 void VKWindow::endSingleTimeCommands(VkCommandBuffer commandBuffer) {
1167 VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));
1168
1169 VkSubmitInfo submitInfo{};
1170 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1171 submitInfo.commandBufferCount = 1;
1172 submitInfo.pCommandBuffers = &commandBuffer;
1173
1174 VK_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE));
1175 VK_CHECK_RESULT(vkQueueWaitIdle(graphicsQueue));
1176
1177 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1178 }
1179
1181 textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
1182 }
1183
1184 VkImageView VKWindow::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) {
1185 VkImageViewCreateInfo viewInfo{};
1186 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1187 viewInfo.image = image;
1188 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1189 viewInfo.format = format;
1190 viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
1191 viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
1192 viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
1193 viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
1194 viewInfo.subresourceRange.aspectMask = aspectFlags;
1195 viewInfo.subresourceRange.baseMipLevel = 0;
1196 viewInfo.subresourceRange.levelCount = 1;
1197 viewInfo.subresourceRange.baseArrayLayer = 0;
1198 viewInfo.subresourceRange.layerCount = 1;
1199
1200 VkImageView imageView;
1201 if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
1202 throw mx::Exception("Failed to create texture image view!");
1203 }
1204 return imageView;
1205 }
1206
1208 VkSamplerCreateInfo samplerInfo{};
1209 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1210 samplerInfo.magFilter = VK_FILTER_LINEAR;
1211 samplerInfo.minFilter = VK_FILTER_LINEAR;
1212 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1213 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1214 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1215 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1216 samplerInfo.anisotropyEnable = VK_TRUE;
1217 samplerInfo.maxAnisotropy = 16.0f;
1218 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1219 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1220 samplerInfo.compareEnable = VK_FALSE;
1221 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1222
1223 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1224 throw mx::Exception("Failed to create texture sampler!");
1225 }
1226 }
1227
1229 std::array<VkDescriptorPoolSize, 2> poolSizes{};
1230 poolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1231 poolSizes[0].descriptorCount = static_cast<uint32_t>(swapChainImages.size() * 2);
1232 poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1233 poolSizes[1].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
1234
1235 VkDescriptorPoolCreateInfo poolInfo{};
1236 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1237 poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
1238 poolInfo.pPoolSizes = poolSizes.data();
1239 poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());
1240 std::cout << ">> [DescriptorPool] Number of swap chain images: "
1241 << swapChainImages.size() << "\n";
1242 std::cout << ">> [DescriptorPool] Device address: " << device << "\n";
1243 std::cout << ">> [DescriptorPool] Instance address: " << instance << "\n";
1244
1245 VkResult result = vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool);
1246 if (result != VK_SUCCESS) {
1247 SDL_Log("Failed to create descriptor pool! VkResult: %d\n", result);
1248 throw mx::Exception("Failed to create descriptor pool!");
1249 }
1250 SDL_Log("Descriptor pool created successfully.\n");
1251 SDL_Log("Descriptor pool: %p\n", descriptorPool);
1252 }
1253
1255 try {
1256 std::cout << ">> [DescriptorSets] Starting descriptor set creation...\n";
1257
1258 if (instance == VK_NULL_HANDLE) throw mx::Exception("Vulkan instance is null!");
1259 if (device == VK_NULL_HANDLE) throw mx::Exception("Vulkan device is null!");
1260 if (descriptorSetLayout == VK_NULL_HANDLE) throw mx::Exception("Descriptor set layout is null!");
1261 if (descriptorPool == VK_NULL_HANDLE) throw mx::Exception("Descriptor pool is null!");
1262
1263 std::vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), descriptorSetLayout);
1264 VkDescriptorSetAllocateInfo allocInfo = {};
1265 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1266 allocInfo.descriptorPool = descriptorPool;
1267 allocInfo.descriptorSetCount = static_cast<uint32_t>(swapChainImages.size());
1268 allocInfo.pSetLayouts = layouts.data();
1269
1270 descriptorSets.resize(swapChainImages.size());
1271 VkResult result = vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data());
1272
1273 if (result != VK_SUCCESS) {
1274 throw mx::Exception("Failed to allocate descriptor sets! Result: " + std::to_string(result));
1275 }
1276 std::cout << ">> [DescriptorSets] Successfully allocated descriptor sets\n";
1277 for (size_t i = 0; i < swapChainImages.size(); i++) {
1278 VkDescriptorImageInfo imageInfo = {};
1279 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1280 imageInfo.imageView = textureImageView;
1281 imageInfo.sampler = textureSampler;
1282
1283 VkDescriptorBufferInfo bufferInfo = {};
1284 bufferInfo.buffer = uniformBuffers[i];
1285 bufferInfo.offset = 0;
1286 bufferInfo.range = sizeof(UniformBufferObject);
1287 std::array<VkWriteDescriptorSet, 2> descriptorWrites{};
1288 descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1289 descriptorWrites[0].dstSet = descriptorSets[i];
1290 descriptorWrites[0].dstBinding = 0;
1291 descriptorWrites[0].dstArrayElement = 0;
1292 descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1293 descriptorWrites[0].descriptorCount = 1;
1294 descriptorWrites[0].pImageInfo = &imageInfo;
1295 descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1296 descriptorWrites[1].dstSet = descriptorSets[i];
1297 descriptorWrites[1].dstBinding = 1;
1298 descriptorWrites[1].dstArrayElement = 0;
1299 descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1300 descriptorWrites[1].descriptorCount = 1;
1301 descriptorWrites[1].pBufferInfo = &bufferInfo;
1302 vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
1303 std::cout << ">> [DescriptorSets] Updated descriptor set " << i << "\n";
1304 }
1305
1306 std::cout << ">> [DescriptorSets] Completed descriptor set creation and updates\n";
1307 }
1308 catch (const std::exception& e) {
1309 SDL_Log("Exception in createDescriptorSets: %s", e.what());
1310 throw;
1311 }
1312 }
1314 for (size_t i = 0; i < swapChainImages.size(); i++) {
1315 VkDescriptorImageInfo imageInfo{};
1316 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1317 imageInfo.imageView = textureImageView;
1318 imageInfo.sampler = textureSampler;
1319
1320 VkWriteDescriptorSet descriptorWrite{};
1321 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1322 descriptorWrite.dstSet = descriptorSets[i];
1323 descriptorWrite.dstBinding = 0;
1324 descriptorWrite.dstArrayElement = 0;
1325 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1326 descriptorWrite.descriptorCount = 1;
1327 descriptorWrite.pImageInfo = &imageInfo;
1328
1329 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
1330 }
1331 }
1332
1334 VkDeviceSize bufferSize = sizeof(UniformBufferObject);
1335 size_t imageCount = swapChainImages.size();
1336 uniformBuffers.resize(imageCount);
1337 uniformBuffersMemory.resize(imageCount);
1338 uniformBuffersMapped.resize(imageCount);
1339
1340 for (size_t i = 0; i < imageCount; i++) {
1341 createBuffer(bufferSize,
1342 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1343 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1344 uniformBuffers[i],
1346
1347 VK_CHECK_RESULT(vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]));
1348 }
1349 }
1350
1352 try {
1353 std::cout << "\n>> [GraphicsPipeline] Creating graphics pipeline...\n";
1354 std::cout << " Device = " << device << "\n";
1355 std::cout << " SwapChainExtent = (" << swapChainExtent.width << ", " << swapChainExtent.height << ")\n";
1356
1357 if (device == VK_NULL_HANDLE) {
1358 throw mx::Exception("Device is invalid in createGraphicsPipeline()");
1359 }
1360
1361 auto vertShaderCode = mx::readFile(util.getFilePath("data/vert.spv"));
1362 auto fragShaderCode = mx::readFile(util.getFilePath("data/frag.spv"));
1363
1364 VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
1365 if (vertShaderModule == VK_NULL_HANDLE) {
1366 throw mx::Exception("Failed to create vertex shader module!");
1367 }
1368
1369 VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
1370 if (fragShaderModule == VK_NULL_HANDLE) {
1371 throw mx::Exception("Failed to create fragment shader module!");
1372 }
1373
1374 std::cout << " SPIR-V shaders loaded successfully.\n";
1375
1376 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1377 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1378 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1379 vertShaderStageInfo.module = vertShaderModule;
1380 vertShaderStageInfo.pName = "main";
1381
1382 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1383 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1384 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1385 fragShaderStageInfo.module = fragShaderModule;
1386 fragShaderStageInfo.pName = "main";
1387
1388 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1389
1390 VkVertexInputBindingDescription bindingDescription{};
1391 bindingDescription.binding = 0;
1392 bindingDescription.stride = sizeof(Vertex);
1393 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1394
1395
1396 std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions{};
1397
1398
1399 attributeDescriptions[0].binding = 0;
1400 attributeDescriptions[0].location = 0;
1401 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1402 attributeDescriptions[0].offset = offsetof(Vertex, pos);
1403
1404
1405 attributeDescriptions[1].binding = 0;
1406 attributeDescriptions[1].location = 1;
1407 attributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
1408 attributeDescriptions[1].offset = offsetof(Vertex, texCoord);
1409
1410
1411 attributeDescriptions[2].binding = 0;
1412 attributeDescriptions[2].location = 2;
1413 attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT;
1414 attributeDescriptions[2].offset = offsetof(Vertex, normal);
1415
1416 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1417 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1418 vertexInputInfo.vertexBindingDescriptionCount = 1;
1419 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1420 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
1421 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1422
1423 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1424 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1425 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1426 inputAssembly.primitiveRestartEnable = VK_FALSE;
1427
1428 VkPipelineViewportStateCreateInfo viewportState{};
1429 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1430 viewportState.viewportCount = 1;
1431 viewportState.pViewports = nullptr;
1432 viewportState.scissorCount = 1;
1433 viewportState.pScissors = nullptr;
1434
1435 std::array<VkDynamicState, 2> baseDynamicStates = {
1436 VK_DYNAMIC_STATE_VIEWPORT,
1437 VK_DYNAMIC_STATE_SCISSOR
1438 };
1439 VkPipelineDynamicStateCreateInfo baseDynamicState{};
1440 baseDynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1441 baseDynamicState.dynamicStateCount = static_cast<uint32_t>(baseDynamicStates.size());
1442 baseDynamicState.pDynamicStates = baseDynamicStates.data();
1443
1444 VkPipelineRasterizationStateCreateInfo rasterizer{};
1445 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1446 rasterizer.depthClampEnable = VK_FALSE;
1447 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1448 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1449 rasterizer.lineWidth = 1.0f;
1450 rasterizer.cullMode = VK_CULL_MODE_NONE;
1451 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1452 rasterizer.depthBiasEnable = VK_FALSE;
1453
1454 VkPipelineMultisampleStateCreateInfo multisampling{};
1455 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1456 multisampling.sampleShadingEnable = VK_FALSE;
1457 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1458
1459 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1460 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1461 depthStencil.depthTestEnable = VK_TRUE;
1462 depthStencil.depthWriteEnable = VK_TRUE;
1463 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1464 depthStencil.depthBoundsTestEnable = VK_FALSE;
1465 depthStencil.stencilTestEnable = VK_FALSE;
1466
1467 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1468 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
1469 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1470 colorBlendAttachment.blendEnable = VK_FALSE;
1471
1472 VkPipelineColorBlendStateCreateInfo colorBlending{};
1473 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1474 colorBlending.logicOpEnable = VK_FALSE;
1475 colorBlending.attachmentCount = 1;
1476 colorBlending.pAttachments = &colorBlendAttachment;
1477
1478 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1479 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1480 pipelineLayoutInfo.setLayoutCount = 1;
1481 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1482 pipelineLayoutInfo.pushConstantRangeCount = 0;
1483 pipelineLayoutInfo.pPushConstantRanges = nullptr;
1484
1485 if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
1486 throw mx::Exception("Failed to create pipeline layout!");
1487 }
1488
1489 std::cout << " Pipeline Layout created (links to descriptor set layout)\n";
1490
1491 VkGraphicsPipelineCreateInfo pipelineInfo{};
1492 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1493 pipelineInfo.stageCount = 2;
1494 pipelineInfo.pStages = shaderStages;
1495 pipelineInfo.pVertexInputState = &vertexInputInfo;
1496 pipelineInfo.pInputAssemblyState = &inputAssembly;
1497 pipelineInfo.pViewportState = &viewportState;
1498 pipelineInfo.pRasterizationState = &rasterizer;
1499 pipelineInfo.pMultisampleState = &multisampling;
1500 pipelineInfo.pDepthStencilState = &depthStencil;
1501 pipelineInfo.pColorBlendState = &colorBlending;
1502 pipelineInfo.pDynamicState = &baseDynamicState;
1503 pipelineInfo.layout = pipelineLayout;
1504 pipelineInfo.renderPass = renderPass;
1505 pipelineInfo.subpass = 0;
1506 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
1507
1508 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
1509 throw mx::Exception("Failed to create graphics pipeline!");
1510 }
1511
1512 rasterizer.polygonMode = supportsWireFrame ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL;
1513 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipelineMatrix) != VK_SUCCESS) {
1514 throw mx::Exception("Failed to create Matrix graphics pipeline!");
1515 }
1516
1517 std::cout << " Graphics Pipeline created successfully!\n";
1518 std::cout << " Matrix Pipeline created successfully!\n";
1519 std::cout << ">> [GraphicsPipeline] Complete\n\n";
1520
1521
1522 vkDestroyShaderModule(device, fragShaderModule, nullptr);
1523 vkDestroyShaderModule(device, vertShaderModule, nullptr);
1524 }
1525 catch (mx::Exception& e) {
1526 SDL_Log("Exception in createGraphicsPipeline: %s\n", e.text().c_str());
1527 throw;
1528 }
1529 }
1530
1532 uint32_t imageIndex = 0;
1533
1534 VkResult result = vkAcquireNextImageKHR(
1535 device,
1536 swapChain,
1537 UINT64_MAX,
1539 VK_NULL_HANDLE,
1540 &imageIndex
1541 );
1542
1543 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1545 return;
1546 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
1547 throw mx::Exception("Failed to acquire swap chain image!");
1548 }
1549
1550 VK_CHECK_RESULT(vkResetCommandBuffer(commandBuffers[imageIndex], 0));
1551
1552 VkCommandBufferBeginInfo beginInfo{};
1553 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1554
1555 if (vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo) != VK_SUCCESS) {
1556 throw mx::Exception("Failed to begin recording command buffer!");
1557 }
1558
1559 VkRenderPassBeginInfo renderPassInfo{};
1560 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1561 renderPassInfo.renderPass = renderPass;
1562 renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
1563 renderPassInfo.renderArea.offset = {0, 0};
1564 renderPassInfo.renderArea.extent = swapChainExtent;
1565
1566 std::array<VkClearValue, 2> clearValues{};
1567 clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
1568 clearValues[1].depthStencil = {1.0f, 0};
1569
1570 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1571 renderPassInfo.pClearValues = clearValues.data();
1572
1573 VkCommandBuffer cmd = commandBuffers[imageIndex];
1574
1575 vkCmdBeginRenderPass(cmd, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1576
1577 VkViewport viewport{};
1578 viewport.x = 0.0f;
1579 viewport.y = 0.0f;
1580 viewport.width = static_cast<float>(swapChainExtent.width);
1581 viewport.height = static_cast<float>(swapChainExtent.height);
1582 viewport.minDepth = 0.0f;
1583 viewport.maxDepth = 1.0f;
1584
1585 VkRect2D scissor{};
1586 scissor.offset = {0, 0};
1587 scissor.extent = swapChainExtent;
1588
1589 vkCmdSetViewport(cmd, 0, 1, &viewport);
1590 vkCmdSetScissor(cmd, 0, 1, &scissor);
1591
1592 if (spritePipeline != VK_NULL_HANDLE && !sprites.empty()) {
1593 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, spritePipeline);
1594
1595 for (auto &sprite : sprites) {
1596 if (!sprite) continue;
1597
1598 sprite->renderSprites(
1599 cmd,
1601 swapChainExtent.width,
1602 swapChainExtent.height
1603 );
1604 }
1605 }
1606
1607 if (textRenderer && textPipeline != VK_NULL_HANDLE) {
1608 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, textPipeline);
1609
1610 textRenderer->renderText(
1611 cmd,
1613 swapChainExtent.width,
1614 swapChainExtent.height
1615 );
1616 }
1617
1618 vkCmdEndRenderPass(cmd);
1619
1620 if (vkEndCommandBuffer(cmd) != VK_SUCCESS) {
1621 throw mx::Exception("Failed to record command buffer!");
1622 }
1623
1624 VkSubmitInfo submitInfo{};
1625 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1626
1627 VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
1628 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1629 submitInfo.waitSemaphoreCount = 1;
1630 submitInfo.pWaitSemaphores = waitSemaphores;
1631 submitInfo.pWaitDstStageMask = waitStages;
1632
1633 submitInfo.commandBufferCount = 1;
1634 submitInfo.pCommandBuffers = &cmd;
1635
1636 VkSemaphore signalSemaphores[] = { renderFinishedSemaphore };
1637 submitInfo.signalSemaphoreCount = 1;
1638 submitInfo.pSignalSemaphores = signalSemaphores;
1639
1640 VkResult submitResult = vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1641 if (submitResult != VK_SUCCESS) {
1642 std::cerr << "vkQueueSubmit failed with VkResult: " << submitResult << std::endl;
1643 throw mx::Exception("Failed to submit draw command buffer!");
1644 }
1645
1646 VkPresentInfoKHR presentInfo{};
1647 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1648 presentInfo.waitSemaphoreCount = 1;
1649 presentInfo.pWaitSemaphores = signalSemaphores;
1650 presentInfo.swapchainCount = 1;
1651 presentInfo.pSwapchains = &swapChain;
1652 presentInfo.pImageIndices = &imageIndex;
1653
1654 result = vkQueuePresentKHR(presentQueue, &presentInfo);
1655
1656 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
1658 } else if (result != VK_SUCCESS) {
1659 throw mx::Exception("Failed to present swap chain image!");
1660 }
1661
1662 vkQueueWaitIdle(graphicsQueue);
1663
1665
1666 for (auto &sprite : sprites) {
1667 if (sprite) sprite->clearQueue();
1668 }
1669}
1670
1671
1673 std::cout << ">> [Cleanup] Releasing Vulkan resources...\n";
1674 vkDeviceWaitIdle(device);
1675
1676 if (textRenderer != nullptr) {
1677 std::cout << " Releasing text renderer\n";
1678 textRenderer.reset();
1679 }
1680
1681 if (!sprites.empty()) {
1682 std::cout << " Releasing sprites: " << sprites.size() << "\n";
1683 }
1684 sprites.clear();
1685
1686 if (spritePipeline != VK_NULL_HANDLE) {
1687 vkDestroyPipeline(device, spritePipeline, nullptr);
1688 }
1689 if (spritePipelineLayout != VK_NULL_HANDLE) {
1690 vkDestroyPipelineLayout(device, spritePipelineLayout, nullptr);
1691 }
1692 if (spriteDescriptorPool != VK_NULL_HANDLE) {
1693 vkDestroyDescriptorPool(device, spriteDescriptorPool, nullptr);
1694 }
1695 if (spriteDescriptorSetLayout != VK_NULL_HANDLE) {
1696 vkDestroyDescriptorSetLayout(device, spriteDescriptorSetLayout, nullptr);
1697 }
1698
1699 if (textPipeline != VK_NULL_HANDLE) {
1700 vkDestroyPipeline(device, textPipeline, nullptr);
1701 }
1702 if (textPipelineLayout != VK_NULL_HANDLE) {
1703 vkDestroyPipelineLayout(device, textPipelineLayout, nullptr);
1704 }
1705 if (textDescriptorPool != VK_NULL_HANDLE) {
1706 vkDestroyDescriptorPool(device, textDescriptorPool, nullptr);
1707 }
1708 if (textDescriptorSetLayout != VK_NULL_HANDLE) {
1709 vkDestroyDescriptorSetLayout(device, textDescriptorSetLayout, nullptr);
1710 }
1711
1713 std::cout << " Releasing uniform buffers: " << uniformBuffers.size() << "\n";
1714 for (size_t i = 0; i < uniformBuffers.size(); i++) {
1715 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
1716 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
1717 }
1718 uniformBuffers.clear();
1719 uniformBuffersMemory.clear();;
1720 uniformBuffersMapped.clear();
1721
1722 vkDestroySampler(device, textureSampler, nullptr);
1723 vkDestroyImageView(device, textureImageView, nullptr);
1724 vkDestroyImage(device, textureImage, nullptr);
1725 vkFreeMemory(device, textureImageMemory, nullptr);
1726
1727 vkDestroyBuffer(device, vertexBuffer, nullptr);
1728 vkFreeMemory(device, vertexBufferMemory, nullptr);
1729 vkDestroyBuffer(device, indexBuffer, nullptr);
1730 vkFreeMemory(device, indexBufferMemory, nullptr);
1731
1732 vkDestroyDescriptorPool(device, descriptorPool, nullptr);
1733 vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
1734 vkDestroySemaphore(device, renderFinishedSemaphore, nullptr);
1735 vkDestroySemaphore(device, imageAvailableSemaphore, nullptr);
1736 vkDestroyFence(device, inFlightFence, nullptr);
1737
1738 if (commandPool != VK_NULL_HANDLE) {
1739 vkDestroyCommandPool(device, commandPool, nullptr);
1740 }
1741
1742 if (pipelineLayout != VK_NULL_HANDLE) {
1743 vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
1744 }
1745
1746 if (device != VK_NULL_HANDLE) {
1747 std::cout << " Destroying logical device\n";
1748 vkDestroyDevice(device, nullptr);
1749 }
1750 if (surface != VK_NULL_HANDLE) {
1751 std::cout << " Destroying surface\n";
1752 vkDestroySurfaceKHR(instance, surface, nullptr);
1753 }
1754
1755 if (enableValidation) {
1756 std::cout << " Destroying debug messenger\n";
1757 vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1758 }
1759 if (instance != VK_NULL_HANDLE) {
1760 std::cout << " Destroying Vulkan instance\n";
1761 vkDestroyInstance(instance, nullptr);
1762 }
1763
1764 #ifndef WITH_MOLTEN
1765 volkFinalize();
1766 #endif
1767
1768 if (window != nullptr) {
1769 std::cout << " Destroying SDL window and quitting SDL\n";
1770 SDL_DestroyWindow(window);
1772 SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK);
1774 }
1775 SDL_Quit();
1776 }
1777
1778 std::cout << ">> [Cleanup] Complete\n";
1779 }
1780
1781 void VKWindow::setFullScreen(const bool full) {
1782 if (!window) return;
1783 vkDeviceWaitIdle(device);
1784 if (full) {
1785 SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
1786 } else {
1787 SDL_SetWindowFullscreen(window, 0);
1788 }
1789 int newW, newH;
1790 SDL_Vulkan_GetDrawableSize(window, &newW, &newH);
1791 if (newW > 0 && newH > 0) {
1792 w = newW;
1793 h = newH;
1794 }
1796 }
1797
1799 vkDeviceWaitIdle(device);
1800 if(window)
1801 SDL_Vulkan_GetDrawableSize(window, &w, &h);
1802
1803 if (spritePipeline != VK_NULL_HANDLE) {
1804 vkDestroyPipeline(device, spritePipeline, nullptr);
1805 spritePipeline = VK_NULL_HANDLE;
1806 }
1807 if (spritePipelineLayout != VK_NULL_HANDLE) {
1808 vkDestroyPipelineLayout(device, spritePipelineLayout, nullptr);
1809 spritePipelineLayout = VK_NULL_HANDLE;
1810 }
1811
1822
1823
1824 if (!sprites.empty()) {
1826 for (auto &sprite : sprites) {
1827 if (sprite) {
1828 sprite->setRenderPass(renderPass);
1829 sprite->setDescriptorSetLayout(spriteDescriptorSetLayout);
1830 sprite->rebuildPipeline();
1831 sprite->rebuildInstancedPipeline();
1832 }
1833 }
1834 }
1835
1836 onResize();
1837 }
1838
1839
1840 VKSprite* VKWindow::createSprite(const std::string &pngPath, const std::string &vertexShader, const std::string &fragmentShaderPath) {
1841 auto sprite = std::make_unique<VKSprite>(device, physicalDevice, graphicsQueue, commandPool);
1842 if (spriteDescriptorSetLayout == VK_NULL_HANDLE) {
1844 }
1845 sprite->setDescriptorSetLayout(spriteDescriptorSetLayout);
1846 if (!fragmentShaderPath.empty()) {
1847 sprite->setRenderPass(renderPass);
1848 sprite->setVertexShaderPath(vertexShader);
1849 }
1850 sprite->loadSprite(pngPath, fragmentShaderPath);
1851 if (spritePipeline == VK_NULL_HANDLE) {
1854 }
1855
1856 VKSprite* ptr = sprite.get();
1857 sprites.push_back(std::move(sprite));
1858 return ptr;
1859 }
1860
1861 VKSprite* VKWindow::createSprite(SDL_Surface* surface, const std::string &vertexShader, const std::string &fragmentShaderPath) {
1862 auto sprite = std::make_unique<VKSprite>(device, physicalDevice, graphicsQueue, commandPool);
1863 if (spriteDescriptorSetLayout == VK_NULL_HANDLE) {
1865 }
1866 sprite->setDescriptorSetLayout(spriteDescriptorSetLayout);
1867 if (!fragmentShaderPath.empty()) {
1868 sprite->setRenderPass(renderPass);
1869 sprite->setVertexShaderPath(vertexShader);
1870 }
1871 sprite->loadSprite(surface, fragmentShaderPath);
1872 if (spritePipeline == VK_NULL_HANDLE) {
1875 }
1876 VKSprite* ptr = sprite.get();
1877 sprites.push_back(std::move(sprite));
1878 return ptr;
1879 }
1880
1881 VKSprite* VKWindow::createSprite(int width, int height, const std::string &vertexShader, const std::string &fragmentShaderPath) {
1882 auto sprite = std::make_unique<VKSprite>(device, physicalDevice, graphicsQueue, commandPool);
1883 if (spriteDescriptorSetLayout == VK_NULL_HANDLE) {
1885 }
1886 sprite->setDescriptorSetLayout(spriteDescriptorSetLayout);
1887 if (!fragmentShaderPath.empty()) {
1888 sprite->setRenderPass(renderPass);
1889 sprite->setVertexShaderPath(vertexShader);
1890 }
1891 sprite->createEmptySprite(width, height, vertexShader, fragmentShaderPath);
1892 if (spritePipeline == VK_NULL_HANDLE) {
1895 }
1896 VKSprite* ptr = sprite.get();
1897 sprites.push_back(std::move(sprite));
1898 return ptr;
1899 }
1900
1902 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1903 samplerLayoutBinding.binding = 0;
1904 samplerLayoutBinding.descriptorCount = 1;
1905 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1906 samplerLayoutBinding.pImmutableSamplers = nullptr;
1907 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1908
1909 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1910 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1911 layoutInfo.bindingCount = 1;
1912 layoutInfo.pBindings = &samplerLayoutBinding;
1913
1914 VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &spriteDescriptorSetLayout));
1915 }
1916
1918 try {
1919 auto spriteVertShaderCode = readFile(util.getFilePath("data/sprite_vert.spv"));
1920 auto spriteFragShaderCode = readFile(util.getFilePath("data/sprite_frag.spv"));
1921
1922 VkShaderModule spriteVertShaderModule = createShaderModule(spriteVertShaderCode);
1923 VkShaderModule spriteFragShaderModule = createShaderModule(spriteFragShaderCode);
1924
1925 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1926 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1927 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1928 vertShaderStageInfo.module = spriteVertShaderModule;
1929 vertShaderStageInfo.pName = "main";
1930
1931 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1932 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1933 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1934 fragShaderStageInfo.module = spriteFragShaderModule;
1935 fragShaderStageInfo.pName = "main";
1936
1937 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1938
1939 VkVertexInputBindingDescription bindingDescription{};
1940 bindingDescription.binding = 0;
1941 bindingDescription.stride = sizeof(float) * 4;
1942 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1943
1944 std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{};
1945 attributeDescriptions[0].binding = 0;
1946 attributeDescriptions[0].location = 0;
1947 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1948 attributeDescriptions[0].offset = 0;
1949
1950 attributeDescriptions[1].binding = 0;
1951 attributeDescriptions[1].location = 1;
1952 attributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
1953 attributeDescriptions[1].offset = sizeof(float) * 2;
1954
1955 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1956 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1957 vertexInputInfo.vertexBindingDescriptionCount = 1;
1958 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1959 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
1960 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1961
1962 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1963 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1964 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1965 inputAssembly.primitiveRestartEnable = VK_FALSE;
1966
1967 std::vector<VkDynamicState> dynamicStates = {
1968 VK_DYNAMIC_STATE_VIEWPORT,
1969 VK_DYNAMIC_STATE_SCISSOR
1970 };
1971
1972 VkPipelineDynamicStateCreateInfo dynamicState{};
1973 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1974 dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
1975 dynamicState.pDynamicStates = dynamicStates.data();
1976
1977 VkPipelineViewportStateCreateInfo viewportState{};
1978 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1979 viewportState.viewportCount = 1;
1980 viewportState.scissorCount = 1;
1981
1982 VkPipelineRasterizationStateCreateInfo rasterizer{};
1983 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1984 rasterizer.depthClampEnable = VK_FALSE;
1985 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1986 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1987 rasterizer.lineWidth = 1.0f;
1988 rasterizer.cullMode = VK_CULL_MODE_NONE;
1989 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1990 rasterizer.depthBiasEnable = VK_FALSE;
1991
1992 VkPipelineMultisampleStateCreateInfo multisampling{};
1993 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1994 multisampling.sampleShadingEnable = VK_FALSE;
1995 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1996
1997 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1998 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1999 depthStencil.depthTestEnable = VK_FALSE;
2000 depthStencil.depthWriteEnable = VK_FALSE;
2001 depthStencil.depthCompareOp = VK_COMPARE_OP_ALWAYS;
2002 depthStencil.depthBoundsTestEnable = VK_FALSE;
2003 depthStencil.stencilTestEnable = VK_FALSE;
2004
2005 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
2006 colorBlendAttachment.colorWriteMask =
2007 VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
2008 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
2009 colorBlendAttachment.blendEnable = VK_TRUE;
2010 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
2011 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
2012 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
2013 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
2014 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
2015 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
2016
2017 VkPipelineColorBlendStateCreateInfo colorBlending{};
2018 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
2019 colorBlending.logicOpEnable = VK_FALSE;
2020 colorBlending.attachmentCount = 1;
2021 colorBlending.pAttachments = &colorBlendAttachment;
2022
2023 VkPushConstantRange pushConstantRange{};
2024 pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
2025 pushConstantRange.offset = 0;
2026 pushConstantRange.size = sizeof(float) * 12;
2027
2028 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
2029 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
2030 pipelineLayoutInfo.setLayoutCount = 1;
2031 pipelineLayoutInfo.pSetLayouts = &spriteDescriptorSetLayout;
2032 pipelineLayoutInfo.pushConstantRangeCount = 1;
2033 pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
2034
2035 VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &spritePipelineLayout));
2036
2037 VkGraphicsPipelineCreateInfo pipelineInfo{};
2038 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
2039 pipelineInfo.stageCount = 2;
2040 pipelineInfo.pStages = shaderStages;
2041 pipelineInfo.pVertexInputState = &vertexInputInfo;
2042 pipelineInfo.pInputAssemblyState = &inputAssembly;
2043 pipelineInfo.pViewportState = &viewportState;
2044 pipelineInfo.pRasterizationState = &rasterizer;
2045 pipelineInfo.pMultisampleState = &multisampling;
2046 pipelineInfo.pDepthStencilState = &depthStencil;
2047 pipelineInfo.pColorBlendState = &colorBlending;
2048 pipelineInfo.pDynamicState = &dynamicState;
2049 pipelineInfo.layout = spritePipelineLayout;
2050 pipelineInfo.renderPass = renderPass;
2051 pipelineInfo.subpass = 0;
2052 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
2053 pipelineInfo.basePipelineIndex = -1;
2054
2055 VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &spritePipeline));
2056
2057 vkDestroyShaderModule(device, spriteFragShaderModule, nullptr);
2058 vkDestroyShaderModule(device, spriteVertShaderModule, nullptr);
2059 } catch (const std::exception& e) {
2060 std::cerr << "Warning: Sprite shader compilation skipped: " << e.what() << std::endl;
2061 spritePipeline = VK_NULL_HANDLE;
2062 }
2063 }
2064
2066 VkDescriptorPoolSize poolSize{};
2067 poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2068 poolSize.descriptorCount = 100;
2069
2070 VkDescriptorPoolCreateInfo poolInfo{};
2071 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
2072 poolInfo.poolSizeCount = 1;
2073 poolInfo.pPoolSizes = &poolSize;
2074 poolInfo.maxSets = 100;
2075 poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
2076
2077 VK_CHECK_RESULT(vkCreateDescriptorPool(device, &poolInfo, nullptr, &spriteDescriptorPool));
2078 }
2079
2080 void VKWindow::printText(const std::string &text, int x, int y, const SDL_Color &col) {
2081 if (textRenderer != nullptr) {
2082 textRenderer->printTextG_Solid(text, x, y, col);
2083 }
2084 }
2085
2087 if (textRenderer != nullptr) {
2088 textRenderer->clearQueue();
2089 }
2090 }
2091
2092 bool VKWindow::getTextDimensions(const std::string &text, int& width, int& height) {
2093 if (textRenderer != nullptr) {
2094 return textRenderer->getTextDimensions(text, width, height);
2095 }
2096 width = 0;
2097 height = 0;
2098 return false;
2099 }
2100
2102 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
2103 samplerLayoutBinding.binding = 0;
2104 samplerLayoutBinding.descriptorCount = 1;
2105 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2106 samplerLayoutBinding.pImmutableSamplers = nullptr;
2107 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
2108
2109 VkDescriptorSetLayoutCreateInfo layoutInfo{};
2110 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
2111 layoutInfo.bindingCount = 1;
2112 layoutInfo.pBindings = &samplerLayoutBinding;
2113
2114 VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &textDescriptorSetLayout));
2115 }
2116
2118 try {
2119 auto textVertShaderCode = readFile(util.getFilePath("data/text_vert.spv"));
2120 auto textFragShaderCode = readFile(util.getFilePath("data/text_frag.spv"));
2121
2122 VkShaderModule textVertShaderModule = createShaderModule(textVertShaderCode);
2123 VkShaderModule textFragShaderModule = createShaderModule(textFragShaderCode);
2124
2125 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
2126 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2127 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
2128 vertShaderStageInfo.module = textVertShaderModule;
2129 vertShaderStageInfo.pName = "main";
2130
2131 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
2132 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2133 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
2134 fragShaderStageInfo.module = textFragShaderModule;
2135 fragShaderStageInfo.pName = "main";
2136
2137 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
2138
2139 VkVertexInputBindingDescription bindingDescription{};
2140 bindingDescription.binding = 0;
2141 bindingDescription.stride = sizeof(float) * 4;
2142 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
2143
2144 std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{};
2145 attributeDescriptions[0].binding = 0;
2146 attributeDescriptions[0].location = 0;
2147 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
2148 attributeDescriptions[0].offset = 0;
2149
2150 attributeDescriptions[1].binding = 0;
2151 attributeDescriptions[1].location = 1;
2152 attributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
2153 attributeDescriptions[1].offset = sizeof(float) * 2;
2154
2155 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
2156 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
2157 vertexInputInfo.vertexBindingDescriptionCount = 1;
2158 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
2159 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
2160 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
2161
2162 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
2163 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
2164 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
2165 inputAssembly.primitiveRestartEnable = VK_FALSE;
2166
2167 VkViewport viewport{};
2168 viewport.x = 0.0f;
2169 viewport.y = 0.0f;
2170 viewport.width = static_cast<float>(swapChainExtent.width);
2171 viewport.height = static_cast<float>(swapChainExtent.height);
2172 viewport.minDepth = 0.0f;
2173 viewport.maxDepth = 1.0f;
2174
2175 VkRect2D scissor{};
2176 scissor.offset = { 0, 0 };
2177 scissor.extent = swapChainExtent;
2178
2179 std::vector<VkDynamicState> dynamicStates = {
2180 VK_DYNAMIC_STATE_VIEWPORT,
2181 VK_DYNAMIC_STATE_SCISSOR
2182 };
2183
2184 VkPipelineDynamicStateCreateInfo dynamicState{};
2185 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
2186 dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
2187 dynamicState.pDynamicStates = dynamicStates.data();
2188
2189 VkPipelineViewportStateCreateInfo viewportState{};
2190 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
2191 viewportState.viewportCount = 1;
2192 viewportState.pViewports = &viewport;
2193 viewportState.scissorCount = 1;
2194 viewportState.pScissors = &scissor;
2195
2196 VkPipelineRasterizationStateCreateInfo rasterizer{};
2197 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
2198 rasterizer.depthClampEnable = VK_FALSE;
2199 rasterizer.rasterizerDiscardEnable = VK_FALSE;
2200 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
2201 rasterizer.lineWidth = 1.0f;
2202 rasterizer.cullMode = VK_CULL_MODE_NONE;
2203 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
2204 rasterizer.depthBiasEnable = VK_FALSE;
2205
2206 VkPipelineMultisampleStateCreateInfo multisampling{};
2207 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
2208 multisampling.sampleShadingEnable = VK_FALSE;
2209 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
2210
2211 VkPipelineDepthStencilStateCreateInfo depthStencil{};
2212 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
2213 depthStencil.depthTestEnable = VK_FALSE;
2214 depthStencil.depthWriteEnable = VK_FALSE;
2215 depthStencil.depthCompareOp = VK_COMPARE_OP_ALWAYS;
2216 depthStencil.depthBoundsTestEnable = VK_FALSE;
2217 depthStencil.stencilTestEnable = VK_FALSE;
2218
2219 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
2220 colorBlendAttachment.colorWriteMask =
2221 VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
2222 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
2223
2224 colorBlendAttachment.blendEnable = VK_TRUE;
2225 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
2226 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
2227 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
2228 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
2229 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
2230 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
2231
2232 VkPipelineColorBlendStateCreateInfo colorBlending{};
2233 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
2234 colorBlending.logicOpEnable = VK_FALSE;
2235 colorBlending.attachmentCount = 1;
2236 colorBlending.pAttachments = &colorBlendAttachment;
2237
2238 VkPushConstantRange pushConstantRange{};
2239 pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
2240 pushConstantRange.offset = 0;
2241 pushConstantRange.size = sizeof(float) * 2;
2242
2243 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
2244 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
2245 pipelineLayoutInfo.setLayoutCount = 1;
2246 pipelineLayoutInfo.pSetLayouts = &textDescriptorSetLayout;
2247 pipelineLayoutInfo.pushConstantRangeCount = 1;
2248 pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
2249
2250 VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &textPipelineLayout));
2251
2252 VkGraphicsPipelineCreateInfo pipelineInfo{};
2253 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
2254 pipelineInfo.stageCount = 2;
2255 pipelineInfo.pStages = shaderStages;
2256 pipelineInfo.pVertexInputState = &vertexInputInfo;
2257 pipelineInfo.pInputAssemblyState = &inputAssembly;
2258 pipelineInfo.pViewportState = &viewportState;
2259 pipelineInfo.pRasterizationState = &rasterizer;
2260 pipelineInfo.pMultisampleState = &multisampling;
2261 pipelineInfo.pDepthStencilState = &depthStencil;
2262 pipelineInfo.pColorBlendState = &colorBlending;
2263 pipelineInfo.pDynamicState = &dynamicState;
2264 pipelineInfo.layout = textPipelineLayout;
2265 pipelineInfo.renderPass = renderPass;
2266 pipelineInfo.subpass = 0;
2267 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
2268
2269 VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &textPipeline));
2270
2271 vkDestroyShaderModule(device, textFragShaderModule, nullptr);
2272 vkDestroyShaderModule(device, textVertShaderModule, nullptr);
2273 }
2274 catch (const std::exception& e) {
2275 std::cerr << "Warning: Text shader compilation skipped: " << e.what() << std::endl;
2276 std::cerr << "Text rendering will be disabled. Compile text shaders with:" << std::endl;
2277 std::cerr << " glslc text_vertex.vert -o text_vert.spv" << std::endl;
2278 std::cerr << " glslc text_fragment.frag -o text_frag.spv" << std::endl;
2279 textPipeline = VK_NULL_HANDLE;
2280 }
2281 }
2282
2284 VkDescriptorPoolSize poolSize{};
2285 poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2286 poolSize.descriptorCount = static_cast<uint32_t>(swapChainImages.size());
2287
2288 VkDescriptorPoolCreateInfo poolInfo{};
2289 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
2290 poolInfo.poolSizeCount = 1;
2291 poolInfo.pPoolSizes = &poolSize;
2292 poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());
2293
2294 VK_CHECK_RESULT(vkCreateDescriptorPool(device, &poolInfo, nullptr, &textDescriptorPool));
2295 }
2296
2298 if(window != NULL) {
2299 SDL_SetWindowSize(window, width, height);
2300 w = width;
2301 h = height;
2302 vkDeviceWaitIdle(device);
2304 }
2305 }
2306
2307}
General-purpose exception class for the mx library.
Definition exception.hpp:20
std::string text() const
Retrieve the error message.
Definition exception.cpp:9
Vulkan 2-D sprite with texture, custom shader, and instancing support.
Definition vk_sprite.hpp:55
Renders SDL_ttf text into Vulkan image textures.
Definition vk_text.hpp:83
void setupTextureImage(uint32_t w, uint32_t h)
Allocate a blank texture image of the given dimensions.
Definition vk.cpp:1075
VkCommandBuffer beginSingleTimeCommands()
Begin a one-shot command buffer for transfer operations.
Definition vk.cpp:1147
VkDescriptorPool descriptorPool
Main descriptor pool.
Definition vk.hpp:381
void createVertexBuffer()
Create and upload the vertex buffer.
Definition vk.cpp:811
void updateDescriptorSets()
Write updated bindings into existing descriptor sets.
Definition vk.cpp:1313
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities)
Determine the swapchain image extent from surface capabilities.
Definition vk.cpp:221
std::unique_ptr< VKText > textRenderer
Text rendering subsystem.
Definition vk.hpp:391
VkFormat findSupportedFormat(const std::vector< VkFormat > &candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
Find a supported VkFormat from the candidate list.
Definition vk.cpp:771
VkImage depthImage
Depth/stencil image.
Definition vk.hpp:384
uint32_t indexCount
Number of indices in the quad.
Definition vk.hpp:373
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector< VkSurfaceFormatKHR > &availableFormats)
Choose the best surface format from available options.
Definition vk.cpp:199
void createDescriptorSetLayout()
Create the main descriptor set layout (UBO + sampler).
Definition vk.cpp:101
void createSurface()
Create the Vulkan surface from the SDL window.
Definition vk.cpp:365
void createTextDescriptorSetLayout()
Create the descriptor set layout for text rendering.
Definition vk.cpp:2101
void initWindow(const std::string &title, int width, int height, bool full=false)
Open the SDL2 window (called automatically by constructor).
Definition vk.cpp:15
VkPipelineLayout pipelineLayout
Pipeline layout for the main pass.
Definition vk.hpp:354
VkPipeline graphicsPipelineMatrix
3-D matrix-enabled graphics pipeline.
Definition vk.hpp:356
std::vector< VkBuffer > uniformBuffers
Per-frame uniform buffers.
Definition vk.hpp:349
void createSyncObjects()
Create semaphores and fences for frame synchronisation.
Definition vk.cpp:689
std::vector< VkCommandBuffer > commandBuffers
Per-frame command buffers.
Definition vk.hpp:363
std::string font
Path to the current TTF font.
Definition vk.hpp:334
std::vector< std::unique_ptr< VKSprite > > sprites
Owned sprite instances.
Definition vk.hpp:397
VkPipelineLayout textPipelineLayout
Pipeline layout for text rendering.
Definition vk.hpp:393
void createTextPipeline()
Create the pipeline for text rendering.
Definition vk.cpp:2117
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
Allocate a Vulkan buffer with the given usage and memory properties.
Definition vk.cpp:872
void recreateSwapChain()
Tear down and rebuild the swapchain and all dependent resources.
Definition vk.cpp:1798
void quit()
Post a quit event to stop the loop.
Definition vk.cpp:31
VkFormat depthFormat
Format used for the depth buffer.
Definition vk.hpp:387
virtual void cleanup()
Free all Vulkan resources (overridable).
Definition vk.cpp:1672
VkImageView textureImageView
View into the primary texture.
Definition vk.hpp:377
VkFence inFlightFence
CPU–GPU frame synchronisation fence.
Definition vk.hpp:367
uint32_t height
Internal width/height tracking.
Definition vk.hpp:403
void createCommandPool()
Create the graphics command pool.
Definition vk.cpp:663
VkDescriptorSetLayout spriteDescriptorSetLayout
Descriptor layout for sprites.
Definition vk.hpp:401
bool enableControllerInput()
Enable joystick and game controller subsystems on demand.
Definition vk.cpp:43
virtual void onResize()
Called when the window is resized (optional override).
Definition vk.hpp:201
VkDeviceMemory vertexBufferMemory
Memory for the vertex buffer.
Definition vk.hpp:370
int w
Current window width.
Definition vk.hpp:219
void setFont(const std::string &font, const int size=24)
Set the font used for printText().
Definition vk.cpp:35
std::vector< VkDescriptorSet > descriptorSets
Per-frame descriptor sets.
Definition vk.hpp:382
void createTextDescriptorPool()
Create the descriptor pool for text rendering.
Definition vk.cpp:2283
virtual void initVulkan()
Initialise all Vulkan objects (overridable).
Definition vk.cpp:60
VkDeviceMemory depthImageMemory
Memory for the depth image.
Definition vk.hpp:385
void createImageViews()
Create image views for every swapchain image.
Definition vk.cpp:559
void clearTextQueue()
Clear all pending text-render entries.
Definition vk.cpp:2086
VkPhysicalDevice physicalDevice
Selected GPU.
Definition vk.hpp:338
uint32_t width
Definition vk.hpp:403
void createLogicalDevice()
Create the logical device and retrieve queue handles.
Definition vk.cpp:435
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout)
Transition an image between two layouts using a pipeline barrier.
Definition vk.cpp:1026
VkDevice device
Logical device.
Definition vk.hpp:339
VkPipelineLayout spritePipelineLayout
Pipeline layout for sprites.
Definition vk.hpp:399
VkShaderModule createShaderModule(const std::vector< char > &code)
Compile a SPIR-V shader module.
Definition vk.cpp:162
VkBuffer vertexBuffer
Full-screen quad vertex buffer.
Definition vk.hpp:369
VkQueue graphicsQueue
Graphics submission queue.
Definition vk.hpp:340
void endSingleTimeCommands(VkCommandBuffer commandBuffer)
End and submit a one-shot command buffer, then wait for completion.
Definition vk.cpp:1166
void setFullScreen(const bool full)
Toggle fullscreen mode.
Definition vk.cpp:1781
VkDeviceMemory textureImageMemory
Memory for the primary texture.
Definition vk.hpp:376
VKSprite * createSprite(const std::string &pngPath, const std::string &vertexShader, const std::string &fragmentShaderPath="")
Create a sprite from a PNG file.
Definition vk.cpp:1840
VkImageLayout textureImageLayout
Current layout of the primary texture.
Definition vk.hpp:378
bool enableValidation
Enable Vulkan validation layers.
Definition vk.hpp:333
VkRenderPass renderPass
Main render pass.
Definition vk.hpp:353
void createDepthResources()
Create depth buffer image, memory, and view.
Definition vk.cpp:792
void createSpriteDescriptorPool()
Create the descriptor pool for sprite rendering.
Definition vk.cpp:2065
VkImage textureImage
Primary texture image.
Definition vk.hpp:375
virtual void createSwapChain()
Create (or recreate) the swapchain and associated images (overridable).
Definition vk.cpp:505
void createCommandBuffers()
Allocate per-frame command buffers from the command pool.
Definition vk.cpp:674
bool active
true while the event loop should keep running.
Definition vk.hpp:331
bool isDeviceSuitable(VkPhysicalDevice device)
Check whether a physical device meets all application requirements.
Definition vk.cpp:134
void updateTexture(SDL_Surface *newSurface)
Replace the primary texture from an SDL_Surface.
Definition vk.cpp:96
std::vector< VkDeviceMemory > uniformBuffersMemory
Memory for per-frame UBOs.
Definition vk.hpp:350
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
Copy data between two Vulkan buffers via a one-shot command.
Definition vk.cpp:904
std::vector< VkFramebuffer > swapChainFramebuffers
Framebuffers for each swapchain image.
Definition vk.hpp:360
virtual void cleanupSwapChain()
Destroy swapchain-dependent resources before recreation (overridable).
Definition vk.cpp:704
VkDescriptorSetLayout textDescriptorSetLayout
Descriptor layout for text.
Definition vk.hpp:395
void createSpritePipeline()
Create the pipeline for sprite rendering.
Definition vk.cpp:1917
void loop()
Run the blocking event/render loop.
Definition vk.cpp:234
SDL_Window * window
SDL window handle.
Definition vk.hpp:418
std::vector< void * > uniformBuffersMapped
Persistently mapped UBO pointers.
Definition vk.hpp:351
std::vector< VkImage > swapChainImages
Swapchain image handles.
Definition vk.hpp:345
void createUniformBuffers()
Allocate per-frame uniform buffers and map them persistently.
Definition vk.cpp:1333
VkPresentModeKHR chooseSwapPresentMode(const std::vector< VkPresentModeKHR > &availablePresentModes)
Choose the best present mode (prefers mailbox, falls back to FIFO).
Definition vk.cpp:212
void createFramebuffers()
Create framebuffers for each swapchain image view.
Definition vk.cpp:642
bool supportsWireFrame
Whether the device supports wireframe.
Definition vk.hpp:359
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags)
Create an image view with specified format and aspect flags.
Definition vk.cpp:1184
VkFormat findDepthFormat()
Select a depth buffer format supported by the physical device.
Definition vk.cpp:784
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
Copy a staging buffer into a Vulkan image.
Definition vk.cpp:1117
VkQueue presentQueue
Presentation queue.
Definition vk.hpp:341
VkDescriptorPool textDescriptorPool
Descriptor pool for text.
Definition vk.hpp:394
int h
Current window height.
Definition vk.hpp:220
void createSpriteDescriptorSetLayout()
Create the descriptor set layout for sprite rendering.
Definition vk.cpp:1901
VkSampler textureSampler
Sampler for the primary texture.
Definition vk.hpp:379
VkImageView depthImageView
View into the depth image.
Definition vk.hpp:386
virtual void proc()
Process one event/render iteration (overridable).
Definition vk.cpp:244
VkCommandPool commandPool
Command pool for graphics commands.
Definition vk.hpp:362
void createTextureSampler()
Create the texture sampler with default filter/addressing modes.
Definition vk.cpp:1207
void createRenderPass()
Create the main render pass with colour and depth attachments.
Definition vk.cpp:582
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
Find a device memory type matching the filter and property flags.
Definition vk.cpp:893
VKWindow()=default
void createTextureImage(SDL_Surface *surfacex)
Create a Vulkan texture image from an SDL surface.
Definition vk.cpp:937
virtual void event(SDL_Event &e)=0
Handle an SDL event (pure virtual).
void createTextureImageView()
Create the image view for the primary texture.
Definition vk.cpp:1180
VkSurfaceKHR surface
Window surface.
Definition vk.hpp:337
VkSemaphore imageAvailableSemaphore
Signals when a swapchain image is acquired.
Definition vk.hpp:365
void setPath(const std::string &path)
Set the asset root directory.
Definition vk.cpp:130
VkPipeline textPipeline
Pipeline for text rendering.
Definition vk.hpp:392
bool getTextDimensions(const std::string &text, int &width, int &height)
Measure a text string without rendering it.
Definition vk.cpp:2092
bool inputControllersInitialized
Whether joystick/controller input is enabled.
Definition vk.hpp:332
VkPipeline spritePipeline
Pipeline for sprite rendering.
Definition vk.hpp:398
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device)
Find queue families supporting graphics and presentation.
Definition vk.cpp:174
void createDescriptorPool()
Create the main descriptor pool.
Definition vk.cpp:1228
void resizeWindow(int width, int height)
Resize the SDL window and recreate the swapchain.
Definition vk.cpp:2297
VkDescriptorSetLayout descriptorSetLayout
Main descriptor set layout.
Definition vk.hpp:342
int font_size
Current font point size.
Definition vk.hpp:335
void createGraphicsPipeline()
(Re)create the graphics pipeline.
Definition vk.cpp:1351
VkSwapchainKHR swapChain
Active swapchain.
Definition vk.hpp:344
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &imageMemory)
Create a Vulkan image with the specified parameters.
Definition vk.cpp:995
VkDeviceMemory indexBufferMemory
Memory for the index buffer.
Definition vk.hpp:372
VkSemaphore renderFinishedSemaphore
Signals when rendering is complete.
Definition vk.hpp:366
VkPipeline graphicsPipeline
Filled-polygon graphics pipeline.
Definition vk.hpp:355
VkExtent2D swapChainExtent
Swapchain pixel dimensions.
Definition vk.hpp:347
virtual void draw()
Record rendering commands for the current frame (overridable).
Definition vk.cpp:1531
VkDescriptorPool spriteDescriptorPool
Descriptor pool for sprites.
Definition vk.hpp:400
VkInstance instance
Vulkan instance.
Definition vk.hpp:336
std::vector< VkImageView > swapChainImageViews
Image views into swapchain images.
Definition vk.hpp:348
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device)
Query swapchain support details for a physical device.
Definition vk.cpp:371
VkBuffer indexBuffer
Full-screen quad index buffer.
Definition vk.hpp:371
VkDebugUtilsMessengerEXT debugMessenger
Vulkan debug messenger (validation layers).
Definition vk.hpp:402
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering at a screen position.
Definition vk.cpp:2080
mxUtil util
Asset path and utility helpers.
Definition vk.hpp:221
void createInstance()
Create the Vulkan instance and enable extensions/layers.
Definition vk.cpp:260
void pickPhysicalDevice()
Enumerate GPUs and select the most suitable physical device.
Definition vk.cpp:391
VkFormat swapChainImageFormat
Colour format of swapchain images.
Definition vk.hpp:346
void createDescriptorSets()
Allocate main descriptor sets from the pool.
Definition vk.cpp:1254
PNG image loading and saving utilities via SDL.
Definition cfg.cpp:11
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData)
Definition vk.cpp:246
std::vector< char > readFile(const std::string &filename)
Read an entire file into a byte vector.
Definition util.cpp:77
Tracks Vulkan queue family assignments needed to create a device.
Definition vk.hpp:72
std::optional< uint32_t > presentFamily
Index of the presentation queue family.
Definition vk.hpp:74
std::optional< uint32_t > graphicsFamily
Index of the graphics queue family.
Definition vk.hpp:73
bool isComplete()
Check whether both required families have been found.
Definition vk.hpp:80
Swapchain capabilities and format/mode candidates for a physical device.
Definition vk.hpp:89
std::vector< VkPresentModeKHR > presentModes
Supported presentation modes.
Definition vk.hpp:92
VkSurfaceCapabilitiesKHR capabilities
Surface capability limits.
Definition vk.hpp:90
std::vector< VkSurfaceFormatKHR > formats
Supported colour formats.
Definition vk.hpp:91
Standard UBO layout uploaded to shaders each frame.
Definition vk.hpp:102
Per-vertex data sent to the Vulkan vertex buffer.
Definition model.cpp:34
Vulkan application window and supporting structs for libmx.
#define VK_CHECK_RESULT(f)
Definition vk.hpp:47