• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "vulkan_renderer.h"
18 
19 #include <android/asset_manager.h>
20 #include <android/log.h>
21 #include <android/native_window.h>
22 #include <assert.h>
23 #include <vulkan/vulkan.h>
24 
25 #include <set>
26 #include <string>
27 #include <vector>
28 
29 #define LOG_TAG "GpuProfilingDataApp"
30 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
31 #define VK_CHECK(x)                                 \
32     do {                                            \
33         VkResult err = x;                           \
34         if (err) {                                  \
35             LOGE("Detected Vulkan error: %d", err); \
36             abort();                                \
37         }                                           \
38     } while (0)
39 
40 namespace {
41 
42 const int MAX_FRAMES_IN_FLIGHT = 2;
43 
loadBinaryFileToVector(const char * file_path,AAssetManager * assetManager)44 std::vector<uint8_t> loadBinaryFileToVector(const char *file_path, AAssetManager *assetManager) {
45     std::vector<uint8_t> file_content;
46     assert(assetManager);
47     AAsset *file = AAssetManager_open(assetManager, file_path, AASSET_MODE_BUFFER);
48     size_t file_length = AAsset_getLength(file);
49 
50     file_content.resize(file_length);
51 
52     AAsset_read(file, file_content.data(), file_length);
53     AAsset_close(file);
54     return file_content;
55 }
56 
57 struct QueueFamilyIndices {
58     std::optional<uint32_t> graphicsFamily;
59     std::optional<uint32_t> presentFamily;
isComplete__anon9e423e100111::QueueFamilyIndices60     bool isComplete() { return graphicsFamily.has_value() && presentFamily.has_value(); }
61 };
62 
findQueueFamilies(VkPhysicalDevice device,VkSurfaceKHR surface)63 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) {
64     QueueFamilyIndices indices;
65 
66     uint32_t queueFamilyCount = 0;
67     vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
68 
69     std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
70     vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
71 
72     int i = 0;
73     for (const auto &queueFamily : queueFamilies) {
74         if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
75             indices.graphicsFamily = i;
76         }
77 
78         VkBool32 presentSupport = false;
79         vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
80         if (presentSupport) {
81             indices.presentFamily = i;
82         }
83 
84         if (indices.isComplete()) {
85             break;
86         }
87 
88         i++;
89     }
90     return indices;
91 }
92 
93 struct SwapChainSupportDetails {
94     VkSurfaceCapabilitiesKHR capabilities;
95     std::vector<VkSurfaceFormatKHR> formats;
96     std::vector<VkPresentModeKHR> presentModes;
97 };
98 
querySwapChainSupport(VkPhysicalDevice device,VkSurfaceKHR surface)99 SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface) {
100     SwapChainSupportDetails details;
101 
102     vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
103 
104     uint32_t formatCount;
105     vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
106 
107     if (formatCount != 0) {
108         details.formats.resize(formatCount);
109         vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
110     }
111 
112     uint32_t presentModeCount;
113     vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
114 
115     if (presentModeCount != 0) {
116         details.presentModes.resize(presentModeCount);
117         vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount,
118                                                   details.presentModes.data());
119     }
120     return details;
121 }
122 
hasDeviceExtensionSupport(VkPhysicalDevice device,const std::vector<const char * > & deviceExtensions)123 bool hasDeviceExtensionSupport(VkPhysicalDevice device,
124                                const std::vector<const char *> &deviceExtensions) {
125     uint32_t extensionCount;
126     vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
127 
128     std::vector<VkExtensionProperties> availableExtensions(extensionCount);
129     vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
130                                          availableExtensions.data());
131 
132     std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
133 
134     for (const auto &extension : availableExtensions) {
135         requiredExtensions.erase(extension.extensionName);
136     }
137 
138     return requiredExtensions.empty();
139 }
140 
findMemoryType(VkPhysicalDevice physicalDevice,uint32_t typeFilter,VkMemoryPropertyFlags properties)141 uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter,
142                         VkMemoryPropertyFlags properties) {
143     VkPhysicalDeviceMemoryProperties memProperties;
144     vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
145 
146     for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
147         if ((typeFilter & (1 << i)) &&
148             (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
149             return i;
150         }
151     }
152 
153     assert(false); // failed to find suitable memory type!
154     return -1;
155 }
156 
createShaderModule(VkDevice device,const std::vector<uint8_t> & code)157 VkShaderModule createShaderModule(VkDevice device, const std::vector<uint8_t> &code) {
158     VkShaderModuleCreateInfo createInfo{};
159     createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
160     createInfo.codeSize = code.size();
161     createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data());
162     VkShaderModule shaderModule;
163     VK_CHECK(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule));
164 
165     return shaderModule;
166 }
167 
168 } // namespace
169 
init()170 void VulkanRenderer::init() {
171     createInstance();
172     createSurface();
173     pickPhysicalDevice();
174     createLogicalDeviceAndQueue();
175     establishDisplaySizeIdentity();
176     createSwapChain();
177     createImageViews();
178     createRenderPass();
179     createGraphicsPipeline();
180     createFramebuffers();
181     createCommandPool();
182     createCommandBuffer();
183     createSyncObjects();
184     initialized = true;
185 }
186 
createBuffer(VkDeviceSize size,VkBufferUsageFlags usage,VkMemoryPropertyFlags properties,VkBuffer & buffer,VkDeviceMemory & bufferMemory)187 void VulkanRenderer::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
188                                   VkMemoryPropertyFlags properties, VkBuffer &buffer,
189                                   VkDeviceMemory &bufferMemory) {
190     VkBufferCreateInfo bufferInfo{};
191     bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
192     bufferInfo.size = size;
193     bufferInfo.usage = usage;
194     bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
195 
196     VK_CHECK(vkCreateBuffer(device, &bufferInfo, nullptr, &buffer));
197 
198     VkMemoryRequirements memRequirements;
199     vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
200 
201     VkMemoryAllocateInfo allocInfo{};
202     allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
203     allocInfo.allocationSize = memRequirements.size;
204     allocInfo.memoryTypeIndex =
205             findMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties);
206 
207     VK_CHECK(vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory));
208 
209     vkBindBufferMemory(device, buffer, bufferMemory, 0);
210 }
211 
reset(ANativeWindow * newWindow,AAssetManager * newManager)212 void VulkanRenderer::reset(ANativeWindow *newWindow, AAssetManager *newManager) {
213     window.reset(newWindow);
214     assetManager = newManager;
215     if (initialized) {
216         createSurface();
217         recreateSwapChain();
218     }
219 }
220 
recreateSwapChain()221 void VulkanRenderer::recreateSwapChain() {
222     vkDeviceWaitIdle(device);
223     cleanupSwapChain();
224     createSwapChain();
225     createImageViews();
226     createFramebuffers();
227 }
228 
render()229 void VulkanRenderer::render() {
230     if (!initialized) {
231         return;
232     }
233 
234     vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
235     uint32_t imageIndex;
236     VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX,
237                                             imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE,
238                                             &imageIndex);
239     if (result == VK_ERROR_OUT_OF_DATE_KHR) {
240         recreateSwapChain();
241         return;
242     }
243     assert(result == VK_SUCCESS ||
244            result == VK_SUBOPTIMAL_KHR); // failed to acquire swap chain image
245 
246     vkResetFences(device, 1, &inFlightFences[currentFrame]);
247     vkResetCommandBuffer(commandBuffers[currentFrame], 0);
248 
249     recordCommandBuffer(commandBuffers[currentFrame], imageIndex);
250 
251     VkSubmitInfo submitInfo{};
252     submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
253 
254     VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
255     VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
256     submitInfo.waitSemaphoreCount = 1;
257     submitInfo.pWaitSemaphores = waitSemaphores;
258     submitInfo.pWaitDstStageMask = waitStages;
259     submitInfo.commandBufferCount = 1;
260     submitInfo.pCommandBuffers = &commandBuffers[currentFrame];
261     VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
262     submitInfo.signalSemaphoreCount = 1;
263     submitInfo.pSignalSemaphores = signalSemaphores;
264 
265     VK_CHECK(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]));
266 
267     VkPresentInfoKHR presentInfo{};
268     presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
269 
270     presentInfo.waitSemaphoreCount = 1;
271     presentInfo.pWaitSemaphores = signalSemaphores;
272 
273     VkSwapchainKHR swapChains[] = {swapChain};
274     presentInfo.swapchainCount = 1;
275     presentInfo.pSwapchains = swapChains;
276     presentInfo.pImageIndices = &imageIndex;
277     presentInfo.pResults = nullptr;
278 
279     result = vkQueuePresentKHR(presentQueue, &presentInfo);
280     if (result == VK_ERROR_OUT_OF_DATE_KHR) {
281         recreateSwapChain();
282     }
283     currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
284 }
285 
recordCommandBuffer(VkCommandBuffer commandBuffer,uint32_t imageIndex)286 void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
287     VkCommandBufferBeginInfo beginInfo{};
288     beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
289     beginInfo.flags = 0;
290     beginInfo.pInheritanceInfo = nullptr;
291 
292     VK_CHECK(vkBeginCommandBuffer(commandBuffer, &beginInfo));
293 
294     VkRenderPassBeginInfo renderPassInfo{};
295     renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
296     renderPassInfo.renderPass = renderPass;
297     renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
298     renderPassInfo.renderArea.offset = {0, 0};
299     renderPassInfo.renderArea.extent = swapChainExtent;
300 
301     VkViewport viewport{};
302     viewport.width = (float)swapChainExtent.width;
303     viewport.height = (float)swapChainExtent.height;
304     viewport.minDepth = 0.0f;
305     viewport.maxDepth = 1.0f;
306     vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
307 
308     VkRect2D scissor{};
309     scissor.extent = swapChainExtent;
310     vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
311 
312     VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
313 
314     renderPassInfo.clearValueCount = 1;
315     renderPassInfo.pClearValues = &clearColor;
316     vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
317     vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
318 
319     vkCmdDraw(commandBuffer, 3, 1, 0, 0);
320     vkCmdEndRenderPass(commandBuffer);
321     VK_CHECK(vkEndCommandBuffer(commandBuffer));
322 }
323 
cleanupSwapChain()324 void VulkanRenderer::cleanupSwapChain() {
325     for (size_t i = 0; i < swapChainFramebuffers.size(); i++) {
326         vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr);
327     }
328 
329     for (size_t i = 0; i < swapChainImageViews.size(); i++) {
330         vkDestroyImageView(device, swapChainImageViews[i], nullptr);
331     }
332 
333     vkDestroySwapchainKHR(device, swapChain, nullptr);
334 }
335 
cleanup()336 void VulkanRenderer::cleanup() {
337     vkDeviceWaitIdle(device);
338     cleanupSwapChain();
339 
340     for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
341         vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
342         vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
343         vkDestroyFence(device, inFlightFences[i], nullptr);
344     }
345     vkDestroyCommandPool(device, commandPool, nullptr);
346     vkDestroyPipeline(device, graphicsPipeline, nullptr);
347     vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
348     vkDestroyRenderPass(device, renderPass, nullptr);
349     vkDestroyDevice(device, nullptr);
350     vkDestroySurfaceKHR(instance, surface, nullptr);
351     vkDestroyInstance(instance, nullptr);
352     initialized = false;
353 }
354 
createInstance()355 void VulkanRenderer::createInstance() {
356     std::vector<const char *> requiredExtensions = {"VK_KHR_surface", "VK_KHR_android_surface"};
357 
358     VkApplicationInfo appInfo{};
359     appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
360     appInfo.pApplicationName = "TestApp";
361     appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
362     appInfo.pEngineName = "No Engine";
363     appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
364     appInfo.apiVersion = VK_API_VERSION_1_1;
365 
366     VkInstanceCreateInfo createInfo{};
367     createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
368     createInfo.pApplicationInfo = &appInfo;
369     createInfo.enabledExtensionCount = (uint32_t)requiredExtensions.size();
370     createInfo.ppEnabledExtensionNames = requiredExtensions.data();
371     createInfo.pApplicationInfo = &appInfo;
372     createInfo.enabledLayerCount = 0;
373     createInfo.pNext = nullptr;
374     VK_CHECK(vkCreateInstance(&createInfo, nullptr, &instance));
375 
376     uint32_t extensionCount = 0;
377     vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
378     std::vector<VkExtensionProperties> extensions(extensionCount);
379     vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
380 }
381 
createSurface()382 void VulkanRenderer::createSurface() {
383     assert(window != nullptr); // window not initialized
384     const VkAndroidSurfaceCreateInfoKHR
385             create_info{.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
386                         .pNext = nullptr,
387                         .flags = 0,
388                         .window = window.get()};
389 
390     VK_CHECK(vkCreateAndroidSurfaceKHR(instance, &create_info, nullptr /* pAllocator */, &surface));
391 }
392 
isDeviceSuitable(VkPhysicalDevice device)393 bool VulkanRenderer::isDeviceSuitable(VkPhysicalDevice device) {
394     QueueFamilyIndices indices = findQueueFamilies(device, surface);
395     bool extensionsSupported = hasDeviceExtensionSupport(device, deviceExtensions);
396     bool swapChainAdequate = false;
397     if (extensionsSupported) {
398         SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device, surface);
399         swapChainAdequate =
400                 !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
401     }
402     return indices.isComplete() && extensionsSupported && swapChainAdequate;
403 }
404 
pickPhysicalDevice()405 void VulkanRenderer::pickPhysicalDevice() {
406     uint32_t deviceCount = 0;
407     vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
408 
409     assert(deviceCount > 0); // failed to find GPUs with Vulkan support!
410 
411     std::vector<VkPhysicalDevice> devices(deviceCount);
412     vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
413 
414     for (const auto &device : devices) {
415         if (isDeviceSuitable(device)) {
416             physicalDevice = device;
417             break;
418         }
419     }
420 
421     assert(physicalDevice != VK_NULL_HANDLE); // failed to find a suitable GPU!
422 }
423 
createLogicalDeviceAndQueue()424 void VulkanRenderer::createLogicalDeviceAndQueue() {
425     QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
426     std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
427     std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(),
428                                               indices.presentFamily.value()};
429     float queuePriority = 1.0f;
430     for (uint32_t queueFamily : uniqueQueueFamilies) {
431         VkDeviceQueueCreateInfo queueCreateInfo{};
432         queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
433         queueCreateInfo.queueFamilyIndex = queueFamily;
434         queueCreateInfo.queueCount = 1;
435         queueCreateInfo.pQueuePriorities = &queuePriority;
436         queueCreateInfos.push_back(queueCreateInfo);
437     }
438 
439     VkPhysicalDeviceFeatures deviceFeatures{};
440 
441     VkDeviceCreateInfo createInfo{};
442     createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
443     createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
444     createInfo.pQueueCreateInfos = queueCreateInfos.data();
445     createInfo.pEnabledFeatures = &deviceFeatures;
446     createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
447     createInfo.ppEnabledExtensionNames = deviceExtensions.data();
448     createInfo.enabledLayerCount = 0;
449 
450     VK_CHECK(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device));
451 
452     vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
453     vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
454 }
455 
chooseSwapExtent(const VkSurfaceCapabilitiesKHR & capabilities)456 VkExtent2D VulkanRenderer::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities) {
457     if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
458         return capabilities.currentExtent;
459     } else {
460         int32_t width = ANativeWindow_getWidth(window.get());
461         int32_t height = ANativeWindow_getHeight(window.get());
462         VkExtent2D actualExtent = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
463 
464         actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width,
465                                         capabilities.maxImageExtent.width);
466         actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height,
467                                          capabilities.maxImageExtent.height);
468         return actualExtent;
469     }
470 }
471 
establishDisplaySizeIdentity()472 void VulkanRenderer::establishDisplaySizeIdentity() {
473     VkSurfaceCapabilitiesKHR capabilities;
474     vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &capabilities);
475 
476     uint32_t width = capabilities.currentExtent.width;
477     uint32_t height = capabilities.currentExtent.height;
478     if (capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
479         capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
480         // Swap to get identity width and height
481         capabilities.currentExtent.height = width;
482         capabilities.currentExtent.width = height;
483     }
484 
485     displaySizeIdentity = capabilities.currentExtent;
486 }
487 
createSwapChain()488 void VulkanRenderer::createSwapChain() {
489     SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice, surface);
490 
491     auto chooseSwapSurfaceFormat = [](const std::vector<VkSurfaceFormatKHR> &availableFormats) {
492         for (const auto &availableFormat : availableFormats) {
493             if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
494                 availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
495                 return availableFormat;
496             }
497         }
498         return availableFormats[0];
499     };
500 
501     VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
502 
503     VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
504 
505     uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
506     if (swapChainSupport.capabilities.maxImageCount > 0 &&
507         imageCount > swapChainSupport.capabilities.maxImageCount) {
508         imageCount = swapChainSupport.capabilities.maxImageCount;
509     }
510     pretransformFlag = swapChainSupport.capabilities.currentTransform;
511 
512     VkSwapchainCreateInfoKHR createInfo{};
513     createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
514     createInfo.surface = surface;
515     createInfo.minImageCount = imageCount;
516     createInfo.imageFormat = surfaceFormat.format;
517     createInfo.imageColorSpace = surfaceFormat.colorSpace;
518     createInfo.imageExtent = displaySizeIdentity;
519     createInfo.imageArrayLayers = 1;
520     createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
521     createInfo.preTransform = pretransformFlag;
522 
523     QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
524     uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
525 
526     if (indices.graphicsFamily != indices.presentFamily) {
527         createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
528         createInfo.queueFamilyIndexCount = 2;
529         createInfo.pQueueFamilyIndices = queueFamilyIndices;
530     } else {
531         createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
532         createInfo.queueFamilyIndexCount = 0;
533         createInfo.pQueueFamilyIndices = nullptr;
534     }
535     createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
536     createInfo.presentMode = presentMode;
537     createInfo.clipped = VK_TRUE;
538     createInfo.oldSwapchain = VK_NULL_HANDLE;
539 
540     VK_CHECK(vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain));
541 
542     vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
543     swapChainImages.resize(imageCount);
544     vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
545 
546     swapChainImageFormat = surfaceFormat.format;
547     swapChainExtent = displaySizeIdentity;
548 }
549 
createImageViews()550 void VulkanRenderer::createImageViews() {
551     swapChainImageViews.resize(swapChainImages.size());
552     for (size_t i = 0; i < swapChainImages.size(); i++) {
553         VkImageViewCreateInfo createInfo{};
554         createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
555         createInfo.image = swapChainImages[i];
556         createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
557         createInfo.format = swapChainImageFormat;
558         createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
559         createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
560         createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
561         createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
562         createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
563         createInfo.subresourceRange.baseMipLevel = 0;
564         createInfo.subresourceRange.levelCount = 1;
565         createInfo.subresourceRange.baseArrayLayer = 0;
566         createInfo.subresourceRange.layerCount = 1;
567         VK_CHECK(vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]));
568     }
569 }
570 
createRenderPass()571 void VulkanRenderer::createRenderPass() {
572     VkAttachmentDescription colorAttachment{};
573     colorAttachment.format = swapChainImageFormat;
574     colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
575 
576     colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
577     colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
578 
579     colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
580     colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
581 
582     colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
583     colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
584 
585     VkAttachmentReference colorAttachmentRef{};
586     colorAttachmentRef.attachment = 0;
587     colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
588 
589     VkSubpassDescription subpass{};
590     subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
591     subpass.colorAttachmentCount = 1;
592     subpass.pColorAttachments = &colorAttachmentRef;
593 
594     VkRenderPassCreateInfo renderPassInfo{};
595     renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
596     renderPassInfo.attachmentCount = 1;
597     renderPassInfo.pAttachments = &colorAttachment;
598     renderPassInfo.subpassCount = 1;
599     renderPassInfo.pSubpasses = &subpass;
600     renderPassInfo.dependencyCount = 0;
601     renderPassInfo.pDependencies = nullptr;
602 
603     VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass));
604 }
605 
createGraphicsPipeline()606 void VulkanRenderer::createGraphicsPipeline() {
607     auto vertShaderCode = loadBinaryFileToVector("shaders/shader.vert.spv", assetManager);
608     auto fragShaderCode = loadBinaryFileToVector("shaders/shader.frag.spv", assetManager);
609 
610     VkShaderModule vertShaderModule = createShaderModule(device, vertShaderCode);
611     VkShaderModule fragShaderModule = createShaderModule(device, fragShaderCode);
612 
613     VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
614     vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
615     vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
616     vertShaderStageInfo.module = vertShaderModule;
617     vertShaderStageInfo.pName = "main";
618 
619     VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
620     fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
621     fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
622     fragShaderStageInfo.module = fragShaderModule;
623     fragShaderStageInfo.pName = "main";
624 
625     VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
626 
627     VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
628     vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
629     vertexInputInfo.vertexBindingDescriptionCount = 0;
630     vertexInputInfo.pVertexBindingDescriptions = nullptr;
631     vertexInputInfo.vertexAttributeDescriptionCount = 0;
632     vertexInputInfo.pVertexAttributeDescriptions = nullptr;
633 
634     VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
635     inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
636     inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
637     inputAssembly.primitiveRestartEnable = VK_FALSE;
638 
639     VkPipelineViewportStateCreateInfo viewportState{};
640     viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
641     viewportState.viewportCount = 1;
642     viewportState.scissorCount = 1;
643 
644     VkPipelineRasterizationStateCreateInfo rasterizer{};
645     rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
646     rasterizer.depthClampEnable = VK_FALSE;
647     rasterizer.rasterizerDiscardEnable = VK_FALSE;
648     rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
649     rasterizer.lineWidth = 1.0f;
650 
651     rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
652     rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
653 
654     rasterizer.depthBiasEnable = VK_FALSE;
655 
656     VkPipelineMultisampleStateCreateInfo multisampling{};
657     multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
658     multisampling.sampleShadingEnable = VK_FALSE;
659     multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
660 
661     VkPipelineColorBlendAttachmentState colorBlendAttachment{};
662     colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
663             VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
664     colorBlendAttachment.blendEnable = VK_FALSE;
665 
666     VkPipelineColorBlendStateCreateInfo colorBlending{};
667     colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
668     colorBlending.logicOpEnable = VK_FALSE;
669     colorBlending.logicOp = VK_LOGIC_OP_COPY;
670     colorBlending.attachmentCount = 1;
671     colorBlending.pAttachments = &colorBlendAttachment;
672 
673     VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
674     pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
675     pipelineLayoutInfo.setLayoutCount = 0;
676     pipelineLayoutInfo.pSetLayouts = nullptr;
677     pipelineLayoutInfo.pushConstantRangeCount = 0;
678     pipelineLayoutInfo.pPushConstantRanges = nullptr;
679 
680     VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
681     std::vector<VkDynamicState> dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT,
682                                                        VK_DYNAMIC_STATE_SCISSOR};
683     VkPipelineDynamicStateCreateInfo dynamicStateCI{};
684     dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
685     dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
686     dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
687 
688     VkGraphicsPipelineCreateInfo pipelineInfo{};
689     pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
690     pipelineInfo.stageCount = 2;
691     pipelineInfo.pStages = shaderStages;
692     pipelineInfo.pVertexInputState = &vertexInputInfo;
693     pipelineInfo.pInputAssemblyState = &inputAssembly;
694     pipelineInfo.pViewportState = &viewportState;
695     pipelineInfo.pRasterizationState = &rasterizer;
696     pipelineInfo.pMultisampleState = &multisampling;
697     pipelineInfo.pDepthStencilState = nullptr;
698     pipelineInfo.pColorBlendState = &colorBlending;
699     pipelineInfo.pDynamicState = &dynamicStateCI;
700     pipelineInfo.layout = pipelineLayout;
701     pipelineInfo.renderPass = renderPass;
702     pipelineInfo.subpass = 0;
703     pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
704     pipelineInfo.basePipelineIndex = -1;
705 
706     VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr,
707                                        &graphicsPipeline));
708     vkDestroyShaderModule(device, fragShaderModule, nullptr);
709     vkDestroyShaderModule(device, vertShaderModule, nullptr);
710 }
711 
createFramebuffers()712 void VulkanRenderer::createFramebuffers() {
713     swapChainFramebuffers.resize(swapChainImageViews.size());
714     for (size_t i = 0; i < swapChainImageViews.size(); i++) {
715         VkImageView attachments[] = {swapChainImageViews[i]};
716 
717         VkFramebufferCreateInfo framebufferInfo{};
718         framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
719         framebufferInfo.renderPass = renderPass;
720         framebufferInfo.attachmentCount = 1;
721         framebufferInfo.pAttachments = attachments;
722         framebufferInfo.width = swapChainExtent.width;
723         framebufferInfo.height = swapChainExtent.height;
724         framebufferInfo.layers = 1;
725 
726         VK_CHECK(vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]));
727     }
728 }
729 
createCommandPool()730 void VulkanRenderer::createCommandPool() {
731     QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice, surface);
732     VkCommandPoolCreateInfo poolInfo{};
733     poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
734     poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
735     poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
736     VK_CHECK(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool));
737 }
738 
createCommandBuffer()739 void VulkanRenderer::createCommandBuffer() {
740     commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
741     VkCommandBufferAllocateInfo allocInfo{};
742     allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
743     allocInfo.commandPool = commandPool;
744     allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
745     allocInfo.commandBufferCount = commandBuffers.size();
746 
747     VK_CHECK(vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()));
748 }
749 
createSyncObjects()750 void VulkanRenderer::createSyncObjects() {
751     imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
752     renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
753     inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
754 
755     VkSemaphoreCreateInfo semaphoreInfo{};
756     semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
757 
758     VkFenceCreateInfo fenceInfo{};
759     fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
760     fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
761     for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
762         VK_CHECK(vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]));
763 
764         VK_CHECK(vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]));
765 
766         VK_CHECK(vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]));
767     }
768 }