diff --git a/android/base/vulkanandroidbase.hpp b/android/base/vulkanandroidbase.hpp new file mode 100644 index 00000000..c72d77c2 --- /dev/null +++ b/android/base/vulkanandroidbase.hpp @@ -0,0 +1,545 @@ +/* +* Shared android example base +* +* Will be replaced once the main examples get proper android support +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include "vulkanandroid.h" +#include "vulkanswapchain.hpp" +#include "vulkantools.h" + +class VulkanAndroidExampleBase +{ +private: + VkShaderModule loadShaderModule(const char *fileName, VkShaderStageFlagBits stage) + { + // Load shader from compressed asset + AAsset* asset = AAssetManager_open(app->activity->assetManager, fileName, AASSET_MODE_STREAMING); + assert(asset); + size_t size = AAsset_getLength(asset); + assert(size > 0); + + char *shaderCode = new char[size]; + AAsset_read(asset, shaderCode, size); + AAsset_close(asset); + + VkShaderModule shaderModule; + VkShaderModuleCreateInfo moduleCreateInfo; + VkResult err; + + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.pNext = NULL; + + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (uint32_t*)shaderCode; + moduleCreateInfo.flags = 0; + err = vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule); + assert(!err); + + return shaderModule; + } +public: + bool prepared = false; + + struct android_app* app; + uint32_t width; + uint32_t height; + + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkDevice device; + VkQueue queue; + VkCommandPool cmdPool; + VkCommandBuffer setupCmdBuffer = VK_NULL_HANDLE; + VkCommandBuffer postPresentCmdBuffer = VK_NULL_HANDLE; + VkCommandBuffer prePresentCmdBuffer = VK_NULL_HANDLE; + std::vector drawCmdBuffers; + VkPipelineCache pipelineCache; + VkDescriptorPool descriptorPool; + VulkanSwapChain swapChain; + std::vector shaderModules; + + uint32_t currentBuffer = 0; + struct + { + VkImage image; + VkDeviceMemory mem; + VkImageView view; + } depthStencil; + std::vectorframeBuffers; + VkRenderPass renderPass; + + struct { + VkSemaphore presentComplete; + VkSemaphore submitSignal; + } semaphores; + + VkBool32 getMemoryType(uint32_t typeBits, VkFlags properties, uint32_t * typeIndex) + { + for (uint32_t i = 0; i < 32; i++) + { + if ((typeBits & 1) == 1) + { + if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + *typeIndex = i; + return true; + } + } + typeBits >>= 1; + } + return false; + } + + void initVulkan() + { + bool libLoaded = loadVulkanLibrary(); + assert(libLoaded); + + VkResult vkRes; + + // Instance + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "Vulkan Android Example"; + appInfo.applicationVersion = 1; + appInfo.pEngineName = "VulkanAndroidExample"; + appInfo.engineVersion = 1; + // todo : Workaround to support implementations that are not using the latest SDK + appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 1); + + VkInstanceCreateInfo instanceCreateInfo = {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pApplicationInfo = &appInfo; + + vkRes = vkCreateInstance(&instanceCreateInfo, NULL, &instance); + assert(vkRes == VK_SUCCESS); + + loadVulkanFunctions(instance); + + // Device + // Always use first physical device + uint32_t gpuCount; + vkRes = vkEnumeratePhysicalDevices(instance, &gpuCount, &physicalDevice); + assert(vkRes == VK_SUCCESS); + + // Find a queue that supports graphics operations + uint32_t graphicsQueueIndex = 0; + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL); + assert(queueCount >= 1); + + std::vector queueProps; + queueProps.resize(queueCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); + + for (graphicsQueueIndex = 0; graphicsQueueIndex < queueCount; graphicsQueueIndex++) + { + if (queueProps[graphicsQueueIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) + break; + } + assert(graphicsQueueIndex < queueCount); + + // Request the queue + float queuePriorities = 0.0f; + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = graphicsQueueIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriorities; + + // Create device + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + + vkRes = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); + assert(vkRes == VK_SUCCESS); + + // Get graphics queue + vkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue); + + // Device memory properties (for finding appropriate memory types) + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + + // Swap chain + swapChain.connect(instance, physicalDevice, device); + swapChain.initSurface(app->window); + + // Command buffer pool + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + vkRes = vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool); + assert(!vkRes); + + // Pipeline cache + VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VkResult err = vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache); + assert(!err); + + // Create semaphores for synchronization + VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo(); + + err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete); + assert(!err); + err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.submitSignal); + assert(!err); + + createSetupCommandBuffer(); + startSetupCommandBuffer(); + + swapChain.create(setupCmdBuffer, &width, &height); + + setupDepthStencil(); + setupRenderPass(); + setupFrameBuffer(); + + flushSetupCommandBuffer(); + + createCommandBuffers(); + } + + void cleanUpVulkan() + { + swapChain.cleanup(); + + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + if (setupCmdBuffer != VK_NULL_HANDLE) + { + vkFreeCommandBuffers(device, cmdPool, 1, &setupCmdBuffer); + + } + vkFreeCommandBuffers(device, cmdPool, drawCmdBuffers.size(), drawCmdBuffers.data()); + vkFreeCommandBuffers(device, cmdPool, 1, &prePresentCmdBuffer); + vkFreeCommandBuffers(device, cmdPool, 1, &postPresentCmdBuffer); + + vkDestroyRenderPass(device, renderPass, nullptr); + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { + vkDestroyFramebuffer(device, frameBuffers[i], nullptr); + } + + for (auto& shaderModule : shaderModules) + { + vkDestroyShaderModule(device, shaderModule, nullptr); + } + + vkDestroyImageView(device, depthStencil.view, nullptr); + vkDestroyImage(device, depthStencil.image, nullptr); + vkFreeMemory(device, depthStencil.mem, nullptr); + + vkDestroySemaphore(device, semaphores.presentComplete, nullptr); + vkDestroySemaphore(device, semaphores.submitSignal, nullptr); + + vkDestroyPipelineCache(device, pipelineCache, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroyInstance(instance, nullptr); + + freeVulkanLibrary(); + } + + VkPipelineShaderStageCreateInfo loadShader(const char * fileName, VkShaderStageFlagBits stage) + { + VkPipelineShaderStageCreateInfo shaderStage = {}; + shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStage.stage = stage; + shaderStage.module = loadShaderModule(fileName, stage); + shaderStage.pName = "main"; + assert(shaderStage.module != NULL); + shaderModules.push_back(shaderStage.module); + return shaderStage; + } + + void createSetupCommandBuffer() + { + VkCommandBufferAllocateInfo cmdBufAllocateInfo = + vkTools::initializers::commandBufferAllocateInfo( + cmdPool, + VK_COMMAND_BUFFER_LEVEL_PRIMARY, + 1); + + VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &setupCmdBuffer); + assert(!vkRes); + } + + void startSetupCommandBuffer() + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + vkBeginCommandBuffer(setupCmdBuffer, &cmdBufInfo); + } + + void flushSetupCommandBuffer() + { + VkResult err; + + if (setupCmdBuffer == VK_NULL_HANDLE) + return; + + err = vkEndCommandBuffer(setupCmdBuffer); + assert(!err); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &setupCmdBuffer; + + err = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + assert(!err); + + err = vkQueueWaitIdle(queue); + assert(!err); + } + + void setupDepthStencil() + { + VkImageCreateInfo image = {}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = VK_FORMAT_D24_UNORM_S8_UINT; + image.extent = { width, height, 1 }; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image.flags = 0; + + VkMemoryAllocateInfo mem_alloc = {}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkImageViewCreateInfo depthStencilView = {}; + depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + depthStencilView.pNext = NULL; + depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; + depthStencilView.format = VK_FORMAT_D24_UNORM_S8_UINT; + depthStencilView.flags = 0; + depthStencilView.subresourceRange = {}; + depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + depthStencilView.subresourceRange.baseMipLevel = 0; + depthStencilView.subresourceRange.levelCount = 1; + depthStencilView.subresourceRange.baseArrayLayer = 0; + depthStencilView.subresourceRange.layerCount = 1; + + VkMemoryRequirements memReqs; + VkResult err; + + err = vkCreateImage(device, &image, nullptr, &depthStencil.image); + assert(!err); + vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); + mem_alloc.allocationSize = memReqs.size; + getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); + err = vkAllocateMemory(device, &mem_alloc, nullptr, &depthStencil.mem); + assert(!err); + + err = vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0); + assert(!err); + vkTools::setImageLayout(setupCmdBuffer, depthStencil.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + depthStencilView.image = depthStencil.image; + err = vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view); + assert(!err); + } + + void setupFrameBuffer() + { + VkImageView attachments[2]; + + // Depth/Stencil attachment is the same for all frame buffers + attachments[1] = depthStencil.view; + + VkFramebufferCreateInfo frameBufferCreateInfo = {}; + frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + frameBufferCreateInfo.pNext = NULL; + frameBufferCreateInfo.renderPass = renderPass; + frameBufferCreateInfo.attachmentCount = 2; + frameBufferCreateInfo.pAttachments = attachments; + frameBufferCreateInfo.width = width; + frameBufferCreateInfo.height = height; + frameBufferCreateInfo.layers = 1; + + // Create frame buffers for every swap chain image + frameBuffers.resize(swapChain.imageCount); + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { + attachments[0] = swapChain.buffers[i].view; + VkResult err = vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i]); + assert(!err); + } + } + + void setupRenderPass() + { + VkAttachmentDescription attachments[2]; + attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachments[1].format = VK_FORMAT_D24_UNORM_S8_UINT; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference colorReference = {}; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthReference = {}; + depthReference.attachment = 1; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorReference; + subpass.pResolveAttachments = NULL; + subpass.pDepthStencilAttachment = &depthReference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.pNext = NULL; + renderPassInfo.attachmentCount = 2; + renderPassInfo.pAttachments = attachments; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 0; + renderPassInfo.pDependencies = NULL; + + VkResult err; + + err = vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass); + assert(!err); + } + + void createCommandBuffers() + { + drawCmdBuffers.resize(swapChain.imageCount); + + VkCommandBufferAllocateInfo cmdBufAllocateInfo = + vkTools::initializers::commandBufferAllocateInfo( + cmdPool, + VK_COMMAND_BUFFER_LEVEL_PRIMARY, + drawCmdBuffers.size()); + + VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, drawCmdBuffers.data()); + assert(!vkRes); + + cmdBufAllocateInfo.commandBufferCount = 1; + // Pre present + vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &prePresentCmdBuffer); + assert(!vkRes); + // Post present + vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &postPresentCmdBuffer); + assert(!vkRes); + } + + void submitPrePresentBarrier(VkImage image) + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VkResult vkRes = vkBeginCommandBuffer(prePresentCmdBuffer, &cmdBufInfo); + assert(!vkRes); + + VkImageMemoryBarrier prePresentBarrier = vkTools::initializers::imageMemoryBarrier(); + prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + prePresentBarrier.dstAccessMask = 0; + prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + prePresentBarrier.image = image; + + vkCmdPipelineBarrier( + prePresentCmdBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_FLAGS_NONE, + 0, nullptr, // No memory barriers, + 0, nullptr, // No buffer barriers, + 1, &prePresentBarrier); + + vkRes = vkEndCommandBuffer(prePresentCmdBuffer); + assert(!vkRes); + + VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &prePresentCmdBuffer; + + vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + assert(!vkRes); + } + + void submitPostPresentBarrier(VkImage image) + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VkResult vkRes = vkBeginCommandBuffer(postPresentCmdBuffer, &cmdBufInfo); + assert(!vkRes); + + VkImageMemoryBarrier postPresentBarrier = vkTools::initializers::imageMemoryBarrier(); + postPresentBarrier.srcAccessMask = 0; + postPresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + postPresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + postPresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + postPresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + postPresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + postPresentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + postPresentBarrier.image = image; + + vkCmdPipelineBarrier( + postPresentCmdBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, nullptr, // No memory barriers, + 0, nullptr, // No buffer barriers, + 1, &postPresentBarrier); + + vkRes = vkEndCommandBuffer(postPresentCmdBuffer); + assert(!vkRes); + + VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &postPresentCmdBuffer; + + vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + assert(!vkRes); + } +}; \ No newline at end of file