diff --git a/hello-vulkan/app/src/main/cpp/CMakeLists.txt b/hello-vulkan/app/src/main/cpp/CMakeLists.txt index 540d6a174..39515c986 100644 --- a/hello-vulkan/app/src/main/cpp/CMakeLists.txt +++ b/hello-vulkan/app/src/main/cpp/CMakeLists.txt @@ -31,7 +31,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall") add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR=1) add_library(${PROJECT_NAME} SHARED - vk_main.cpp) + vk_main.cpp + hellovk.cpp) # add lib dependencies target_link_libraries(${PROJECT_NAME} PUBLIC diff --git a/hello-vulkan/app/src/main/cpp/hellovk.cpp b/hello-vulkan/app/src/main/cpp/hellovk.cpp new file mode 100644 index 000000000..a24463c74 --- /dev/null +++ b/hello-vulkan/app/src/main/cpp/hellovk.cpp @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hellovk.h" + +namespace vkt { + +std::vector LoadBinaryFileToVector(const char *file_path, + AAssetManager *assetManager) { + std::vector file_content; + assert(assetManager); + AAsset *file = + AAssetManager_open(assetManager, file_path, AASSET_MODE_BUFFER); + size_t file_length = AAsset_getLength(file); + + file_content.resize(file_length); + + AAsset_read(file, file_content.data(), file_length); + AAsset_close(file); + return file_content; +} + +const char *toStringMessageSeverity(VkDebugUtilsMessageSeverityFlagBitsEXT s) { + switch (s) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + return "VERBOSE"; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + return "ERROR"; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + return "WARNING"; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + return "INFO"; + default: + return "UNKNOWN"; + } +} +const char *toStringMessageType(VkDebugUtilsMessageTypeFlagsEXT s) { + if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) + return "General | Validation | Performance"; + if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) + return "Validation | Performance"; + if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) + return "General | Performance"; + if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) + return "Performance"; + if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)) + return "General | Validation"; + if (s == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) return "Validation"; + if (s == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) return "General"; + return "Unknown"; +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL +debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, + void * /* pUserData */) { + auto ms = toStringMessageSeverity(messageSeverity); + auto mt = toStringMessageType(messageType); + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + LOGE("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); + } else if (messageSeverity & + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + LOGW("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); + } else { + LOGI("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); + } + + return VK_FALSE; +} + +static void populateDebugMessengerCreateInfo( + VkDebugUtilsMessengerCreateInfoEXT &createInfo) { + createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = debugCallback; +} + +static VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkDebugUtilsMessengerEXT *pDebugMessenger) { + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +static void DestroyDebugUtilsMessengerEXT( + VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks *pAllocator) { + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } +} + +void HelloVK::initVulkan() { + createInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDeviceAndQueue(); + setupDebugMessenger(); + establishDisplaySizeIdentity(); + createSwapChain(); + createImageViews(); + createRenderPass(); + createDescriptorSetLayout(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); + createGraphicsPipeline(); + createFrameBuffers(); + createCommandPool(); + createCommandBuffer(); + createSyncObjects(); + initialized = true; +} + +/* + * Create a buffer with specified usage and memory properties + * i.e a uniform buffer which uses HOST_COHERENT memory + * Upon creation, these buffers will list memory requirements which need to be + * satisfied by the device in use in order to be created. + */ +void HelloVK::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, + VkMemoryPropertyFlags properties, VkBuffer &buffer, + VkDeviceMemory &bufferMemory) { + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK(vkCreateBuffer(device, &bufferInfo, nullptr, &buffer)); + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = + findMemoryType(memRequirements.memoryTypeBits, properties); + + VK_CHECK(vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory)); + + vkBindBufferMemory(device, buffer, bufferMemory, 0); +} + +/* + * Finds the index of the memory heap which matches a particular buffer's memory + * requirements. Vulkan manages these requirements as a bitset, in this case + * expressed through a uint32_t. + */ +uint32_t HelloVK::findMemoryType(uint32_t typeFilter, + VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & + properties) == properties) { + return i; + } + } + + LOGE("Failed to find suitable memory type for properties(%d)!", properties); + assert(false); +} + +void HelloVK::createUniformBuffers() { + LOGV("HelloVK::createUniformBuffers"); + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); + uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + uniformBuffers[i], uniformBuffersMemory[i]); + } +} + +void HelloVK::createDescriptorSetLayout() { + LOGV("HelloVK::createDescriptorSetLayout"); + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + uboLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + VK_CHECK(vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, + &descriptorSetLayout)); +} + +void HelloVK::reset(ANativeWindow *newWindow, AAssetManager *newManager) { + window.reset(newWindow); + assetManager = newManager; + if (initialized) { + createSurface(); + recreateSwapChain(); + } +} + +void HelloVK::recreateSwapChain() { + vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); + createImageViews(); + createFrameBuffers(); +} + +void HelloVK::render() { + if (orientationChanged) { + onOrientationChange(); + } + + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, + UINT64_MAX); + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR( + device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], + VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + return; + } + assert(result == VK_SUCCESS || + result == VK_SUBOPTIMAL_KHR); // failed to acquire swap chain image + updateUniformBuffer(currentFrame); + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + vkResetCommandBuffer(commandBuffers[currentFrame], 0); + + recordCommandBuffer(commandBuffers[currentFrame], imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkPipelineStageFlags waitStages[] = { + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + VK_CHECK(vkQueueSubmit(graphicsQueue, 1, &submitInfo, + inFlightFences[currentFrame])); + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {swapChain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + result = vkQueuePresentKHR(presentQueue, &presentInfo); + if (result == VK_SUBOPTIMAL_KHR) { + orientationChanged = true; + } else if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + } else { + assert(result == VK_SUCCESS); // failed to present swap chain image! + } + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; +} + +/* + * getPreRotationMatrix handles screen rotation with 3 hardcoded rotation + * matrices (detailed below). We skip the 180 degrees rotation. + */ +void getPreRotationMatrix(const VkSurfaceTransformFlagBitsKHR &preTransformFlag, + std::array &mat) { + // mat is initialized to the identity matrix + mat = {1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; + if (preTransformFlag & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) { + // mat is set to a 90 deg rotation matrix + mat = {0., 1., 0., 0., -1., 0, 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; + } else if (preTransformFlag & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { + // mat is set to 270 deg rotation matrix + mat = {0., -1., 0., 0., 1., 0, 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; + } +} + +void HelloVK::createDescriptorPool() { + LOGV("HelloVK::createDescriptorPool"); + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); + + VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool)); +} + +void HelloVK::createDescriptorSets() { + LOGV("HelloVK::createDescriptorSets"); + std::vector layouts(MAX_FRAMES_IN_FLIGHT, + descriptorSetLayout); + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); + allocInfo.pSetLayouts = layouts.data(); + + descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); + VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data())); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = uniformBuffers[i]; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); + } +} + +void HelloVK::updateUniformBuffer(uint32_t currentImage) { + UniformBufferObject ubo{}; + getPreRotationMatrix(preTransformFlag, ubo.mvp); + void *data; + VK_CHECK(vkMapMemory(device, uniformBuffersMemory[currentImage], 0, + sizeof(ubo), 0, &data)); + memcpy(data, &ubo, sizeof(ubo)); + vkUnmapMemory(device, uniformBuffersMemory[currentImage]); +} + +void HelloVK::onOrientationChange() { + recreateSwapChain(); + orientationChanged = false; +} + +void HelloVK::recordCommandBuffer(VkCommandBuffer commandBuffer, + uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + VK_CHECK(vkBeginCommandBuffer(commandBuffer, &beginInfo)); + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFrameBuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = swapChainExtent; + + VkViewport viewport{}; + viewport.width = (float)swapChainExtent.width; + viewport.height = (float)swapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.extent = swapChainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + static float grey; + grey += 0.005f; + if (grey > 1.0f) { + grey = 0.0f; + } + VkClearValue clearColor = {{{grey, grey, grey, 1.0f}}}; + + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, + VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + graphicsPipeline); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, 0, 1, &descriptorSets[currentFrame], + 0, nullptr); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + vkCmdEndRenderPass(commandBuffer); + VK_CHECK(vkEndCommandBuffer(commandBuffer)); +} + +void HelloVK::cleanupSwapChain() { + LOGV("HelloVK::cleanupSwapChain"); + for (size_t i = 0; i < swapChainFrameBuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFrameBuffers[i], nullptr); + } + + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); +} + +void HelloVK::cleanup() { + LOGV("HelloVK::cleanup"); + vkDeviceWaitIdle(device); + cleanupSwapChain(); + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(device, uniformBuffers[i], nullptr); + vkFreeMemory(device, uniformBuffersMemory[i], nullptr); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + vkDestroyCommandPool(device, commandPool, nullptr); + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + vkDestroyDevice(device, nullptr); + if (enableValidationLayers) { + DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); + } + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + initialized = false; +} + +void HelloVK::setupDebugMessenger() { + if (!enableValidationLayers) { + return; + } + + VkDebugUtilsMessengerCreateInfoEXT createInfo{}; + populateDebugMessengerCreateInfo(createInfo); + + VK_CHECK(CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, + &debugMessenger)); +} + +bool HelloVK::checkValidationLayerSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char *layerName : validationLayers) { + bool layerFound = false; + for (const auto &layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } + } + + if (!layerFound) { + return false; + } + } + return true; +} + +std::vector HelloVK::getRequiredExtensions() { + std::vector extensions; + extensions.push_back("VK_KHR_surface"); + extensions.push_back("VK_KHR_android_surface"); + if (enableValidationLayers) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + return extensions; +} + +void HelloVK::createInstance() { + LOGV("HelloVK::createInstance"); + if (enableValidationLayers) { + bool layerExists = checkValidationLayerSupport(); + if (!layerExists) { + LOGE("Could not load validation layers!"); + enableValidationLayers = false; + } + } + auto requiredExtensions = getRequiredExtensions(); + + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "Hello Vulkan"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "No Engine"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.enabledExtensionCount = (uint32_t)requiredExtensions.size(); + createInfo.ppEnabledExtensionNames = requiredExtensions.data(); + createInfo.pApplicationInfo = &appInfo; + + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + populateDebugMessengerCreateInfo(debugCreateInfo); + + if (enableValidationLayers) { + createInfo.enabledLayerCount = + static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo; + } else { + createInfo.enabledLayerCount = 0; + createInfo.pNext = nullptr; + } + VK_CHECK(vkCreateInstance(&createInfo, nullptr, &instance)); + + uint32_t extensionCount = 0; + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + std::vector extensions(extensionCount); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, + extensions.data()); + LOGI("available extensions"); + for (const auto &extension : extensions) { + LOGI("\t %s", extension.extensionName); + } +} + +/* + * createSurface can only be called after the android ecosystem has had the + * chance to provide a native window. This happens after the APP_CMD_START event + * has had a chance to be called. + * + * Notice the window.get() call which is only valid after window has been set to + * a non null value + */ +void HelloVK::createSurface() { + LOGV("HelloVK::createSurface"); + assert(window != nullptr); // window not initialized + const VkAndroidSurfaceCreateInfoKHR create_info{ + .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .window = window.get()}; + + VK_CHECK(vkCreateAndroidSurfaceKHR(instance, &create_info, + nullptr /* pAllocator */, &surface)); +} + +// BEGIN DEVICE SUITABILITY +// Functions to find a suitable physical device to execute Vulkan commands. + +QueueFamilyIndices HelloVK::findQueueFamilies( + VkPhysicalDevice candidateDevice) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(candidateDevice, &queueFamilyCount, + nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(candidateDevice, &queueFamilyCount, + queueFamilies.data()); + + int i = 0; + for (const auto &queueFamily : queueFamilies) { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(candidateDevice, i, surface, + &presentSupport); + if (presentSupport) { + indices.presentFamily = i; + } + + if (indices.isComplete()) { + break; + } + + i++; + } + return indices; +} + +bool HelloVK::checkDeviceExtensionSupport(VkPhysicalDevice candidateDevice) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(candidateDevice, nullptr, + &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties( + candidateDevice, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), + deviceExtensions.end()); + + for (const auto &extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); +} + +SwapChainSupportDetails HelloVK::querySwapChainSupport( + VkPhysicalDevice candidateDevice) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(candidateDevice, surface, + &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(candidateDevice, surface, &formatCount, + nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(candidateDevice, surface, &formatCount, + details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(candidateDevice, surface, + &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(candidateDevice, surface, + &presentModeCount, + details.presentModes.data()); + } + return details; +} + +bool HelloVK::isDeviceSuitable(VkPhysicalDevice candidateDevice) { + QueueFamilyIndices indices = findQueueFamilies(candidateDevice); + if (!indices.isComplete()) return false; + + if (!checkDeviceExtensionSupport(candidateDevice)) return false; + + SwapChainSupportDetails swapChainSupport = + querySwapChainSupport(candidateDevice); + return !swapChainSupport.formats.empty() && + !swapChainSupport.presentModes.empty(); +} + +void HelloVK::pickPhysicalDevice() { + LOGV("HelloVK::pickPhysicalDevice"); + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + assert(deviceCount > 0); // failed to find GPUs with Vulkan support! + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + for (const auto &candidateDevice : devices) { + if (isDeviceSuitable(candidateDevice)) { + physicalDevice = candidateDevice; + break; + } + } + + assert(physicalDevice != VK_NULL_HANDLE); // failed to find a suitable GPU! +} +// END DEVICE SUITABILITY + +void HelloVK::createLogicalDeviceAndQueue() { + LOGV("HelloVK::createLogicalDeviceAndQueue"); + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), + indices.presentFamily.value()}; + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures{}; + + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = + static_cast(queueCreateInfos.size()); + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.pEnabledFeatures = &deviceFeatures; + createInfo.enabledExtensionCount = + static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + if (enableValidationLayers) { + createInfo.enabledLayerCount = + static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } else { + createInfo.enabledLayerCount = 0; + } + + VK_CHECK(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)); + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); +} + +void HelloVK::establishDisplaySizeIdentity() { + VkSurfaceCapabilitiesKHR capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, + &capabilities); + + uint32_t width = capabilities.currentExtent.width; + uint32_t height = capabilities.currentExtent.height; + if (capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || + capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { + // Swap to get identity width and height + capabilities.currentExtent.height = width; + capabilities.currentExtent.width = height; + } + + displaySizeIdentity = capabilities.currentExtent; +} + +void HelloVK::createSwapChain() { + LOGV("HelloVK::createSwapChain"); + SwapChainSupportDetails swapChainSupport = + querySwapChainSupport(physicalDevice); + + auto chooseSwapSurfaceFormat = + [](const std::vector &availableFormats) { + for (const auto &availableFormat : availableFormats) { + // Check if it's a preferred format + if (availableFormat.colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + continue; + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB || + availableFormat.format == VK_FORMAT_R8G8B8A8_SRGB) { + return availableFormat; + } + } + + // Preferred formats are not supported, return the first supported + return availableFormats[0]; + }; + + VkSurfaceFormatKHR surfaceFormat = + chooseSwapSurfaceFormat(swapChainSupport.formats); + + // Please check + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html + // for a discourse on different present modes. + // + // VK_PRESENT_MODE_FIFO_KHR = Hard Vsync + // This is always supported on Android phones + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && + imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + preTransformFlag = swapChainSupport.capabilities.currentTransform; + + VkCompositeAlphaFlagBitsKHR compositeAlpha = + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + if (swapChainSupport.capabilities.supportedCompositeAlpha & + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) { + compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + } + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), + indices.presentFamily.value()}; + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = displaySizeIdentity; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + createInfo.preTransform = preTransformFlag; + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + createInfo.compositeAlpha = compositeAlpha; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + VK_CHECK(vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain)); + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, + swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = displaySizeIdentity; +} + +void HelloVK::createImageViews() { + LOGV("HelloVK::createImageViews"); + swapChainImageViews.resize(swapChainImages.size()); + + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + for (size_t i = 0; i < swapChainImages.size(); i++) { + createInfo.image = swapChainImages[i]; + VK_CHECK(vkCreateImageView(device, &createInfo, nullptr, + &swapChainImageViews[i])); + } +} + +void HelloVK::createRenderPass() { + LOGV("HelloVK::createRenderPass"); + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = swapChainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass)); +} + +/* + * Creates a graphics pipeline loading a simple vertex and fragment shader, both + * with 'main' set as entrypoint A list of standard parameters are provided: + * - The vertex input coming from the application is set to empty - we are + * hardcoding the triangle in the vertex shader. + * - The input assembly is configured to draw triangle lists + * - We intend to draw onto the whole screen, so the scissoring extent is + * specified as being the whole swapchain extent. + * - The rasterizer is set to discard fragments beyond the near and far + * planes (depthClampEnable=false) as well as sending geometry to the frame + * buffer and generate fragments for the whole area of the geometry. We consider + * geometry in terms of the clockwise order of their respective vertex input. + * - Multisampling is disabled + * - Depth and stencil testing are disabled + * - ColorBlending is set to opaque mode, meaning any new fragments will + * overwrite the ones already existing in the framebuffer + * - We utilise Vulkan's concept of dynamic state for viewport and scissoring. + * The other option is to hardcode the viewport/scissor options, + * however this means needing to recreate the whole graphics pipeline object + * when the screen is rotated. + * - The pipeline layout sends 1 uniform buffer object to the shader containing + * a 4x4 rotation matrix specified by the descriptorSetLayout. This is required + * in order to render a rotated scene when the device has been rotated. + */ +void HelloVK::createGraphicsPipeline() { + LOGV("HelloVK::createGraphicsPipeline"); + auto vertShaderCode = + LoadBinaryFileToVector("shaders/shader.vert.spv", assetManager); + auto fragShaderCode = + LoadBinaryFileToVector("shaders/shader.frag.spv", assetManager); + + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, + fragShaderStageInfo}; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = nullptr; + + VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, + &pipelineLayout)); + std::vector dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamicStateCI{}; + dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); + dynamicStateCI.dynamicStateCount = + static_cast(dynamicStateEnables.size()); + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicStateCI; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, + nullptr, &graphicsPipeline)); + vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(device, vertShaderModule, nullptr); +} + +VkShaderModule HelloVK::createShaderModule(const std::vector &code) { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + + // Satisfies alignment requirements since the allocator + // in vector ensures worst case requirements + createInfo.pCode = reinterpret_cast(code.data()); + VkShaderModule shaderModule; + VK_CHECK(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule)); + + return shaderModule; +} + +void HelloVK::createFrameBuffers() { + LOGV("HelloVK::createFrameBuffers"); + swapChainFrameBuffers.resize(swapChainImageViews.size()); + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + VkImageView attachments[] = {swapChainImageViews[i]}; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapChainExtent.width; + framebufferInfo.height = swapChainExtent.height; + framebufferInfo.layers = 1; + + VK_CHECK(vkCreateFramebuffer(device, &framebufferInfo, nullptr, + &swapChainFrameBuffers[i])); + } +} + +void HelloVK::createCommandPool() { + LOGV("HelloVK::createCommandPool"); + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + VK_CHECK(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool)); +} + +void HelloVK::createCommandBuffer() { + LOGV("HelloVK::createCommandBuffer"); + commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = commandBuffers.size(); + + VK_CHECK(vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data())); +} + +void HelloVK::createSyncObjects() { + LOGV("HelloVK::createSyncObjects"); + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VK_CHECK(vkCreateSemaphore(device, &semaphoreInfo, nullptr, + &imageAvailableSemaphores[i])); + + VK_CHECK(vkCreateSemaphore(device, &semaphoreInfo, nullptr, + &renderFinishedSemaphores[i])); + + VK_CHECK(vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i])); + } +} + +} // namespace vkt diff --git a/hello-vulkan/app/src/main/cpp/hellovk.h b/hello-vulkan/app/src/main/cpp/hellovk.h index d6e7a5f5b..bdf672b7c 100644 --- a/hello-vulkan/app/src/main/cpp/hellovk.h +++ b/hello-vulkan/app/src/main/cpp/hellovk.h @@ -40,8 +40,10 @@ namespace vkt { #define LOG_TAG "hellovkjni" -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define VK_CHECK(x) \ do { \ VkResult err = x; \ @@ -75,104 +77,6 @@ struct ANativeWindowDeleter { void operator()(ANativeWindow *window) { ANativeWindow_release(window); } }; -std::vector LoadBinaryFileToVector(const char *file_path, - AAssetManager *assetManager) { - std::vector file_content; - assert(assetManager); - AAsset *file = - AAssetManager_open(assetManager, file_path, AASSET_MODE_BUFFER); - size_t file_length = AAsset_getLength(file); - - file_content.resize(file_length); - - AAsset_read(file, file_content.data(), file_length); - AAsset_close(file); - return file_content; -} - -const char *toStringMessageSeverity(VkDebugUtilsMessageSeverityFlagBitsEXT s) { - switch (s) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - return "VERBOSE"; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - return "ERROR"; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - return "WARNING"; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - return "INFO"; - default: - return "UNKNOWN"; - } -} -const char *toStringMessageType(VkDebugUtilsMessageTypeFlagsEXT s) { - if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) - return "General | Validation | Performance"; - if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) - return "Validation | Performance"; - if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) - return "General | Performance"; - if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) - return "Performance"; - if (s == (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)) - return "General | Validation"; - if (s == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) return "Validation"; - if (s == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) return "General"; - return "Unknown"; -} - -static VKAPI_ATTR VkBool32 VKAPI_CALL -debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, - void * /* pUserData */) { - auto ms = toStringMessageSeverity(messageSeverity); - auto mt = toStringMessageType(messageType); - printf("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); - - return VK_FALSE; -} - -static void populateDebugMessengerCreateInfo( - VkDebugUtilsMessengerCreateInfoEXT &createInfo) { - createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - createInfo.pfnUserCallback = debugCallback; -} - -static VkResult CreateDebugUtilsMessengerEXT( - VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, - const VkAllocationCallbacks *pAllocator, - VkDebugUtilsMessengerEXT *pDebugMessenger) { - auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( - instance, "vkCreateDebugUtilsMessengerEXT"); - if (func != nullptr) { - return func(instance, pCreateInfo, pAllocator, pDebugMessenger); - } else { - return VK_ERROR_EXTENSION_NOT_PRESENT; - } -} - -static void DestroyDebugUtilsMessengerEXT( - VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, - const VkAllocationCallbacks *pAllocator) { - auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( - instance, "vkDestroyDebugUtilsMessengerEXT"); - if (func != nullptr) { - func(instance, debugMessenger, pAllocator); - } -} - class HelloVK { public: void initVulkan(); @@ -194,17 +98,16 @@ class HelloVK { void createRenderPass(); void createDescriptorSetLayout(); void createGraphicsPipeline(); - void createFramebuffers(); + void createFrameBuffers(); void createCommandPool(); void createCommandBuffer(); void createSyncObjects(); - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); - bool checkDeviceExtensionSupport(VkPhysicalDevice device); - bool isDeviceSuitable(VkPhysicalDevice device); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice candidateDevice); + bool checkDeviceExtensionSupport(VkPhysicalDevice candidateDevice); + bool isDeviceSuitable(VkPhysicalDevice candidateDevice); bool checkValidationLayerSupport(); - std::vector getRequiredExtensions(bool enableValidation); - SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); - VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities); + std::vector getRequiredExtensions(); + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice candidateDevice); VkShaderModule createShaderModule(const std::vector &code); void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); void recreateSwapChain(); @@ -251,7 +154,7 @@ class HelloVK { VkExtent2D swapChainExtent; VkExtent2D displaySizeIdentity; std::vector swapChainImageViews; - std::vector swapChainFramebuffers; + std::vector swapChainFrameBuffers; VkCommandPool commandPool; std::vector commandBuffers; @@ -274,1036 +177,7 @@ class HelloVK { uint32_t currentFrame = 0; bool orientationChanged = false; - VkSurfaceTransformFlagBitsKHR pretransformFlag; + VkSurfaceTransformFlagBitsKHR preTransformFlag; }; -void HelloVK::initVulkan() { - createInstance(); - createSurface(); - pickPhysicalDevice(); - createLogicalDeviceAndQueue(); - setupDebugMessenger(); - establishDisplaySizeIdentity(); - createSwapChain(); - createImageViews(); - createRenderPass(); - createDescriptorSetLayout(); - createUniformBuffers(); - createDescriptorPool(); - createDescriptorSets(); - createGraphicsPipeline(); - createFramebuffers(); - createCommandPool(); - createCommandBuffer(); - createSyncObjects(); - initialized = true; -} - -/* - * Create a buffer with specified usage and memory properties - * i.e a uniform buffer which uses HOST_COHERENT memory - * Upon creation, these buffers will list memory requirements which need to be - * satisfied by the device in use in order to be created. - */ -void HelloVK::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, - VkMemoryPropertyFlags properties, VkBuffer &buffer, - VkDeviceMemory &bufferMemory) { - VkBufferCreateInfo bufferInfo{}; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = size; - bufferInfo.usage = usage; - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK(vkCreateBuffer(device, &bufferInfo, nullptr, &buffer)); - - VkMemoryRequirements memRequirements; - vkGetBufferMemoryRequirements(device, buffer, &memRequirements); - - VkMemoryAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = - findMemoryType(memRequirements.memoryTypeBits, properties); - - VK_CHECK(vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory)); - - vkBindBufferMemory(device, buffer, bufferMemory, 0); -} - -/* - * Finds the index of the memory heap which matches a particular buffer's memory - * requirements. Vulkan manages these requirements as a bitset, in this case - * expressed through a uint32_t. - */ -uint32_t HelloVK::findMemoryType(uint32_t typeFilter, - VkMemoryPropertyFlags properties) { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { - if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & - properties) == properties) { - return i; - } - } - - assert(false); // failed to find suitable memory type! - return -1; -} - -void HelloVK::createUniformBuffers() { - VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); - uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - uniformBuffers[i], uniformBuffersMemory[i]); - } -} - -void HelloVK::createDescriptorSetLayout() { - VkDescriptorSetLayoutBinding uboLayoutBinding{}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - uboLayoutBinding.pImmutableSamplers = nullptr; - - VkDescriptorSetLayoutCreateInfo layoutInfo{}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = 1; - layoutInfo.pBindings = &uboLayoutBinding; - - VK_CHECK(vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, - &descriptorSetLayout)); -} - -void HelloVK::reset(ANativeWindow *newWindow, AAssetManager *newManager) { - window.reset(newWindow); - assetManager = newManager; - if (initialized) { - createSurface(); - recreateSwapChain(); - } -} - -void HelloVK::recreateSwapChain() { - vkDeviceWaitIdle(device); - cleanupSwapChain(); - createSwapChain(); - createImageViews(); - createFramebuffers(); -} - -void HelloVK::render() { - if (orientationChanged) { - onOrientationChange(); - } - - vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, - UINT64_MAX); - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR( - device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], - VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - recreateSwapChain(); - return; - } - assert(result == VK_SUCCESS || - result == VK_SUBOPTIMAL_KHR); // failed to acquire swap chain image - updateUniformBuffer(currentFrame); - - vkResetFences(device, 1, &inFlightFences[currentFrame]); - vkResetCommandBuffer(commandBuffers[currentFrame], 0); - - recordCommandBuffer(commandBuffers[currentFrame], imageIndex); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; - VkPipelineStageFlags waitStages[] = { - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; - VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - VK_CHECK(vkQueueSubmit(graphicsQueue, 1, &submitInfo, - inFlightFences[currentFrame])); - - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - - VkSwapchainKHR swapChains[] = {swapChain}; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapChains; - presentInfo.pImageIndices = &imageIndex; - presentInfo.pResults = nullptr; - - result = vkQueuePresentKHR(presentQueue, &presentInfo); - if (result == VK_SUBOPTIMAL_KHR) { - orientationChanged = true; - } else if (result == VK_ERROR_OUT_OF_DATE_KHR) { - recreateSwapChain(); - } else { - assert(result == VK_SUCCESS); // failed to present swap chain image! - } - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; -} - -/* - * getPrerotationMatrix handles screen rotation with 3 hardcoded rotation - * matrices (detailed below). We skip the 180 degrees rotation. - */ -void getPrerotationMatrix(const VkSurfaceCapabilitiesKHR &capabilities, - const VkSurfaceTransformFlagBitsKHR &pretransformFlag, - std::array &mat) { - // mat is initialized to the identity matrix - mat = {1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; - if (pretransformFlag & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) { - // mat is set to a 90 deg rotation matrix - mat = {0., 1., 0., 0., -1., 0, 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; - } - - else if (pretransformFlag & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { - // mat is set to 270 deg rotation matrix - mat = {0., -1., 0., 0., 1., 0, 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; - } -} - -void HelloVK::createDescriptorPool() { - VkDescriptorPoolSize poolSize{}; - poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); - - VkDescriptorPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = 1; - poolInfo.pPoolSizes = &poolSize; - poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); - - VK_CHECK(vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool)); -} - -void HelloVK::createDescriptorSets() { - std::vector layouts(MAX_FRAMES_IN_FLIGHT, - descriptorSetLayout); - VkDescriptorSetAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = descriptorPool; - allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); - allocInfo.pSetLayouts = layouts.data(); - - descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); - VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data())); - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = uniformBuffers[i]; - bufferInfo.offset = 0; - bufferInfo.range = sizeof(UniformBufferObject); - - VkWriteDescriptorSet descriptorWrite{}; - descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite.dstSet = descriptorSets[i]; - descriptorWrite.dstBinding = 0; - descriptorWrite.dstArrayElement = 0; - descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrite.descriptorCount = 1; - descriptorWrite.pBufferInfo = &bufferInfo; - - vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); - } -} - -void HelloVK::updateUniformBuffer(uint32_t currentImage) { - SwapChainSupportDetails swapChainSupport = - querySwapChainSupport(physicalDevice); - UniformBufferObject ubo{}; - getPrerotationMatrix(swapChainSupport.capabilities, pretransformFlag, - ubo.mvp); - void *data; - vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, - &data); - memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(device, uniformBuffersMemory[currentImage]); -} - -void HelloVK::onOrientationChange() { - recreateSwapChain(); - orientationChanged = false; -} - -void HelloVK::recordCommandBuffer(VkCommandBuffer commandBuffer, - uint32_t imageIndex) { - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = nullptr; - - VK_CHECK(vkBeginCommandBuffer(commandBuffer, &beginInfo)); - - VkRenderPassBeginInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; - renderPassInfo.renderArea.offset = {0, 0}; - renderPassInfo.renderArea.extent = swapChainExtent; - - VkViewport viewport{}; - viewport.width = (float)swapChainExtent.width; - viewport.height = (float)swapChainExtent.height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - - VkRect2D scissor{}; - scissor.extent = swapChainExtent; - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - static float grey; - grey += 0.005f; - if (grey > 1.0f) { - grey = 0.0f; - } - VkClearValue clearColor = {{{grey, grey, grey, 1.0f}}}; - - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, - VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - graphicsPipeline); - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipelineLayout, 0, 1, &descriptorSets[currentFrame], - 0, nullptr); - - vkCmdDraw(commandBuffer, 3, 1, 0, 0); - vkCmdEndRenderPass(commandBuffer); - VK_CHECK(vkEndCommandBuffer(commandBuffer)); -} - -void HelloVK::cleanupSwapChain() { - for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { - vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); - } - - for (size_t i = 0; i < swapChainImageViews.size(); i++) { - vkDestroyImageView(device, swapChainImageViews[i], nullptr); - } - - vkDestroySwapchainKHR(device, swapChain, nullptr); -} - -void HelloVK::cleanup() { - vkDeviceWaitIdle(device); - cleanupSwapChain(); - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroyBuffer(device, uniformBuffers[i], nullptr); - vkFreeMemory(device, uniformBuffersMemory[i], nullptr); - } - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } - vkDestroyCommandPool(device, commandPool, nullptr); - vkDestroyPipeline(device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - vkDestroyDevice(device, nullptr); - if (enableValidationLayers) { - DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); - } - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - initialized = false; -} - -void HelloVK::setupDebugMessenger() { - if (!enableValidationLayers) { - return; - } - - VkDebugUtilsMessengerCreateInfoEXT createInfo{}; - populateDebugMessengerCreateInfo(createInfo); - - VK_CHECK(CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, - &debugMessenger)); -} - -bool HelloVK::checkValidationLayerSupport() { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for (const char *layerName : validationLayers) { - bool layerFound = false; - for (const auto &layerProperties : availableLayers) { - if (strcmp(layerName, layerProperties.layerName) == 0) { - layerFound = true; - break; - } - } - - if (!layerFound) { - return false; - } - } - return true; -} - -std::vector HelloVK::getRequiredExtensions( - bool enableValidationLayers) { - std::vector extensions; - extensions.push_back("VK_KHR_surface"); - extensions.push_back("VK_KHR_android_surface"); - if (enableValidationLayers) { - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - return extensions; -} - -void HelloVK::createInstance() { - assert(!enableValidationLayers || - checkValidationLayerSupport()); // validation layers requested, but - // not available! - auto requiredExtensions = getRequiredExtensions(enableValidationLayers); - - VkApplicationInfo appInfo{}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "Hello Triangle"; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.pEngineName = "No Engine"; - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.apiVersion = VK_API_VERSION_1_0; - - VkInstanceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; - createInfo.enabledExtensionCount = (uint32_t)requiredExtensions.size(); - createInfo.ppEnabledExtensionNames = requiredExtensions.data(); - createInfo.pApplicationInfo = &appInfo; - - if (enableValidationLayers) { - VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; - createInfo.enabledLayerCount = - static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); - populateDebugMessengerCreateInfo(debugCreateInfo); - createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo; - } else { - createInfo.enabledLayerCount = 0; - createInfo.pNext = nullptr; - } - VK_CHECK(vkCreateInstance(&createInfo, nullptr, &instance)); - - uint32_t extensionCount = 0; - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); - std::vector extensions(extensionCount); - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, - extensions.data()); - LOGI("available extensions"); - for (const auto &extension : extensions) { - LOGI("\t %s", extension.extensionName); - } -} - -/* - * createSurface can only be called after the android ecosystem has had the - * chance to provide a native window. This happens after the APP_CMD_START event - * has had a chance to be called. - * - * Notice the window.get() call which is only valid after window has been set to - * a non null value - */ -void HelloVK::createSurface() { - assert(window != nullptr); // window not initialized - const VkAndroidSurfaceCreateInfoKHR create_info{ - .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, - .pNext = nullptr, - .flags = 0, - .window = window.get()}; - - VK_CHECK(vkCreateAndroidSurfaceKHR(instance, &create_info, - nullptr /* pAllocator */, &surface)); -} - -// BEGIN DEVICE SUITABILITY -// Functions to find a suitable physical device to execute Vulkan commands. - -QueueFamilyIndices HelloVK::findQueueFamilies(VkPhysicalDevice device) { - QueueFamilyIndices indices; - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, - queueFamilies.data()); - - int i = 0; - for (const auto &queueFamily : queueFamilies) { - if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - indices.graphicsFamily = i; - } - - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (presentSupport) { - indices.presentFamily = i; - } - - if (indices.isComplete()) { - break; - } - - i++; - } - return indices; -} - -bool HelloVK::checkDeviceExtensionSupport(VkPhysicalDevice device) { - uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, - nullptr); - - std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, - availableExtensions.data()); - - std::set requiredExtensions(deviceExtensions.begin(), - deviceExtensions.end()); - - for (const auto &extension : availableExtensions) { - requiredExtensions.erase(extension.extensionName); - } - - return requiredExtensions.empty(); -} - -SwapChainSupportDetails HelloVK::querySwapChainSupport( - VkPhysicalDevice device) { - SwapChainSupportDetails details; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, - &details.capabilities); - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - - if (formatCount != 0) { - details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, - details.formats.data()); - } - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, - nullptr); - - if (presentModeCount != 0) { - details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR( - device, surface, &presentModeCount, details.presentModes.data()); - } - return details; -} - -bool HelloVK::isDeviceSuitable(VkPhysicalDevice device) { - QueueFamilyIndices indices = findQueueFamilies(device); - bool extensionsSupported = checkDeviceExtensionSupport(device); - bool swapChainAdequate = false; - if (extensionsSupported) { - SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); - swapChainAdequate = !swapChainSupport.formats.empty() && - !swapChainSupport.presentModes.empty(); - } - return indices.isComplete() && extensionsSupported && swapChainAdequate; -} - -void HelloVK::pickPhysicalDevice() { - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); - - assert(deviceCount > 0); // failed to find GPUs with Vulkan support! - - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - - for (const auto &device : devices) { - if (isDeviceSuitable(device)) { - physicalDevice = device; - break; - } - } - - assert(physicalDevice != VK_NULL_HANDLE); // failed to find a suitable GPU! -} -// END DEVICE SUITABILITY - -void HelloVK::createLogicalDeviceAndQueue() { - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - std::vector queueCreateInfos; - std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), - indices.presentFamily.value()}; - float queuePriority = 1.0f; - for (uint32_t queueFamily : uniqueQueueFamilies) { - VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = queueFamily; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - queueCreateInfos.push_back(queueCreateInfo); - } - - VkPhysicalDeviceFeatures deviceFeatures{}; - - VkDeviceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.queueCreateInfoCount = - static_cast(queueCreateInfos.size()); - createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = - static_cast(deviceExtensions.size()); - createInfo.ppEnabledExtensionNames = deviceExtensions.data(); - if (enableValidationLayers) { - createInfo.enabledLayerCount = - static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); - } else { - createInfo.enabledLayerCount = 0; - } - - VK_CHECK(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)); - - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); - vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); -} - -VkExtent2D HelloVK::chooseSwapExtent( - const VkSurfaceCapabilitiesKHR &capabilities) { - if (capabilities.currentExtent.width != - std::numeric_limits::max()) { - return capabilities.currentExtent; - } else { - int32_t width = ANativeWindow_getWidth(window.get()); - int32_t height = ANativeWindow_getHeight(window.get()); - VkExtent2D actualExtent = {static_cast(width), - static_cast(height)}; - - actualExtent.width = - std::clamp(actualExtent.width, capabilities.minImageExtent.width, - capabilities.maxImageExtent.width); - actualExtent.height = - std::clamp(actualExtent.height, capabilities.minImageExtent.height, - capabilities.maxImageExtent.height); - return actualExtent; - } -} - -void HelloVK::establishDisplaySizeIdentity() { - VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, - &capabilities); - - uint32_t width = capabilities.currentExtent.width; - uint32_t height = capabilities.currentExtent.height; - if (capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || - capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { - // Swap to get identity width and height - capabilities.currentExtent.height = width; - capabilities.currentExtent.width = height; - } - - displaySizeIdentity = capabilities.currentExtent; -} - -void HelloVK::createSwapChain() { - SwapChainSupportDetails swapChainSupport = - querySwapChainSupport(physicalDevice); - - auto chooseSwapSurfaceFormat = - [](const std::vector &availableFormats) { - for (const auto &availableFormat : availableFormats) { - if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && - availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - return availableFormat; - } - } - return availableFormats[0]; - }; - - VkSurfaceFormatKHR surfaceFormat = - chooseSwapSurfaceFormat(swapChainSupport.formats); - - // Please check - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html - // for a discourse on different present modes. - // - // VK_PRESENT_MODE_FIFO_KHR = Hard Vsync - // This is always supported on Android phones - VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; - - uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; - if (swapChainSupport.capabilities.maxImageCount > 0 && - imageCount > swapChainSupport.capabilities.maxImageCount) { - imageCount = swapChainSupport.capabilities.maxImageCount; - } - pretransformFlag = swapChainSupport.capabilities.currentTransform; - - VkSwapchainCreateInfoKHR createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageColorSpace = surfaceFormat.colorSpace; - createInfo.imageExtent = displaySizeIdentity; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - createInfo.preTransform = pretransformFlag; - - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), - indices.presentFamily.value()}; - - if (indices.graphicsFamily != indices.presentFamily) { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueFamilyIndices; - } else { - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; - createInfo.pQueueFamilyIndices = nullptr; - } - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; - createInfo.presentMode = presentMode; - createInfo.clipped = VK_TRUE; - createInfo.oldSwapchain = VK_NULL_HANDLE; - - VK_CHECK(vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain)); - - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); - swapChainImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, - swapChainImages.data()); - - swapChainImageFormat = surfaceFormat.format; - swapChainExtent = displaySizeIdentity; -} - -void HelloVK::createImageViews() { - swapChainImageViews.resize(swapChainImages.size()); - for (size_t i = 0; i < swapChainImages.size(); i++) { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swapChainImages[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapChainImageFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - VK_CHECK(vkCreateImageView(device, &createInfo, nullptr, - &swapChainImageViews[i])); - } -} - -void HelloVK::createRenderPass() { - VkAttachmentDescription colorAttachment{}; - colorAttachment.format = swapChainImageFormat; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkSubpassDependency dependency{}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass)); -} - -/* - * Creates a graphics pipeline loading a simple vertex and fragment shader, both - * with 'main' set as entrypoint A list of standard parameters are provided: - * - The vertex input coming from the application is set to empty - we are - * hardcoding the triangle in the vertex shader. - * - The input assembly is configured to draw triangle lists - * - We intend to draw onto the whole screen, so the scissoring extent is - * specified as being the whole swapchain extent. - * - The rasterizer is set to discard fragmets beyond the near and far - * planes (depthClampEnable=false) as well as sending geometry to the frame - * buffer and generate fragments for the whole area of the geometry. We consider - * geometry in terms of the clockwise order of their respective vertex input. - * - Multisampling is disabled - * - Depth and stencil testing are disabled - * - ColorBlending is set to opaque mode, meaning any new fragments will - * overwrite the ones already existing in the framebuffer - * - We utilise Vulkan's concept of dynamic state for viewport and scissoring. - * The other option is to hardcode the viewport/scissor options, - * however this means needing to recreate the whole graphics pipeline object - * when the screen is rotated. - * - The pipeline layout sends 1 uniform buffer object to the shader containing - * a 4x4 rotation matrix specified by the descriptorSetLayout. This is required - * in order to render a rotated scene when the device has been rotated. - */ -void HelloVK::createGraphicsPipeline() { - auto vertShaderCode = - LoadBinaryFileToVector("shaders/shader.vert.spv", assetManager); - auto fragShaderCode = - LoadBinaryFileToVector("shaders/shader.frag.spv", assetManager); - - VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); - VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); - - VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; - vertShaderStageInfo.sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; - fragShaderStageInfo.sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, - fragShaderStageInfo}; - - VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; - vertexInputInfo.sType = - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = nullptr; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = nullptr; - - VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; - inputAssembly.sType = - VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkPipelineViewportStateCreateInfo viewportState{}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.scissorCount = 1; - - VkPipelineRasterizationStateCreateInfo rasterizer{}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; - rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 0.0f; - - VkPipelineMultisampleStateCreateInfo multisampling{}; - multisampling.sType = - VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; - multisampling.pSampleMask = nullptr; - multisampling.alphaToCoverageEnable = VK_FALSE; - multisampling.alphaToOneEnable = VK_FALSE; - - VkPipelineColorBlendAttachmentState colorBlendAttachment{}; - colorBlendAttachment.colorWriteMask = - VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlending{}; - colorBlending.sType = - VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - pipelineLayoutInfo.pushConstantRangeCount = 0; - pipelineLayoutInfo.pPushConstantRanges = nullptr; - - VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, - &pipelineLayout)); - std::vector dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR}; - VkPipelineDynamicStateCreateInfo dynamicStateCI{}; - dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); - dynamicStateCI.dynamicStateCount = - static_cast(dynamicStateEnables.size()); - - VkGraphicsPipelineCreateInfo pipelineInfo{}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pDepthStencilState = nullptr; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pDynamicState = &dynamicStateCI; - pipelineInfo.layout = pipelineLayout; - pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - - VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, - nullptr, &graphicsPipeline)); - vkDestroyShaderModule(device, fragShaderModule, nullptr); - vkDestroyShaderModule(device, vertShaderModule, nullptr); -} - -VkShaderModule HelloVK::createShaderModule(const std::vector &code) { - VkShaderModuleCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = code.size(); - - // Satisifies alignment requirements since the allocator - // in vector ensures worst case requirements - createInfo.pCode = reinterpret_cast(code.data()); - VkShaderModule shaderModule; - VK_CHECK(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule)); - - return shaderModule; -} - -void HelloVK::createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size()); - for (size_t i = 0; i < swapChainImageViews.size(); i++) { - VkImageView attachments[] = {swapChainImageViews[i]}; - - VkFramebufferCreateInfo framebufferInfo{}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = swapChainExtent.width; - framebufferInfo.height = swapChainExtent.height; - framebufferInfo.layers = 1; - - VK_CHECK(vkCreateFramebuffer(device, &framebufferInfo, nullptr, - &swapChainFramebuffers[i])); - } -} - -void HelloVK::createCommandPool() { - QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); - VkCommandPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); - VK_CHECK(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool)); -} - -void HelloVK::createCommandBuffer() { - commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = commandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = commandBuffers.size(); - - VK_CHECK(vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data())); -} - -void HelloVK::createSyncObjects() { - imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - - VkSemaphoreCreateInfo semaphoreInfo{}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - VK_CHECK(vkCreateSemaphore(device, &semaphoreInfo, nullptr, - &imageAvailableSemaphores[i])); - - VK_CHECK(vkCreateSemaphore(device, &semaphoreInfo, nullptr, - &renderFinishedSemaphores[i])); - - VK_CHECK(vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i])); - } -} - } // namespace vkt \ No newline at end of file