diff --git a/HelloTriangleApplication.cpp b/HelloTriangleApplication.cpp new file mode 100644 index 0000000..a54b2a1 --- /dev/null +++ b/HelloTriangleApplication.cpp @@ -0,0 +1,1183 @@ +#include "HelloTriangleApplication.h" + +#include +#include + +VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo + , const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { + auto func = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); + if (func != nullptr) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { + auto func = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } +} + +std::vector HelloTriangleApplication::readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); + } + + size_t fileSize = static_cast(file.tellg()); + std::vector buffer(fileSize); + file.seekg(0); + file.read(buffer.data(), static_cast(fileSize)); + + file.close(); + + return buffer; +} + +HelloTriangleApplication::HelloTriangleApplication() +{ + +} + +void HelloTriangleApplication::run() +{ + initWindow(); + initVulkan(); + mainLoop(); + cleanup(); +} + +void HelloTriangleApplication::initWindow() +{ + glfwInit(); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + _window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); + glfwSetWindowUserPointer(_window, this); + glfwSetFramebufferSizeCallback(_window, framebufferResizeCallback); +} + +void HelloTriangleApplication::framebufferResizeCallback(GLFWwindow* window, int /*width*/, int /*height*/) { + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->framebufferResized = true; +} + + + +void HelloTriangleApplication::initVulkan() +{ +// if (enableValidationLayers && !checkValidationLayerSupport()) { +// throw std::runtime_error("validation layers requested, but not available!"); +// } + + createInstance(); + setupDebugMessenger(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + createRenderPass(); + createDescriptorSetLayout(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandPool(); + createVertexBuffer(); + createIndexBuffer(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); + createCommandBuffer(); + createSyncObjects(); +} + +void HelloTriangleApplication::createInstance() +{ + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "Vulkan App"; + 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; + + auto extensions = getRequiredExtensions(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + + populateDebugMessengerCreateInfo(debugCreateInfo); + createInfo.pNext = reinterpret_cast(&debugCreateInfo); + } else { + createInfo.enabledLayerCount = 0; + + createInfo.pNext = nullptr; + } + +// VkResult result = vkCreateInstance(&createInfo, nullptr, &_instance); + if (vkCreateInstance(&createInfo, nullptr, &_instance) != VK_SUCCESS) { + throw std::runtime_error("failed to create instance!"); + } + +} + +void HelloTriangleApplication::setupDebugMessenger() { + if (!enableValidationLayers) + { + return; + } + + VkDebugUtilsMessengerCreateInfoEXT createInfo{}; + populateDebugMessengerCreateInfo(createInfo); + + if (CreateDebugUtilsMessengerEXT(_instance, &createInfo, nullptr, &_debugMessenger) != VK_SUCCESS) { + throw std::runtime_error("failed to set up debug messenger!"); + } +} + +bool HelloTriangleApplication::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 HelloTriangleApplication::getRequiredExtensions() +{ + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + + if (enableValidationLayers) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + return extensions; +} + +void HelloTriangleApplication::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; + createInfo.pUserData = nullptr; // Optional +} + +void HelloTriangleApplication::createSurface() +{ + if (glfwCreateWindowSurface(_instance, _window, nullptr, &_surface) != VK_SUCCESS) { + throw std::runtime_error("failed to create window surface!"); + } +} + +void HelloTriangleApplication::pickPhysicalDevice() { + _physicalDevice = VK_NULL_HANDLE; + + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(_instance, &deviceCount, nullptr); + if (deviceCount == 0) { + throw std::runtime_error("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; + } + } + + if (_physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("failed to find a suitable GPU!"); + } +} + +bool HelloTriangleApplication::isDeviceSuitable(VkPhysicalDevice device) { +// VkPhysicalDeviceProperties deviceProperties; +// VkPhysicalDeviceFeatures deviceFeatures; +// vkGetPhysicalDeviceProperties(device, &deviceProperties); +// vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + +// return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && +// deviceFeatures.geometryShader; + + 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 HelloTriangleApplication::createLogicalDevice() +{ + 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; + } + + if (vkCreateDevice(_physicalDevice, &createInfo, nullptr, &_device) != VK_SUCCESS) { + throw std::runtime_error("failed to create logical device!"); + } + + vkGetDeviceQueue(_device, indices.graphicsFamily.value(), 0, &_graphicsQueue); + vkGetDeviceQueue(_device, indices.presentFamily.value(), 0, &_presentQueue); +} + +QueueFamilyIndices HelloTriangleApplication::findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + uint32_t 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; +} + +void HelloTriangleApplication::createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(_physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + 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 = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + 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; // Optional + createInfo.pQueueFamilyIndices = nullptr; // Optional + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(_device, &createInfo, nullptr, &_swapChain) != VK_SUCCESS) { + throw std::runtime_error("failed to create swap chain!"); + } + + vkGetSwapchainImagesKHR(_device, _swapChain, &imageCount, nullptr); + _swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(_device, _swapChain, &imageCount, _swapChainImages.data()); + + _swapChainImageFormat = surfaceFormat.format; + _swapChainExtent = extent; +} + +bool HelloTriangleApplication::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(); +} + +VkSurfaceFormatKHR HelloTriangleApplication::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]; +} + +VkPresentModeKHR HelloTriangleApplication::chooseSwapPresentMode(const std::vector& availablePresentModes) { + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D HelloTriangleApplication::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(_window, &width, &height); + + 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; + } +} + +SwapChainSupportDetails HelloTriangleApplication::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; +} + +void HelloTriangleApplication::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; + + if (vkCreateImageView(_device, &createInfo, nullptr, &_swapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create image views!"); + } + } +} + +void HelloTriangleApplication::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; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + 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; + + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(_device, &renderPassInfo, nullptr, &_renderPass) != VK_SUCCESS) { + throw std::runtime_error("failed to create render pass!"); + } +} + +void HelloTriangleApplication::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; // Optional + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + if (vkCreateDescriptorSetLayout(_device, &layoutInfo, nullptr, &_descriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor set layout!"); + } +} + +void HelloTriangleApplication::createGraphicsPipeline() { + auto vertShaderCode = readFile("shaders/vert.spv"); + auto fragShaderCode = readFile("shaders/frag.spv"); + + 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; + + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + std::vector dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicState{}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = static_cast(dynamicStates.size()); + dynamicState.pDynamicStates = dynamicStates.data(); + + 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_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; // Optional + rasterizer.depthBiasClamp = 0.0f; // Optional + rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + + 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; // Optional + multisampling.pSampleMask = nullptr; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + 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; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; // Optional + colorBlending.blendConstants[1] = 0.0f; // Optional + colorBlending.blendConstants[2] = 0.0f; // Optional + colorBlending.blendConstants[3] = 0.0f; // Optional + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &_descriptorSetLayout; + pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional + pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional + + if (vkCreatePipelineLayout(_device, &pipelineLayoutInfo, nullptr, &_pipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("failed to create pipeline layout!"); + } + + 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; // Optional + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = _pipelineLayout; + pipelineInfo.renderPass = _renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional + pipelineInfo.basePipelineIndex = -1; // Optional + + if (vkCreateGraphicsPipelines(_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &_graphicsPipeline) != VK_SUCCESS) { + throw std::runtime_error("failed to create graphics pipeline!"); + } + + vkDestroyShaderModule(_device, fragShaderModule, nullptr); + vkDestroyShaderModule(_device, vertShaderModule, nullptr); +} + +VkShaderModule HelloTriangleApplication::createShaderModule(const std::vector& code) +{ + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(_device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw std::runtime_error("failed to create shader module!"); + } + + return shaderModule; +} + +void HelloTriangleApplication::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; + + if (vkCreateFramebuffer(_device, &framebufferInfo, nullptr, &_swapChainFramebuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create framebuffer!"); + } + } +} + +void HelloTriangleApplication::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(); + + if (vkCreateCommandPool(_device, &poolInfo, nullptr, &_commandPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create command pool!"); + } +} + +void HelloTriangleApplication::createVertexBuffer() +{ + VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + , stagingBuffer, stagingBufferMemory); + + void* data; + vkMapMemory(_device, stagingBufferMemory, 0, bufferSize, 0, &data); + memcpy(data, vertices.data(), reinterpret_cast(bufferSize)); + vkUnmapMemory(_device, stagingBufferMemory); + + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + , _vertexBuffer, _vertexBufferMemory); + + copyBuffer(stagingBuffer, _vertexBuffer, bufferSize); + + vkDestroyBuffer(_device, stagingBuffer, nullptr); + vkFreeMemory(_device, stagingBufferMemory, nullptr); +} + +void HelloTriangleApplication::createIndexBuffer() { + VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + , stagingBuffer, stagingBufferMemory); + + void* data; + vkMapMemory(_device, stagingBufferMemory, 0, bufferSize, 0, &data); + memcpy(data, indices.data(), reinterpret_cast(bufferSize)); + vkUnmapMemory(_device, stagingBufferMemory); + + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + , _indexBuffer, _indexBufferMemory); + + copyBuffer(stagingBuffer, _indexBuffer, bufferSize); + + vkDestroyBuffer(_device, stagingBuffer, nullptr); + vkFreeMemory(_device, stagingBufferMemory, nullptr); +} + +void HelloTriangleApplication::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; + + if (vkCreateBuffer(_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + throw std::runtime_error("failed to create vertex 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); + + if (vkAllocateMemory(_device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate vertex buffer memory!"); + } + + vkBindBufferMemory(_device, buffer, bufferMemory, 0); +} + +void HelloTriangleApplication::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) +{ + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = _commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(_device, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkBufferCopy copyRegion{}; + copyRegion.srcOffset = 0; // Optional + copyRegion.dstOffset = 0; // Optional + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(_graphicsQueue); + + vkFreeCommandBuffers(_device, _commandPool, 1, &commandBuffer); +} + +uint32_t HelloTriangleApplication::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; + } + } + + throw std::runtime_error("failed to find suitable memory type!"); +} + +void HelloTriangleApplication::createUniformBuffers() { + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + _uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); + _uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); + _uniformBuffersMapped.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]); + + vkMapMemory(_device, _uniformBuffersMemory[i], 0, bufferSize, 0, &_uniformBuffersMapped[i]); + } +} + +void HelloTriangleApplication::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); + + if (vkCreateDescriptorPool(_device, &poolInfo, nullptr, &_descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor pool!"); + } +} + +void HelloTriangleApplication::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); + if (vkAllocateDescriptorSets(_device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate descriptor sets!"); + } + + 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; + descriptorWrite.pImageInfo = nullptr; // Optional + descriptorWrite.pTexelBufferView = nullptr; // Optional + + vkUpdateDescriptorSets(_device, 1, &descriptorWrite, 0, nullptr); + } +} + +void HelloTriangleApplication::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 = static_cast(_commandBuffers.size()); + + if (vkAllocateCommandBuffers(_device, &allocInfo, _commandBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } +} + +void HelloTriangleApplication::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; // Optional + beginInfo.pInheritanceInfo = nullptr; // Optional + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin recording command buffer!"); + } + + 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; + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _graphicsPipeline); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(_swapChainExtent.width); + viewport.height = static_cast(_swapChainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = _swapChainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + VkBuffer vertexBuffers[] = {_vertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + + vkCmdBindIndexBuffer(commandBuffer, _indexBuffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 0, 1, &descriptorSets[currentFrame], 0, nullptr); + vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); + + vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to record command buffer!"); + } +} + +void HelloTriangleApplication::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++) + { + if (vkCreateSemaphore(_device, &semaphoreInfo, nullptr, &_imageAvailableSemaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(_device, &semaphoreInfo, nullptr, &_renderFinishedSemaphores[i]) != VK_SUCCESS || + vkCreateFence(_device, &fenceInfo, nullptr, &_inFlightFences[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create synchronization objects for a frame!"); + } + } +} + + + +void HelloTriangleApplication::mainLoop() +{ + while (!glfwWindowShouldClose(_window)) { + glfwPollEvents(); + drawFrame(); + } + + vkDeviceWaitIdle(_device); +} + +void HelloTriangleApplication::drawFrame(){ + 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; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw std::runtime_error("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; + + if (vkQueueSubmit(_graphicsQueue, 1, &submitInfo, _inFlightFences[currentFrame]) != VK_SUCCESS) { + throw std::runtime_error("failed to submit draw command buffer!"); + } + + 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; // Optional + + vkQueuePresentKHR(_presentQueue, &presentInfo); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) + { + framebufferResized = false; + recreateSwapChain(); + } else if (result != VK_SUCCESS) { + throw std::runtime_error("failed to present swap chain image!"); + } + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; +} + +void HelloTriangleApplication::updateUniformBuffer(uint32_t currentImage) { + static auto startTime = std::chrono::high_resolution_clock::now(); + + auto currentTime = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(currentTime - startTime).count(); + + UniformBufferObject ubo{}; + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.proj = glm::perspective(glm::radians(45.0f), _swapChainExtent.width / static_cast(_swapChainExtent.height), 0.1f, 10.0f); + ubo.proj[1][1] *= -1; + + memcpy(_uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); +} + +void HelloTriangleApplication::recreateSwapChain() { + int width = 0, height = 0; + glfwGetFramebufferSize(_window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(_window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(_device); + + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createFramebuffers(); +} + + + +void HelloTriangleApplication::cleanupSwapChain() { + for (auto framebuffer : _swapChainFramebuffers) { + vkDestroyFramebuffer(_device, framebuffer, nullptr); + } + + for (auto imageView : _swapChainImageViews) { + vkDestroyImageView(_device, imageView, nullptr); + } + + vkDestroySwapchainKHR(_device, _swapChain, nullptr); +} + +void HelloTriangleApplication::cleanup() +{ + 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); + } + + vkDestroyBuffer(_device, _indexBuffer, nullptr); + vkFreeMemory(_device, _indexBufferMemory, nullptr); + + vkDestroyBuffer(_device, _vertexBuffer, nullptr); + vkFreeMemory(_device, _vertexBufferMemory, nullptr); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(_device, _renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(_device, _imageAvailableSemaphores[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); + + vkDestroySurfaceKHR(_instance, _surface, nullptr); + + if (enableValidationLayers) { + DestroyDebugUtilsMessengerEXT(_instance, _debugMessenger, nullptr); + } + + vkDestroyInstance(_instance, nullptr); + + glfwDestroyWindow(_window); + + glfwTerminate(); +} diff --git a/HelloTriangleApplication.h b/HelloTriangleApplication.h new file mode 100644 index 0000000..e210d57 --- /dev/null +++ b/HelloTriangleApplication.h @@ -0,0 +1,218 @@ +#ifndef HELLOTRIANGLEAPPLICATION_H +#define HELLOTRIANGLEAPPLICATION_H + +#ifdef NDEBUG + const bool enableValidationLayers = false; +#else + const bool enableValidationLayers = true; +#endif + +//#include + +#define VK_USE_PLATFORM_ +#define GLFW_INCLUDE_VULKAN +#include +#define GLFW_EXPOSE_NATIVE_ +#include + +#define GLM_FORCE_RADIANS +#include +#include + +#include + +#include +#include +#include +#include // Necessary for uint32_t +#include // Necessary for std::numeric_limits +#include // Necessary for std::clamp +#include +#include + +const int MAX_FRAMES_IN_FLIGHT = 2; + +struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } +}; + +struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; +}; + +struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static VkVertexInputBindingDescription getBindingDescription() { + VkVertexInputBindingDescription bindingDescription{}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + return bindingDescription; + } + + static std::array getAttributeDescriptions() { + std::array attributeDescriptions{}; + + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + + return attributeDescriptions; + } +}; + +struct UniformBufferObject { + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; +}; + +class HelloTriangleApplication +{ +public: + HelloTriangleApplication(); + void run(); + + // Message Callback + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT /*messageSeverity*/, + VkDebugUtilsMessageTypeFlagsEXT /*messageType*/,const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,void* /*pUserData*/) { + + std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; + + return VK_FALSE; + } + static std::vector readFile(const std::string& filename); + static void framebufferResizeCallback(GLFWwindow* window, int width, int height); + +private: + void initWindow(); + void initVulkan(); + void mainLoop(); + void cleanup(); + void createInstance(); + + // Enable Validation Layers + bool checkValidationLayerSupport(); + + // Message Callback + std::vector getRequiredExtensions(); + void setupDebugMessenger(); + void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo); + + void pickPhysicalDevice(); + bool isDeviceSuitable(VkPhysicalDevice device); + + void createLogicalDevice(); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + + void createSurface(); + + bool checkDeviceExtensionSupport(VkPhysicalDevice device); + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + void createSwapChain(); + + void createImageViews(); + + void createGraphicsPipeline(); + VkShaderModule createShaderModule(const std::vector& code); + void createRenderPass(); + + void createFramebuffers(); + void createCommandPool(); + void createCommandBuffer(); + void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); + + void drawFrame(); + void createSyncObjects(); + + void recreateSwapChain(); + void cleanupSwapChain(); + + void createVertexBuffer(); + uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); + void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); + void createIndexBuffer(); + + void createDescriptorSetLayout(); + void createUniformBuffers(); + void updateUniformBuffer(uint32_t currentImage); + void createDescriptorPool(); + void createDescriptorSets(); + + const int32_t WIDTH = 800; + const int32_t HEIGHT = 600; + const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" + }; + const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + const std::vector vertices = { + {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, + {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, + {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, + {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} + }; + const std::vector indices = { + 0, 1, 2, 2, 3, 0 + }; + + uint32_t currentFrame = 0; + bool framebufferResized = false; + + GLFWwindow *_window; + VkInstance _instance; + VkDebugUtilsMessengerEXT _debugMessenger; + VkPhysicalDevice _physicalDevice; + VkDevice _device; + VkQueue _graphicsQueue; + VkSurfaceKHR _surface; + VkQueue _presentQueue; + VkSwapchainKHR _swapChain; + std::vector _swapChainImages; + VkFormat _swapChainImageFormat; + VkExtent2D _swapChainExtent; + std::vector _swapChainImageViews; + VkRenderPass _renderPass; + VkDescriptorSetLayout _descriptorSetLayout; + VkPipelineLayout _pipelineLayout; + VkPipeline _graphicsPipeline; + std::vector _swapChainFramebuffers; + VkCommandPool _commandPool; + std::vector _commandBuffers; + std::vector _imageAvailableSemaphores; + std::vector _renderFinishedSemaphores; + std::vector _inFlightFences; + VkBuffer _vertexBuffer; + VkDeviceMemory _vertexBufferMemory; + VkBuffer _indexBuffer; + VkDeviceMemory _indexBufferMemory; + + std::vector _uniformBuffers; + std::vector _uniformBuffersMemory; + std::vector _uniformBuffersMapped; + VkDescriptorPool _descriptorPool; + std::vector descriptorSets; +}; + +#endif // HELLOTRIANGLEAPPLICATION_H diff --git a/VkTest.pro b/VkTest.pro new file mode 100644 index 0000000..51d65e1 --- /dev/null +++ b/VkTest.pro @@ -0,0 +1,34 @@ +QT += + +CONFIG += c++17 + +INCLUDEPATH += /usr/include/GLFW + +LIBS += -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi + +SOURCES += \ + HelloTriangleApplication.cpp \ + main.cpp + +HEADERS += \ + HelloTriangleApplication.h + +DISTFILES += \ + android/AndroidManifest.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew \ + android/gradlew.bat \ + android/res/values/libs.xml \ + shaders/compile.sh \ + shaders/frag.spv \ + shaders/shader.frag \ + shaders/shader.vert \ + shaders/vert.spv + +contains(ANDROID_TARGET_ARCH,arm64-v8a) { + ANDROID_PACKAGE_SOURCE_DIR = \ + $$PWD/android + OTHER_FILES += android/src/Vulkan +} diff --git a/VkTest.pro.user b/VkTest.pro.user new file mode 100644 index 0000000..8950846 --- /dev/null +++ b/VkTest.pro.user @@ -0,0 +1,651 @@ + + + + + + EnvironmentId + {9e9cdaa4-317b-4f62-9b8d-2ca42fb59b56} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + true + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.13.2 GCC 64bit + Desktop Qt 5.13.2 GCC 64bit + qt.qt5.5132.gcc_64_kit + 0 + 0 + 0 + + /home/ali-mehrabani/Qt_projects/build-VkTest-Desktop_Qt_5_13_2_GCC_64bit-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /home/ali-mehrabani/Qt_projects/build-VkTest-Desktop_Qt_5_13_2_GCC_64bit-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + /home/ali-mehrabani/Qt_projects/build-VkTest-Desktop_Qt_5_13_2_GCC_64bit-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy Configuration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + VkTest + + Qt4ProjectManager.Qt4RunConfiguration:/home/ali-mehrabani/Qt_projects/VkTest/VkTest.pro + + 3768 + false + true + true + false + false + true + /home/ali-mehrabani/Qt_projects/VkTest + /home/ali-mehrabani/Qt_projects/build-VkTest-Desktop_Qt_5_13_2_GCC_64bit-Debug + + 1 + + + + ProjectExplorer.Project.Target.1 + + Android for arm64-v8a (Clang Qt 5.13.2 for Android ARM64-v8a) + Android for arm64-v8a (Clang Qt 5.13.2 for Android ARM64-v8a) + {81fbc123-4ee0-45a6-968a-bd5314a00c8f} + 0 + 0 + 0 + + /home/ali-mehrabani/Qt_projects/build-VkTest-Android_for_arm64_v8a_Clang_Qt_5_13_2_for_Android_ARM64_v8a-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + + true + Copy application data + + Qt4ProjectManager.AndroidPackageInstallationStep + + + android-29 + + true + Build Android APK + + QmakeProjectManager.AndroidBuildApkStep + false + false + + 4 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /home/ali-mehrabani/Qt_projects/build-VkTest-Android_for_arm64_v8a_Clang_Qt_5_13_2_for_Android_ARM64_v8a-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + + true + Copy application data + + Qt4ProjectManager.AndroidPackageInstallationStep + + + android-30 + + true + Build Android APK + + QmakeProjectManager.AndroidBuildApkStep + false + false + + 4 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + /home/ali-mehrabani/Qt_projects/build-VkTest-Android_for_arm64_v8a_Clang_Qt_5_13_2_for_Android_ARM64_v8a-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + + true + Copy application data + + Qt4ProjectManager.AndroidPackageInstallationStep + + + android-30 + + true + Build Android APK + + QmakeProjectManager.AndroidBuildApkStep + false + false + + 4 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + + true + Deploy to Android device + + Qt4ProjectManager.AndroidDeployQtStep + false + + 1 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy to Android device + + Qt4ProjectManager.AndroidDeployConfiguration2 + + 1 + + + dwarf + + cpu-cycles + + + 250 + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + + + + 0 + + + + Qt4ProjectManager.AndroidRunConfiguration:/home/ali-mehrabani/Qt_projects/VkTest/VkTest.pro + + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 2 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000..25336e6 --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..ed704c4 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,57 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.0' + } +} + +repositories { + google() + jcenter() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + buildToolsVersion '28.0.3' + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + lintOptions { + abortOnError false + } +} diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bf3de21 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/res/values/libs.xml b/android/res/values/libs.xml new file mode 100644 index 0000000..4009a77 --- /dev/null +++ b/android/res/values/libs.xml @@ -0,0 +1,25 @@ + + + + https://download.qt.io/ministro/android/qt5/qt-5.9 + + + + + + + + + + + + + + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..8b95b15 --- /dev/null +++ b/main.cpp @@ -0,0 +1,19 @@ +#include + +#include +#include +#include + +int main(int /*argc*/, char */*argv*/[]) { + + HelloTriangleApplication app; + + try { + app.run(); + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/shaders/compile.sh b/shaders/compile.sh new file mode 100755 index 0000000..34d491d --- /dev/null +++ b/shaders/compile.sh @@ -0,0 +1,2 @@ +/usr/bin/glslc shader.vert -o vert.spv +/usr/bin/glslc shader.frag -o frag.spv diff --git a/shaders/frag.spv b/shaders/frag.spv new file mode 100644 index 0000000..da37f7e Binary files /dev/null and b/shaders/frag.spv differ diff --git a/shaders/shader.frag b/shaders/shader.frag new file mode 100644 index 0000000..7c5b0e7 --- /dev/null +++ b/shaders/shader.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 0000000..1d078d1 --- /dev/null +++ b/shaders/shader.vert @@ -0,0 +1,19 @@ +#version 450 + +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +} ubo; + +layout(location = 0) in vec2 inPosition; + +layout(location = 1) in vec3 inColor; + +layout(location = 0) out vec3 fragColor; + +void main() { +// gl_Position = vec4(inPosition, 0.0, 1.0); + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); + fragColor = inColor; +} diff --git a/shaders/vert.spv b/shaders/vert.spv new file mode 100644 index 0000000..00c2804 Binary files /dev/null and b/shaders/vert.spv differ