Separate vulkan code into gpu module

This commit is contained in:
Camden Dixie O'Brien committed 2025-09-26 14:30:38 +01:00
1 parent 51c01a2f9e
commit d4214941b1
4 files changed
+311 -236

No files matched your search

+6 -2
View File
@@ -36,6 +36,10 @@ add_shaders(shaders
frag.glsl
)
add_executable(demo demo.c)
add_executable(demo
main.c
gpu.c
)
configure_target(demo)
target_link_libraries(demo PRIVATE glfw m shaders vulkan)
target_include_directories(demo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(demo PRIVATE glfw m vulkan shaders)
+195 -234
View File
@@ -1,66 +1,36 @@
#define GLFW_INCLUDE_VULKAN
#include "gpu.h"
#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 NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
#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)
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
typedef struct {
float position[2], colour[3];
} vertex_t;
#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)))
static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
static const char *extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
static const char vert_shader_code[] = {
static constexpr size_t extension_count = NELEMS(extensions);
static const char vert_shader[] = {
#embed VERT_SHADER_SPV
};
static const char frag_shader_code[] = {
static const char frag_shader[] = {
#embed FRAG_SHADER_SPV
};
static const vertex_t vertices[] = {
{ { 0.0, -0.5 }, { 1.0, 0.0, 0.0 } },
{ { 0.5, 0.5 }, { 0.0, 1.0, 0.0 } },
{ { -0.5, 0.5 }, { 0.0, 0.0, 1.0 } },
};
#define NEXTENSIONS NELEMS(extensions)
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)
{
@@ -77,22 +47,12 @@ load_shader_module(VkDevice dev, const char *code, size_t code_size)
return mod;
}
int main()
void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config)
{
// 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",
.pApplicationName = config->application_name,
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
.pEngineName = "No Engine",
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
@@ -109,19 +69,19 @@ int main()
.enabledLayerCount = sizeof(layers) / sizeof(layers[0]),
.ppEnabledLayerNames = layers,
};
VkInstance inst;
VkResult result = vkCreateInstance(&inst_create_info, nullptr, &inst);
VkResult result
= vkCreateInstance(&inst_create_info, nullptr, &ctx->inst);
assert(result == VK_SUCCESS);
// Create surface
VkSurfaceKHR surface;
result = glfwCreateWindowSurface(inst, window, nullptr, &surface);
result = glfwCreateWindowSurface(
ctx->inst, config->window, nullptr, &ctx->surface);
assert(result == VK_SUCCESS);
// Select a physical device
VkPhysicalDevice physical_dev;
uint32_t dev_count = 1;
result = vkEnumeratePhysicalDevices(inst, &dev_count, &physical_dev);
result = vkEnumeratePhysicalDevices(
ctx->inst, &dev_count, &ctx->physical_dev);
assert(result == VK_SUCCESS || result == VK_INCOMPLETE);
// Select queue family
@@ -129,14 +89,14 @@ int main()
VkQueueFamilyProperties
queue_family_props[QUEUE_FAMILY_PROP_BUFFER_SIZE];
vkGetPhysicalDeviceQueueFamilyProperties(
physical_dev, &queue_family_count, queue_family_props);
ctx->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);
ctx->physical_dev, i, ctx->surface, &surface_support);
if (surface_support) {
found_queue_family = true;
queue_family = i;
@@ -150,18 +110,18 @@ int main()
uint32_t ext_count = EXT_PROP_BUFFER_SIZE;
VkExtensionProperties ext_props[EXT_PROP_BUFFER_SIZE];
result = vkEnumerateDeviceExtensionProperties(
physical_dev, nullptr, &ext_count, ext_props);
ctx->physical_dev, nullptr, &ext_count, ext_props);
assert(result == VK_SUCCESS);
bool supported[NEXTENSIONS] = {};
bool supported[extension_count] = {};
for (unsigned i = 0; i < ext_count; ++i) {
for (unsigned j = 0; j < NEXTENSIONS; ++j) {
for (unsigned j = 0; j < extension_count; ++j) {
if (strcmp(extensions[j], ext_props[i].extensionName) == 0) {
supported[j] = true;
break;
}
}
}
for (unsigned j = 0; j < NEXTENSIONS; ++j) {
for (unsigned j = 0; j < extension_count; ++j) {
if (!supported[j]) {
fprintf(stderr, "Unsupported: %s\n", extensions[j]);
assert(false);
@@ -180,21 +140,20 @@ int main()
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pQueueCreateInfos = &queue_create_info,
.queueCreateInfoCount = 1,
.enabledExtensionCount = NEXTENSIONS,
.enabledExtensionCount = extension_count,
.ppEnabledExtensionNames = extensions,
};
VkDevice dev;
result = vkCreateDevice(physical_dev, &dev_create_info, nullptr, &dev);
result = vkCreateDevice(
ctx->physical_dev, &dev_create_info, nullptr, &ctx->dev);
assert(result == VK_SUCCESS);
VkQueue queue;
vkGetDeviceQueue(dev, queue_family, 0, &queue);
vkGetDeviceQueue(ctx->dev, queue_family, 0, &ctx->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);
ctx->physical_dev, ctx->surface, &surface_fmt_count, surface_fmts);
assert(result == VK_SUCCESS);
assert(surface_fmt_count > 0);
VkSurfaceFormatKHR surface_fmt = surface_fmts[0];
@@ -209,26 +168,26 @@ int main()
// Query surface capabilities
VkSurfaceCapabilitiesKHR surface_caps;
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
physical_dev, surface, &surface_caps);
ctx->physical_dev, ctx->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(
glfwGetFramebufferSize(config->window, &width, &height);
ctx->swapchain_extent.width = CLAMP(
(uint32_t)width, surface_caps.minImageExtent.width,
surface_caps.maxImageExtent.width);
swapchain_extent.height = CLAMP(
ctx->swapchain_extent.height = CLAMP(
(uint32_t)height, surface_caps.minImageExtent.height,
surface_caps.maxImageExtent.height);
} else {
swapchain_extent = surface_caps.currentExtent;
ctx->swapchain_extent = surface_caps.currentExtent;
}
// Select image count
uint32_t swapchain_img_count = surface_caps.minImageCount + 1;
assert(swapchain_img_count <= SWAPCHAIN_IMG_BUFFER_SIZE);
assert(
surface_caps.maxImageCount == 0
|| swapchain_img_count <= surface_caps.maxImageCount);
@@ -236,11 +195,11 @@ int main()
// Create the swap chain
const VkSwapchainCreateInfoKHR swapchain_create_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.surface = ctx->surface,
.minImageCount = swapchain_img_count,
.imageFormat = surface_fmt.format,
.imageColorSpace = surface_fmt.colorSpace,
.imageExtent = swapchain_extent,
.imageExtent = ctx->swapchain_extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
@@ -250,20 +209,18 @@ int main()
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
VkSwapchainKHR swapchain;
result = vkCreateSwapchainKHR(
dev, &swapchain_create_info, nullptr, &swapchain);
ctx->dev, &swapchain_create_info, nullptr, &ctx->swapchain);
assert(result == VK_SUCCESS);
// Retreive swapchain images
VkImage swapchain_imgs[SWAPCHAIN_IMG_BUFFER_SIZE];
swapchain_img_count = SWAPCHAIN_IMG_BUFFER_SIZE;
ctx->swapchain_img_count = SWAPCHAIN_IMG_BUFFER_SIZE;
result = vkGetSwapchainImagesKHR(
dev, swapchain, &swapchain_img_count, swapchain_imgs);
ctx->dev, ctx->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,
@@ -285,26 +242,28 @@ int main()
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);
ctx->dev, &img_view_create_info, nullptr,
ctx->swapchain_img_views + i);
assert(result == VK_SUCCESS);
}
// 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));
ctx->vert_shader
= load_shader_module(ctx->dev, vert_shader, sizeof(vert_shader));
ctx->frag_shader
= load_shader_module(ctx->dev, frag_shader, sizeof(frag_shader));
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",
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = ctx->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",
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = ctx->frag_shader,
.pName = "main",
},
};
@@ -344,10 +303,10 @@ int main()
// Set up viewport configuration
const VkViewport viewport = {
.width = (float)swapchain_extent.width,
.height = (float)swapchain_extent.height,
.width = (float)ctx->swapchain_extent.width,
.height = (float)ctx->swapchain_extent.height,
};
const VkRect2D scissor = { .extent = swapchain_extent };
const VkRect2D scissor = { .extent = ctx->swapchain_extent };
VkPipelineViewportStateCreateInfo viewport_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
@@ -391,9 +350,8 @@ int main()
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 0,
};
VkPipelineLayout pipeline_layout;
result = vkCreatePipelineLayout(
dev, &pipeline_layout_config, nullptr, &pipeline_layout);
ctx->dev, &pipeline_layout_config, nullptr, &ctx->pipeline_layout);
assert(result == VK_SUCCESS);
// Set up colour attachment
@@ -435,9 +393,8 @@ int main()
.dependencyCount = 1,
.pDependencies = &dependency,
};
VkRenderPass render_pass;
result = vkCreateRenderPass(
dev, &render_pass_config, nullptr, &render_pass);
ctx->dev, &render_pass_config, nullptr, &ctx->render_pass);
assert(result == VK_SUCCESS);
// Create pipeline
@@ -451,42 +408,41 @@ int main()
.pRasterizationState = &rasterizer_config,
.pMultisampleState = &multisampling_config,
.pColorBlendState = &colour_blend_config,
.layout = pipeline_layout,
.renderPass = render_pass,
.layout = ctx->pipeline_layout,
.renderPass = ctx->render_pass,
.subpass = 0,
};
VkPipeline pipeline;
result = vkCreateGraphicsPipelines(
dev, VK_NULL_HANDLE, 1, &pipeline_config, nullptr, &pipeline);
ctx->dev, VK_NULL_HANDLE, 1, &pipeline_config, nullptr,
&ctx->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,
.renderPass = ctx->render_pass,
.attachmentCount = 1,
.pAttachments = swapchain_img_views + i,
.width = swapchain_extent.width,
.height = swapchain_extent.height,
.pAttachments = ctx->swapchain_img_views + i,
.width = ctx->swapchain_extent.width,
.height = ctx->swapchain_extent.height,
.layers = 1,
};
result = vkCreateFramebuffer(
dev, &framebuffer_config, nullptr, framebuffers + i);
ctx->dev, &framebuffer_config, nullptr, ctx->framebuffers + i);
assert(result == VK_SUCCESS);
}
// Create vertex buffer
const VkBufferCreateInfo vertex_buffer_config = {
ctx->vertex_data_size = config->vertex_data_size;
const VkBufferCreateInfo vertex_buf_config = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(vertices),
.size = ctx->vertex_data_size,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VkBuffer vertex_buffer;
result = vkCreateBuffer(
dev, &vertex_buffer_config, nullptr, &vertex_buffer);
ctx->dev, &vertex_buf_config, nullptr, &ctx->vertex_buf);
assert(result == VK_SUCCESS);
// Allocate memory to vertex buffer
@@ -494,9 +450,10 @@ int main()
= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VkMemoryRequirements mem_requirements;
vkGetBufferMemoryRequirements(dev, vertex_buffer, &mem_requirements);
vkGetBufferMemoryRequirements(
ctx->dev, ctx->vertex_buf, &mem_requirements);
VkPhysicalDeviceMemoryProperties mem_props;
vkGetPhysicalDeviceMemoryProperties(physical_dev, &mem_props);
vkGetPhysicalDeviceMemoryProperties(ctx->physical_dev, &mem_props);
uint32_t mem_type = UINT32_MAX;
for (unsigned i = 0; i < mem_props.memoryTypeCount; ++i) {
const bool valid_type
@@ -515,18 +472,18 @@ int main()
.allocationSize = mem_requirements.size,
.memoryTypeIndex = mem_type,
};
VkDeviceMemory vertex_buffer_mem;
result = vkAllocateMemory(
dev, &allocate_config, nullptr, &vertex_buffer_mem);
ctx->dev, &allocate_config, nullptr, &ctx->vertex_buf_mem);
assert(result == VK_SUCCESS);
vkBindBufferMemory(dev, vertex_buffer, vertex_buffer_mem, 0);
vkBindBufferMemory(ctx->dev, ctx->vertex_buf, ctx->vertex_buf_mem, 0);
// Copy vertex data into vertex buffer
void *vertex_data;
void *vertex_buf_ptr;
vkMapMemory(
dev, vertex_buffer_mem, 0, sizeof(vertices), 0, &vertex_data);
memcpy(vertex_data, vertices, sizeof(vertices));
vkUnmapMemory(dev, vertex_buffer_mem);
ctx->dev, ctx->vertex_buf_mem, 0, ctx->vertex_data_size, 0,
&vertex_buf_ptr);
memcpy(vertex_buf_ptr, config->vertex_data, ctx->vertex_data_size);
vkUnmapMemory(ctx->dev, ctx->vertex_buf_mem);
// Create command pool
const VkCommandPoolCreateInfo cmd_pool_config = {
@@ -534,135 +491,139 @@ int main()
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queue_family,
};
VkCommandPool cmd_pool;
result = vkCreateCommandPool(dev, &cmd_pool_config, nullptr, &cmd_pool);
result = vkCreateCommandPool(
ctx->dev, &cmd_pool_config, nullptr, &ctx->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,
.commandPool = ctx->cmd_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
VkCommandBuffer cmd_buf;
result = vkAllocateCommandBuffers(dev, &cmd_buf_config, &cmd_buf);
result
= vkAllocateCommandBuffers(ctx->dev, &cmd_buf_config, &ctx->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);
VkSemaphoreCreateInfo semaphore_config = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
result = vkCreateSemaphore(
ctx->dev, &semaphore_config, nullptr, &ctx->img_avail);
assert(result == VK_SUCCESS);
result = vkCreateSemaphore(dev, &semaphore_config, nullptr, &render_fin);
result = vkCreateSemaphore(
ctx->dev, &semaphore_config, nullptr, &ctx->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);
result
= vkCreateFence(ctx->dev, &fence_config, nullptr, &ctx->in_flight);
assert(result == VK_SUCCESS);
}
void gpu_cleanup(const gpu_ctx_t *ctx)
{
vkDeviceWaitIdle(ctx->dev);
vkDestroySemaphore(ctx->dev, ctx->img_avail, nullptr);
vkDestroySemaphore(ctx->dev, ctx->render_fin, nullptr);
vkDestroyFence(ctx->dev, ctx->in_flight, nullptr);
vkDestroyCommandPool(ctx->dev, ctx->cmd_pool, nullptr);
vkDestroyPipeline(ctx->dev, ctx->pipeline, nullptr);
vkDestroyRenderPass(ctx->dev, ctx->render_pass, nullptr);
vkDestroyPipelineLayout(ctx->dev, ctx->pipeline_layout, nullptr);
vkDestroyShaderModule(ctx->dev, ctx->vert_shader, nullptr);
vkDestroyShaderModule(ctx->dev, ctx->frag_shader, nullptr);
for (unsigned i = 0; i < ctx->swapchain_img_count; ++i) {
vkDestroyFramebuffer(ctx->dev, ctx->framebuffers[i], nullptr);
vkDestroyImageView(ctx->dev, ctx->swapchain_img_views[i], nullptr);
}
vkDestroySwapchainKHR(ctx->dev, ctx->swapchain, nullptr);
vkDestroyBuffer(ctx->dev, ctx->vertex_buf, nullptr);
vkFreeMemory(ctx->dev, ctx->vertex_buf_mem, nullptr);
vkDestroyDevice(ctx->dev, nullptr);
vkDestroySurfaceKHR(ctx->inst, ctx->surface, nullptr);
vkDestroyInstance(ctx->inst, nullptr);
}
void gpu_render(const gpu_ctx_t *ctx)
{
VkResult result;
// Wait for previous frame to finish
vkWaitForFences(ctx->dev, 1, &ctx->in_flight, VK_TRUE, UINT64_MAX);
vkResetFences(ctx->dev, 1, &ctx->in_flight);
// Aquire image from swapchain
uint32_t img_index;
result = vkAcquireNextImageKHR(
ctx->dev, ctx->swapchain, UINT64_MAX, ctx->img_avail, VK_NULL_HANDLE,
&img_index);
assert(result == VK_SUCCESS);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Reset command buffer, then begin writing
vkResetCommandBuffer(ctx->cmd_buf, 0);
const VkCommandBufferBeginInfo cmd_begin = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
};
result = vkBeginCommandBuffer(ctx->cmd_buf, &cmd_begin);
assert(result == VK_SUCCESS);
// Wait for previous frame to finish
vkWaitForFences(dev, 1, &in_flight, VK_TRUE, UINT64_MAX);
vkResetFences(dev, 1, &in_flight);
// 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 = ctx->render_pass,
.framebuffer = ctx->framebuffers[img_index],
.renderArea.extent = ctx->swapchain_extent,
.clearValueCount = 1,
.pClearValues = &clear_value,
};
vkCmdBeginRenderPass(
ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
// 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);
// Bind graphics pipeline and vertex buffer, then draw
vkCmdBindPipeline(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline);
vkCmdBindVertexBuffers(
ctx->cmd_buf, 0, 1, &ctx->vertex_buf, &(VkDeviceSize) { 0 });
vkCmdDraw(
ctx->cmd_buf, ctx->vertex_data_size / sizeof(vertex_t), 1, 0, 0);
// 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);
// Finish render pass and end writing to command buffer
vkCmdEndRenderPass(ctx->cmd_buf);
result = vkEndCommandBuffer(ctx->cmd_buf);
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);
// 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 = &ctx->img_avail,
.pWaitDstStageMask = &wait_stage,
.commandBufferCount = 1,
.pCommandBuffers = &ctx->cmd_buf,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &ctx->render_fin,
};
result = vkQueueSubmit(ctx->queue, 1, &submit, ctx->in_flight);
assert(result == VK_SUCCESS);
// Bind graphics pipeline and vertex buffer, then draw
vkCmdBindPipeline(
cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindVertexBuffers(
cmd_buf, 0, 1, &vertex_buffer, &(VkDeviceSize) { 0 });
vkCmdDraw(cmd_buf, NELEMS(vertices), 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);
vkDestroyBuffer(dev, vertex_buffer, nullptr);
vkFreeMemory(dev, vertex_buffer_mem, nullptr);
vkDestroyDevice(dev, nullptr);
vkDestroySurfaceKHR(inst, surface, nullptr);
vkDestroyInstance(inst, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
// Present frame
const VkPresentInfoKHR present_info = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &ctx->render_fin,
.swapchainCount = 1,
.pSwapchains = &ctx->swapchain,
.pImageIndices = &img_index,
};
result = vkQueuePresentKHR(ctx->queue, &present_info);
assert(result == VK_SUCCESS);
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef GPU_H
#define GPU_H
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#define SWAPCHAIN_IMG_BUFFER_SIZE 4
typedef struct {
float position[2], colour[3];
} vertex_t;
typedef struct {
VkInstance inst;
VkSurfaceKHR surface;
VkPhysicalDevice physical_dev;
VkDevice dev;
VkQueue queue;
VkSwapchainKHR swapchain;
VkExtent2D swapchain_extent;
uint32_t swapchain_img_count;
VkImageView swapchain_img_views[SWAPCHAIN_IMG_BUFFER_SIZE];
VkFramebuffer framebuffers[SWAPCHAIN_IMG_BUFFER_SIZE];
VkShaderModule vert_shader;
VkShaderModule frag_shader;
VkPipelineLayout pipeline_layout;
VkRenderPass render_pass;
VkPipeline pipeline;
VkCommandPool cmd_pool;
VkCommandBuffer cmd_buf;
VkFence in_flight;
VkSemaphore render_fin;
VkSemaphore img_avail;
VkBuffer vertex_buf;
VkDeviceMemory vertex_buf_mem;
size_t vertex_data_size;
} gpu_ctx_t;
typedef struct {
const char *application_name;
GLFWwindow *window;
const vertex_t *vertex_data;
size_t vertex_data_size;
} gpu_config_t;
void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config);
void gpu_cleanup(const gpu_ctx_t *ctx);
void gpu_render(const gpu_ctx_t *ctx);
#endif
+60
View File
@@ -0,0 +1,60 @@
#include "gpu.h"
#include <assert.h>
#include <math.h>
static const vertex_t vertices[] = {
{ { 0.0, -0.5 }, { 1.0, 0.0, 0.0 } },
{ { 0.5, 0.5 }, { 0.0, 1.0, 0.0 } },
{ { -0.5, 0.5 }, { 0.0, 0.0, 1.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;
}
}
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 GPU
const gpu_config_t gpu_config = {
.application_name = "Foo",
.window = window,
.vertex_data = vertices,
.vertex_data_size = sizeof(vertices),
};
gpu_ctx_t gpu;
gpu_init(&gpu, &gpu_config);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
gpu_render(&gpu);
}
// Clean up
gpu_cleanup(&gpu);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}