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 frag.glsl
) )
add_executable(demo demo.c) add_executable(demo
main.c
gpu.c
)
configure_target(demo) 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 <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.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 QUEUE_FAMILY_PROP_BUFFER_SIZE 16
#define EXT_PROP_BUFFER_SIZE 256 #define EXT_PROP_BUFFER_SIZE 256
#define SURFACE_FMT_BUFFER_SIZE 64 #define SURFACE_FMT_BUFFER_SIZE 64
#define PRESENT_MODE_BUFFER_SIZE 8 #define PRESENT_MODE_BUFFER_SIZE 8
#define SWAPCHAIN_IMG_BUFFER_SIZE 4 #define SWAPCHAIN_IMG_BUFFER_SIZE 4
#define MAX_SHADER_SIZE (8 * 1024) #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
typedef struct { #define MIN(a, b) ((a) < (b) ? (a) : (b))
float position[2], colour[3]; #define MAX(a, b) ((a) > (b) ? (a) : (b))
} vertex_t; #define CLAMP(x, min, max) MAX((min), MIN(x, (max)))
static const char *layers[] = { "VK_LAYER_KHRONOS_validation" }; static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
static const char *extensions[] = { static const char *extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, 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 #embed VERT_SHADER_SPV
}; };
static const char frag_shader_code[] = { static const char frag_shader[] = {
#embed FRAG_SHADER_SPV #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 static VkShaderModule
load_shader_module(VkDevice dev, const char *code, size_t code_size) 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; 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. // Initialise Vulkan instance.
const VkApplicationInfo app_info = { const VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Foo", .pApplicationName = config->application_name,
.applicationVersion = VK_MAKE_VERSION(1, 0, 0), .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
.pEngineName = "No Engine", .pEngineName = "No Engine",
.engineVersion = VK_MAKE_VERSION(1, 0, 0), .engineVersion = VK_MAKE_VERSION(1, 0, 0),
@@ -109,19 +69,19 @@ int main()
.enabledLayerCount = sizeof(layers) / sizeof(layers[0]), .enabledLayerCount = sizeof(layers) / sizeof(layers[0]),
.ppEnabledLayerNames = layers, .ppEnabledLayerNames = layers,
}; };
VkInstance inst; VkResult result
VkResult result = vkCreateInstance(&inst_create_info, nullptr, &inst); = vkCreateInstance(&inst_create_info, nullptr, &ctx->inst);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Create surface // Create surface
VkSurfaceKHR surface; result = glfwCreateWindowSurface(
result = glfwCreateWindowSurface(inst, window, nullptr, &surface); ctx->inst, config->window, nullptr, &ctx->surface);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Select a physical device // Select a physical device
VkPhysicalDevice physical_dev;
uint32_t dev_count = 1; 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); assert(result == VK_SUCCESS || result == VK_INCOMPLETE);
// Select queue family // Select queue family
@@ -129,14 +89,14 @@ int main()
VkQueueFamilyProperties VkQueueFamilyProperties
queue_family_props[QUEUE_FAMILY_PROP_BUFFER_SIZE]; queue_family_props[QUEUE_FAMILY_PROP_BUFFER_SIZE];
vkGetPhysicalDeviceQueueFamilyProperties( vkGetPhysicalDeviceQueueFamilyProperties(
physical_dev, &queue_family_count, queue_family_props); ctx->physical_dev, &queue_family_count, queue_family_props);
uint32_t queue_family; uint32_t queue_family;
bool found_queue_family = false; bool found_queue_family = false;
for (unsigned i = 0; i < queue_family_count; ++i) { for (unsigned i = 0; i < queue_family_count; ++i) {
if (queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
VkBool32 surface_support = false; VkBool32 surface_support = false;
vkGetPhysicalDeviceSurfaceSupportKHR( vkGetPhysicalDeviceSurfaceSupportKHR(
physical_dev, i, surface, &surface_support); ctx->physical_dev, i, ctx->surface, &surface_support);
if (surface_support) { if (surface_support) {
found_queue_family = true; found_queue_family = true;
queue_family = i; queue_family = i;
@@ -150,18 +110,18 @@ int main()
uint32_t ext_count = EXT_PROP_BUFFER_SIZE; uint32_t ext_count = EXT_PROP_BUFFER_SIZE;
VkExtensionProperties ext_props[EXT_PROP_BUFFER_SIZE]; VkExtensionProperties ext_props[EXT_PROP_BUFFER_SIZE];
result = vkEnumerateDeviceExtensionProperties( result = vkEnumerateDeviceExtensionProperties(
physical_dev, nullptr, &ext_count, ext_props); ctx->physical_dev, nullptr, &ext_count, ext_props);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
bool supported[NEXTENSIONS] = {}; bool supported[extension_count] = {};
for (unsigned i = 0; i < ext_count; ++i) { 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) { if (strcmp(extensions[j], ext_props[i].extensionName) == 0) {
supported[j] = true; supported[j] = true;
break; break;
} }
} }
} }
for (unsigned j = 0; j < NEXTENSIONS; ++j) { for (unsigned j = 0; j < extension_count; ++j) {
if (!supported[j]) { if (!supported[j]) {
fprintf(stderr, "Unsupported: %s\n", extensions[j]); fprintf(stderr, "Unsupported: %s\n", extensions[j]);
assert(false); assert(false);
@@ -180,21 +140,20 @@ int main()
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pQueueCreateInfos = &queue_create_info, .pQueueCreateInfos = &queue_create_info,
.queueCreateInfoCount = 1, .queueCreateInfoCount = 1,
.enabledExtensionCount = NEXTENSIONS, .enabledExtensionCount = extension_count,
.ppEnabledExtensionNames = extensions, .ppEnabledExtensionNames = extensions,
}; };
VkDevice dev; result = vkCreateDevice(
result = vkCreateDevice(physical_dev, &dev_create_info, nullptr, &dev); ctx->physical_dev, &dev_create_info, nullptr, &ctx->dev);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
VkQueue queue; vkGetDeviceQueue(ctx->dev, queue_family, 0, &ctx->queue);
vkGetDeviceQueue(dev, queue_family, 0, &queue);
// Select a surface format, preferring R8G8B8A8_SRGB with // Select a surface format, preferring R8G8B8A8_SRGB with
// SRGB_NONLINEAR colour space. // SRGB_NONLINEAR colour space.
uint32_t surface_fmt_count = SURFACE_FMT_BUFFER_SIZE; uint32_t surface_fmt_count = SURFACE_FMT_BUFFER_SIZE;
VkSurfaceFormatKHR surface_fmts[SURFACE_FMT_BUFFER_SIZE]; VkSurfaceFormatKHR surface_fmts[SURFACE_FMT_BUFFER_SIZE];
result = vkGetPhysicalDeviceSurfaceFormatsKHR( 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(result == VK_SUCCESS);
assert(surface_fmt_count > 0); assert(surface_fmt_count > 0);
VkSurfaceFormatKHR surface_fmt = surface_fmts[0]; VkSurfaceFormatKHR surface_fmt = surface_fmts[0];
@@ -209,26 +168,26 @@ int main()
// Query surface capabilities // Query surface capabilities
VkSurfaceCapabilitiesKHR surface_caps; VkSurfaceCapabilitiesKHR surface_caps;
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
physical_dev, surface, &surface_caps); ctx->physical_dev, ctx->surface, &surface_caps);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Select swap chain extent // Select swap chain extent
VkExtent2D swapchain_extent;
if (surface_caps.currentExtent.width == UINT32_MAX) { if (surface_caps.currentExtent.width == UINT32_MAX) {
int width, height; int width, height;
glfwGetFramebufferSize(window, &width, &height); glfwGetFramebufferSize(config->window, &width, &height);
swapchain_extent.width = CLAMP( ctx->swapchain_extent.width = CLAMP(
(uint32_t)width, surface_caps.minImageExtent.width, (uint32_t)width, surface_caps.minImageExtent.width,
surface_caps.maxImageExtent.width); surface_caps.maxImageExtent.width);
swapchain_extent.height = CLAMP( ctx->swapchain_extent.height = CLAMP(
(uint32_t)height, surface_caps.minImageExtent.height, (uint32_t)height, surface_caps.minImageExtent.height,
surface_caps.maxImageExtent.height); surface_caps.maxImageExtent.height);
} else { } else {
swapchain_extent = surface_caps.currentExtent; ctx->swapchain_extent = surface_caps.currentExtent;
} }
// Select image count // Select image count
uint32_t swapchain_img_count = surface_caps.minImageCount + 1; uint32_t swapchain_img_count = surface_caps.minImageCount + 1;
assert(swapchain_img_count <= SWAPCHAIN_IMG_BUFFER_SIZE);
assert( assert(
surface_caps.maxImageCount == 0 surface_caps.maxImageCount == 0
|| swapchain_img_count <= surface_caps.maxImageCount); || swapchain_img_count <= surface_caps.maxImageCount);
@@ -236,11 +195,11 @@ int main()
// Create the swap chain // Create the swap chain
const VkSwapchainCreateInfoKHR swapchain_create_info = { const VkSwapchainCreateInfoKHR swapchain_create_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface, .surface = ctx->surface,
.minImageCount = swapchain_img_count, .minImageCount = swapchain_img_count,
.imageFormat = surface_fmt.format, .imageFormat = surface_fmt.format,
.imageColorSpace = surface_fmt.colorSpace, .imageColorSpace = surface_fmt.colorSpace,
.imageExtent = swapchain_extent, .imageExtent = ctx->swapchain_extent,
.imageArrayLayers = 1, .imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
@@ -250,20 +209,18 @@ int main()
.clipped = VK_TRUE, .clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE, .oldSwapchain = VK_NULL_HANDLE,
}; };
VkSwapchainKHR swapchain;
result = vkCreateSwapchainKHR( result = vkCreateSwapchainKHR(
dev, &swapchain_create_info, nullptr, &swapchain); ctx->dev, &swapchain_create_info, nullptr, &ctx->swapchain);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Retreive swapchain images // Retreive swapchain images
VkImage swapchain_imgs[SWAPCHAIN_IMG_BUFFER_SIZE]; VkImage swapchain_imgs[SWAPCHAIN_IMG_BUFFER_SIZE];
swapchain_img_count = SWAPCHAIN_IMG_BUFFER_SIZE; ctx->swapchain_img_count = SWAPCHAIN_IMG_BUFFER_SIZE;
result = vkGetSwapchainImagesKHR( result = vkGetSwapchainImagesKHR(
dev, swapchain, &swapchain_img_count, swapchain_imgs); ctx->dev, ctx->swapchain, &swapchain_img_count, swapchain_imgs);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Create swapchain image views // Create swapchain image views
VkImageView swapchain_img_views[SWAPCHAIN_IMG_BUFFER_SIZE];
VkImageViewCreateInfo img_view_create_info = { VkImageViewCreateInfo img_view_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.viewType = VK_IMAGE_VIEW_TYPE_2D, .viewType = VK_IMAGE_VIEW_TYPE_2D,
@@ -285,26 +242,28 @@ int main()
for (unsigned i = 0; i < swapchain_img_count; ++i) { for (unsigned i = 0; i < swapchain_img_count; ++i) {
img_view_create_info.image = swapchain_imgs[i]; img_view_create_info.image = swapchain_imgs[i];
result = vkCreateImageView( 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 // Load vertex and fragment shader modules
VkShaderModule vert_shader = load_shader_module( ctx->vert_shader
dev, vert_shader_code, sizeof(vert_shader_code)); = load_shader_module(ctx->dev, vert_shader, sizeof(vert_shader));
VkShaderModule frag_shader = load_shader_module( ctx->frag_shader
dev, frag_shader_code, sizeof(frag_shader_code)); = load_shader_module(ctx->dev, frag_shader, sizeof(frag_shader));
const VkPipelineShaderStageCreateInfo shader_stages[] = { const VkPipelineShaderStageCreateInfo shader_stages[] = {
[0] = { {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT, .stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vert_shader, .module = ctx->vert_shader,
.pName = "main", .pName = "main",
}, },
[1] = { {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT, .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = frag_shader, .module = ctx->frag_shader,
.pName = "main", .pName = "main",
}, },
}; };
@@ -344,10 +303,10 @@ int main()
// Set up viewport configuration // Set up viewport configuration
const VkViewport viewport = { const VkViewport viewport = {
.width = (float)swapchain_extent.width, .width = (float)ctx->swapchain_extent.width,
.height = (float)swapchain_extent.height, .height = (float)ctx->swapchain_extent.height,
}; };
const VkRect2D scissor = { .extent = swapchain_extent }; const VkRect2D scissor = { .extent = ctx->swapchain_extent };
VkPipelineViewportStateCreateInfo viewport_config = { VkPipelineViewportStateCreateInfo viewport_config = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1, .viewportCount = 1,
@@ -391,9 +350,8 @@ int main()
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 0, .pushConstantRangeCount = 0,
}; };
VkPipelineLayout pipeline_layout;
result = vkCreatePipelineLayout( result = vkCreatePipelineLayout(
dev, &pipeline_layout_config, nullptr, &pipeline_layout); ctx->dev, &pipeline_layout_config, nullptr, &ctx->pipeline_layout);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Set up colour attachment // Set up colour attachment
@@ -435,9 +393,8 @@ int main()
.dependencyCount = 1, .dependencyCount = 1,
.pDependencies = &dependency, .pDependencies = &dependency,
}; };
VkRenderPass render_pass;
result = vkCreateRenderPass( result = vkCreateRenderPass(
dev, &render_pass_config, nullptr, &render_pass); ctx->dev, &render_pass_config, nullptr, &ctx->render_pass);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Create pipeline // Create pipeline
@@ -451,42 +408,41 @@ int main()
.pRasterizationState = &rasterizer_config, .pRasterizationState = &rasterizer_config,
.pMultisampleState = &multisampling_config, .pMultisampleState = &multisampling_config,
.pColorBlendState = &colour_blend_config, .pColorBlendState = &colour_blend_config,
.layout = pipeline_layout, .layout = ctx->pipeline_layout,
.renderPass = render_pass, .renderPass = ctx->render_pass,
.subpass = 0, .subpass = 0,
}; };
VkPipeline pipeline;
result = vkCreateGraphicsPipelines( 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); assert(result == VK_SUCCESS);
// Create framebuffers // Create framebuffers
VkFramebuffer framebuffers[SWAPCHAIN_IMG_BUFFER_SIZE];
for (unsigned i = 0; i < swapchain_img_count; ++i) { for (unsigned i = 0; i < swapchain_img_count; ++i) {
const VkFramebufferCreateInfo framebuffer_config = { const VkFramebufferCreateInfo framebuffer_config = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = render_pass, .renderPass = ctx->render_pass,
.attachmentCount = 1, .attachmentCount = 1,
.pAttachments = swapchain_img_views + i, .pAttachments = ctx->swapchain_img_views + i,
.width = swapchain_extent.width, .width = ctx->swapchain_extent.width,
.height = swapchain_extent.height, .height = ctx->swapchain_extent.height,
.layers = 1, .layers = 1,
}; };
result = vkCreateFramebuffer( result = vkCreateFramebuffer(
dev, &framebuffer_config, nullptr, framebuffers + i); ctx->dev, &framebuffer_config, nullptr, ctx->framebuffers + i);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
} }
// Create vertex buffer // 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, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(vertices), .size = ctx->vertex_data_size,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
}; };
VkBuffer vertex_buffer;
result = vkCreateBuffer( result = vkCreateBuffer(
dev, &vertex_buffer_config, nullptr, &vertex_buffer); ctx->dev, &vertex_buf_config, nullptr, &ctx->vertex_buf);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Allocate memory to vertex buffer // Allocate memory to vertex buffer
@@ -494,9 +450,10 @@ int main()
= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VkMemoryRequirements mem_requirements; VkMemoryRequirements mem_requirements;
vkGetBufferMemoryRequirements(dev, vertex_buffer, &mem_requirements); vkGetBufferMemoryRequirements(
ctx->dev, ctx->vertex_buf, &mem_requirements);
VkPhysicalDeviceMemoryProperties mem_props; VkPhysicalDeviceMemoryProperties mem_props;
vkGetPhysicalDeviceMemoryProperties(physical_dev, &mem_props); vkGetPhysicalDeviceMemoryProperties(ctx->physical_dev, &mem_props);
uint32_t mem_type = UINT32_MAX; uint32_t mem_type = UINT32_MAX;
for (unsigned i = 0; i < mem_props.memoryTypeCount; ++i) { for (unsigned i = 0; i < mem_props.memoryTypeCount; ++i) {
const bool valid_type const bool valid_type
@@ -515,18 +472,18 @@ int main()
.allocationSize = mem_requirements.size, .allocationSize = mem_requirements.size,
.memoryTypeIndex = mem_type, .memoryTypeIndex = mem_type,
}; };
VkDeviceMemory vertex_buffer_mem;
result = vkAllocateMemory( result = vkAllocateMemory(
dev, &allocate_config, nullptr, &vertex_buffer_mem); ctx->dev, &allocate_config, nullptr, &ctx->vertex_buf_mem);
assert(result == VK_SUCCESS); 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 // Copy vertex data into vertex buffer
void *vertex_data; void *vertex_buf_ptr;
vkMapMemory( vkMapMemory(
dev, vertex_buffer_mem, 0, sizeof(vertices), 0, &vertex_data); ctx->dev, ctx->vertex_buf_mem, 0, ctx->vertex_data_size, 0,
memcpy(vertex_data, vertices, sizeof(vertices)); &vertex_buf_ptr);
vkUnmapMemory(dev, vertex_buffer_mem); memcpy(vertex_buf_ptr, config->vertex_data, ctx->vertex_data_size);
vkUnmapMemory(ctx->dev, ctx->vertex_buf_mem);
// Create command pool // Create command pool
const VkCommandPoolCreateInfo cmd_pool_config = { const VkCommandPoolCreateInfo cmd_pool_config = {
@@ -534,135 +491,139 @@ int main()
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queue_family, .queueFamilyIndex = queue_family,
}; };
VkCommandPool cmd_pool; result = vkCreateCommandPool(
result = vkCreateCommandPool(dev, &cmd_pool_config, nullptr, &cmd_pool); ctx->dev, &cmd_pool_config, nullptr, &ctx->cmd_pool);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Allocate command buffer // Allocate command buffer
const VkCommandBufferAllocateInfo cmd_buf_config = { const VkCommandBufferAllocateInfo cmd_buf_config = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = cmd_pool, .commandPool = ctx->cmd_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1, .commandBufferCount = 1,
}; };
VkCommandBuffer cmd_buf; result
result = vkAllocateCommandBuffers(dev, &cmd_buf_config, &cmd_buf); = vkAllocateCommandBuffers(ctx->dev, &cmd_buf_config, &ctx->cmd_buf);
assert(result == VK_SUCCESS); assert(result == VK_SUCCESS);
// Create syncronisation primitives // Create syncronisation primitives
VkSemaphore img_avail, render_fin; VkSemaphoreCreateInfo semaphore_config = {
VkSemaphoreCreateInfo semaphore_config .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
= { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; };
result = vkCreateSemaphore(dev, &semaphore_config, nullptr, &img_avail); result = vkCreateSemaphore(
ctx->dev, &semaphore_config, nullptr, &ctx->img_avail);
assert(result == VK_SUCCESS); 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); assert(result == VK_SUCCESS);
VkFence in_flight;
VkFenceCreateInfo fence_config = { VkFenceCreateInfo fence_config = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT, .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); assert(result == VK_SUCCESS);
while (!glfwWindowShouldClose(window)) { // Reset command buffer, then begin writing
glfwPollEvents(); 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 // Start render pass
vkWaitForFences(dev, 1, &in_flight, VK_TRUE, UINT64_MAX); VkClearValue clear_value = { { { 0.0, 0.0, 0.0, 1.0 } } };
vkResetFences(dev, 1, &in_flight); 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 // Bind graphics pipeline and vertex buffer, then draw
uint32_t img_index; vkCmdBindPipeline(
result = vkAcquireNextImageKHR( ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline);
dev, swapchain, UINT64_MAX, img_avail, VK_NULL_HANDLE, vkCmdBindVertexBuffers(
&img_index); ctx->cmd_buf, 0, 1, &ctx->vertex_buf, &(VkDeviceSize) { 0 });
assert(result == VK_SUCCESS); vkCmdDraw(
ctx->cmd_buf, ctx->vertex_data_size / sizeof(vertex_t), 1, 0, 0);
// Reset command buffer, then begin writing // Finish render pass and end writing to command buffer
vkResetCommandBuffer(cmd_buf, 0); vkCmdEndRenderPass(ctx->cmd_buf);
const VkCommandBufferBeginInfo cmd_begin = { result = vkEndCommandBuffer(ctx->cmd_buf);
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, assert(result == VK_SUCCESS);
};
result = vkBeginCommandBuffer(cmd_buf, &cmd_begin);
assert(result == VK_SUCCESS);
// Start render pass // Submit command buffer
VkClearValue clear_value = { { { 0.0, 0.0, 0.0, 1.0 } } }; VkPipelineStageFlags wait_stage
VkRenderPassBeginInfo render_pass_begin = { = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, const VkSubmitInfo submit = {
.renderPass = render_pass, .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.framebuffer = framebuffers[img_index], .waitSemaphoreCount = 1,
.renderArea.extent = swapchain_extent, .pWaitSemaphores = &ctx->img_avail,
.clearValueCount = 1, .pWaitDstStageMask = &wait_stage,
.pClearValues = &clear_value, .commandBufferCount = 1,
}; .pCommandBuffers = &ctx->cmd_buf,
vkCmdBeginRenderPass( .signalSemaphoreCount = 1,
cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); .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 // Present frame
vkCmdBindPipeline( const VkPresentInfoKHR present_info = {
cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
vkCmdBindVertexBuffers( .waitSemaphoreCount = 1,
cmd_buf, 0, 1, &vertex_buffer, &(VkDeviceSize) { 0 }); .pWaitSemaphores = &ctx->render_fin,
vkCmdDraw(cmd_buf, NELEMS(vertices), 1, 0, 0); .swapchainCount = 1,
.pSwapchains = &ctx->swapchain,
// Finish render pass and end writing to command buffer .pImageIndices = &img_index,
vkCmdEndRenderPass(cmd_buf); };
result = vkEndCommandBuffer(cmd_buf); result = vkQueuePresentKHR(ctx->queue, &present_info);
assert(result == VK_SUCCESS); 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();
} }
+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;
}