#include "core/VulkanWrapper.hpp" #include #include "GLFW.hpp" #include "core/VulkanBase.hpp" #include "core/WindowManager.hpp" import Core.File; import Core.UniquePointer; import Core.List; import Core.Logger; import Core.Types; import Core.Math; import Core.Array; import Core.Vector; namespace Vulkan = Core::Vulkan; using Core::List; using Core::Vulkan::VertexBuffer; using Vulkan::CommandBuffer; using Vulkan::Queue; using Vulkan::SwapchainImages; using Vulkan::SwapchainResult; #define WRAPPER_DESTRUCT(Type, ...) \ using Core::Vulkan::Type; \ Type::~Type() { \ if(device != VK_NULL_HANDLE) { \ vkDestroy##Type##__VA_ARGS__(device, handle, nullptr); \ } \ device = VK_NULL_HANDLE; \ handle = VK_NULL_HANDLE; \ } WRAPPER_DESTRUCT(ImageView) WRAPPER_DESTRUCT(Framebuffer) WRAPPER_DESTRUCT(Semaphore) WRAPPER_DESTRUCT(Fence) WRAPPER_DESTRUCT(Swapchain, KHR) WRAPPER_DESTRUCT(ShaderModule) WRAPPER_DESTRUCT(PipelineLayout) WRAPPER_DESTRUCT(Pipeline) WRAPPER_DESTRUCT(CommandPool) WRAPPER_DESTRUCT(RenderPass) bool ImageView::init(VkDevice d, VkImage image, VkFormat format) { ImageView::~ImageView(); VkImageViewCreateInfo info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = format, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; VK_CHECK_TRUE(vkCreateImageView(d, &info, nullptr, &handle)); device = d; return false; } bool Framebuffer::init( const ImageView& iv, VkRenderPass rp, u32 width, u32 height) { Framebuffer::~Framebuffer(); VkFramebufferCreateInfo info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = rp, .attachmentCount = 1, .pAttachments = iv, .width = width, .height = height, .layers = 1}; VK_CHECK_TRUE(vkCreateFramebuffer(iv.device, &info, nullptr, &handle)); device = iv.device; return false; } bool Semaphore::init(VkDevice d) { Semaphore::~Semaphore(); VkSemaphoreCreateInfo info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; VK_CHECK_TRUE(vkCreateSemaphore(d, &info, nullptr, &handle)); device = d; return false; } bool Fence::init(VkDevice d) { Fence::~Fence(); VkFenceCreateInfo info = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .flags = VK_FENCE_CREATE_SIGNALED_BIT}; VK_CHECK_TRUE(vkCreateFence(d, &info, nullptr, &handle)); device = d; return false; } void Fence::reset() { VK_CHECK_VOID(vkResetFences(device, 1, &handle)); } void Fence::waitFor(u64 timeout) { VK_CHECK_VOID(vkWaitForFences(device, 1, &handle, true, timeout)); } static u32 getSwapImageCount(const VkSurfaceCapabilitiesKHR* caps) { u32 c = caps->minImageCount + 1; // according to VkSurfaceCapabilitiesKHR doc: // maxImageCount is 0 when there is no strict limit if(caps->maxImageCount != 0) { return Core::min(c, caps->maxImageCount); } return c; } bool Swapchain::init(Data& d) { Swapchain::~Swapchain(); VkSurfaceCapabilitiesKHR caps = {}; if(d.base.getSurfaceCapabilities(caps)) { return true; } VkSwapchainCreateInfoKHR ci = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = d.base, .minImageCount = getSwapImageCount(&caps), .imageFormat = d.surfaceFormat.format, .imageColorSpace = d.surfaceFormat.colorSpace, .imageExtent = d.size, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = d.sharingMode, .queueFamilyIndexCount = static_cast(d.queueFamilies.getLength()), .pQueueFamilyIndices = &d.queueFamilies[0], .preTransform = caps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = d.presentMode, .clipped = VK_TRUE, .oldSwapchain = VK_NULL_HANDLE}; VK_CHECK_TRUE(vkCreateSwapchainKHR(d.base, &ci, nullptr, &handle)); device = d.base; return false; } SwapchainResult Swapchain::nextImage( u32& imageIndex, Semaphore& s, u64 timeout) { VkResult r = vkAcquireNextImageKHR( device, handle, timeout, s, VK_NULL_HANDLE, &imageIndex); if(r == VK_SUBOPTIMAL_KHR) { return SwapchainResult::RECREATE; } else if(r == VK_ERROR_OUT_OF_DATE_KHR) { return SwapchainResult::ERROR_RECREATE; } VK_CHECK(SwapchainResult::ERROR, r); return SwapchainResult::SUCCESS; } bool ShaderModule::init(VkDevice d, const char* path) { ShaderModule::~ShaderModule(); List f; if(Core::readFile(f, path)) { return true; } size_t l = f.getLength() - 1; if((l % 4) != 0) { VK_REPORT_ERROR("Shader size is not a multiple of 4"); return true; } VkShaderModuleCreateInfo info = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = l, .pCode = reinterpret_cast(static_cast(&f[0]))}; VK_CHECK_TRUE(vkCreateShaderModule(d, &info, nullptr, &handle)); device = d; return false; } bool PipelineLayout::init(VkDevice d) { PipelineLayout::~PipelineLayout(); VkPipelineLayoutCreateInfo info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; VK_CHECK_TRUE(vkCreatePipelineLayout(d, &info, nullptr, &handle)); device = d; return false; } bool SwapchainImages::init(const Swapchain& s, VkFormat format) { u32 c = 0; VK_CHECK_TRUE(vkGetSwapchainImagesKHR(s.device, s.handle, &c, nullptr)); images.resize(c); imageViews.resize(c); renderFinishedSemaphore.resize(c); VK_CHECK_TRUE(vkGetSwapchainImagesKHR(s.device, s.handle, &c, &images[0])); for(u32 x = 0; x < c; x++) { if(imageViews[x].init(s.device, images[x], format)) { return true; } else if(renderFinishedSemaphore[x].init(s.device)) { return true; } } return false; } bool RenderPass::init(VkDevice d, VkFormat format) { RenderPass::~RenderPass(); VkAttachmentDescription c = { .format = 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_CHECK_TRUE(vkCreateRenderPass(d, &info, nullptr, &handle)); device = d; return false; } Pipeline::Pipeline() : viewport{.maxDepth = 1.0f}, scissor{} { } void Pipeline::updateSize(u32 width, u32 height) { viewport.width = static_cast(width); viewport.height = static_cast(height); scissor.extent.width = width; scissor.extent.height = height; } bool Pipeline::init( PipelineLayout& pl, RenderPass& rp, const List& bd, const List& ad) { Pipeline::~Pipeline(); ShaderModule vertexShaderModule; ShaderModule fragmentShaderModule; if(vertexShaderModule.init(pl.device, "shaders/vertex.spv") || fragmentShaderModule.init(pl.device, "shaders/fragment.spv")) { return true; } 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, .vertexBindingDescriptionCount = static_cast(bd.getLength()), .pVertexBindingDescriptions = &bd[0], .vertexAttributeDescriptionCount = static_cast(ad.getLength()), .pVertexAttributeDescriptions = &ad[0]}; VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = false}; 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}}; Core::Array dynamicStates = { {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}}; VkPipelineDynamicStateCreateInfo dynamicState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = dynamicStates.getLength(), .pDynamicStates = dynamicStates.begin()}; 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 = pl, .renderPass = rp, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1}; VK_CHECK_TRUE(vkCreateGraphicsPipelines( pl.device, VK_NULL_HANDLE, 1, &info, nullptr, &handle)); device = pl.device; return false; } bool CommandPool::init(VkDevice d, u32 queueFamily) { CommandPool::~CommandPool(); VkCommandPoolCreateInfo info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .queueFamilyIndex = queueFamily}; VK_CHECK_TRUE(vkCreateCommandPool(d, &info, nullptr, &handle)); device = d; return false; } bool CommandBuffer::init(CommandPool& cp) { CommandBuffer::~CommandBuffer(); VkCommandBufferAllocateInfo info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = cp, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1}; VK_CHECK_TRUE(vkAllocateCommandBuffers(cp.device, &info, &handle)); device = cp.device; return false; } void CommandBuffer::reset() { VK_CHECK_VOID(vkResetCommandBuffer(handle, 0)); } void CommandBuffer::begin() { VkCommandBufferBeginInfo info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; VK_CHECK_VOID(vkBeginCommandBuffer(handle, &info)); } void CommandBuffer::end() { VK_CHECK_VOID(vkEndCommandBuffer(handle)); } void CommandBuffer::beginRenderPass( RenderPass& rp, Framebuffer& f, const VkExtent2D& size) { VkClearValue v = {.color = {.float32 = {0.0f, 0.0f, 0.0f, 1.0f}}}; VkRenderPassBeginInfo info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = rp, .framebuffer = f, .renderArea = {.offset = {0, 0}, .extent = size}, .clearValueCount = 1, .pClearValues = &v}; vkCmdBeginRenderPass(handle, &info, VK_SUBPASS_CONTENTS_INLINE); } void CommandBuffer::endRenderPass() { vkCmdEndRenderPass(handle); } void CommandBuffer::bindPipeline(Pipeline& p) { vkCmdBindPipeline(handle, VK_PIPELINE_BIND_POINT_GRAPHICS, p); vkCmdSetViewport(handle, 0, 1, &p.viewport); vkCmdSetScissor(handle, 0, 1, &p.scissor); } void CommandBuffer::draw(u32 vertices) { vkCmdDraw(handle, vertices, 1, 0, 0); } bool Queue::init(VkDevice d, u32 queueFamily) { vkGetDeviceQueue(d, queueFamily, 0, &handle); return handle == VK_NULL_HANDLE; } void Queue::submit( CommandBuffer& cb, Fence& f, Semaphore& wait, Semaphore& signal, VkPipelineStageFlags flags) { VkSubmitInfo info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = wait, .pWaitDstStageMask = &flags, .commandBufferCount = 1, .pCommandBuffers = cb, .signalSemaphoreCount = 1, .pSignalSemaphores = signal}; VK_CHECK_VOID(vkQueueSubmit(handle, 1, &info, f)); } SwapchainResult Queue::present(Semaphore& signal, Swapchain& s, u32 index) { VkPresentInfoKHR info = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = signal, .swapchainCount = 1, .pSwapchains = s, .pImageIndices = &index}; VkResult r = vkQueuePresentKHR(handle, &info); if(r == VK_SUBOPTIMAL_KHR) { return SwapchainResult::RECREATE; } else if(r == VK_ERROR_OUT_OF_DATE_KHR) { return SwapchainResult::ERROR_RECREATE; } VK_CHECK(SwapchainResult::ERROR, r); return SwapchainResult::SUCCESS; } VertexBuffer::VertexBuffer() : BaseWrapper(), memory(VK_NULL_HANDLE) { } VertexBuffer::~VertexBuffer() { if(device != VK_NULL_HANDLE) { vkDestroyBuffer(device, handle, nullptr); vkFreeMemory(device, memory, nullptr); } device = VK_NULL_HANDLE; handle = VK_NULL_HANDLE; } bool VertexBuffer::init(Base& b, size_t size, const void* data) { VkBufferCreateInfo info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = size, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE}; VK_CHECK_TRUE(vkCreateBuffer(b, &info, nullptr, &handle)); device = b; VkMemoryRequirements m; vkGetBufferMemoryRequirements(device, handle, &m); VkMemoryAllocateInfo aInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = m.size, .memoryTypeIndex = b.findMemoryType( m.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)}; VK_CHECK_TRUE(vkAllocateMemory(device, &aInfo, nullptr, &memory)); VK_CHECK_TRUE(vkBindBufferMemory(device, handle, memory, 0)); void* destination = nullptr; VK_CHECK_TRUE(vkMapMemory(device, memory, 0, size, 0, &destination)); memcpy(destination, data, size); vkUnmapMemory(device, memory); return false; } struct VulkanDummy { Core::Vulkan::Base base{}; struct BaseData { u32 graphicsFamily = 0; u32 presentFamily = 0; VkSurfaceFormatKHR surfaceFormat{}; VkPresentModeKHR presentMode{}; }; BaseData baseData{}; Queue graphicsQueue{}; Queue presentQueue{}; VkExtent2D swapchainSize{}; Swapchain swapchain{}; SwapchainImages images{}; PipelineLayout pipelineLayout{}; RenderPass renderPass{}; Pipeline pipeline{}; List framebuffers{}; CommandPool commandPool{}; size_t currentFrame = 0; struct Frame { CommandBuffer commandBuffer{}; Semaphore imageAvailableSemaphore{}; Fence inFlightFence{}; }; Core::Array frames{}; struct Vertex { Core::Vector2 position; Core::Vector3 color; static auto getBindingDescription() { VkVertexInputBindingDescription d = { .binding = 0, .stride = sizeof(Vertex), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX}; return d; } static auto getAttributeDescriptions() { Core::Array d = { {{.location = 0, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(Vertex, position)}, {.location = 1, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(Vertex, color)}}}; return d; } }; VertexBuffer vertexBuffer{}; 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; } bool getSwapchainSize(VkExtent2D* size) { VkSurfaceCapabilitiesKHR c = {}; if(base.getSurfaceCapabilities(c)) { return true; } if(c.currentExtent.width != 0xFFFF'FFFFu && c.currentExtent.height != 0xFFFF'FFFFu) { *size = c.currentExtent; Core::logInfo("Swapchain size: #x#", size->width, size->height); return false; } int w = 0; int h = 0; glfwGetFramebufferSize(Core::Window::get(), &w, &h); if(w <= 0 || h <= 0) { Core::logError("Could not get framebuffer size"); return true; } Core::logInfo("Framebuffer size: #x#", w, h); size->width = Core::clamp( static_cast(w), c.minImageExtent.width, c.maxImageExtent.width); size->height = Core::clamp( static_cast(h), c.minImageExtent.height, c.maxImageExtent.height); Core::logInfo("Swapchain size: #x#", size->width, size->height); return false; } bool initSwapchain() { Vulkan::Swapchain::Data d = { .base = base, .surfaceFormat = baseData.surfaceFormat, .presentMode = baseData.presentMode}; if(getSwapchainSize(&d.size)) { Core::logError("Could not retrieve any swapchain size"); return true; } swapchainSize = d.size; d.queueFamilies.add(baseData.graphicsFamily); if(baseData.graphicsFamily != baseData.presentFamily) { d.queueFamilies.add(baseData.presentFamily); d.sharingMode = VK_SHARING_MODE_CONCURRENT; } else { d.sharingMode = VK_SHARING_MODE_EXCLUSIVE; } return swapchain.init(d); } bool initDevice() { bool same = baseData.graphicsFamily == baseData.presentFamily; List data; data.add(baseData.graphicsFamily, 1.0f); if(!same) { data.add(baseData.presentFamily, 1.0f); } List extensions; extensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME); if(base.initDevice(data, extensions)) { return true; } else if(graphicsQueue.init(base, baseData.graphicsFamily)) { Core::logError("Cannot get device graphics queue"); return true; } else if(presentQueue.init(base, baseData.presentFamily)) { Core::logError("Cannot get device present queue"); return true; } return false; } static int getDevicePoints(Vulkan::Base& b, BaseData& d) { int points = 0; VkPhysicalDeviceProperties p; b.getPhysicalDeviceProperties(p); Core::logInfo("Checking '#'", 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; } d.graphicsFamily = b.findQueueFamily(VK_QUEUE_GRAPHICS_BIT); if(d.graphicsFamily == Vulkan::INVALID_QUEUE_FAMILY) { Core::logInfo("> ... has no graphics family"); points = -1000; } d.presentFamily = b.findSurfaceQueueFamily(); if(d.presentFamily == Vulkan::INVALID_QUEUE_FAMILY) { Core::logInfo("> ... has no present family"); points = -1000; } if(!b.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { Core::logInfo("> ... has no swapchain support"); points = -1000; } if(b.findSurfaceFormat(d.surfaceFormat, getSurfaceFormatPoints)) { Core::logInfo("> ... has no matching surface format"); points = -1000; } else { points += getSurfaceFormatPoints(d.surfaceFormat); } if(b.findSurfacePresentMode( d.presentMode, getSurfacePresentModePoints)) { Core::logInfo("> ... has no matching present mode"); points = -1000; } else { points += getSurfacePresentModePoints(d.presentMode); } Core::logInfo("> Final points: #", points); return points; } bool initPhysicalDevice() { Core::logInfo("Searching for physical devices ..."); if(base.findPhysicalDevice(baseData, getDevicePoints)) { Core::logError("No matching physical device was found"); return true; } VkPhysicalDeviceProperties p; base.getPhysicalDeviceProperties(p); Core::logInfo("Best Device: #", p.deviceName); return false; } bool initSwapchainImages() { if(images.init(swapchain, baseData.surfaceFormat.format)) { Core::logError("Could not get swapchain images"); return true; } Core::logInfo("Found # images", images.images.getLength()); return false; } bool initPipeline() { pipeline.updateSize(swapchainSize.width, swapchainSize.height); List bd; bd.add(Vertex::getBindingDescription()); List ad; for(auto a : Vertex::getAttributeDescriptions()) { ad.add(a); } return pipeline.init(pipelineLayout, renderPass, bd, ad); } bool initFramebuffers() { framebuffers.resize(images.imageViews.getLength()); size_t i = 0; for(Framebuffer& f : framebuffers) { if(f.init( images.imageViews[i++], renderPass, swapchainSize.width, swapchainSize.height)) { return true; } } return false; } void fillCommandBuffer(CommandBuffer& cb, u32 index) { cb.begin(); cb.beginRenderPass(renderPass, framebuffers[index], swapchainSize); cb.bindPipeline(pipeline); VkDeviceSize offset = 0; vkCmdBindVertexBuffers(cb, 0, 1, vertexBuffer, &offset); cb.draw(3); cb.endRenderPass(); cb.end(); } bool initFrames() { for(Frame& f : frames) { if(f.commandBuffer.init(commandPool) || f.imageAvailableSemaphore.init(base) || f.inFlightFence.init(base)) { return true; } } return false; } bool initVertexBuffer() { List l; l.add(Core::Vector2{-0.5f, -0.5f}, Core::Vector3{1.0, 1.0, 1.0}); l.add(Core::Vector2{0.5f, 0.0f}, Core::Vector3{0.0, 1.0, 1.0}); l.add(Core::Vector2{0.0f, 0.5f}, Core::Vector3{1.0, 1.0, 0.0}); return vertexBuffer.init(base, sizeof(l[0]) * l.getLength(), &l[0]); } bool init() { return base.init() || initPhysicalDevice() || initDevice() || initSwapchain() || initSwapchainImages() || pipelineLayout.init(base) || renderPass.init(base, baseData.surfaceFormat.format) || initPipeline() || initFramebuffers() || commandPool.init(base, baseData.graphicsFamily) || initFrames() || initVertexBuffer(); } bool recreateSwapchain() { Core::logInfo("Recreate swapchain"); base.waitForIdle(); if(initSwapchain() || initSwapchainImages() || initFramebuffers()) { return true; } pipeline.updateSize(swapchainSize.width, swapchainSize.height); return false; } bool render() { Frame& f = frames[currentFrame]; f.inFlightFence.waitFor(); u32 imageIndex = 0; switch(swapchain.nextImage(imageIndex, f.imageAvailableSemaphore)) { case SwapchainResult::SUCCESS: break; case SwapchainResult::RECREATE: break; case SwapchainResult::ERROR_RECREATE: return recreateSwapchain(); case SwapchainResult::ERROR: return true; } // https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html Semaphore& renderSemaphore = images.renderFinishedSemaphore[imageIndex]; f.inFlightFence.reset(); f.commandBuffer.reset(); fillCommandBuffer(f.commandBuffer, imageIndex); graphicsQueue.submit( f.commandBuffer, f.inFlightFence, f.imageAvailableSemaphore, renderSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); bool recreate = false; switch(presentQueue.present(renderSemaphore, swapchain, imageIndex)) { case SwapchainResult::SUCCESS: break; case SwapchainResult::RECREATE: case SwapchainResult::ERROR_RECREATE: recreate = true; case SwapchainResult::ERROR: break; } currentFrame = (currentFrame + 1) % frames.getLength(); if(recreate || Core::Window::hasSizeChanged()) { recreateSwapchain(); } return false; } }; static Core::UniquePointer dummy; bool Vulkan::init() { dummy = new VulkanDummy(); return dummy->init(); } void Vulkan::render() { dummy->render(); } void Vulkan::destroy() { dummy->base.waitForIdle(); dummy = nullptr; }