Skip to content

Instantly share code, notes, and snippets.

@Darianopolis
Last active March 19, 2024 16:58
Show Gist options
  • Save Darianopolis/6d461dcfce184805e504e0ce32c220bb to your computer and use it in GitHub Desktop.
Save Darianopolis/6d461dcfce184805e504e0ce32c220bb to your computer and use it in GitHub Desktop.
vkhellotriangle
#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