938 lines
31 KiB
C
938 lines
31 KiB
C
#include "gpu.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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 NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
|
|
|
|
#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 constexpr size_t extension_count = NELEMS(extensions);
|
|
|
|
static const char vert_shader[] = {
|
|
#embed VERT_SHADER_SPV
|
|
};
|
|
|
|
static const char frag_shader[] = {
|
|
#embed FRAG_SHADER_SPV
|
|
};
|
|
|
|
static VkShaderModule
|
|
load_shader_module(VkDevice dev, const char *code, size_t code_size)
|
|
{
|
|
const VkShaderModuleCreateInfo create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
.codeSize = code_size,
|
|
.pCode = (uint32_t *)code,
|
|
};
|
|
VkShaderModule mod;
|
|
const VkResult result
|
|
= vkCreateShaderModule(dev, &create_info, nullptr, &mod);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
return mod;
|
|
}
|
|
|
|
static void alloc_dev_mem(
|
|
const gpu_ctx_t *ctx, VkMemoryRequirements reqs,
|
|
VkMemoryPropertyFlags props, VkDeviceMemory *mem_out)
|
|
{
|
|
// Find appropriate memory type
|
|
VkPhysicalDeviceMemoryProperties mem_props;
|
|
vkGetPhysicalDeviceMemoryProperties(ctx->physical_dev, &mem_props);
|
|
uint32_t mem_type = UINT32_MAX;
|
|
for (unsigned i = 0; i < mem_props.memoryTypeCount; ++i) {
|
|
if ((reqs.memoryTypeBits & (1 << i)) == 0)
|
|
continue;
|
|
if ((mem_props.memoryTypes[i].propertyFlags & props) == props) {
|
|
mem_type = i;
|
|
break;
|
|
}
|
|
}
|
|
assert(mem_type != UINT32_MAX);
|
|
|
|
// Allocate memory
|
|
VkMemoryAllocateInfo alloc_config = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
.allocationSize = reqs.size,
|
|
.memoryTypeIndex = mem_type,
|
|
};
|
|
const VkResult result
|
|
= vkAllocateMemory(ctx->dev, &alloc_config, nullptr, mem_out);
|
|
assert(result == VK_SUCCESS);
|
|
}
|
|
|
|
static void create_buffer(
|
|
const gpu_ctx_t *ctx, VkDeviceSize size, VkBufferUsageFlags usage,
|
|
VkMemoryPropertyFlags props, VkBuffer *buf_out, VkDeviceMemory *mem_out)
|
|
{
|
|
// Create buffer
|
|
const VkBufferCreateInfo buf_config = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.size = size,
|
|
.usage = usage,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
};
|
|
const VkResult result
|
|
= vkCreateBuffer(ctx->dev, &buf_config, nullptr, buf_out);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Allocate and bind memory
|
|
VkMemoryRequirements mem_reqs;
|
|
vkGetBufferMemoryRequirements(ctx->dev, *buf_out, &mem_reqs);
|
|
alloc_dev_mem(ctx, mem_reqs, props, mem_out);
|
|
vkBindBufferMemory(ctx->dev, *buf_out, *mem_out, 0);
|
|
}
|
|
|
|
static void load_buffer(
|
|
const gpu_ctx_t *ctx, VkBuffer dest_buf, const void *data, size_t size,
|
|
VkBuffer staging_buf, VkDeviceMemory staging_buf_mem,
|
|
VkCommandPool cmd_pool)
|
|
{
|
|
VkResult result;
|
|
|
|
// Copy data to staging buffer
|
|
void *staging_buf_ptr;
|
|
vkMapMemory(ctx->dev, staging_buf_mem, 0, size, 0, &staging_buf_ptr);
|
|
memcpy(staging_buf_ptr, data, size);
|
|
vkUnmapMemory(ctx->dev, staging_buf_mem);
|
|
|
|
// Allocate command buffer
|
|
const VkCommandBufferAllocateInfo cmd_buf_config = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
.commandPool = cmd_pool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
.commandBufferCount = 1,
|
|
};
|
|
VkCommandBuffer cmd_buf;
|
|
result = vkAllocateCommandBuffers(ctx->dev, &cmd_buf_config, &cmd_buf);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Record copy command
|
|
VkCommandBufferBeginInfo cmd_begin = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
};
|
|
vkBeginCommandBuffer(cmd_buf, &cmd_begin);
|
|
VkBufferCopy cmd_copy = { .size = size };
|
|
vkCmdCopyBuffer(cmd_buf, staging_buf, dest_buf, 1, &cmd_copy);
|
|
vkEndCommandBuffer(cmd_buf);
|
|
|
|
// Send copy command
|
|
VkSubmitInfo copy_submit = {
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = &cmd_buf,
|
|
};
|
|
vkQueueSubmit(ctx->queue, 1, ©_submit, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(ctx->queue);
|
|
|
|
vkFreeCommandBuffers(ctx->dev, cmd_pool, 1, &cmd_buf);
|
|
}
|
|
|
|
void gpu_init(gpu_ctx_t *ctx, const gpu_config_t *config, model_t model)
|
|
{
|
|
// Initialise Vulkan instance.
|
|
const VkApplicationInfo app_info = {
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
.pApplicationName = config->application_name,
|
|
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
|
.pEngineName = "No Engine",
|
|
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
|
.apiVersion = VK_API_VERSION_1_0,
|
|
};
|
|
uint32_t glfw_ext_count = 0;
|
|
const char **glfw_ext_names
|
|
= glfwGetRequiredInstanceExtensions(&glfw_ext_count);
|
|
const VkInstanceCreateInfo inst_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pApplicationInfo = &app_info,
|
|
.enabledExtensionCount = glfw_ext_count,
|
|
.ppEnabledExtensionNames = glfw_ext_names,
|
|
.enabledLayerCount = sizeof(layers) / sizeof(layers[0]),
|
|
.ppEnabledLayerNames = layers,
|
|
};
|
|
VkResult result
|
|
= vkCreateInstance(&inst_create_info, nullptr, &ctx->inst);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create surface
|
|
result = glfwCreateWindowSurface(
|
|
ctx->inst, config->window, nullptr, &ctx->surface);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Select a physical device
|
|
uint32_t dev_count = 1;
|
|
result = vkEnumeratePhysicalDevices(
|
|
ctx->inst, &dev_count, &ctx->physical_dev);
|
|
assert(result == VK_SUCCESS || result == VK_INCOMPLETE);
|
|
|
|
// Select queue family
|
|
uint32_t queue_family_count = QUEUE_FAMILY_PROP_BUFFER_SIZE;
|
|
VkQueueFamilyProperties
|
|
queue_family_props[QUEUE_FAMILY_PROP_BUFFER_SIZE];
|
|
vkGetPhysicalDeviceQueueFamilyProperties(
|
|
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(
|
|
ctx->physical_dev, i, ctx->surface, &surface_support);
|
|
if (surface_support) {
|
|
found_queue_family = true;
|
|
queue_family = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
assert(found_queue_family);
|
|
|
|
// Check that the physical device supports all extensions
|
|
uint32_t ext_count = EXT_PROP_BUFFER_SIZE;
|
|
VkExtensionProperties ext_props[EXT_PROP_BUFFER_SIZE];
|
|
result = vkEnumerateDeviceExtensionProperties(
|
|
ctx->physical_dev, nullptr, &ext_count, ext_props);
|
|
assert(result == VK_SUCCESS);
|
|
bool supported[extension_count] = {};
|
|
for (unsigned i = 0; i < ext_count; ++i) {
|
|
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 < extension_count; ++j) {
|
|
if (!supported[j]) {
|
|
fprintf(stderr, "Unsupported: %s\n", extensions[j]);
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// Create the logical device and get the queue handle.
|
|
float queue_priority = 1.0;
|
|
const VkDeviceQueueCreateInfo queue_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = queue_family,
|
|
.queueCount = 1,
|
|
.pQueuePriorities = &queue_priority,
|
|
};
|
|
const VkDeviceCreateInfo dev_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
.pQueueCreateInfos = &queue_create_info,
|
|
.queueCreateInfoCount = 1,
|
|
.enabledExtensionCount = extension_count,
|
|
.ppEnabledExtensionNames = extensions,
|
|
};
|
|
result = vkCreateDevice(
|
|
ctx->physical_dev, &dev_create_info, nullptr, &ctx->dev);
|
|
assert(result == VK_SUCCESS);
|
|
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(
|
|
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];
|
|
for (unsigned i = 0; i < surface_fmt_count; ++i) {
|
|
if (surface_fmts[i].format == VK_FORMAT_R8G8B8A8_SRGB
|
|
&& surface_fmts[i].colorSpace
|
|
== VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
|
surface_fmt = surface_fmts[i];
|
|
}
|
|
}
|
|
|
|
// Query surface capabilities
|
|
VkSurfaceCapabilitiesKHR surface_caps;
|
|
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
|
ctx->physical_dev, ctx->surface, &surface_caps);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Select swap chain extent
|
|
if (surface_caps.currentExtent.width == UINT32_MAX) {
|
|
int width, height;
|
|
glfwGetFramebufferSize(config->window, &width, &height);
|
|
ctx->swapchain_extent.width = CLAMP(
|
|
(uint32_t)width, surface_caps.minImageExtent.width,
|
|
surface_caps.maxImageExtent.width);
|
|
ctx->swapchain_extent.height = CLAMP(
|
|
(uint32_t)height, surface_caps.minImageExtent.height,
|
|
surface_caps.maxImageExtent.height);
|
|
} else {
|
|
ctx->swapchain_extent = surface_caps.currentExtent;
|
|
}
|
|
|
|
// Select image count
|
|
uint32_t swapchain_img_count = surface_caps.minImageCount + 1;
|
|
assert(swapchain_img_count <= MAX_FRAMES_IN_FLIGHT);
|
|
assert(
|
|
surface_caps.maxImageCount == 0
|
|
|| swapchain_img_count <= surface_caps.maxImageCount);
|
|
|
|
// Create the swap chain
|
|
const VkSwapchainCreateInfoKHR swapchain_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
|
.surface = ctx->surface,
|
|
.minImageCount = swapchain_img_count,
|
|
.imageFormat = surface_fmt.format,
|
|
.imageColorSpace = surface_fmt.colorSpace,
|
|
.imageExtent = ctx->swapchain_extent,
|
|
.imageArrayLayers = 1,
|
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
.preTransform = surface_caps.currentTransform,
|
|
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
.presentMode = VK_PRESENT_MODE_FIFO_KHR,
|
|
.clipped = VK_TRUE,
|
|
.oldSwapchain = VK_NULL_HANDLE,
|
|
};
|
|
result = vkCreateSwapchainKHR(
|
|
ctx->dev, &swapchain_create_info, nullptr, &ctx->swapchain);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Retreive swapchain images
|
|
VkImage swapchain_imgs[MAX_FRAMES_IN_FLIGHT];
|
|
ctx->swapchain_img_count = MAX_FRAMES_IN_FLIGHT;
|
|
result = vkGetSwapchainImagesKHR(
|
|
ctx->dev, ctx->swapchain, &swapchain_img_count, swapchain_imgs);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create swapchain image views
|
|
VkImageViewCreateInfo img_view_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = surface_fmt.format,
|
|
.components = {
|
|
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
},
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1,
|
|
},
|
|
};
|
|
for (unsigned i = 0; i < swapchain_img_count; ++i) {
|
|
img_view_create_info.image = swapchain_imgs[i];
|
|
result = vkCreateImageView(
|
|
ctx->dev, &img_view_create_info, nullptr,
|
|
ctx->swapchain_img_views + i);
|
|
assert(result == VK_SUCCESS);
|
|
}
|
|
|
|
// Set up depth image
|
|
const VkFormat depth_img_fmt = VK_FORMAT_D32_SFLOAT;
|
|
const VkImageCreateInfo depth_img_config = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
.imageType = VK_IMAGE_TYPE_2D,
|
|
.extent = {
|
|
.width = ctx->swapchain_extent.width,
|
|
.height = ctx->swapchain_extent.height,
|
|
.depth = 1,
|
|
},
|
|
.mipLevels = 1,
|
|
.arrayLayers = 1,
|
|
.format = depth_img_fmt,
|
|
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
};
|
|
result = vkCreateImage(
|
|
ctx->dev, &depth_img_config, nullptr, &ctx->depth_img);
|
|
assert(result == VK_SUCCESS);
|
|
VkMemoryRequirements mem_reqs;
|
|
vkGetImageMemoryRequirements(ctx->dev, ctx->depth_img, &mem_reqs);
|
|
alloc_dev_mem(
|
|
ctx, mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
&ctx->depth_img_mem);
|
|
vkBindImageMemory(ctx->dev, ctx->depth_img, ctx->depth_img_mem, 0);
|
|
|
|
// Create depth image view
|
|
const VkImageViewCreateInfo depth_img_view_config = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = ctx->depth_img,
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = depth_img_fmt,
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
|
|
.levelCount = 1,
|
|
.layerCount = 1,
|
|
},
|
|
};
|
|
result = vkCreateImageView(
|
|
ctx->dev, &depth_img_view_config, nullptr, &ctx->depth_img_view);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Load vertex and fragment shader modules
|
|
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[] = {
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
.module = ctx->vert_shader,
|
|
.pName = "main",
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.module = ctx->frag_shader,
|
|
.pName = "main",
|
|
},
|
|
};
|
|
|
|
// Create descriptor set layout
|
|
const VkDescriptorSetLayoutCreateInfo desc_set_layout_config = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
.bindingCount = 1,
|
|
.pBindings = &(VkDescriptorSetLayoutBinding) {
|
|
.binding = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
.descriptorCount = 1,
|
|
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
|
},
|
|
};
|
|
result = vkCreateDescriptorSetLayout(
|
|
ctx->dev, &desc_set_layout_config, nullptr, &ctx->desc_set_layout);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create descriptor pool
|
|
const VkDescriptorPoolCreateInfo desc_pool_config = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
.poolSizeCount = 1,
|
|
.pPoolSizes = &(VkDescriptorPoolSize) {
|
|
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
.descriptorCount = swapchain_img_count,
|
|
},
|
|
.maxSets = swapchain_img_count,
|
|
};
|
|
result = vkCreateDescriptorPool(
|
|
ctx->dev, &desc_pool_config, nullptr, &ctx->desc_pool);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create and configure descriptor sets
|
|
VkDescriptorSetLayout desc_set_layouts[swapchain_img_count];
|
|
for (unsigned i = 0; i < swapchain_img_count; ++i)
|
|
desc_set_layouts[i] = ctx->desc_set_layout;
|
|
const VkDescriptorSetAllocateInfo desc_sets_alloc_config = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.descriptorPool = ctx->desc_pool,
|
|
.descriptorSetCount = swapchain_img_count,
|
|
.pSetLayouts = desc_set_layouts,
|
|
};
|
|
result = vkAllocateDescriptorSets(
|
|
ctx->dev, &desc_sets_alloc_config, ctx->desc_sets);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create and map MVP buffer
|
|
const size_t mvp_buffer_size = swapchain_img_count * sizeof(mvp_t);
|
|
create_buffer(
|
|
ctx, mvp_buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&ctx->mvp_buffer, &ctx->mvp_buffer_mem);
|
|
vkMapMemory(
|
|
ctx->dev, ctx->mvp_buffer_mem, 0, mvp_buffer_size, 0,
|
|
(void **)&ctx->mvps);
|
|
|
|
// Create and map bone matrix buffer
|
|
const size_t bone_matrix_buffer_size
|
|
= swapchain_img_count * model.bone_count * sizeof(mat4_t);
|
|
create_buffer(
|
|
ctx, bone_matrix_buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&ctx->bone_matrix_buffer, &ctx->bone_matrix_buffer_mem);
|
|
vkMapMemory(
|
|
ctx->dev, ctx->bone_matrix_buffer_mem, 0, bone_matrix_buffer_size, 0,
|
|
(void **)&ctx->bone_matrices);
|
|
|
|
// Configure descriptor sets to use MVP buffer
|
|
for (unsigned i = 0; i < swapchain_img_count; ++i) {
|
|
const VkWriteDescriptorSet desc_write = {
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstSet = ctx->desc_sets[i],
|
|
.dstBinding = 0,
|
|
.dstArrayElement = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
.descriptorCount = 1,
|
|
.pBufferInfo = &(VkDescriptorBufferInfo) {
|
|
.buffer = ctx->mvp_buffer,
|
|
.offset = i * sizeof(mvp_t),
|
|
.range = sizeof(mvp_t),
|
|
},
|
|
};
|
|
vkUpdateDescriptorSets(ctx->dev, 1, &desc_write, 0, nullptr);
|
|
}
|
|
|
|
// Set up input configuration
|
|
const VkVertexInputBindingDescription input_binding_descs[] = {
|
|
{
|
|
.binding = 0,
|
|
.stride = sizeof(vertex_t),
|
|
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
|
},
|
|
};
|
|
const VkVertexInputAttributeDescription input_attribute_descs[] = {
|
|
{
|
|
.binding = 0,
|
|
.location = 0,
|
|
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
|
.offset = offsetof(vertex_t, position),
|
|
},
|
|
{
|
|
.binding = 0,
|
|
.location = 1,
|
|
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
|
.offset = offsetof(vertex_t, normal),
|
|
},
|
|
{
|
|
.binding = 0,
|
|
.location = 2,
|
|
.format = VK_FORMAT_R16G16B16A16_UINT,
|
|
.offset = offsetof(vertex_t, bones),
|
|
},
|
|
{
|
|
.binding = 0,
|
|
.location = 3,
|
|
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
|
.offset = offsetof(vertex_t, weights),
|
|
},
|
|
};
|
|
const VkPipelineVertexInputStateCreateInfo vertex_input_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
.vertexBindingDescriptionCount = NELEMS(input_binding_descs),
|
|
.pVertexBindingDescriptions = input_binding_descs,
|
|
.vertexAttributeDescriptionCount = NELEMS(input_attribute_descs),
|
|
.pVertexAttributeDescriptions = input_attribute_descs,
|
|
};
|
|
const VkPipelineInputAssemblyStateCreateInfo input_assembly_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
};
|
|
|
|
// Set up viewport configuration
|
|
const VkViewport viewport = {
|
|
.width = (float)ctx->swapchain_extent.width,
|
|
.height = (float)ctx->swapchain_extent.height,
|
|
.minDepth = 0.0,
|
|
.maxDepth = 1.0,
|
|
};
|
|
const VkRect2D scissor = { .extent = ctx->swapchain_extent };
|
|
VkPipelineViewportStateCreateInfo viewport_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
.viewportCount = 1,
|
|
.pViewports = &viewport,
|
|
.scissorCount = 1,
|
|
.pScissors = &scissor,
|
|
};
|
|
|
|
// Populate rasterizer configuration
|
|
const VkPipelineRasterizationStateCreateInfo rasterizer_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
.depthClampEnable = VK_FALSE,
|
|
.rasterizerDiscardEnable = VK_FALSE,
|
|
.polygonMode = VK_POLYGON_MODE_FILL,
|
|
.lineWidth = 1.0f,
|
|
.cullMode = VK_CULL_MODE_BACK_BIT,
|
|
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
|
};
|
|
|
|
// Populate multisampling configuration
|
|
const VkPipelineMultisampleStateCreateInfo multisampling_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
.sampleShadingEnable = VK_FALSE,
|
|
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
|
};
|
|
|
|
// Set up colour blending configuration
|
|
const VkPipelineColorBlendAttachmentState colour_blend_attatchment = {
|
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
|
|
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
|
.blendEnable = VK_FALSE,
|
|
};
|
|
const VkPipelineColorBlendStateCreateInfo colour_blend_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
.logicOpEnable = VK_FALSE,
|
|
.attachmentCount = 1,
|
|
.pAttachments = &colour_blend_attatchment,
|
|
};
|
|
|
|
// Create pipeline layout
|
|
const VkPipelineLayoutCreateInfo pipeline_layout_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
.setLayoutCount = 1,
|
|
.pSetLayouts = &ctx->desc_set_layout,
|
|
};
|
|
result = vkCreatePipelineLayout(
|
|
ctx->dev, &pipeline_layout_config, nullptr, &ctx->pipeline_layout);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Set up colour attachment
|
|
const VkAttachmentDescription colour_attachment = {
|
|
.format = surface_fmt.format,
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
};
|
|
const VkAttachmentReference colour_attachment_ref = {
|
|
.attachment = 0,
|
|
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
};
|
|
|
|
// Set up depth attatchment
|
|
const VkAttachmentDescription depth_attatchment = {
|
|
.format = depth_img_fmt,
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
};
|
|
VkAttachmentReference depth_attachment_ref = {
|
|
.attachment = 1,
|
|
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
};
|
|
|
|
// Create render pass
|
|
const VkAttachmentDescription attachment_descs[] = {
|
|
colour_attachment,
|
|
depth_attatchment,
|
|
};
|
|
const VkSubpassDependency dependency = {
|
|
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
|
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
|
|
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
|
|
| VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
|
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
|
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
};
|
|
const VkRenderPassCreateInfo render_pass_config = {
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
.attachmentCount = 2,
|
|
.pAttachments = attachment_descs,
|
|
.subpassCount = 1,
|
|
.pSubpasses = &(VkSubpassDescription){
|
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
.colorAttachmentCount = 1,
|
|
.pColorAttachments = &colour_attachment_ref,
|
|
.pDepthStencilAttachment = &depth_attachment_ref,
|
|
},
|
|
.dependencyCount = 1,
|
|
.pDependencies = &dependency,
|
|
};
|
|
result = vkCreateRenderPass(
|
|
ctx->dev, &render_pass_config, nullptr, &ctx->render_pass);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create pipeline
|
|
const VkPipelineDepthStencilStateCreateInfo depth_stencil_config = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
|
.depthTestEnable = VK_TRUE,
|
|
.depthWriteEnable = VK_TRUE,
|
|
.depthCompareOp = VK_COMPARE_OP_LESS,
|
|
.depthBoundsTestEnable = VK_FALSE,
|
|
.stencilTestEnable = VK_FALSE,
|
|
};
|
|
const VkGraphicsPipelineCreateInfo pipeline_config = {
|
|
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
.stageCount = 2,
|
|
.pStages = shader_stages,
|
|
.pVertexInputState = &vertex_input_config,
|
|
.pInputAssemblyState = &input_assembly_config,
|
|
.pViewportState = &viewport_config,
|
|
.pRasterizationState = &rasterizer_config,
|
|
.pMultisampleState = &multisampling_config,
|
|
.pColorBlendState = &colour_blend_config,
|
|
.layout = ctx->pipeline_layout,
|
|
.renderPass = ctx->render_pass,
|
|
.subpass = 0,
|
|
.pDepthStencilState = &depth_stencil_config,
|
|
};
|
|
result = vkCreateGraphicsPipelines(
|
|
ctx->dev, VK_NULL_HANDLE, 1, &pipeline_config, nullptr,
|
|
&ctx->pipeline);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create framebuffers
|
|
for (unsigned i = 0; i < swapchain_img_count; ++i) {
|
|
VkImageView attachments[] = {
|
|
ctx->swapchain_img_views[i],
|
|
ctx->depth_img_view,
|
|
};
|
|
const VkFramebufferCreateInfo framebuffer_config = {
|
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
.renderPass = ctx->render_pass,
|
|
.attachmentCount = 2,
|
|
.pAttachments = attachments,
|
|
.width = ctx->swapchain_extent.width,
|
|
.height = ctx->swapchain_extent.height,
|
|
.layers = 1,
|
|
};
|
|
result = vkCreateFramebuffer(
|
|
ctx->dev, &framebuffer_config, nullptr, ctx->framebuffers + i);
|
|
assert(result == VK_SUCCESS);
|
|
}
|
|
|
|
// Create command pool
|
|
const VkCommandPoolCreateInfo cmd_pool_config = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
.queueFamilyIndex = queue_family,
|
|
};
|
|
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 = ctx->cmd_pool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
.commandBufferCount = 1,
|
|
};
|
|
result
|
|
= vkAllocateCommandBuffers(ctx->dev, &cmd_buf_config, &ctx->cmd_buf);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Create syncronisation primitives
|
|
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(
|
|
ctx->dev, &semaphore_config, nullptr, &ctx->render_fin);
|
|
assert(result == VK_SUCCESS);
|
|
VkFenceCreateInfo fence_config = {
|
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
|
};
|
|
result
|
|
= vkCreateFence(ctx->dev, &fence_config, nullptr, &ctx->in_flight);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Set up staging, vertex and index buffers
|
|
const size_t vertex_data_size = model.vertex_count * sizeof(vertex_t);
|
|
const size_t index_data_size = model.index_count * sizeof(vertex_t);
|
|
ctx->index_count = model.index_count;
|
|
VkBuffer staging_buf;
|
|
VkDeviceMemory staging_buf_mem;
|
|
const size_t staging_buf_size = MAX(vertex_data_size, index_data_size);
|
|
create_buffer(
|
|
ctx, staging_buf_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&staging_buf, &staging_buf_mem);
|
|
create_buffer(
|
|
ctx, vertex_data_size,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &ctx->vertex_buf,
|
|
&ctx->vertex_buf_mem);
|
|
create_buffer(
|
|
ctx, index_data_size,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &ctx->index_buf,
|
|
&ctx->index_buf_mem);
|
|
|
|
// Create transient command pool
|
|
const VkCommandPoolCreateInfo transient_cmd_pool_config = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
|
.queueFamilyIndex = queue_family,
|
|
};
|
|
VkCommandPool transient_cmd_pool;
|
|
result = vkCreateCommandPool(
|
|
ctx->dev, &transient_cmd_pool_config, nullptr, &transient_cmd_pool);
|
|
assert(result == VK_SUCCESS);
|
|
|
|
// Load vertex and index data
|
|
load_buffer(
|
|
ctx, ctx->vertex_buf, model.vertices, vertex_data_size, staging_buf,
|
|
staging_buf_mem, transient_cmd_pool);
|
|
load_buffer(
|
|
ctx, ctx->index_buf, model.indices, index_data_size, staging_buf,
|
|
staging_buf_mem, transient_cmd_pool);
|
|
|
|
// Clean up staging buffer and transient command pool
|
|
vkDestroyCommandPool(ctx->dev, transient_cmd_pool, nullptr);
|
|
vkDestroyBuffer(ctx->dev, staging_buf, nullptr);
|
|
vkFreeMemory(ctx->dev, staging_buf_mem, nullptr);
|
|
}
|
|
|
|
void gpu_cleanup(const gpu_ctx_t *ctx)
|
|
{
|
|
vkDeviceWaitIdle(ctx->dev);
|
|
|
|
vkDestroyBuffer(ctx->dev, ctx->index_buf, nullptr);
|
|
vkFreeMemory(ctx->dev, ctx->index_buf_mem, nullptr);
|
|
vkDestroyBuffer(ctx->dev, ctx->vertex_buf, nullptr);
|
|
vkFreeMemory(ctx->dev, ctx->vertex_buf_mem, nullptr);
|
|
|
|
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);
|
|
|
|
vkUnmapMemory(ctx->dev, ctx->bone_matrix_buffer_mem);
|
|
vkDestroyBuffer(ctx->dev, ctx->bone_matrix_buffer, nullptr);
|
|
vkFreeMemory(ctx->dev, ctx->bone_matrix_buffer_mem, nullptr);
|
|
|
|
vkUnmapMemory(ctx->dev, ctx->mvp_buffer_mem);
|
|
vkDestroyBuffer(ctx->dev, ctx->mvp_buffer, nullptr);
|
|
vkFreeMemory(ctx->dev, ctx->mvp_buffer_mem, nullptr);
|
|
|
|
vkDestroyDescriptorPool(ctx->dev, ctx->desc_pool, nullptr);
|
|
vkDestroyDescriptorSetLayout(ctx->dev, ctx->desc_set_layout, nullptr);
|
|
|
|
vkDestroyShaderModule(ctx->dev, ctx->vert_shader, nullptr);
|
|
vkDestroyShaderModule(ctx->dev, ctx->frag_shader, nullptr);
|
|
|
|
vkDestroyImageView(ctx->dev, ctx->depth_img_view, nullptr);
|
|
vkDestroyImage(ctx->dev, ctx->depth_img, nullptr);
|
|
vkFreeMemory(ctx->dev, ctx->depth_img_mem, 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);
|
|
|
|
vkDestroyDevice(ctx->dev, nullptr);
|
|
vkDestroySurfaceKHR(ctx->inst, ctx->surface, nullptr);
|
|
vkDestroyInstance(ctx->inst, nullptr);
|
|
}
|
|
|
|
void gpu_render(const gpu_ctx_t *ctx, const mvp_t *mvp)
|
|
{
|
|
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);
|
|
|
|
// Copy MVP matricies into uniform buffer for this image
|
|
memcpy(ctx->mvps + img_index, mvp, sizeof(mvp_t));
|
|
|
|
// 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);
|
|
|
|
// Start render pass
|
|
VkClearValue clear_values[] = {
|
|
[0] = { .color = { { 0.0, 0.0, 0.0, 1.0 } } },
|
|
[1] = { .depthStencil = { 1.0, 0.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 = 2,
|
|
.pClearValues = clear_values,
|
|
};
|
|
vkCmdBeginRenderPass(
|
|
ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdBindPipeline(
|
|
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline);
|
|
|
|
// Bind vertex and index buffers
|
|
vkCmdBindVertexBuffers(
|
|
ctx->cmd_buf, 0, 1, &ctx->vertex_buf, &(VkDeviceSize) { 0 });
|
|
vkCmdBindIndexBuffer(
|
|
ctx->cmd_buf, ctx->index_buf, 0, VK_INDEX_TYPE_UINT16);
|
|
|
|
// Bind descriptor set for this frame
|
|
vkCmdBindDescriptorSets(
|
|
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pipeline_layout,
|
|
0, 1, ctx->desc_sets + img_index, 0, nullptr);
|
|
|
|
// Draw
|
|
vkCmdDrawIndexed(ctx->cmd_buf, ctx->index_count, 1, 0, 0, 0);
|
|
|
|
// Finish render pass and end writing to command buffer
|
|
vkCmdEndRenderPass(ctx->cmd_buf);
|
|
result = vkEndCommandBuffer(ctx->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 = &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);
|
|
|
|
// 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);
|
|
}
|