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 }