Last active
March 19, 2024 16:58
-
-
Save Darianopolis/6d461dcfce184805e504e0ce32c220bb to your computer and use it in GitHub Desktop.
vkhellotriangle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <vulkan/vulkan.h> | |
#include <GLFW/glfw3.h> | |
#include <glslang/Public/ShaderLang.h> | |
#include <glslang/Public/ResourceLimits.h> | |
#include <glslang/SPIRV/GlslangToSpv.h> | |
#include <array> | |
#include <iostream> | |
#include <vector> | |
template<class T> T* Temp(T&& v) { return &v; } | |
template<typename Container, typename Fn, typename ... Args> | |
void Enumerate(Container&& container, Fn&& fn, Args&& ... args) { | |
uint32_t count; | |
fn(std::forward<Args>(args)..., &count, nullptr); | |
container.resize(count); | |
fn(std::forward<Args>(args)..., &count, container.data()); | |
} | |
// ----------------------------------------------------------------------------- | |
VkPipelineShaderStageCreateInfo compile(VkDevice device, VkShaderStageFlagBits vkStage, const char* src) { | |
EShLanguage stage = vkStage == VK_SHADER_STAGE_VERTEX_BIT ? EShLangVertex : EShLangFragment; | |
glslang::TShader shader{stage}; | |
shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); | |
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); | |
shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0); | |
shader.setStrings(&src, 1); | |
shader.parse(GetDefaultResources(), | |
100, ENoProfile, EShMessages::EShMsgDefault); | |
glslang::TProgram program{}; | |
program.addShader(&shader); | |
program.link(EShMessages(int(EShMessages::EShMsgSpvRules) | int(EShMessages::EShMsgVulkanRules))); | |
std::vector<uint32_t> spirv; | |
glslang::SpvOptions spvOptions { .validate = true }; | |
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv, &spvOptions); | |
VkShaderModule shaderModule; | |
vkCreateShaderModule(device, Temp(VkShaderModuleCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
.codeSize = spirv.size() * sizeof(uint32_t), | |
.pCode = spirv.data(), | |
}), nullptr, &shaderModule); | |
return VkPipelineShaderStageCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
.stage = vkStage, | |
.module = shaderModule, | |
.pName = "main", | |
}; | |
} | |
// ----------------------------------------------------------------------------- | |
int main() { | |
glfwInit(); | |
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
GLFWwindow* window = glfwCreateWindow(800, 600, "Hello UV", nullptr, nullptr); | |
int iWidth, iHeight; | |
glfwGetFramebufferSize(window, &iWidth, &iHeight); | |
uint32_t width = uint32_t(iWidth), height = uint32_t(iHeight); | |
uint32_t glfwExtensionCount; | |
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
// ----------------------------------------------------------------------------- | |
VkInstance instance{}; | |
vkCreateInstance(Temp(VkInstanceCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
.pApplicationInfo = Temp(VkApplicationInfo { | |
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
.apiVersion = VK_API_VERSION_1_0, | |
}), | |
.enabledExtensionCount = glfwExtensionCount, | |
.ppEnabledExtensionNames = glfwExtensions, | |
}), nullptr, &instance); | |
// ----------------------------------------------------------------------------- | |
VkSurfaceKHR surface{}; | |
glfwCreateWindowSurface(instance, window, nullptr, &surface); | |
// ----------------------------------------------------------------------------- | |
VkPhysicalDevice physicalDevice{}; | |
vkEnumeratePhysicalDevices(instance, Temp(1u), &physicalDevice); | |
// ----------------------------------------------------------------------------- | |
uint32_t queueFamily = 0; | |
std::vector<VkQueueFamilyProperties> queueFamilies; | |
Enumerate(queueFamilies, vkGetPhysicalDeviceQueueFamilyProperties, physicalDevice); | |
for (queueFamily = 0; queueFamily < queueFamilies.size(); ++queueFamily) | |
if (queueFamilies[queueFamily].queueFlags & VK_QUEUE_GRAPHICS_BIT) | |
break; | |
VkDevice device{}; | |
vkCreateDevice(physicalDevice, Temp(VkDeviceCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
.queueCreateInfoCount = 1, | |
.pQueueCreateInfos = Temp(VkDeviceQueueCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
.queueFamilyIndex = queueFamily, | |
.queueCount = 1, | |
.pQueuePriorities = Temp(1.f), | |
}), | |
.enabledExtensionCount = 1, | |
.ppEnabledExtensionNames = Temp<const char*>(VK_KHR_SWAPCHAIN_EXTENSION_NAME), | |
}), nullptr, &device); | |
VkQueue queue{}; | |
vkGetDeviceQueue(device, queueFamily, 0, &queue); | |
// ----------------------------------------------------------------------------- | |
VkSwapchainKHR swapchain{}; | |
std::vector<VkSurfaceFormatKHR> formats; | |
VkFormat format{}; | |
Enumerate(formats, vkGetPhysicalDeviceSurfaceFormatsKHR, physicalDevice, surface); | |
for (uint32_t i = 0; i < formats.size(); ++i) { | |
format = formats[i].format; | |
if (format != VK_FORMAT_B8G8R8A8_UNORM && format != VK_FORMAT_R8G8B8A8_UNORM) | |
continue; | |
vkCreateSwapchainKHR(device, Temp(VkSwapchainCreateInfoKHR { | |
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | |
.surface = surface, | |
.minImageCount = 2, | |
.imageFormat = format, | |
.imageColorSpace = formats[i].colorSpace, | |
.imageExtent = { width, height }, | |
.imageArrayLayers = 1, | |
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | |
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, | |
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, | |
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, | |
.presentMode = VK_PRESENT_MODE_FIFO_KHR, | |
.clipped = VK_TRUE, | |
}), nullptr, &swapchain); | |
break; | |
} | |
std::array<VkImage, 2> images{}; | |
vkGetSwapchainImagesKHR(device, swapchain, Temp(2u), images.data()); | |
// ----------------------------------------------------------------------------- | |
glslang::InitializeProcess(); | |
auto vertexShader = compile(device, VK_SHADER_STAGE_VERTEX_BIT, R"( | |
#version 450 | |
const vec2 positions[3] = vec2[](vec2(-1, 1), vec2(1, 1), vec2(0, -1)); | |
const vec3 colors[3] = vec3[](vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1)); | |
layout(location = 0) out vec3 color; | |
void main() { | |
color = colors[gl_VertexIndex]; | |
gl_Position = vec4(positions[gl_VertexIndex] * vec2(0.75), 0, 1); | |
})"); | |
auto fragmentShader = compile(device, VK_SHADER_STAGE_FRAGMENT_BIT, R"( | |
#version 450 | |
layout(location = 0) in vec3 inColor; | |
layout(location = 0) out vec4 fragColor; | |
void main() { | |
fragColor = vec4(inColor, 1); | |
})"); | |
// ----------------------------------------------------------------------------- | |
VkRenderPass renderPass{}; | |
vkCreateRenderPass(device, Temp(VkRenderPassCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
.attachmentCount = 1, | |
.pAttachments = Temp(VkAttachmentDescription { | |
.format = format, | |
.samples = VK_SAMPLE_COUNT_1_BIT, | |
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | |
.storeOp = VK_ATTACHMENT_STORE_OP_STORE, | |
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | |
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
}), | |
.subpassCount = 1, | |
.pSubpasses = Temp(VkSubpassDescription { | |
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, | |
.colorAttachmentCount = 1, | |
.pColorAttachments = Temp(VkAttachmentReference { | |
.attachment = 0, | |
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
}) | |
}), | |
}), nullptr, &renderPass); | |
// ----------------------------------------------------------------------------- | |
VkPipelineLayout pipelineLayout; | |
vkCreatePipelineLayout(device, Temp(VkPipelineLayoutCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
}), nullptr, &pipelineLayout); | |
// ----------------------------------------------------------------------------- | |
VkPipeline pipeline; | |
vkCreateGraphicsPipelines(device, nullptr, 1, Temp(VkGraphicsPipelineCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
.stageCount = 2, | |
.pStages = std::array { vertexShader, fragmentShader }.data(), | |
.pVertexInputState = Temp(VkPipelineVertexInputStateCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
}), | |
.pInputAssemblyState = Temp(VkPipelineInputAssemblyStateCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
}), | |
.pViewportState = Temp(VkPipelineViewportStateCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
.viewportCount = 1, | |
.pViewports = Temp(VkViewport { 0.f, 0.f, float(width), float(height), 0.f, 1.f }), | |
.scissorCount = 1, | |
.pScissors = Temp(VkRect2D { { 0, 0 }, { width, height } }) | |
}), | |
.pRasterizationState = Temp(VkPipelineRasterizationStateCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
.lineWidth = 1.f | |
}), | |
.pMultisampleState = Temp(VkPipelineMultisampleStateCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, | |
}), | |
.pDepthStencilState = nullptr, | |
.pColorBlendState = Temp(VkPipelineColorBlendStateCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
.attachmentCount = 1, | |
.pAttachments = Temp(VkPipelineColorBlendAttachmentState { | |
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | |
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | |
}), | |
}), | |
.pDynamicState = nullptr, | |
.layout = pipelineLayout, | |
.renderPass = renderPass, | |
.subpass = 0, | |
}), nullptr, &pipeline); | |
// ----------------------------------------------------------------------------- | |
VkSemaphoreCreateInfo semaphoreInfo { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; | |
VkSemaphore imageReady{}, imagePresentable{}; | |
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageReady); | |
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imagePresentable); | |
// ----------------------------------------------------------------------------- | |
uint32_t imageIndex; | |
vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageReady, nullptr, &imageIndex); | |
VkImage image = images[imageIndex]; | |
// ----------------------------------------------------------------------------- | |
VkImageView imageView{}; | |
vkCreateImageView(device, Temp(VkImageViewCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
.image = image, | |
.viewType = VK_IMAGE_VIEW_TYPE_2D, | |
.format = format, | |
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, | |
}), nullptr, &imageView); | |
// ----------------------------------------------------------------------------- | |
VkFramebuffer framebuffer; | |
vkCreateFramebuffer(device, Temp(VkFramebufferCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
.renderPass = renderPass, | |
.attachmentCount = 1, | |
.pAttachments = &imageView, | |
.width = width, | |
.height = height, | |
.layers = 1, | |
}), nullptr, &framebuffer); | |
// ----------------------------------------------------------------------------- | |
VkCommandPool cmdPool{}; | |
vkCreateCommandPool(device, Temp(VkCommandPoolCreateInfo { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
.queueFamilyIndex = queueFamily | |
}), nullptr, &cmdPool); | |
// ----------------------------------------------------------------------------- | |
VkCommandBuffer cmd{}; | |
vkAllocateCommandBuffers(device, Temp(VkCommandBufferAllocateInfo { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
.commandPool = cmdPool, | |
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
.commandBufferCount = 1, | |
}), &cmd); | |
vkBeginCommandBuffer(cmd, Temp(VkCommandBufferBeginInfo { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO | |
})); | |
// ----------------------------------------------------------------------------- | |
vkCmdBeginRenderPass(cmd, Temp(VkRenderPassBeginInfo { | |
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
.renderPass = renderPass, | |
.framebuffer = framebuffer, | |
.renderArea = { {}, { width, height } }, | |
.clearValueCount = 1, | |
.pClearValues = Temp(VkClearValue { .color = {{ 0.1f, 0.1f, 0.1f, 1.f }} }), | |
}), VK_SUBPASS_CONTENTS_INLINE); | |
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); | |
vkCmdDraw(cmd, 3, 1, 0, 0); | |
vkCmdEndRenderPass(cmd); | |
// ----------------------------------------------------------------------------- | |
vkEndCommandBuffer(cmd); | |
vkQueueSubmit(queue, 1, Temp(VkSubmitInfo { | |
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
.waitSemaphoreCount = 1, | |
.pWaitSemaphores = &imageReady, | |
.pWaitDstStageMask = Temp<VkPipelineStageFlags>(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT), | |
.commandBufferCount = 1, | |
.pCommandBuffers = &cmd, | |
.signalSemaphoreCount = 1, | |
.pSignalSemaphores = &imagePresentable, | |
}), nullptr); | |
vkQueuePresentKHR(queue, Temp(VkPresentInfoKHR { | |
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
.waitSemaphoreCount = 1, | |
.pWaitSemaphores = &imagePresentable, | |
.swapchainCount = 1, | |
.pSwapchains = &swapchain, | |
.pImageIndices = &imageIndex, | |
})); | |
// ----------------------------------------------------------------------------- | |
while (!glfwWindowShouldClose(window)) | |
glfwWaitEvents(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment