Amber
2 years ago
4 changed files with 697 additions and 2 deletions
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
#version 450 |
||||
#extension GL_EXT_debug_printf : enable |
||||
|
||||
layout(local_size_x_id = 1) in; |
||||
|
||||
layout(binding = 0) uniform Input { |
||||
uint num; |
||||
} in_vals; |
||||
|
||||
layout(binding = 1) buffer Output { |
||||
uint num; |
||||
} out_vals; |
||||
|
||||
void main() { |
||||
out_vals.num = in_vals.num; |
||||
} |
@ -0,0 +1,678 @@
@@ -0,0 +1,678 @@
|
||||
#define GLFW_INCLUDE_VULKAN |
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE |
||||
#include <GLFW/glfw3.h> |
||||
#include <vulkan/vulkan.h> |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <cglm/cglm.h> |
||||
#include <cglm/affine.h> |
||||
#include <cglm/cam.h> |
||||
#include <time.h> |
||||
#include <cglm/util.h> |
||||
#define STB_IMAGE_IMPLEMENTATION |
||||
#include "stb_image.h" |
||||
|
||||
#define WIDTH 2560 |
||||
#define HEIGHT 1440 |
||||
#define VALIDATE true |
||||
|
||||
#define ARRSIZE(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) |
||||
|
||||
const char* validation_layers[] = { |
||||
"VK_LAYER_KHRONOS_validation", |
||||
}; |
||||
|
||||
const char* required_extensions[] = { |
||||
}; |
||||
|
||||
typedef struct SwapChainSupportDetails { |
||||
VkSurfaceCapabilitiesKHR capabilities; |
||||
uint32_t num_formats; |
||||
VkSurfaceFormatKHR* formats; |
||||
uint32_t num_modes; |
||||
VkPresentModeKHR* present_modes; |
||||
} SwapChainSupportDetails; |
||||
|
||||
typedef struct ApplicationState { |
||||
VkInstance instance; |
||||
VkPhysicalDevice physical_device; |
||||
VkDevice device; |
||||
|
||||
struct { |
||||
VkQueue queue; |
||||
|
||||
VkCommandPool command_pool; |
||||
VkCommandBuffer command_buffer; |
||||
|
||||
VkDescriptorSetLayout descriptor_set_layout; |
||||
VkDescriptorPool descriptor_pool; |
||||
VkDescriptorSet descriptor_sets; |
||||
VkPipelineLayout pipeline_layout; |
||||
VkPipeline pipeline; |
||||
|
||||
VkBuffer input_buffer; |
||||
VkDeviceMemory input_buffer_memory; |
||||
|
||||
VkBuffer output_buffer; |
||||
VkDeviceMemory output_buffer_memory; |
||||
|
||||
VkFence compute_done; |
||||
} compute; |
||||
} ApplicationState; |
||||
|
||||
typedef struct QueueFamilyIndices { |
||||
bool compute_present; |
||||
uint32_t compute_family; |
||||
} QueueFamilyIndices; |
||||
|
||||
bool check_validation_layer_support() { |
||||
uint32_t layer_count; |
||||
vkEnumerateInstanceLayerProperties(&layer_count, NULL); |
||||
|
||||
VkLayerProperties* available_layers = malloc(sizeof(VkLayerProperties) * layer_count); |
||||
vkEnumerateInstanceLayerProperties(&layer_count, available_layers); |
||||
|
||||
printf("supported validation layers:\n"); |
||||
for (int i = 0; i < layer_count; i++) { |
||||
printf("\t%s\n", available_layers[i].layerName); |
||||
} |
||||
|
||||
for (int i = 0; i < ARRSIZE(validation_layers); i++) { |
||||
bool found = false; |
||||
for (int j = 0; j < layer_count; j++) { |
||||
if (!strcmp(available_layers[j].layerName, validation_layers[i])) { |
||||
found = true; |
||||
} |
||||
} |
||||
|
||||
if (!found) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
free (available_layers); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
//we first create an instance, filling it in with our application information.
|
||||
void create_instance(ApplicationState* state) { |
||||
if (VALIDATE && !check_validation_layer_support()) { |
||||
printf("Requested validation layers but they're not supported"); |
||||
exit(1); |
||||
} |
||||
|
||||
VkApplicationInfo appinfo = {0}; |
||||
|
||||
appinfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
||||
appinfo.pApplicationName = "Vulkan learning triangle example"; |
||||
appinfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); |
||||
appinfo.pEngineName = "No Engine"; |
||||
appinfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); |
||||
appinfo.apiVersion = VK_API_VERSION_1_0; |
||||
|
||||
VkInstanceCreateInfo create_info = {0}; |
||||
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
||||
create_info.pApplicationInfo = &appinfo; |
||||
|
||||
//now let's require the global extensions that are required for GLFW to work
|
||||
uint32_t glfw_extension_num = 0; |
||||
const char** glfw_extensions; |
||||
|
||||
glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_num); |
||||
|
||||
create_info.enabledExtensionCount = glfw_extension_num; |
||||
create_info.ppEnabledExtensionNames = glfw_extensions; |
||||
|
||||
if (VALIDATE) { |
||||
create_info.enabledLayerCount = ARRSIZE(validation_layers); |
||||
create_info.ppEnabledLayerNames = validation_layers; |
||||
} else { |
||||
create_info.enabledLayerCount = 0; |
||||
} |
||||
|
||||
//now we actually create the instance
|
||||
if (vkCreateInstance(&create_info, NULL, &state->instance) != VK_SUCCESS) { |
||||
printf("error creating instance\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
printf("Instance successfully created\n"); |
||||
|
||||
//let's just list all supported vulkan instance extensions
|
||||
uint32_t extension_num = 0; |
||||
vkEnumerateInstanceExtensionProperties(NULL, &extension_num, NULL); |
||||
VkExtensionProperties* extensions = malloc(sizeof(VkExtensionProperties) * extension_num); |
||||
vkEnumerateInstanceExtensionProperties(NULL, &extension_num, extensions); |
||||
|
||||
printf("instance extensions:\n"); |
||||
|
||||
for (int i = 0; i < extension_num; i++) { |
||||
printf("\t%s\n", extensions[i].extensionName); |
||||
} |
||||
|
||||
free(extensions); |
||||
} |
||||
|
||||
uint32_t find_memory_type (ApplicationState* state, uint32_t type_filter, VkMemoryPropertyFlags props) { |
||||
VkPhysicalDeviceMemoryProperties mem_props; |
||||
vkGetPhysicalDeviceMemoryProperties(state->physical_device, &mem_props); |
||||
|
||||
for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { |
||||
if ((type_filter & (1 << i)) && (mem_props.memoryTypes[i].propertyFlags & props) == props) { |
||||
return i; |
||||
} |
||||
} |
||||
|
||||
printf("no suitable memory type\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
void create_buffer(ApplicationState* state, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, |
||||
VkBuffer* buffer, VkDeviceMemory* memory) { |
||||
VkBufferCreateInfo buffer_info = {0}; |
||||
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
||||
buffer_info.size = size; |
||||
buffer_info.usage = usage; |
||||
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
||||
|
||||
if (vkCreateBuffer(state->device, &buffer_info, NULL, buffer) != VK_SUCCESS) { |
||||
printf("Failed to create buffer\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
VkMemoryRequirements mem_req; |
||||
vkGetBufferMemoryRequirements(state->device, *buffer, &mem_req); |
||||
|
||||
VkMemoryAllocateInfo alloc_info = {0}; |
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
||||
alloc_info.allocationSize = mem_req.size; |
||||
alloc_info.memoryTypeIndex = find_memory_type(state, mem_req.memoryTypeBits, properties); |
||||
|
||||
if (vkAllocateMemory(state->device, &alloc_info, NULL, memory) != VK_SUCCESS) { |
||||
printf("failed to allocate memory for buffer\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
vkBindBufferMemory(state->device, *buffer, *memory, 0); |
||||
} |
||||
|
||||
//find the queues families from which we can allocate all the types of queues we need
|
||||
QueueFamilyIndices find_queue_families(ApplicationState* state, VkPhysicalDevice device) { |
||||
struct QueueFamilyIndices ret = {0}; |
||||
uint32_t queue_family_count = 0; |
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); |
||||
VkQueueFamilyProperties* queue_fam_props = malloc(sizeof(VkQueueFamilyProperties) * queue_family_count); |
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_fam_props); |
||||
|
||||
for (int i = 0; i < queue_family_count; i++) { |
||||
if (queue_fam_props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { |
||||
ret.compute_present = true; |
||||
ret.compute_family = i; |
||||
} |
||||
} |
||||
|
||||
free(queue_fam_props); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
bool extensions_supported(ApplicationState* state, VkPhysicalDevice device) { |
||||
uint32_t num_extensions; |
||||
vkEnumerateDeviceExtensionProperties(device, NULL, &num_extensions, NULL); |
||||
|
||||
VkExtensionProperties* available_extensions = malloc(sizeof(VkExtensionProperties) * num_extensions); |
||||
vkEnumerateDeviceExtensionProperties(device, NULL, &num_extensions, available_extensions); |
||||
|
||||
printf("Device extensions:\n"); |
||||
for (int i = 0; i < num_extensions; i++) { |
||||
printf("\t%s\n", available_extensions[i].extensionName); |
||||
} |
||||
|
||||
for (int i = 0; i < ARRSIZE(required_extensions); i++) { |
||||
bool found = false; |
||||
for (int j = 0; j < num_extensions; j++) { |
||||
if (!strcmp(required_extensions[i], available_extensions[j].extensionName)) { |
||||
found = true; |
||||
} |
||||
} |
||||
|
||||
if (!found) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
free(available_extensions); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
//check all the requirements for a physical device
|
||||
void pick_physical_device(ApplicationState* state) { |
||||
uint32_t device_count = 0; |
||||
vkEnumeratePhysicalDevices(state->instance, &device_count, NULL); |
||||
|
||||
if (device_count == 0) { |
||||
printf("vulkan is not supported\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
VkPhysicalDevice* devices = malloc(sizeof(VkPhysicalDevice) * device_count); |
||||
vkEnumeratePhysicalDevices(state->instance, &device_count, devices); |
||||
|
||||
//now let's check if any device is suitable for our usage
|
||||
int i; |
||||
for (i = 0; i < device_count; i++) { |
||||
VkPhysicalDevice device = devices[i]; |
||||
|
||||
VkPhysicalDeviceProperties device_props; |
||||
vkGetPhysicalDeviceProperties(device, &device_props); |
||||
|
||||
VkPhysicalDeviceFeatures device_features; |
||||
vkGetPhysicalDeviceFeatures(device, &device_features); |
||||
|
||||
QueueFamilyIndices families = find_queue_families(state, device); |
||||
|
||||
// SwapChainSupportDetails details = get_swapchain_details(state, device);
|
||||
|
||||
//for now we only require that a graphics queue is present
|
||||
if (families.compute_present) { |
||||
VkPhysicalDeviceFeatures supportedFeatures; |
||||
vkGetPhysicalDeviceFeatures(device, &supportedFeatures); |
||||
// state->details = details;
|
||||
break; |
||||
} else { |
||||
printf("device does not support all necessary extensions and queues"); |
||||
} |
||||
} |
||||
//TODO print unsupported message
|
||||
state->physical_device = devices[i]; |
||||
free(devices); |
||||
} |
||||
|
||||
int cmp(const void* a, const void* b) { |
||||
return (*(int*)a - *(int*)b); |
||||
} |
||||
|
||||
void create_logical_device(ApplicationState* state) {; |
||||
//we already ensured that all of the required queues exist
|
||||
QueueFamilyIndices indices = find_queue_families(state, state->physical_device); |
||||
|
||||
//now we need to find out the number of unique queues we need to create
|
||||
//since there aren't going to be many queues in this case we don't need
|
||||
//a particularly efficient algorithm
|
||||
uint32_t indices_array[] = {indices.compute_family}; |
||||
qsort(indices_array, ARRSIZE(indices_array), sizeof(uint32_t), cmp); |
||||
|
||||
int unique_queues[ARRSIZE(indices_array)] = {0}; |
||||
int j = 0; |
||||
|
||||
for (int i = 0; i < ARRSIZE(indices_array) - 1; i++) { |
||||
if (indices_array[i] != indices_array[i + 1]) { |
||||
unique_queues[j++] = indices_array[i]; |
||||
} |
||||
} |
||||
unique_queues[j++] = unique_queues[ARRSIZE(indices_array) - 1]; |
||||
printf("number of queues: %x\n", j); |
||||
|
||||
VkDeviceQueueCreateInfo* queue_create_infos = calloc(j, sizeof(VkDeviceCreateInfo)); |
||||
|
||||
float prio = 1.0f; |
||||
for (int i = 0; i < j; i++) { |
||||
queue_create_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
||||
queue_create_infos[i].queueFamilyIndex = unique_queues[i]; |
||||
queue_create_infos[i].queueCount = 1; |
||||
queue_create_infos[i].pQueuePriorities = &prio; |
||||
} |
||||
|
||||
//for now we won't use any features
|
||||
VkPhysicalDeviceFeatures device_features = {0}; |
||||
device_features.samplerAnisotropy = VK_TRUE; |
||||
|
||||
//now create a logical device knowing which features and queues we need
|
||||
VkDeviceCreateInfo create_info = {0}; |
||||
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
||||
create_info.pQueueCreateInfos = queue_create_infos; |
||||
create_info.queueCreateInfoCount = j; |
||||
create_info.pEnabledFeatures = &device_features; |
||||
create_info.enabledExtensionCount = ARRSIZE(required_extensions); |
||||
create_info.ppEnabledExtensionNames = required_extensions; |
||||
|
||||
if (VALIDATE) { |
||||
create_info.enabledLayerCount = ARRSIZE(validation_layers); |
||||
create_info.ppEnabledLayerNames = validation_layers; |
||||
} else { |
||||
create_info.enabledLayerCount = 0;; |
||||
} |
||||
|
||||
printf("creating logical device\n"); |
||||
if (vkCreateDevice(state->physical_device, &create_info, NULL, &state->device) != VK_SUCCESS) { |
||||
printf("Error creating logical device\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
//get a handle to the queue
|
||||
// vkGetDeviceQueue(state->device, indices.graphics_queue, 0, &state->graphics_queue);
|
||||
// vkGetDeviceQueue(state->device, indices.present_queue, 0, &state->present_queue);
|
||||
vkGetDeviceQueue(state->device, indices.compute_family, 0, &state->compute.queue); |
||||
|
||||
printf("Logical device created\n"); |
||||
} |
||||
|
||||
void setup_compute_queues(ApplicationState* state) { |
||||
QueueFamilyIndices families = find_queue_families(state, state->physical_device); |
||||
VkCommandPoolCreateInfo pool_info = {0}; |
||||
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
||||
//Allow command buffers to be rerecorded individually
|
||||
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
||||
pool_info.queueFamilyIndex = families.compute_family; |
||||
|
||||
if (vkCreateCommandPool(state->device, &pool_info, NULL, &state->compute.command_pool) != VK_SUCCESS) { |
||||
printf("failed to create command pool\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
printf("created command pools\n"); |
||||
|
||||
VkCommandBufferAllocateInfo command_buf_allocate_info = {0}; |
||||
command_buf_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
||||
command_buf_allocate_info.commandPool = state->compute.command_pool; |
||||
//VK_COMMAND_BUFFER_LEVEL_PRIMARY: Can be submitted to a queue for execution, but cannot be called from other command buffers.
|
||||
//VK_COMMAND_BUFFER_LEVEL_SECONDARY: Cannot be submitted directly, but can be called from primary command buffers
|
||||
command_buf_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
||||
command_buf_allocate_info.commandBufferCount = 1; |
||||
|
||||
if (vkAllocateCommandBuffers(state->device, &command_buf_allocate_info, &state->compute.command_buffer) != VK_SUCCESS) { |
||||
printf("failed to allocated command buffer"); |
||||
exit(1); |
||||
} |
||||
printf("created command buffer\n"); |
||||
|
||||
} |
||||
|
||||
void create_descriptor_set_layout(ApplicationState* state) { |
||||
VkDescriptorSetLayoutBinding input_binding = {0}; |
||||
input_binding.binding = 0; |
||||
input_binding.descriptorCount = 1; |
||||
input_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
||||
input_binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; |
||||
|
||||
VkDescriptorSetLayoutBinding output_binding = {0}; |
||||
output_binding.binding = 1; |
||||
output_binding.descriptorCount = 1; |
||||
output_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
||||
output_binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; |
||||
|
||||
VkDescriptorSetLayoutBinding bindings[2] = {input_binding, output_binding}; |
||||
|
||||
VkDescriptorSetLayoutCreateInfo layout_info = {0}; |
||||
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; |
||||
layout_info.bindingCount = ARRSIZE(bindings); |
||||
layout_info.pBindings = bindings; |
||||
|
||||
if (vkCreateDescriptorSetLayout(state->device, &layout_info, NULL, &state->compute.descriptor_set_layout)) { |
||||
printf("could not create descriptor set layout\n"); |
||||
exit(1); |
||||
} |
||||
printf("created descriptor set layout\n"); |
||||
} |
||||
|
||||
char* load_file(const char* name, size_t* size) { |
||||
FILE *fileptr; |
||||
char *buffer; |
||||
size_t filelen; |
||||
|
||||
fileptr = fopen(name, "rb"); |
||||
fseek(fileptr, 0, SEEK_END); |
||||
filelen = ftell(fileptr); |
||||
rewind(fileptr); |
||||
|
||||
buffer = (char*)malloc(filelen * sizeof(char)); |
||||
fread(buffer, filelen, 1, fileptr); |
||||
fclose(fileptr); |
||||
|
||||
*size = filelen; |
||||
|
||||
return buffer; |
||||
} |
||||
|
||||
VkShaderModule create_shader_module(ApplicationState* state, const char* code, size_t size) { |
||||
VkShaderModuleCreateInfo create_info = {0}; |
||||
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
||||
create_info.codeSize = size; |
||||
create_info.pCode = (uint32_t*)code; |
||||
|
||||
VkShaderModule ret; |
||||
|
||||
if (vkCreateShaderModule(state->device, &create_info, NULL, &ret) != VK_SUCCESS) { |
||||
printf("shader creation failed\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void create_compute_pipeline(ApplicationState* state) { |
||||
VkShaderModule compute_shader_module; |
||||
size_t compute_shader_size; |
||||
char* compute_shader = load_file("./compute_simple.spv", &compute_shader_size); |
||||
|
||||
compute_shader_module = create_shader_module(state, compute_shader, compute_shader_size); |
||||
|
||||
VkPipelineShaderStageCreateInfo compute_shader_stage_info = {0}; |
||||
compute_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
||||
compute_shader_stage_info.stage = VK_SHADER_STAGE_COMPUTE_BIT; |
||||
compute_shader_stage_info.module = compute_shader_module; |
||||
compute_shader_stage_info.pName = "main"; |
||||
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_create_info = {0}; |
||||
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
||||
pipeline_layout_create_info.setLayoutCount = 1; |
||||
pipeline_layout_create_info.pSetLayouts = &state->compute.descriptor_set_layout; |
||||
pipeline_layout_create_info.pushConstantRangeCount = 0; |
||||
pipeline_layout_create_info.pPushConstantRanges = NULL; |
||||
|
||||
if (vkCreatePipelineLayout(state->device, &pipeline_layout_create_info, NULL, &state->compute.pipeline_layout) != VK_SUCCESS) { |
||||
printf("failed to create pipeline layout"); |
||||
exit(1); |
||||
} |
||||
|
||||
printf("created pipeline layout\n"); |
||||
|
||||
VkComputePipelineCreateInfo create_info = {0}; |
||||
create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; |
||||
create_info.layout = state->compute.pipeline_layout; |
||||
create_info.basePipelineHandle = VK_NULL_HANDLE; |
||||
create_info.stage = compute_shader_stage_info; |
||||
create_info.flags = 0; |
||||
|
||||
if (vkCreateComputePipelines(state->device, VK_NULL_HANDLE, 1, &create_info, NULL, &state->compute.pipeline) != VK_SUCCESS) { |
||||
printf("failed to create compute pipeline\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
printf("created compute pipeline\n"); |
||||
|
||||
vkDestroyShaderModule(state->device, compute_shader_module, NULL); |
||||
|
||||
return; |
||||
} |
||||
|
||||
void create_descriptor_pool(ApplicationState* state) { |
||||
VkDescriptorPoolSize pool_size[2] = {0}; |
||||
pool_size[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
||||
pool_size[0].descriptorCount = 1; |
||||
pool_size[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
||||
pool_size[1].descriptorCount = 1; |
||||
|
||||
VkDescriptorPoolCreateInfo pool_info = {0}; |
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; |
||||
pool_info.poolSizeCount = ARRSIZE(pool_size); |
||||
pool_info.pPoolSizes = pool_size; |
||||
pool_info.maxSets = 1; |
||||
|
||||
if (vkCreateDescriptorPool(state->device, &pool_info, NULL, &state->compute.descriptor_pool) != VK_SUCCESS) { |
||||
printf("failed to create descriptor pool\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
printf("created descriptor pool\n"); |
||||
} |
||||
|
||||
void create_descriptor_sets(ApplicationState* state) { |
||||
VkDescriptorSetAllocateInfo alloc_info = {0}; |
||||
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; |
||||
alloc_info.descriptorPool = state->compute.descriptor_pool; |
||||
alloc_info.descriptorSetCount = 1; |
||||
alloc_info.pSetLayouts = &state->compute.descriptor_set_layout; |
||||
|
||||
if (vkAllocateDescriptorSets(state->device, &alloc_info, &state->compute.descriptor_sets) != VK_SUCCESS) { |
||||
printf("failed to allocate descriptor sets\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
create_buffer(state, sizeof(uint32_t), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
||||
&state->compute.input_buffer, &state->compute.input_buffer_memory); |
||||
create_buffer(state, sizeof(uint32_t), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
||||
&state->compute.output_buffer, &state->compute.output_buffer_memory); |
||||
|
||||
VkDescriptorBufferInfo in_buffer_info = {0}; |
||||
VkDescriptorBufferInfo out_buffer_info = {0}; |
||||
|
||||
in_buffer_info.buffer = state->compute.input_buffer; |
||||
in_buffer_info.range = sizeof(uint32_t); |
||||
|
||||
out_buffer_info.buffer = state->compute.output_buffer; |
||||
out_buffer_info.range = sizeof(uint32_t); |
||||
|
||||
VkWriteDescriptorSet descriptor_writes[2] = {0}; |
||||
|
||||
descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
||||
descriptor_writes[0].dstSet = state->compute.descriptor_sets; |
||||
descriptor_writes[0].dstBinding = 0; |
||||
descriptor_writes[0].dstArrayElement = 0; |
||||
descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
||||
descriptor_writes[0].descriptorCount = 1; |
||||
descriptor_writes[0].pBufferInfo = &in_buffer_info; |
||||
descriptor_writes[0].pImageInfo = NULL; |
||||
descriptor_writes[0].pTexelBufferView = NULL; |
||||
|
||||
descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
||||
descriptor_writes[1].dstSet = state->compute.descriptor_sets; |
||||
descriptor_writes[1].dstBinding = 1; |
||||
descriptor_writes[1].dstArrayElement = 0; |
||||
descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
||||
descriptor_writes[1].descriptorCount = 1; |
||||
descriptor_writes[1].pBufferInfo = &out_buffer_info; |
||||
descriptor_writes[1].pImageInfo = NULL; |
||||
descriptor_writes[1].pTexelBufferView = NULL; |
||||
|
||||
vkUpdateDescriptorSets(state->device, ARRSIZE(descriptor_writes), descriptor_writes, 0, NULL); |
||||
|
||||
printf("allocated and written descriptor sets\n"); |
||||
} |
||||
|
||||
void create_sync_objects(ApplicationState* state) { |
||||
VkSemaphoreCreateInfo semaphore_info = {0}; |
||||
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
||||
|
||||
VkFenceCreateInfo fence_info = {0}; |
||||
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
||||
//initialize it to signalled so we can draw the first frame
|
||||
// fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
|
||||
if (vkCreateFence(state->device, &fence_info, NULL, &state->compute.compute_done)) { |
||||
printf("failed to create sync objects\n"); |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
void init_vulkan(ApplicationState* state) { |
||||
create_instance(state); |
||||
pick_physical_device(state); |
||||
create_logical_device(state); |
||||
create_descriptor_set_layout(state); |
||||
create_compute_pipeline(state); |
||||
create_descriptor_pool(state); |
||||
create_descriptor_sets(state); |
||||
setup_compute_queues(state); |
||||
create_sync_objects(state); |
||||
} |
||||
|
||||
void terminate(ApplicationState* state) { |
||||
vkDestroyFence(state->device, state->compute.compute_done, NULL); |
||||
// vkDestroyCommandPool(state->device, state->compute.command_pool, NULL);
|
||||
|
||||
vkFreeMemory(state->device, state->compute.input_buffer_memory, NULL); |
||||
vkFreeMemory(state->device, state->compute.output_buffer_memory, NULL); |
||||
vkDestroyBuffer(state->device, state->compute.input_buffer, NULL); |
||||
vkDestroyBuffer(state->device, state->compute.output_buffer, NULL); |
||||
|
||||
vkDestroyDescriptorPool(state->device, state->compute.descriptor_pool, NULL); |
||||
vkDestroyPipeline(state->device, state->compute.pipeline, NULL); |
||||
vkDestroyPipelineLayout(state->device, state->compute.pipeline_layout, NULL); |
||||
vkDestroyDescriptorSetLayout(state->device, state->compute.descriptor_set_layout, NULL); |
||||
vkDestroyCommandPool(state->device, state->compute.command_pool, NULL); |
||||
vkDestroyDevice(state->device, NULL); |
||||
vkDestroyInstance(state->instance, NULL); |
||||
} |
||||
|
||||
int main() { |
||||
ApplicationState state = {0}; |
||||
init_vulkan(&state); |
||||
|
||||
VkCommandBufferBeginInfo begin_info = {0}; |
||||
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
||||
begin_info.flags = 0; |
||||
begin_info.pInheritanceInfo = NULL; |
||||
|
||||
if (vkBeginCommandBuffer(state.compute.command_buffer, &begin_info) != VK_SUCCESS) { |
||||
printf("failed to begin recording\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
vkCmdBindPipeline(state.compute.command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, state.compute.pipeline); |
||||
vkCmdBindDescriptorSets(state.compute.command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, |
||||
state.compute.pipeline_layout, 0, 1, &state.compute.descriptor_sets, 0, NULL); |
||||
vkCmdDispatch(state.compute.command_buffer, 1, 1, 1); |
||||
|
||||
printf("start recording command buffer\n"); |
||||
|
||||
if (vkEndCommandBuffer(state.compute.command_buffer) != VK_SUCCESS) { |
||||
printf("failed to record command buffer\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
printf("command buffer recorded\n"); |
||||
|
||||
VkSubmitInfo compute_submit_info = {0}; |
||||
compute_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
||||
compute_submit_info.commandBufferCount = 1; |
||||
compute_submit_info.pCommandBuffers = &state.compute.command_buffer; |
||||
compute_submit_info.signalSemaphoreCount = 0; |
||||
compute_submit_info.waitSemaphoreCount = 0; |
||||
|
||||
volatile uint32_t* data; |
||||
vkMapMemory(state.device, state.compute.input_buffer_memory, 0, sizeof(uint32_t), 0, (void**)&data); |
||||
*data = 15; |
||||
vkUnmapMemory(state.device, state.compute.input_buffer_memory); |
||||
|
||||
if (vkQueueSubmit(state.compute.queue, 1, &compute_submit_info, state.compute.compute_done)) { |
||||
printf("submit failed\n"); |
||||
} |
||||
|
||||
vkWaitForFences(state.device, 1, &state.compute.compute_done, VK_TRUE, UINT64_MAX); |
||||
vkQueueWaitIdle(state.compute.queue); |
||||
|
||||
vkMapMemory(state.device, state.compute.output_buffer_memory, 0, sizeof(uint32_t), 0, (void**)&data); |
||||
printf("submit complete, result: %X\n", *data); |
||||
vkUnmapMemory(state.device, state.compute.output_buffer_memory); |
||||
|
||||
|
||||
terminate(&state); |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue