Separate vulkan code into gpu module
This commit is contained in:
1 parent
51c01a2f9e
commit
d4214941b1
4 files changed
+311
-236
No files matched your search
+6
-2
@@ -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
@@ -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();
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user