Create Vulkan demo

This commit is contained in:
Camden Dixie O'Brien committed 2025-09-25 17:17:52 +01:00
commit cf7a0f099a
5 files changed
+647

No files matched your search

+1
View File
@@ -0,0 +1 @@
build/
+41
View File
@@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.10)
project(renderer LANGUAGES C)
macro(configure_target target)
set_property(TARGET ${target} PROPERTY C_STANDARD 23)
set_property(TARGET ${target} PROPERTY C_EXTENSIONS OFF)
target_compile_options(${target} PRIVATE -Wall -Wextra -pedantic)
endmacro()
find_program(glslc glslc)
function(add_shaders target)
set(bin_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}")
foreach(src ${ARGN})
get_filename_component(stage ${src} NAME_WLE)
set(bin "${bin_dir}/${stage}.spv")
list(APPEND bins ${bin})
add_custom_command(
OUTPUT ${bin}
COMMAND ${glslc} -fshader-stage=${stage}
${CMAKE_CURRENT_SOURCE_DIR}/${src} -o ${bin}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}"
)
endforeach()
add_custom_target(${target}-spirv DEPENDS ${bins})
add_library(${target} INTERFACE)
target_compile_definitions(${target} INTERFACE
VERT_SHADER_SPV="${bin_dir}/vert.spv"
FRAG_SHADER_SPV="${bin_dir}/frag.spv")
add_dependencies(${target} ${target}-spirv)
endfunction()
add_shaders(shaders
vert.glsl
frag.glsl
)
add_executable(demo demo.c)
configure_target(demo)
target_link_libraries(demo PRIVATE glfw m shaders vulkan)
+576
View File
@@ -0,0 +1,576 @@
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define CLAMP(x, min, max) MAX((min), MIN(x, (max)))
#define QUEUE_FAMILY_PROP_BUFFER_SIZE 16
#define EXT_PROP_BUFFER_SIZE 256
#define SURFACE_FMT_BUFFER_SIZE 64
#define PRESENT_MODE_BUFFER_SIZE 8
#define SWAPCHAIN_IMG_BUFFER_SIZE 4
#define MAX_SHADER_SIZE (8 * 1024)
static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
static const char *extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
static const char vert_shader_code[] = {
#embed VERT_SHADER_SPV
};
static const char frag_shader_code[] = {
#embed FRAG_SHADER_SPV
};
#define NEXTENSIONS (sizeof(extensions) / sizeof(extensions[0]))
static void
key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
(void)window;
(void)scancode;
(void)mods;
switch (key) {
case GLFW_KEY_Q:
if (action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
break;
}
}
static VkShaderModule
load_shader_module(VkDevice dev, const char *code, size_t code_size)
{
const VkShaderModuleCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = code_size,
.pCode = (uint32_t *)code,
};
VkShaderModule mod;
const VkResult result
= vkCreateShaderModule(dev, &create_info, nullptr, &mod);
assert(result == VK_SUCCESS);
return mod;
}
int main()
{
// Set up window
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow *window = glfwCreateWindow(800, 600, "Foo", nullptr, nullptr);
assert(window);
// Register input callbacks
glfwSetKeyCallback(window, key_callback);
// Initialise Vulkan instance.
const VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Foo",
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
.pEngineName = "No Engine",
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
.apiVersion = VK_API_VERSION_1_0,
};
uint32_t glfw_ext_count = 0;
const char **glfw_ext_names
= glfwGetRequiredInstanceExtensions(&glfw_ext_count);
const VkInstanceCreateInfo inst_create_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app_info,
.enabledExtensionCount = glfw_ext_count,
.ppEnabledExtensionNames = glfw_ext_names,
.enabledLayerCount = sizeof(layers) / sizeof(layers[0]),
.ppEnabledLayerNames = layers,
};
VkInstance inst;
VkResult result = vkCreateInstance(&inst_create_info, nullptr, &inst);
assert(result == VK_SUCCESS);
// Create surface
VkSurfaceKHR surface;
result = glfwCreateWindowSurface(inst, window, nullptr, &surface);
assert(result == VK_SUCCESS);
// Select a physical device
VkPhysicalDevice physical_dev;
uint32_t dev_count = 1;
result = vkEnumeratePhysicalDevices(inst, &dev_count, &physical_dev);
assert(result == VK_SUCCESS || result == VK_INCOMPLETE);
// Select queue family
uint32_t queue_family_count = QUEUE_FAMILY_PROP_BUFFER_SIZE;
VkQueueFamilyProperties
queue_family_props[QUEUE_FAMILY_PROP_BUFFER_SIZE];
vkGetPhysicalDeviceQueueFamilyProperties(
physical_dev, &queue_family_count, queue_family_props);
uint32_t queue_family;
bool found_queue_family = false;
for (unsigned i = 0; i < queue_family_count; ++i) {
if (queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
VkBool32 surface_support = false;
vkGetPhysicalDeviceSurfaceSupportKHR(
physical_dev, i, surface, &surface_support);
if (surface_support) {
found_queue_family = true;
queue_family = i;
break;
}
}
}
assert(found_queue_family);
// Check that the physical device supports all extensions
uint32_t ext_count = EXT_PROP_BUFFER_SIZE;
VkExtensionProperties ext_props[EXT_PROP_BUFFER_SIZE];
result = vkEnumerateDeviceExtensionProperties(
physical_dev, nullptr, &ext_count, ext_props);
assert(result == VK_SUCCESS);
bool supported[NEXTENSIONS] = {};
for (unsigned i = 0; i < ext_count; ++i) {
for (unsigned j = 0; j < NEXTENSIONS; ++j) {
if (strcmp(extensions[j], ext_props[i].extensionName) == 0) {
supported[j] = true;
break;
}
}
}
for (unsigned j = 0; j < NEXTENSIONS; ++j) {
if (!supported[j]) {
fprintf(stderr, "Unsupported: %s\n", extensions[j]);
assert(false);
}
}
// Create the logical device and get the queue handle.
float queue_priority = 1.0;
const VkDeviceQueueCreateInfo queue_create_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = queue_family,
.queueCount = 1,
.pQueuePriorities = &queue_priority,
};
const VkDeviceCreateInfo dev_create_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pQueueCreateInfos = &queue_create_info,
.queueCreateInfoCount = 1,
.enabledExtensionCount = NEXTENSIONS,
.ppEnabledExtensionNames = extensions,
};
VkDevice dev;
result = vkCreateDevice(physical_dev, &dev_create_info, nullptr, &dev);
assert(result == VK_SUCCESS);
VkQueue queue;
vkGetDeviceQueue(dev, queue_family, 0, &queue);
// Select a surface format, preferring R8G8B8A8_SRGB with
// SRGB_NONLINEAR colour space.
uint32_t surface_fmt_count = SURFACE_FMT_BUFFER_SIZE;
VkSurfaceFormatKHR surface_fmts[SURFACE_FMT_BUFFER_SIZE];
result = vkGetPhysicalDeviceSurfaceFormatsKHR(
physical_dev, surface, &surface_fmt_count, surface_fmts);
assert(result == VK_SUCCESS);
assert(surface_fmt_count > 0);
VkSurfaceFormatKHR surface_fmt = surface_fmts[0];
for (unsigned i = 0; i < surface_fmt_count; ++i) {
if (surface_fmts[i].format == VK_FORMAT_R8G8B8A8_SRGB
&& surface_fmts[i].colorSpace
== VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
surface_fmt = surface_fmts[i];
}
}
// Query surface capabilities
VkSurfaceCapabilitiesKHR surface_caps;
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
physical_dev, surface, &surface_caps);
assert(result == VK_SUCCESS);
// Select swap chain extent
VkExtent2D swapchain_extent;
if (surface_caps.currentExtent.width == UINT32_MAX) {
int width, height;
glfwGetFramebufferSize(window, &width, &height);
swapchain_extent.width = CLAMP(
(uint32_t)width, surface_caps.minImageExtent.width,
surface_caps.maxImageExtent.width);
swapchain_extent.height = CLAMP(
(uint32_t)height, surface_caps.minImageExtent.height,
surface_caps.maxImageExtent.height);
} else {
swapchain_extent = surface_caps.currentExtent;
}
// Select image count
uint32_t swapchain_img_count = surface_caps.minImageCount + 1;
assert(
surface_caps.maxImageCount == 0
|| swapchain_img_count <= surface_caps.maxImageCount);
// Create the swap chain
const VkSwapchainCreateInfoKHR swapchain_create_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.minImageCount = swapchain_img_count,
.imageFormat = surface_fmt.format,
.imageColorSpace = surface_fmt.colorSpace,
.imageExtent = swapchain_extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.preTransform = surface_caps.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
VkSwapchainKHR swapchain;
result = vkCreateSwapchainKHR(
dev, &swapchain_create_info, nullptr, &swapchain);
assert(result == VK_SUCCESS);
// Retreive swapchain images
VkImage swapchain_imgs[SWAPCHAIN_IMG_BUFFER_SIZE];
swapchain_img_count = SWAPCHAIN_IMG_BUFFER_SIZE;
result = vkGetSwapchainImagesKHR(
dev, swapchain, &swapchain_img_count, swapchain_imgs);
assert(result == VK_SUCCESS);
// Create swapchain image views
VkImageView swapchain_img_views[SWAPCHAIN_IMG_BUFFER_SIZE];
VkImageViewCreateInfo img_view_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = surface_fmt.format,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
for (unsigned i = 0; i < swapchain_img_count; ++i) {
img_view_create_info.image = swapchain_imgs[i];
result = vkCreateImageView(
dev, &img_view_create_info, nullptr, swapchain_img_views + i);
}
// Load vertex and fragment shader modules
VkShaderModule vert_shader = load_shader_module(
dev, vert_shader_code, sizeof(vert_shader_code));
VkShaderModule frag_shader = load_shader_module(
dev, frag_shader_code, sizeof(frag_shader_code));
const VkPipelineShaderStageCreateInfo shader_stages[] = {
[0] = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vert_shader,
.pName = "main",
},
[1] = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = frag_shader,
.pName = "main",
},
};
// Set up input configuration
const VkPipelineVertexInputStateCreateInfo vertex_input_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
};
// Set up viewport configuration
const VkViewport viewport = {
.width = (float)swapchain_extent.width,
.height = (float)swapchain_extent.height,
};
const VkRect2D scissor = { .extent = swapchain_extent };
VkPipelineViewportStateCreateInfo viewport_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor,
};
// Populate rasterizer configuration
const VkPipelineRasterizationStateCreateInfo rasterizer_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_NONE,
};
// Populate multisampling configuration
const VkPipelineMultisampleStateCreateInfo multisampling_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
};
// Set up colour blending configuration
const VkPipelineColorBlendAttachmentState colour_blend_attatchment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_FALSE,
};
const VkPipelineColorBlendStateCreateInfo colour_blend_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.attachmentCount = 1,
.pAttachments = &colour_blend_attatchment,
};
// Create pipeline layout
const VkPipelineLayoutCreateInfo pipeline_layout_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 0,
};
VkPipelineLayout pipeline_layout;
result = vkCreatePipelineLayout(
dev, &pipeline_layout_config, nullptr, &pipeline_layout);
assert(result == VK_SUCCESS);
// Set up colour attachment
const VkAttachmentDescription colour_attachment = {
.format = surface_fmt.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,
};
const VkAttachmentReference colour_attachment_ref = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
const VkSubpassDescription subpass = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &colour_attachment_ref,
};
// Create render pass
const VkSubpassDependency dependency = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
};
const VkRenderPassCreateInfo render_pass_config = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &colour_attachment,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
};
VkRenderPass render_pass;
result = vkCreateRenderPass(
dev, &render_pass_config, nullptr, &render_pass);
assert(result == VK_SUCCESS);
// Create pipeline
const VkGraphicsPipelineCreateInfo pipeline_config = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = shader_stages,
.pVertexInputState = &vertex_input_config,
.pInputAssemblyState = &input_assembly_config,
.pViewportState = &viewport_config,
.pRasterizationState = &rasterizer_config,
.pMultisampleState = &multisampling_config,
.pColorBlendState = &colour_blend_config,
.layout = pipeline_layout,
.renderPass = render_pass,
.subpass = 0,
};
VkPipeline pipeline;
result = vkCreateGraphicsPipelines(
dev, VK_NULL_HANDLE, 1, &pipeline_config, nullptr, &pipeline);
assert(result == VK_SUCCESS);
// Create framebuffers
VkFramebuffer framebuffers[SWAPCHAIN_IMG_BUFFER_SIZE];
for (unsigned i = 0; i < swapchain_img_count; ++i) {
const VkFramebufferCreateInfo framebuffer_config = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = render_pass,
.attachmentCount = 1,
.pAttachments = swapchain_img_views + i,
.width = swapchain_extent.width,
.height = swapchain_extent.height,
.layers = 1,
};
result = vkCreateFramebuffer(
dev, &framebuffer_config, nullptr, framebuffers + i);
assert(result == VK_SUCCESS);
}
// Create command pool
const VkCommandPoolCreateInfo cmd_pool_config = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queue_family,
};
VkCommandPool cmd_pool;
result = vkCreateCommandPool(dev, &cmd_pool_config, nullptr, &cmd_pool);
assert(result == VK_SUCCESS);
// Allocate command buffer
const VkCommandBufferAllocateInfo cmd_buf_config = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = cmd_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
VkCommandBuffer cmd_buf;
result = vkAllocateCommandBuffers(dev, &cmd_buf_config, &cmd_buf);
assert(result == VK_SUCCESS);
// Create syncronisation primitives
VkSemaphore img_avail, render_fin;
VkSemaphoreCreateInfo semaphore_config
= { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
result = vkCreateSemaphore(dev, &semaphore_config, nullptr, &img_avail);
assert(result == VK_SUCCESS);
result = vkCreateSemaphore(dev, &semaphore_config, nullptr, &render_fin);
assert(result == VK_SUCCESS);
VkFence in_flight;
VkFenceCreateInfo fence_config = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
result = vkCreateFence(dev, &fence_config, nullptr, &in_flight);
assert(result == VK_SUCCESS);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Wait for previous frame to finish
vkWaitForFences(dev, 1, &in_flight, VK_TRUE, UINT64_MAX);
vkResetFences(dev, 1, &in_flight);
// Aquire image from swapchain
uint32_t img_index;
result = vkAcquireNextImageKHR(
dev, swapchain, UINT64_MAX, img_avail, VK_NULL_HANDLE,
&img_index);
assert(result == VK_SUCCESS);
// Reset command buffer, then begin writing
vkResetCommandBuffer(cmd_buf, 0);
const VkCommandBufferBeginInfo cmd_begin = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
};
result = vkBeginCommandBuffer(cmd_buf, &cmd_begin);
assert(result == VK_SUCCESS);
// Start render pass
VkClearValue clear_value = { { { 0.0, 0.0, 0.0, 1.0 } } };
VkRenderPassBeginInfo render_pass_begin = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = render_pass,
.framebuffer = framebuffers[img_index],
.renderArea.extent = swapchain_extent,
.clearValueCount = 1,
.pClearValues = &clear_value,
};
vkCmdBeginRenderPass(
cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
// Bind graphics pipeline and draw
vkCmdBindPipeline(
cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdDraw(cmd_buf, 4, 1, 0, 0);
// Finish render pass and end writing to command buffer
vkCmdEndRenderPass(cmd_buf);
result = vkEndCommandBuffer(cmd_buf);
assert(result == VK_SUCCESS);
// Submit command buffer
VkPipelineStageFlags wait_stage
= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
const VkSubmitInfo submit = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &img_avail,
.pWaitDstStageMask = &wait_stage,
.commandBufferCount = 1,
.pCommandBuffers = &cmd_buf,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &render_fin,
};
result = vkQueueSubmit(queue, 1, &submit, in_flight);
assert(result == VK_SUCCESS);
// Present frame
const VkPresentInfoKHR present_info = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &render_fin,
.swapchainCount = 1,
.pSwapchains = &swapchain,
.pImageIndices = &img_index,
};
result = vkQueuePresentKHR(queue, &present_info);
assert(result == VK_SUCCESS);
}
vkDeviceWaitIdle(dev);
// Clean up
vkDestroySemaphore(dev, img_avail, nullptr);
vkDestroySemaphore(dev, render_fin, nullptr);
vkDestroyFence(dev, in_flight, nullptr);
vkDestroyCommandPool(dev, cmd_pool, nullptr);
vkDestroyPipeline(dev, pipeline, nullptr);
vkDestroyRenderPass(dev, render_pass, nullptr);
vkDestroyPipelineLayout(dev, pipeline_layout, nullptr);
vkDestroyShaderModule(dev, vert_shader, nullptr);
vkDestroyShaderModule(dev, frag_shader, nullptr);
for (unsigned i = 0; i < swapchain_img_count; ++i) {
vkDestroyFramebuffer(dev, framebuffers[i], nullptr);
vkDestroyImageView(dev, swapchain_img_views[i], nullptr);
}
vkDestroySwapchainKHR(dev, swapchain, nullptr);
vkDestroyDevice(dev, nullptr);
vkDestroySurfaceKHR(inst, surface, nullptr);
vkDestroyInstance(inst, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
}
+9
View File
@@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec3 frag_colour;
layout(location = 0) out vec4 out_colour;
void main()
{
out_colour = vec4(frag_colour, 1.0);
}
+20
View File
@@ -0,0 +1,20 @@
#version 450
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
vec3 colours[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
layout(location = 0) out vec3 frag_colour;
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
frag_colour = colours[gl_VertexIndex];
}