From 9b7b62553b5b41aceb2987306f2378b7588307b3 Mon Sep 17 00:00:00 2001 From: Serdar Kocdemir Date: Mon, 26 Feb 2024 10:46:51 +0000 Subject: [PATCH 1/3] Move member functions to a cpp file in hello-vulkan --- hello-vulkan/app/src/main/cpp/CMakeLists.txt | 3 +- hello-vulkan/app/src/main/cpp/hellovk.cpp | 1148 ++++++++++++++++++ hello-vulkan/app/src/main/cpp/hellovk.h | 1127 ----------------- 3 files changed, 1150 insertions(+), 1128 deletions(-) create mode 100644 hello-vulkan/app/src/main/cpp/hellovk.cpp 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..0b8befd06 --- /dev/null +++ b/hello-vulkan/app/src/main/cpp/hellovk.cpp @@ -0,0 +1,1148 @@ +/* + * 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); + 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); + } +} + +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 diff --git a/hello-vulkan/app/src/main/cpp/hellovk.h b/hello-vulkan/app/src/main/cpp/hellovk.h index d6e7a5f5b..897063f8f 100644 --- a/hello-vulkan/app/src/main/cpp/hellovk.h +++ b/hello-vulkan/app/src/main/cpp/hellovk.h @@ -75,104 +75,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(); @@ -277,1033 +179,4 @@ class HelloVK { 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 From 7b9978b6e8ebbde6a37d34dbad43381ae2a7198e Mon Sep 17 00:00:00 2001 From: Serdar Kocdemir Date: Mon, 26 Feb 2024 11:20:16 +0000 Subject: [PATCH 2/3] Usability improvements for hello-vulkan sample Remove redundant functions and calls Report validation layer messages to logcat Auto-disable validation layers when not found Feature support checks to avoid validation failures Rename some function and variables to fix linter and name shadowing warnings Extra logging to help with debugging --- hello-vulkan/app/src/main/cpp/hellovk.cpp | 233 ++++++++++++---------- hello-vulkan/app/src/main/cpp/hellovk.h | 21 +- 2 files changed, 137 insertions(+), 117 deletions(-) diff --git a/hello-vulkan/app/src/main/cpp/hellovk.cpp b/hello-vulkan/app/src/main/cpp/hellovk.cpp index 0b8befd06..5f8b46ec1 100644 --- a/hello-vulkan/app/src/main/cpp/hellovk.cpp +++ b/hello-vulkan/app/src/main/cpp/hellovk.cpp @@ -75,7 +75,14 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, void * /* pUserData */) { auto ms = toStringMessageSeverity(messageSeverity); auto mt = toStringMessageType(messageType); - printf("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); + 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; } @@ -131,7 +138,7 @@ void HelloVK::initVulkan() { createDescriptorPool(); createDescriptorSets(); createGraphicsPipeline(); - createFramebuffers(); + createFrameBuffers(); createCommandPool(); createCommandBuffer(); createSyncObjects(); @@ -186,11 +193,12 @@ uint32_t HelloVK::findMemoryType(uint32_t typeFilter, } } - assert(false); // failed to find suitable memory type! - return -1; + 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); @@ -205,6 +213,7 @@ void HelloVK::createUniformBuffers() { } void HelloVK::createDescriptorSetLayout() { + LOGV("HelloVK::createDescriptorSetLayout"); VkDescriptorSetLayoutBinding uboLayoutBinding{}; uboLayoutBinding.binding = 0; uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; @@ -235,7 +244,7 @@ void HelloVK::recreateSwapChain() { cleanupSwapChain(); createSwapChain(); createImageViews(); - createFramebuffers(); + createFrameBuffers(); } void HelloVK::render() { @@ -304,26 +313,24 @@ void HelloVK::render() { } /* - * getPrerotationMatrix handles screen rotation with 3 hardcoded rotation + * 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, +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) { + 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) { + } 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); @@ -338,6 +345,7 @@ void HelloVK::createDescriptorPool() { } void HelloVK::createDescriptorSets() { + LOGV("HelloVK::createDescriptorSets"); std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); VkDescriptorSetAllocateInfo allocInfo{}; @@ -369,14 +377,11 @@ void HelloVK::createDescriptorSets() { } void HelloVK::updateUniformBuffer(uint32_t currentImage) { - SwapChainSupportDetails swapChainSupport = - querySwapChainSupport(physicalDevice); UniformBufferObject ubo{}; - getPrerotationMatrix(swapChainSupport.capabilities, pretransformFlag, - ubo.mvp); + getPreRotationMatrix(preTransformFlag, ubo.mvp); void *data; - vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, - &data); + VK_CHECK(vkMapMemory(device, uniformBuffersMemory[currentImage], 0, + sizeof(ubo), 0, &data)); memcpy(data, &ubo, sizeof(ubo)); vkUnmapMemory(device, uniformBuffersMemory[currentImage]); } @@ -398,7 +403,7 @@ void HelloVK::recordCommandBuffer(VkCommandBuffer commandBuffer, VkRenderPassBeginInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; + renderPassInfo.framebuffer = swapChainFrameBuffers[imageIndex]; renderPassInfo.renderArea.offset = {0, 0}; renderPassInfo.renderArea.extent = swapChainExtent; @@ -436,8 +441,9 @@ void HelloVK::recordCommandBuffer(VkCommandBuffer commandBuffer, } void HelloVK::cleanupSwapChain() { - for (size_t i = 0; i < swapChainFramebuffers.size(); i++) { - vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + 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++) { @@ -448,6 +454,7 @@ void HelloVK::cleanupSwapChain() { } void HelloVK::cleanup() { + LOGV("HelloVK::cleanup"); vkDeviceWaitIdle(device); cleanupSwapChain(); vkDestroyDescriptorPool(device, descriptorPool, nullptr); @@ -512,8 +519,7 @@ bool HelloVK::checkValidationLayerSupport() { return true; } -std::vector HelloVK::getRequiredExtensions( - bool enableValidationLayers) { +std::vector HelloVK::getRequiredExtensions() { std::vector extensions; extensions.push_back("VK_KHR_surface"); extensions.push_back("VK_KHR_android_surface"); @@ -524,14 +530,19 @@ std::vector HelloVK::getRequiredExtensions( } void HelloVK::createInstance() { - assert(!enableValidationLayers || - checkValidationLayerSupport()); // validation layers requested, but - // not available! - auto requiredExtensions = getRequiredExtensions(enableValidationLayers); + 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 Triangle"; + appInfo.pApplicationName = "Hello Vulkan"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "No Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); @@ -577,6 +588,7 @@ void HelloVK::createInstance() { * 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, @@ -591,14 +603,16 @@ void HelloVK::createSurface() { // BEGIN DEVICE SUITABILITY // Functions to find a suitable physical device to execute Vulkan commands. -QueueFamilyIndices HelloVK::findQueueFamilies(VkPhysicalDevice device) { +QueueFamilyIndices HelloVK::findQueueFamilies( + VkPhysicalDevice candidateDevice) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + vkGetPhysicalDeviceQueueFamilyProperties(candidateDevice, &queueFamilyCount, + nullptr); std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, + vkGetPhysicalDeviceQueueFamilyProperties(candidateDevice, &queueFamilyCount, queueFamilies.data()); int i = 0; @@ -608,7 +622,8 @@ QueueFamilyIndices HelloVK::findQueueFamilies(VkPhysicalDevice device) { } VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + vkGetPhysicalDeviceSurfaceSupportKHR(candidateDevice, i, surface, + &presentSupport); if (presentSupport) { indices.presentFamily = i; } @@ -622,14 +637,14 @@ QueueFamilyIndices HelloVK::findQueueFamilies(VkPhysicalDevice device) { return indices; } -bool HelloVK::checkDeviceExtensionSupport(VkPhysicalDevice device) { +bool HelloVK::checkDeviceExtensionSupport(VkPhysicalDevice candidateDevice) { uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, - nullptr); + vkEnumerateDeviceExtensionProperties(candidateDevice, nullptr, + &extensionCount, nullptr); std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, - availableExtensions.data()); + vkEnumerateDeviceExtensionProperties( + candidateDevice, nullptr, &extensionCount, availableExtensions.data()); std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); @@ -642,46 +657,49 @@ bool HelloVK::checkDeviceExtensionSupport(VkPhysicalDevice device) { } SwapChainSupportDetails HelloVK::querySwapChainSupport( - VkPhysicalDevice device) { + VkPhysicalDevice candidateDevice) { SwapChainSupportDetails details; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(candidateDevice, surface, &details.capabilities); uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + vkGetPhysicalDeviceSurfaceFormatsKHR(candidateDevice, surface, &formatCount, + nullptr); if (formatCount != 0) { details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, + vkGetPhysicalDeviceSurfaceFormatsKHR(candidateDevice, surface, &formatCount, details.formats.data()); } uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, - nullptr); + vkGetPhysicalDeviceSurfacePresentModesKHR(candidateDevice, surface, + &presentModeCount, nullptr); if (presentModeCount != 0) { details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR( - device, surface, &presentModeCount, details.presentModes.data()); + vkGetPhysicalDeviceSurfacePresentModesKHR(candidateDevice, 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; +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); @@ -690,9 +708,9 @@ void HelloVK::pickPhysicalDevice() { std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - for (const auto &device : devices) { - if (isDeviceSuitable(device)) { - physicalDevice = device; + for (const auto &candidateDevice : devices) { + if (isDeviceSuitable(candidateDevice)) { + physicalDevice = candidateDevice; break; } } @@ -702,6 +720,7 @@ void HelloVK::pickPhysicalDevice() { // END DEVICE SUITABILITY void HelloVK::createLogicalDeviceAndQueue() { + LOGV("HelloVK::createLogicalDeviceAndQueue"); QueueFamilyIndices indices = findQueueFamilies(physicalDevice); std::vector queueCreateInfos; std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), @@ -741,27 +760,6 @@ void HelloVK::createLogicalDeviceAndQueue() { 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, @@ -780,17 +778,23 @@ void HelloVK::establishDisplaySizeIdentity() { } void HelloVK::createSwapChain() { + LOGV("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) { + // 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]; }; @@ -810,7 +814,18 @@ void HelloVK::createSwapChain() { imageCount > swapChainSupport.capabilities.maxImageCount) { imageCount = swapChainSupport.capabilities.maxImageCount; } - pretransformFlag = swapChainSupport.capabilities.currentTransform; + 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; @@ -821,12 +836,7 @@ void HelloVK::createSwapChain() { 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()}; - + createInfo.preTransform = preTransformFlag; if (indices.graphicsFamily != indices.presentFamily) { createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; createInfo.queueFamilyIndexCount = 2; @@ -836,7 +846,7 @@ void HelloVK::createSwapChain() { createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = nullptr; } - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + createInfo.compositeAlpha = compositeAlpha; createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = VK_NULL_HANDLE; @@ -853,28 +863,32 @@ void HelloVK::createSwapChain() { } 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++) { - 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() { + LOGV("HelloVK::createRenderPass"); VkAttachmentDescription colorAttachment{}; colorAttachment.format = swapChainImageFormat; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; @@ -925,7 +939,7 @@ void HelloVK::createRenderPass() { * - 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 + * - 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. @@ -942,6 +956,7 @@ void HelloVK::createRenderPass() { * 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 = @@ -1075,7 +1090,7 @@ VkShaderModule HelloVK::createShaderModule(const std::vector &code) { createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); - // Satisifies alignment requirements since the allocator + // Satisfies alignment requirements since the allocator // in vector ensures worst case requirements createInfo.pCode = reinterpret_cast(code.data()); VkShaderModule shaderModule; @@ -1084,8 +1099,9 @@ VkShaderModule HelloVK::createShaderModule(const std::vector &code) { return shaderModule; } -void HelloVK::createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size()); +void HelloVK::createFrameBuffers() { + LOGV("HelloVK::createFrameBuffers"); + swapChainFrameBuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = {swapChainImageViews[i]}; @@ -1099,11 +1115,12 @@ void HelloVK::createFramebuffers() { framebufferInfo.layers = 1; VK_CHECK(vkCreateFramebuffer(device, &framebufferInfo, nullptr, - &swapChainFramebuffers[i])); + &swapChainFrameBuffers[i])); } } void HelloVK::createCommandPool() { + LOGV("HelloVK::createCommandPool"); QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); VkCommandPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -1113,6 +1130,7 @@ void HelloVK::createCommandPool() { } void HelloVK::createCommandBuffer() { + LOGV("HelloVK::createCommandBuffer"); commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -1124,6 +1142,7 @@ void HelloVK::createCommandBuffer() { } void HelloVK::createSyncObjects() { + LOGV("HelloVK::createSyncObjects"); imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); diff --git a/hello-vulkan/app/src/main/cpp/hellovk.h b/hello-vulkan/app/src/main/cpp/hellovk.h index 897063f8f..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; \ @@ -96,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(); @@ -153,7 +154,7 @@ class HelloVK { VkExtent2D swapChainExtent; VkExtent2D displaySizeIdentity; std::vector swapChainImageViews; - std::vector swapChainFramebuffers; + std::vector swapChainFrameBuffers; VkCommandPool commandPool; std::vector commandBuffers; @@ -176,7 +177,7 @@ class HelloVK { uint32_t currentFrame = 0; bool orientationChanged = false; - VkSurfaceTransformFlagBitsKHR pretransformFlag; + VkSurfaceTransformFlagBitsKHR preTransformFlag; }; } // namespace vkt \ No newline at end of file From 56c4fd8a62742d2ed96aa895723c4ccd0af59dba Mon Sep 17 00:00:00 2001 From: Serdar Kocdemir Date: Wed, 6 Mar 2024 15:17:25 +0000 Subject: [PATCH 3/3] Fix pNext usage from stack on instance creation --- hello-vulkan/app/src/main/cpp/hellovk.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hello-vulkan/app/src/main/cpp/hellovk.cpp b/hello-vulkan/app/src/main/cpp/hellovk.cpp index 5f8b46ec1..a24463c74 100644 --- a/hello-vulkan/app/src/main/cpp/hellovk.cpp +++ b/hello-vulkan/app/src/main/cpp/hellovk.cpp @@ -555,12 +555,13 @@ void HelloVK::createInstance() { createInfo.ppEnabledExtensionNames = requiredExtensions.data(); createInfo.pApplicationInfo = &appInfo; + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + populateDebugMessengerCreateInfo(debugCreateInfo); + if (enableValidationLayers) { - VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); - populateDebugMessengerCreateInfo(debugCreateInfo); createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo; } else { createInfo.enabledLayerCount = 0;