|
@@ -0,0 +1,495 @@
|
|
|
+#include "core/VulkanWrapper.h"
|
|
|
+
|
|
|
+#include <core/Logger.h>
|
|
|
+#include <core/Utility.h>
|
|
|
+
|
|
|
+#include "GLFW.h"
|
|
|
+#include "VulkanUtils.h"
|
|
|
+
|
|
|
+static VkPhysicalDevice physicalDevice;
|
|
|
+static u32 graphicsFamily = 0;
|
|
|
+static u32 presentFamily = 0;
|
|
|
+static VkDevice device;
|
|
|
+static VkQueue graphicsQueue;
|
|
|
+static VkQueue presentQueue;
|
|
|
+static VkSurfaceFormatKHR surfaceFormat;
|
|
|
+static VkSurfaceKHR surface;
|
|
|
+static VkExtent2D swapchainSize;
|
|
|
+static VkSwapchainKHR swapchain;
|
|
|
+static VulkanSwapchainImages images;
|
|
|
+static VkShaderModule vertexShaderModule;
|
|
|
+static VkShaderModule fragmentShaderModule;
|
|
|
+static VkPipelineLayout pipelineLayout;
|
|
|
+static VkRenderPass renderPass;
|
|
|
+static VkViewport viewport;
|
|
|
+static VkRect2D scissor;
|
|
|
+static VkPipeline pipeline;
|
|
|
+static VkFramebuffer* framebuffers;
|
|
|
+static VkCommandPool commandPool;
|
|
|
+static constexpr size_t MAX_FRAMES = 2;
|
|
|
+static size_t currentFrame = 0;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ VkCommandBuffer commandBuffer;
|
|
|
+ VkSemaphore imageAvailableSemaphore;
|
|
|
+ VkSemaphore renderFinishedSemaphore;
|
|
|
+ VkFence inFlightFence;
|
|
|
+} Frame;
|
|
|
+
|
|
|
+static Frame frames[MAX_FRAMES];
|
|
|
+
|
|
|
+static int getSurfaceFormatPoints(const VkSurfaceFormatKHR* sf) {
|
|
|
+ if(sf->format == VK_FORMAT_B8G8R8A8_UNORM &&
|
|
|
+ sf->colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
|
|
+ return 10;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int getSurfacePresentModePoints(VkPresentModeKHR m) {
|
|
|
+ if(m == VK_PRESENT_MODE_MAILBOX_KHR) {
|
|
|
+ return 5;
|
|
|
+ } else if(m == VK_PRESENT_MODE_FIFO_KHR) {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static bool getSwapchainSize(VkExtent2D* size) {
|
|
|
+ VkSurfaceCapabilitiesKHR c = {0};
|
|
|
+ VK_ASSERT(
|
|
|
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &c));
|
|
|
+ if(c.currentExtent.width != 0xFFFF'FFFFu &&
|
|
|
+ c.currentExtent.height != 0xFFFF'FFFFu) {
|
|
|
+ *size = c.currentExtent;
|
|
|
+ LOG_INFO("Swapchain size: %ux%u", size->width, size->height);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int w = 0;
|
|
|
+ int h = 0;
|
|
|
+ glfwGetFramebufferSize(getWindow(), &w, &h);
|
|
|
+ if(w <= 0 || h <= 0) {
|
|
|
+ LOG_ERROR("Could not get framebuffer size");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ LOG_INFO("Framebuffer size: %dx%d", w, h);
|
|
|
+ size->width =
|
|
|
+ clampU32((u32)w, c.minImageExtent.width, c.maxImageExtent.width);
|
|
|
+ size->height =
|
|
|
+ clampU32((u32)h, c.minImageExtent.height, c.maxImageExtent.height);
|
|
|
+ LOG_INFO("Swapchain size: %ux%u", size->width, size->height);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initSwapchain() {
|
|
|
+ VulkanSwapchainData d = {
|
|
|
+ .physicalDevice = physicalDevice, .device = device, .surface = surface};
|
|
|
+ if(getSwapchainSize(&d.size)) {
|
|
|
+ LOG_ERROR("Could not retrieve any swapchain size");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ swapchainSize = d.size;
|
|
|
+ if(findVulkanSurfaceFormat(
|
|
|
+ &d.surfaceFormat, physicalDevice, surface, getSurfaceFormatPoints)) {
|
|
|
+ LOG_ERROR("Could not find surface format");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ surfaceFormat = d.surfaceFormat;
|
|
|
+ if(findVulkanSurfacePresentMode(
|
|
|
+ &d.presentMode, physicalDevice, surface,
|
|
|
+ getSurfacePresentModePoints)) {
|
|
|
+ LOG_ERROR("Could not find present mode");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ u32 queueFamilyIndices[] = {graphicsFamily, presentFamily};
|
|
|
+ if(graphicsFamily != presentFamily) {
|
|
|
+ d.sharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
|
+ d.queueFamilyIndexCount = 2;
|
|
|
+ d.queueFamilyIndices = queueFamilyIndices;
|
|
|
+ } else {
|
|
|
+ d.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
+ }
|
|
|
+ return initVulkanSwapchain(&swapchain, &d);
|
|
|
+}
|
|
|
+
|
|
|
+static bool initDevice() {
|
|
|
+ graphicsFamily =
|
|
|
+ findVulkanQueueFamily(physicalDevice, VK_QUEUE_GRAPHICS_BIT);
|
|
|
+ presentFamily = findVulkanSurfaceQueueFamily(physicalDevice, surface);
|
|
|
+ if(graphicsFamily == INVALID_VULKAN_QUEUE_FAMILY ||
|
|
|
+ presentFamily == INVALID_VULKAN_QUEUE_FAMILY) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ bool same = graphicsFamily == presentFamily;
|
|
|
+ VulkanDeviceQueueData data[] = {
|
|
|
+ {graphicsFamily, 1.0f}, {presentFamily, 1.0f}};
|
|
|
+ const char* extensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
|
|
+ if(initVulkanDevice(
|
|
|
+ &device, physicalDevice, data, same ? 1 : 2, extensions,
|
|
|
+ ARRAY_LENGTH(extensions))) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue);
|
|
|
+ if(graphicsQueue == VK_NULL_HANDLE) {
|
|
|
+ LOG_ERROR("Cannot get device graphics queue");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if(same) {
|
|
|
+ presentQueue = graphicsQueue;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ vkGetDeviceQueue(device, presentFamily, 0, &presentQueue);
|
|
|
+ if(presentQueue == VK_NULL_HANDLE) {
|
|
|
+ LOG_ERROR("Cannot get device present queue");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static int getDevicePoints(VkPhysicalDevice pd) {
|
|
|
+ int points = 0;
|
|
|
+ VkPhysicalDeviceProperties p;
|
|
|
+ vkGetPhysicalDeviceProperties(pd, &p);
|
|
|
+ LOG_INFO("Checking '%s'", p.deviceName);
|
|
|
+ switch(p.deviceType) {
|
|
|
+ case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: points += 100; break;
|
|
|
+ case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: points += 50; break;
|
|
|
+ case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: points += 20; break;
|
|
|
+ default: break;
|
|
|
+ }
|
|
|
+ u32 gf = findVulkanQueueFamily(pd, VK_QUEUE_GRAPHICS_BIT);
|
|
|
+ if(gf == INVALID_VULKAN_QUEUE_FAMILY) {
|
|
|
+ LOG_INFO("> ... has no graphics family");
|
|
|
+ points = -1;
|
|
|
+ }
|
|
|
+ u32 pf = findVulkanSurfaceQueueFamily(pd, surface);
|
|
|
+ if(pf == INVALID_VULKAN_QUEUE_FAMILY) {
|
|
|
+ LOG_INFO("> ... has no present family");
|
|
|
+ points = -1;
|
|
|
+ }
|
|
|
+ if(!hasVulkanExtension(pd, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
|
|
|
+ LOG_INFO("> ... has no swapchain support");
|
|
|
+ points = -1;
|
|
|
+ }
|
|
|
+ VkSurfaceFormatKHR sf = {0};
|
|
|
+ if(findVulkanSurfaceFormat(&sf, pd, surface, getSurfaceFormatPoints)) {
|
|
|
+ LOG_INFO("> ... has no matching surface format");
|
|
|
+ points = -1;
|
|
|
+ } else {
|
|
|
+ points += getSurfaceFormatPoints(&sf);
|
|
|
+ }
|
|
|
+ VkPresentModeKHR m = 0;
|
|
|
+ if(findVulkanSurfacePresentMode(
|
|
|
+ &m, pd, surface, getSurfacePresentModePoints)) {
|
|
|
+ LOG_INFO("> ... has no matching present mode");
|
|
|
+ points = -1;
|
|
|
+ } else {
|
|
|
+ points += getSurfacePresentModePoints(m);
|
|
|
+ }
|
|
|
+ LOG_INFO("> Final points: %d", points);
|
|
|
+ return points;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initPhysicalDevice() {
|
|
|
+ LOG_INFO("Searching for physical devices ...");
|
|
|
+ if(findVulkanPhysicalDevice(&physicalDevice, getDevicePoints)) {
|
|
|
+ LOG_ERROR("No matching physical device was found");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ VkPhysicalDeviceProperties p;
|
|
|
+ vkGetPhysicalDeviceProperties(physicalDevice, &p);
|
|
|
+ LOG_INFO("Best Device: %s", p.deviceName);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initSwapchainImages() {
|
|
|
+ if(initVulkanSwapchainImages(
|
|
|
+ &images, device, swapchain, surfaceFormat.format)) {
|
|
|
+ LOG_ERROR("Could not get swapchain images");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ LOG_INFO("Found %u images", images.amount);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initShaders() {
|
|
|
+ return initVulkanShaderModule(
|
|
|
+ &vertexShaderModule, device, "shaders/vertex.spv") ||
|
|
|
+ initVulkanShaderModule(
|
|
|
+ &fragmentShaderModule, device, "shaders/fragment.spv");
|
|
|
+}
|
|
|
+
|
|
|
+static bool initPipeline() {
|
|
|
+ VkPipelineShaderStageCreateInfo stages[2] = {
|
|
|
+ {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
|
+ .stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
|
+ .module = vertexShaderModule,
|
|
|
+ .pName = "main"},
|
|
|
+ {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
|
+ .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
+ .module = fragmentShaderModule,
|
|
|
+ .pName = "main"}};
|
|
|
+
|
|
|
+ VkPipelineVertexInputStateCreateInfo vertexInputState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
|
|
+
|
|
|
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
|
+ .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
|
+ .primitiveRestartEnable = false};
|
|
|
+
|
|
|
+ viewport = (VkViewport){.x = 0.0f,
|
|
|
+ .y = 0.0f,
|
|
|
+ .width = (float)swapchainSize.width,
|
|
|
+ .height = (float)swapchainSize.height,
|
|
|
+ .minDepth = 0.0f,
|
|
|
+ .maxDepth = 1.0f};
|
|
|
+ scissor = (VkRect2D){.offset = {0, 0}, .extent = swapchainSize};
|
|
|
+ VkPipelineViewportStateCreateInfo viewportState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
|
+ .viewportCount = 1,
|
|
|
+ .pViewports = &viewport,
|
|
|
+ .scissorCount = 1,
|
|
|
+ .pScissors = &scissor};
|
|
|
+
|
|
|
+ VkPipelineRasterizationStateCreateInfo rasterizationState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
|
+ .depthClampEnable = false,
|
|
|
+ .rasterizerDiscardEnable = false,
|
|
|
+ .polygonMode = VK_POLYGON_MODE_FILL,
|
|
|
+ .cullMode = VK_CULL_MODE_BACK_BIT,
|
|
|
+ .frontFace = VK_FRONT_FACE_CLOCKWISE,
|
|
|
+ .depthBiasEnable = false,
|
|
|
+ .depthBiasConstantFactor = 0.0f,
|
|
|
+ .depthBiasClamp = 0.0f,
|
|
|
+ .depthBiasSlopeFactor = 0.0f,
|
|
|
+ .lineWidth = 1.0f};
|
|
|
+
|
|
|
+ VkPipelineMultisampleStateCreateInfo multisampleState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
|
+ .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
|
|
+ .sampleShadingEnable = false,
|
|
|
+ .minSampleShading = 1.0f,
|
|
|
+ .pSampleMask = nullptr,
|
|
|
+ .alphaToCoverageEnable = false,
|
|
|
+ .alphaToOneEnable = false};
|
|
|
+
|
|
|
+ VkPipelineColorBlendAttachmentState colorBlendAttachmentState = {
|
|
|
+ .blendEnable = false,
|
|
|
+ .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
|
|
+ .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
|
+ .colorBlendOp = VK_BLEND_OP_ADD,
|
|
|
+ .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
|
|
+ .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
|
+ .alphaBlendOp = VK_BLEND_OP_ADD,
|
|
|
+ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
|
|
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};
|
|
|
+
|
|
|
+ VkPipelineColorBlendStateCreateInfo colorBlendState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
|
+ .logicOpEnable = false,
|
|
|
+ .logicOp = VK_LOGIC_OP_COPY,
|
|
|
+ .attachmentCount = 1,
|
|
|
+ .pAttachments = &colorBlendAttachmentState,
|
|
|
+ .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}};
|
|
|
+
|
|
|
+ VkDynamicState dynamicStates[] = {
|
|
|
+ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
|
|
+
|
|
|
+ VkPipelineDynamicStateCreateInfo dynamicState = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
|
+ .dynamicStateCount = ARRAY_LENGTH(dynamicStates),
|
|
|
+ .pDynamicStates = dynamicStates};
|
|
|
+
|
|
|
+ VkGraphicsPipelineCreateInfo info = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
|
+ .stageCount = 2,
|
|
|
+ .pStages = stages,
|
|
|
+ .pVertexInputState = &vertexInputState,
|
|
|
+ .pInputAssemblyState = &inputAssemblyState,
|
|
|
+ .pTessellationState = nullptr,
|
|
|
+ .pViewportState = &viewportState,
|
|
|
+ .pRasterizationState = &rasterizationState,
|
|
|
+ .pMultisampleState = &multisampleState,
|
|
|
+ .pDepthStencilState = nullptr,
|
|
|
+ .pColorBlendState = &colorBlendState,
|
|
|
+ .pDynamicState = &dynamicState,
|
|
|
+ .layout = pipelineLayout,
|
|
|
+ .renderPass = renderPass,
|
|
|
+ .subpass = 0,
|
|
|
+ .basePipelineHandle = VK_NULL_HANDLE,
|
|
|
+ .basePipelineIndex = -1};
|
|
|
+
|
|
|
+ VK_ASSERT(vkCreateGraphicsPipelines(
|
|
|
+ device, VK_NULL_HANDLE, 1, &info, nullptr, &pipeline));
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initRenderPass() {
|
|
|
+ VkAttachmentDescription c = {
|
|
|
+ .format = surfaceFormat.format,
|
|
|
+ .samples = VK_SAMPLE_COUNT_1_BIT,
|
|
|
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
|
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
|
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
|
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
|
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
|
+ .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR};
|
|
|
+ VkAttachmentReference ca = {
|
|
|
+ .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
+ VkSubpassDescription subpass = {
|
|
|
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
|
+ .colorAttachmentCount = 1,
|
|
|
+ .pColorAttachments = &ca};
|
|
|
+ VkSubpassDependency dependency = {
|
|
|
+ .srcSubpass = VK_SUBPASS_EXTERNAL,
|
|
|
+ .dstSubpass = 0,
|
|
|
+ .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
+ .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
+ .srcAccessMask = 0,
|
|
|
+ .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
+ };
|
|
|
+ VkRenderPassCreateInfo info = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
|
+ .attachmentCount = 1,
|
|
|
+ .pAttachments = &c,
|
|
|
+ .subpassCount = 1,
|
|
|
+ .pSubpasses = &subpass,
|
|
|
+ .dependencyCount = 1,
|
|
|
+ .pDependencies = &dependency};
|
|
|
+ VK_ASSERT(vkCreateRenderPass(device, &info, nullptr, &renderPass));
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initCommandPool() {
|
|
|
+ VkCommandPoolCreateInfo info = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
|
+ .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
|
+ .queueFamilyIndex = graphicsFamily};
|
|
|
+ VK_ASSERT(vkCreateCommandPool(device, &info, nullptr, &commandPool));
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
|
|
|
+ VkCommandBufferBeginInfo info = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
|
|
|
+ VK_ASSERT(vkBeginCommandBuffer(cb, &info));
|
|
|
+ VkClearValue v = {.color = {.float32 = {0.0f, 0.5f, 0.0f, 1.0f}}};
|
|
|
+ VkRenderPassBeginInfo rInfo = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
|
+ .renderPass = renderPass,
|
|
|
+ .framebuffer = framebuffers[index],
|
|
|
+ .renderArea = {.offset = {0, 0}, .extent = swapchainSize},
|
|
|
+ .clearValueCount = 1,
|
|
|
+ .pClearValues = &v};
|
|
|
+ vkCmdBeginRenderPass(cb, &rInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
+ vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
|
+ vkCmdSetViewport(cb, 0, 1, &viewport);
|
|
|
+ vkCmdSetScissor(cb, 0, 1, &scissor);
|
|
|
+ vkCmdDraw(cb, 3, 1, 0, 0);
|
|
|
+ vkCmdEndRenderPass(cb);
|
|
|
+ VK_ASSERT(vkEndCommandBuffer(cb));
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initFrames() {
|
|
|
+ for(size_t i = 0; i < MAX_FRAMES; i++) {
|
|
|
+ if(initCommandVulkanBuffer(
|
|
|
+ &frames[i].commandBuffer, device, commandPool) ||
|
|
|
+ initVulkanSemaphore(&frames[i].imageAvailableSemaphore, device) ||
|
|
|
+ initVulkanSemaphore(&frames[i].renderFinishedSemaphore, device) ||
|
|
|
+ initVulkanFence(&frames[i].inFlightFence, device)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool initVulkan() {
|
|
|
+ return initVulkanInstance() || initVulkanDebugging() ||
|
|
|
+ initVulkanSurface(&surface, getWindow()) || initPhysicalDevice() ||
|
|
|
+ initDevice() || initSwapchain() || initSwapchainImages() ||
|
|
|
+ initShaders() || initVulkanPipelineLayout(&pipelineLayout, device) ||
|
|
|
+ initRenderPass() || initPipeline() ||
|
|
|
+ initVulkanFramebuffers(
|
|
|
+ &framebuffers, &images, device, renderPass, swapchainSize.width,
|
|
|
+ swapchainSize.height) ||
|
|
|
+ initCommandPool() || initFrames();
|
|
|
+}
|
|
|
+
|
|
|
+static bool shouldWait = false;
|
|
|
+
|
|
|
+static bool render() {
|
|
|
+ if(shouldWait) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Frame* f = frames + currentFrame;
|
|
|
+ VK_ASSERT(vkWaitForFences(device, 1, &f->inFlightFence, true, UINT64_MAX));
|
|
|
+ VK_ASSERT(vkResetFences(device, 1, &f->inFlightFence));
|
|
|
+
|
|
|
+ uint32_t imageIndex = 0;
|
|
|
+ VK_ASSERT(vkAcquireNextImageKHR(
|
|
|
+ device, swapchain, UINT64_MAX, f->imageAvailableSemaphore,
|
|
|
+ VK_NULL_HANDLE, &imageIndex));
|
|
|
+
|
|
|
+ vkResetCommandBuffer(f->commandBuffer, 0);
|
|
|
+ fillCommandBuffer(f->commandBuffer, imageIndex);
|
|
|
+
|
|
|
+ VkSemaphore waitSemaphores[] = {f->imageAvailableSemaphore};
|
|
|
+ VkPipelineStageFlags waitStages[] = {
|
|
|
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
|
|
+ VkSemaphore signalSemaphores[] = {f->renderFinishedSemaphore};
|
|
|
+ VkSubmitInfo info = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
|
+ .waitSemaphoreCount = 1,
|
|
|
+ .pWaitSemaphores = waitSemaphores,
|
|
|
+ .pWaitDstStageMask = waitStages,
|
|
|
+ .commandBufferCount = 1,
|
|
|
+ .pCommandBuffers = &f->commandBuffer,
|
|
|
+ .signalSemaphoreCount = 1,
|
|
|
+ .pSignalSemaphores = signalSemaphores};
|
|
|
+ VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &info, f->inFlightFence));
|
|
|
+
|
|
|
+ VkPresentInfoKHR presentInfo = {
|
|
|
+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
|
|
+ .waitSemaphoreCount = 1,
|
|
|
+ .pWaitSemaphores = signalSemaphores,
|
|
|
+ .swapchainCount = 1,
|
|
|
+ .pSwapchains = &swapchain,
|
|
|
+ .pImageIndices = &imageIndex};
|
|
|
+
|
|
|
+ VK_ASSERT(vkQueuePresentKHR(presentQueue, &presentInfo));
|
|
|
+ currentFrame = (currentFrame + 1) % MAX_FRAMES;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void renderVulkan() {
|
|
|
+ if(render()) {
|
|
|
+ shouldWait = true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void destroyVulkan(void) {
|
|
|
+ if(device != VK_NULL_HANDLE) {
|
|
|
+ vkDeviceWaitIdle(device);
|
|
|
+ for(size_t i = 0; i < MAX_FRAMES; i++) {
|
|
|
+ Frame* f = frames + i;
|
|
|
+ destroyVulkanFence(f->inFlightFence, device);
|
|
|
+ destroyVulkanSemaphore(f->imageAvailableSemaphore, device);
|
|
|
+ destroyVulkanSemaphore(f->renderFinishedSemaphore, device);
|
|
|
+ }
|
|
|
+ vkDestroyCommandPool(device, commandPool, nullptr);
|
|
|
+ destroyVulkanFramebuffers(&framebuffers, images.amount, device);
|
|
|
+ vkDestroyPipeline(device, pipeline, nullptr);
|
|
|
+ vkDestroyRenderPass(device, renderPass, nullptr);
|
|
|
+ }
|
|
|
+ destroyVulkanPipelineLayout(pipelineLayout, device);
|
|
|
+ destroyVulkanShaderModule(vertexShaderModule, device);
|
|
|
+ destroyVulkanShaderModule(fragmentShaderModule, device);
|
|
|
+ destroyVulkanSwapchainImages(&images, device);
|
|
|
+ destroyVulkanSwapchain(swapchain, device);
|
|
|
+ destroyVulkanDevice(device);
|
|
|
+ destroyVulkanSurface(surface);
|
|
|
+ destroyVulkanDebugging();
|
|
|
+ destroyVulkanInstance();
|
|
|
+}
|