#include #include #include #include #include #include struct vertex { glm::vec2 position; glm::vec3 color; }; std::vector read_file(const char *filename) { std::ifstream file(filename, std::ios::binary); file.seekg(0,std::ios::end); std::streampos length = file.tellg(); file.seekg(0,std::ios::beg); std::vector buffer(length); file.read(&buffer[0],length); return buffer; } GLFWwindow *create_window(int width, int height, const char *title) { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); GLFWwindow *window = glfwCreateWindow(width, height, title, nullptr, nullptr); if (!window) { std::println("GLFW window creation failed!"); return nullptr; } return window; } int main() { GLFWwindow *window = create_window(1000, 800, "hello"); if (!window) return -1; vk::ApplicationInfo appinfo = vk::ApplicationInfo("Test_vk", VK_MAKE_VERSION(0,1,0), NULL, VK_MAKE_VERSION(0,1,0), VK_API_VERSION_1_4); uint32_t extension_count = 0; const char **glfwextensions = glfwGetRequiredInstanceExtensions(&extension_count); std::println("GLFW requested extensions:"); std::vector extensions; std::vector layers; for (int i = 0; i < extension_count;i++) { extensions.push_back(glfwextensions[i]); std::println("{}", glfwextensions[i]); } #ifdef __APPLE__ extensions.push_back("VK_KHR_portability_enumeration"); extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); //Apple silicon extensions #endif #ifndef NDEBUG layers.push_back("VK_LAYER_KHRONOS_validation"); #endif vk::InstanceCreateInfo createinfo = vk::InstanceCreateInfo( vk::InstanceCreateFlags(VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR), &appinfo,layers.size(), layers.data(), extensions.size(), extensions.data()); vk::Instance instance = vk::createInstance(createinfo); uint32_t count = 0; VkResult res = vkEnumeratePhysicalDevices(instance, &count, nullptr); if (res != VK_SUCCESS) { std::println("Enumerating physical devices failed!"); return -1; } std::println("Physical devices available {}", count); std::vector devices; devices.resize(count); VkResult res2 = vkEnumeratePhysicalDevices(instance, &count, devices.data()); if (res2 != VK_SUCCESS) { std::println("Enumerating physical devices failed!"); return -1; } vk::PhysicalDevice selected_physical_device; for (auto device: devices) { VkPhysicalDeviceProperties dev_prop = {}; vkGetPhysicalDeviceProperties(device, &dev_prop); std::println("{}", dev_prop.deviceName); selected_physical_device = device; } std::vector queue_families = selected_physical_device.getQueueFamilyProperties(); uint32_t graphics_queue_index = 0; for (uint32_t i = 0; i < queue_families.size(); i++) { if (queue_families[i].queueFlags & vk::QueueFlagBits::eGraphics) { graphics_queue_index = i; break; } } float queue_priority = 1.0f; vk::DeviceQueueCreateInfo queue_info = vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), graphics_queue_index, 1, &queue_priority); std::vector device_extensions; device_extensions.push_back("VK_KHR_swapchain"); #ifdef __APPLE__ device_extensions.push_back("VK_KHR_portability_subset"); #endif vk::PhysicalDeviceFeatures device_features = vk::PhysicalDeviceFeatures(); vk::DeviceCreateInfo device_info = vk::DeviceCreateInfo(vk::DeviceCreateFlags(), 1, &queue_info, 0, nullptr, device_extensions.size(), device_extensions.data(), &device_features); vk::Device device = selected_physical_device.createDevice(device_info); vk::Queue graphics_queue = device.getQueue(graphics_queue_index, 0); VkSurfaceKHR raw_surface; glfwCreateWindowSurface(instance, window, nullptr, &raw_surface); vk::SurfaceKHR surface = raw_surface; VkBool32 surface_supported = selected_physical_device.getSurfaceSupportKHR(graphics_queue_index, surface); if (surface_supported == VK_TRUE) std::println("Surface supported!"); else std::println("Surface unsupported!"); vk::SurfaceCapabilitiesKHR surface_capabilities = selected_physical_device.getSurfaceCapabilitiesKHR(surface); int width = 0; int height = 0; if (surface_capabilities.currentExtent.height == UINT32_MAX || surface_capabilities.currentExtent.width == UINT32_MAX) { glfwGetFramebufferSize(window, &width, &height); width = std::clamp((uint32_t)width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width); height = std::clamp((uint32_t)height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height); } else { width = surface_capabilities.currentExtent.width; height = surface_capabilities.currentExtent.height; } vk::Extent2D framebuffer_extension = vk::Extent2D(width, height); std::println("Extents width {} height {}", width, height); std::vector surface_formats = selected_physical_device.getSurfaceFormatsKHR(surface); vk::SurfaceFormatKHR format; for (auto form: surface_formats) { if (form.format == vk::Format::eB8G8R8A8Srgb && form.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { format = form; break; } } std::vector present_modes = selected_physical_device.getSurfacePresentModesKHR(surface); vk::PresentModeKHR mode = vk::PresentModeKHR::eFifo; if (std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eFifoRelaxed) != present_modes.end()) { mode = vk::PresentModeKHR::eFifoRelaxed; std::println("Selected relaxed FIFO"); } vk::SwapchainCreateInfoKHR swapchain_info = vk::SwapchainCreateInfoKHR(vk::SwapchainCreateFlagsKHR(), surface, 2, format.format, format.colorSpace, framebuffer_extension, 1, vk::ImageUsageFlagBits::eColorAttachment, vk::SharingMode::eExclusive); swapchain_info.preTransform = surface_capabilities.currentTransform; swapchain_info.presentMode = mode; swapchain_info.clipped = VK_TRUE; swapchain_info.oldSwapchain = vk::SwapchainKHR(nullptr); swapchain_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchain_info); std::vector images = device.getSwapchainImagesKHR(swapchain); std::println("Got {} images from swapchain", images.size()); std::vector image_views; for (auto &image: images) { vk::ImageViewCreateInfo image_view_info = {}; image_view_info.image = image; image_view_info.viewType = vk::ImageViewType::e2D; image_view_info.format = format.format; image_view_info.components.r = vk::ComponentSwizzle::eIdentity; image_view_info.components.g = vk::ComponentSwizzle::eIdentity; image_view_info.components.b = vk::ComponentSwizzle::eIdentity; image_view_info.components.a = vk::ComponentSwizzle::eIdentity; image_view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; image_view_info.subresourceRange.baseMipLevel = 0; image_view_info.subresourceRange.levelCount = 1; image_view_info.subresourceRange.baseArrayLayer = 0; image_view_info.subresourceRange.layerCount = 1; image_views.push_back(device.createImageView(image_view_info)); } std::vector vertex_shader = read_file("../shaders/vertex.spv"); std::vector fragment_shader = read_file("../shaders/fragment.spv"); vk::ShaderModuleCreateInfo vertex_shader_info = vk::ShaderModuleCreateInfo(vk::ShaderModuleCreateFlags(), vertex_shader.size()); vertex_shader_info.pCode = (const uint32_t*)(vertex_shader.data()); vk::ShaderModuleCreateInfo fragment_shader_info = vk::ShaderModuleCreateInfo(vk::ShaderModuleCreateFlags(), fragment_shader.size()); fragment_shader_info.pCode = (const uint32_t*)(fragment_shader.data()); vk::ShaderModule vertex_module = device.createShaderModule(vertex_shader_info); vk::ShaderModule fragment_module = device.createShaderModule(fragment_shader_info); vk::PipelineShaderStageCreateInfo vertex_stage_info = {}; vertex_stage_info.stage = vk::ShaderStageFlagBits::eVertex; vertex_stage_info.module = vertex_module; vertex_stage_info.pName = "main"; vk::PipelineShaderStageCreateInfo fragment_stage_info = {}; fragment_stage_info.stage = vk::ShaderStageFlagBits::eFragment; fragment_stage_info.module = fragment_module; fragment_stage_info.pName = "main"; std::vector pipeline_shaders = {vertex_stage_info, fragment_stage_info}; vk::VertexInputBindingDescription binding_description = {}; binding_description.binding = 0; binding_description.stride = sizeof(vertex); binding_description.inputRate = vk::VertexInputRate::eVertex; vk::VertexInputAttributeDescription att_description_pos = {}; att_description_pos.binding = 0; att_description_pos.location = 0; att_description_pos.format = vk::Format::eR32G32Sfloat; att_description_pos.offset = offsetof(vertex, position); vk::VertexInputAttributeDescription att_description_color = {}; att_description_color.binding = 0; att_description_color.location = 1; att_description_color.format = vk::Format::eR32G32B32Sfloat; att_description_color.offset = offsetof(vertex, color); std::vector att_descriptions = {att_description_pos, att_description_color}; vk::PipelineVertexInputStateCreateInfo vertex_input_info = {}; vertex_input_info.vertexAttributeDescriptionCount = 2; vertex_input_info.vertexBindingDescriptionCount = 1; vertex_input_info.pVertexBindingDescriptions = &binding_description; vertex_input_info.pVertexAttributeDescriptions = att_descriptions.data(); vk::PipelineInputAssemblyStateCreateInfo input_assembly_info = vk::PipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleList, VK_FALSE); vk::Viewport viewport = vk::Viewport(0.0f, 0.0f, framebuffer_extension.width, framebuffer_extension.height, 0.0f, 1.0f); vk::Rect2D scissor; scissor.setOffset({0, 0}); scissor.extent = framebuffer_extension; vk::PipelineViewportStateCreateInfo viewport_info = vk::PipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, &viewport, 1, &scissor); vk::PipelineRasterizationStateCreateInfo raster_info = {}; raster_info.depthClampEnable = VK_FALSE; raster_info.polygonMode = vk::PolygonMode::eFill; raster_info.lineWidth = 1.0f; raster_info.cullMode = vk::CullModeFlagBits::eNone; raster_info.frontFace = vk::FrontFace::eClockwise; raster_info.depthBiasEnable = VK_FALSE; vk::PipelineMultisampleStateCreateInfo multisampling_info = {}; multisampling_info.sampleShadingEnable = VK_FALSE; multisampling_info.rasterizationSamples = vk::SampleCountFlagBits::e1; vk::PipelineColorBlendAttachmentState color_blend_attachment = vk::PipelineColorBlendAttachmentState(VK_FALSE); vk::PipelineColorBlendStateCreateInfo color_blend_info = {}; color_blend_attachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA; color_blend_info.logicOpEnable = VK_FALSE; color_blend_info.attachmentCount = 1; color_blend_info.pAttachments = &color_blend_attachment; vk::PipelineLayoutCreateInfo layout_info = {}; vk::PipelineLayout pipeline_layout = device.createPipelineLayout(layout_info); vk::AttachmentDescription color_attachment = vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), format.format, vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR); vk::AttachmentReference attachment_ref = vk::AttachmentReference(0, vk::ImageLayout::eColorAttachmentOptimal); vk::SubpassDescription subpass_description = {}; subpass_description.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; subpass_description.colorAttachmentCount = 1; subpass_description.pColorAttachments = &attachment_ref; vk::SubpassDependency subpass_dependency = {}; subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL; subpass_dependency.dstSubpass = 0; subpass_dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; subpass_dependency.dstStageMask = vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput); subpass_dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; vk::RenderPassCreateInfo render_pass_info = {}; render_pass_info.attachmentCount = 1; render_pass_info.pAttachments = &color_attachment; render_pass_info.subpassCount = 1; render_pass_info.pSubpasses = &subpass_description; render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = &subpass_dependency; vk::RenderPass render_pass = device.createRenderPass(render_pass_info); vk::GraphicsPipelineCreateInfo pipeline_info = {}; pipeline_info.stageCount = 2; pipeline_info.pStages = pipeline_shaders.data(); pipeline_info.pVertexInputState = &vertex_input_info; pipeline_info.pInputAssemblyState = &input_assembly_info; pipeline_info.pViewportState = &viewport_info; pipeline_info.pRasterizationState = &raster_info; pipeline_info.pMultisampleState = &multisampling_info; pipeline_info.pColorBlendState = &color_blend_info; pipeline_info.layout = pipeline_layout; pipeline_info.renderPass = render_pass; pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; auto pipeline_result = device.createGraphicsPipeline(VK_NULL_HANDLE, pipeline_info); if (pipeline_result.result != vk::Result::eSuccess) { std::println("Pipeline creation failed!"); return -1; } vk::Pipeline pipeline = pipeline_result.value; std::println("Pipeline creation success!"); std::vector framebuffers; for (auto &view: image_views) { vk::FramebufferCreateInfo framebuffer_info = vk::FramebufferCreateInfo(vk::FramebufferCreateFlags(), render_pass, view, framebuffer_extension.width, framebuffer_extension.height, 1); framebuffers.push_back(device.createFramebuffer(framebuffer_info)); } std::vector vertices = { {{0.0f, -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}} }; vk::BufferCreateInfo buffer_info = vk::BufferCreateInfo(vk::BufferCreateFlags(), sizeof(vertices[0]) * vertices.size(), vk::BufferUsageFlagBits::eVertexBuffer, vk::SharingMode::eExclusive); vk::Buffer vertex_buffer = device.createBuffer(buffer_info); VkMemoryRequirements memory_requirements; vkGetBufferMemoryRequirements(device, vertex_buffer, &memory_requirements); vk::PhysicalDeviceMemoryProperties memory_propierties = selected_physical_device.getMemoryProperties(); int propierty_index = -1; for (int i = 0; i < memory_propierties.memoryTypeCount; i++) { if (memory_propierties.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlags(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent)) { propierty_index = i; break; } } if (propierty_index == -1) { throw std::runtime_error("Didnt find a suitable memory"); } vk::MemoryAllocateInfo alloc_info = vk::MemoryAllocateInfo(memory_requirements.size, propierty_index); vk::DeviceMemory vertex_buffer_memory = device.allocateMemory(alloc_info); device.bindBufferMemory(vertex_buffer, vertex_buffer_memory, 0); char *data = (char *)device.mapMemory(vertex_buffer_memory, 0, buffer_info.size); memcpy(data, vertices.data(), buffer_info.size); device.unmapMemory(vertex_buffer_memory); vk::CommandPoolCreateInfo command_pool_info = {}; command_pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; command_pool_info.queueFamilyIndex = graphics_queue_index; vk::CommandPool command_pool = device.createCommandPool(command_pool_info); vk::CommandBufferAllocateInfo cmd_alloc_info = vk::CommandBufferAllocateInfo(command_pool, vk::CommandBufferLevel::ePrimary, 1); auto command_buffers = device.allocateCommandBuffers(cmd_alloc_info); vk::SemaphoreCreateInfo semaphore_info = vk::SemaphoreCreateInfo(); vk::FenceCreateInfo fence_info = vk::FenceCreateInfo(); fence_info.flags = vk::FenceCreateFlagBits::eSignaled; vk::Semaphore image_semaphore = device.createSemaphore(semaphore_info); vk::Semaphore render_semaphore = device.createSemaphore(semaphore_info); vk::Fence next_frame_fence = device.createFence(fence_info); while(!glfwWindowShouldClose(window)) { glfwPollEvents(); device.waitForFences(next_frame_fence, VK_TRUE, UINT64_MAX); device.resetFences(next_frame_fence); uint32_t image_index = device.acquireNextImageKHR(swapchain, UINT64_MAX, image_semaphore).value; vkResetCommandBuffer(command_buffers[0], 0); VkCommandBufferBeginInfo begin_info{}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; if (vkBeginCommandBuffer(command_buffers[0], &begin_info) != VK_SUCCESS) { throw std::runtime_error("Command buffer creation failed!"); } vk::ClearValue clear_color = vk::ClearValue({0.0f, 0.0f, 0.0f, 1.0f}); vk::Rect2D render_area = {{0, 0}, framebuffer_extension}; vk::RenderPassBeginInfo render_pass_begin = vk::RenderPassBeginInfo(render_pass, framebuffers[image_index], render_area, 1, &clear_color); vk::DeviceSize offset = 0; command_buffers[0].beginRenderPass(render_pass_begin, vk::SubpassContents::eInline); command_buffers[0].bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); //command_buffers[0].setViewport(0, viewport); //command_buffers[0].setScissor(0, scissor); command_buffers[0].bindVertexBuffers(0, 1, &vertex_buffer, &offset); command_buffers[0].draw(3, 1, 0, 0); command_buffers[0].endRenderPass(); if (vkEndCommandBuffer(command_buffers[0]) != VK_SUCCESS) { throw std::runtime_error("Command buffer creation failed!"); } vk::PipelineStageFlags flags = vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput); vk::SubmitInfo submit_info = vk::SubmitInfo(); submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &image_semaphore; submit_info.pWaitDstStageMask = &flags; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &render_semaphore; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &command_buffers[0]; graphics_queue.submit(submit_info, next_frame_fence); vk::PresentInfoKHR present_info = {}; present_info.waitSemaphoreCount = 1; present_info.pWaitSemaphores = &render_semaphore; present_info.swapchainCount = 1; present_info.pSwapchains = &swapchain; present_info.pImageIndices = &image_index; graphics_queue.presentKHR(present_info); } device.waitIdle(); device.destroyBuffer(vertex_buffer); device.freeMemory(vertex_buffer_memory); for (auto &framebuffer: framebuffers) { device.destroyFramebuffer(framebuffer); } device.destroyFence(next_frame_fence); device.destroySemaphore(render_semaphore); device.destroySemaphore(image_semaphore); device.destroyCommandPool(command_pool); device.destroyPipeline(pipeline); device.destroyRenderPass(render_pass); device.destroyPipelineLayout(pipeline_layout); device.destroyShaderModule(vertex_module); device.destroyShaderModule(fragment_module); for (auto &image: image_views) { device.destroyImageView(image); } device.destroySwapchainKHR(swapchain); instance.destroySurfaceKHR(surface); device.destroy(); instance.destroy(); glfwTerminate(); return 0; }