From 20ab9d8de6b5e0422af539276c0b1798021cb0a5 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Wed, 12 Feb 2025 04:45:59 +0100 Subject: Refactor --- vulkan_compute_minimal_example.c | 470 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100755 vulkan_compute_minimal_example.c (limited to 'vulkan_compute_minimal_example.c') diff --git a/vulkan_compute_minimal_example.c b/vulkan_compute_minimal_example.c new file mode 100755 index 0000000..26f7e34 --- /dev/null +++ b/vulkan_compute_minimal_example.c @@ -0,0 +1,470 @@ +#if 0 /* +#/ ================================================================ +#/ +#/ vulkan_compute_minimal_example.c +#/ +#/ ---------------------------------------------------------------- +#/ +#/ (C) 2025 Mitya Selivanov +#/ +#/ ================================================================ +#/ +#/ COMPUTE SHADER SOURCE CODE +#/ +#/ ================================================================ +cat <~module.comp + +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +layout(binding = 0) buffer output_buffer { + uint values_out[16]; +}; +void main() { + for (uint i = 0; i < 16; i++) values_out[i] = i; +} + +EOF +#/ ================================================================ +#/ +#/ BUILD AND RUN SCRIPT +#/ +#/ ================================================================ + +SRC=${0##*./} +BIN=${SRC%.*} +glslc ~module.comp -o ~module.spv && gcc $SRC -o $BIN -lm -lvulkan && ./$BIN +STATUS=$? +rm -f $BIN +rm -f ~module.comp +rm -f ~module.spv +exit $STATUS # */ +#endif + +// ================================================================ +// +// C SOURCE CODE +// +// ================================================================ + +#include +#include + +#include + +typedef signed i32; +typedef unsigned u32; +typedef float f32; +typedef char c8; + +enum { + MAX_DEVICE_COUNT = 16, + MAX_QUEUE_FAMILY_COUNT = 64, + BUFFER_SIZE = 64, + GROUP_COUNT_X = 1, + GROUP_COUNT_Y = 1, + GROUP_COUNT_Z = 1, +}; + +i32 main(i32 argc, c8 **argv) { + i32 status = 0; + u32 queue_family_index = 0; + + VkInstance instance = NULL; + VkPhysicalDevice physical_device = NULL; + VkDevice device = NULL; + VkQueue queue = NULL; + VkBuffer buffer = NULL; + VkDeviceMemory buffer_memory = NULL; + VkDescriptorSetLayout descriptor_set_layout = NULL; + VkDescriptorPool descriptor_pool = NULL; + VkDescriptorSet descriptor_set = NULL; + VkShaderModule compute_module = NULL; + VkPipelineLayout pipeline_layout = NULL; + VkPipeline pipeline = NULL; + VkCommandPool command_pool = NULL; + VkCommandBuffer command_buffer = NULL; + VkFence fence = NULL; + + if (vkCreateInstance(&(VkInstanceCreateInfo) { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &(VkApplicationInfo) { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .apiVersion = VK_API_VERSION_1_1, + }, + }, NULL, &instance) != VK_SUCCESS) { + printf("vkCreateInstance failed.\n"); + status = -1; + goto _cleanup; + } + + // Find physical device + { + u32 device_count = MAX_DEVICE_COUNT; + VkPhysicalDevice devices[MAX_DEVICE_COUNT]; + + VkResult res = vkEnumeratePhysicalDevices(instance, &device_count, devices); + + if (res != VK_SUCCESS && res != VK_INCOMPLETE) { + printf("vkEnumeratePhysicalDevices failed.\n"); + status = -1; + goto _cleanup; + } + + if (device_count <= 0) { + printf("Compatible physical device not found.\n"); + status = -1; + goto _cleanup; + } + + u32 i = 0; + VkPhysicalDeviceProperties properties; + + for (; i < device_count; ++i) { + vkGetPhysicalDeviceProperties(devices[i], &properties); + + if (strstr(properties.deviceName, "NVIDIA") != NULL || strstr(properties.deviceName, "AMD") != NULL) { + physical_device = devices[i]; + break; + } + } + + if (i >= device_count) + physical_device = devices[0]; + + vkGetPhysicalDeviceProperties(physical_device, &properties); + + printf("Physical device selected: %s\n", properties.deviceName); + } + + // Find queue family + { + u32 queue_family_count = MAX_QUEUE_FAMILY_COUNT; + VkQueueFamilyProperties queue_families[MAX_QUEUE_FAMILY_COUNT]; + + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families); + + u32 index = 0; + + for (; index < queue_family_count; ++index) + if (queue_families[index].queueCount > 0 && (queue_families[index].queueFlags & VK_QUEUE_COMPUTE_BIT)) + break; + + if (index >= queue_family_count) { + printf("Compatible queue family not found.\n"); + status = -1; + goto _cleanup; + } + + queue_family_index = index; + } + + // Create logical device + { + VkPhysicalDeviceFeatures device_features; + memset(&device_features, 0, sizeof device_features); + + if (vkCreateDevice(physical_device, &(VkDeviceCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pQueueCreateInfos = &(VkDeviceQueueCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueCount = 1, + .pQueuePriorities = (f32[1]) { 1.f, }, + .queueFamilyIndex = queue_family_index, + }, + .queueCreateInfoCount = 1, + .pEnabledFeatures = &device_features, + }, NULL, &device) != VK_SUCCESS) { + printf("vkCreateDevice failed.\n"); + status = -1; + goto _cleanup; + } + + vkGetDeviceQueue(device, queue_family_index, 0, &queue); + } + + // Create buffer + { + if (vkCreateBuffer(device, &(VkBufferCreateInfo) { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = BUFFER_SIZE, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }, NULL, &buffer) != VK_SUCCESS) { + printf("vkCreateBuffer failed.\n"); + status = -1; + goto _cleanup; + } + + VkMemoryRequirements memory_requirements; + VkPhysicalDeviceMemoryProperties memory_properties; + + vkGetBufferMemoryRequirements(device, buffer, &memory_requirements); + vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties); + + u32 i = 0; + u32 properties = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + u32 memory_type_index = 0; + + for (; i < memory_properties.memoryTypeCount; ++i) + if ((memory_requirements.memoryTypeBits & (1 << i)) && ((memory_properties.memoryTypes[i].propertyFlags & properties) == properties)) { + memory_type_index = i; + break; + } + + if (i >= memory_properties.memoryTypeCount) { + printf("Compatible memory properties not found.\n"); + status = -1; + goto _cleanup; + } + + if (vkAllocateMemory(device, &(VkMemoryAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memory_requirements.size, + .memoryTypeIndex = memory_type_index, + }, NULL, &buffer_memory) != VK_SUCCESS) { + printf("vkAllocateMemory failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkBindBufferMemory(device, buffer, buffer_memory, 0) != VK_SUCCESS) { + printf("vkBindBufferMemory failed.\n"); + status = -1; + goto _cleanup; + } + } + + if (vkCreateDescriptorSetLayout(device, &(VkDescriptorSetLayoutCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .pBindings = &(VkDescriptorSetLayoutBinding) { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }, NULL, &descriptor_set_layout) != VK_SUCCESS) { + printf("vkCreateDescriptorSetLayout failed.\n"); + status = -1; + goto _cleanup; + } + + // Create descriptor set + { + if (vkCreateDescriptorPool(device, &(VkDescriptorPoolCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = &(VkDescriptorPoolSize) { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + }, + }, NULL, &descriptor_pool) != VK_SUCCESS) { + printf("vkCreateDescriptorPool failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkAllocateDescriptorSets(device, &(VkDescriptorSetAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = descriptor_pool, + .descriptorSetCount = 1, + .pSetLayouts = &descriptor_set_layout, + }, &descriptor_set) != VK_SUCCESS) { + printf("vkAllocateDescriptorSets failed.\n"); + status = -1; + goto _cleanup; + } + + vkUpdateDescriptorSets(device, 1, &(VkWriteDescriptorSet) { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = &(VkDescriptorBufferInfo) { + .buffer = buffer, + .offset = 0, + .range = BUFFER_SIZE, + }, + }, 0, NULL); + } + + // Create compute pipeline + { + FILE *f = fopen("~module.spv", "rb"); + if (f == NULL) { + printf("fopen failed.\n"); + status = -1; + goto _cleanup; + } + static u32 code[4096]; + u32 n = fread(code, 1, sizeof code, f); + fclose(f); + + if (vkCreateShaderModule(device, &(VkShaderModuleCreateInfo) { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pCode = code, + .codeSize = n, + }, NULL, &compute_module) != VK_SUCCESS) { + printf("vkCreateShaderModule failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkCreatePipelineLayout(device, &(VkPipelineLayoutCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 1, + .pSetLayouts = &descriptor_set_layout, + }, NULL, &pipeline_layout) != VK_SUCCESS) { + printf("vkCreatePipelineLayout failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &(VkComputePipelineCreateInfo) { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = compute_module, + .pName = "main", + }, + .layout = pipeline_layout, + }, NULL, &pipeline) != VK_SUCCESS) { + printf("vkCreateComputePipelines failed.\n"); + status = -1; + goto _cleanup; + } + } + + // Create command buffer + { + if (vkCreateCommandPool(device, &(VkCommandPoolCreateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .queueFamilyIndex = queue_family_index, + }, NULL, &command_pool) != VK_SUCCESS) { + printf("vkCreateCommandPool failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkAllocateCommandBuffers(device, &(VkCommandBufferAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }, &command_buffer) != VK_SUCCESS) { + printf("vkAllocateCommandBuffers failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkBeginCommandBuffer(command_buffer, &(VkCommandBufferBeginInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }) != VK_SUCCESS) { + printf("vkBeginCommandBuffer failed.\n"); + status = -1; + goto _cleanup; + } + + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set, 0, NULL); + vkCmdDispatch(command_buffer, GROUP_COUNT_X, GROUP_COUNT_Y, GROUP_COUNT_Z); + + if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { + printf("vkEndCommandBuffer failed.\n"); + status = -1; + goto _cleanup; + } + } + + // Run command buffer + { + if (vkCreateFence(device, &(VkFenceCreateInfo) { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }, NULL, &fence) != VK_SUCCESS) { + printf("vkCreateFence failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkQueueSubmit(queue, 1, &(VkSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &command_buffer, + }, fence) != VK_SUCCESS) { + printf("vkQueueSubmit failed.\n"); + status = -1; + goto _cleanup; + } + + if (vkWaitForFences(device, 1, &fence, VK_TRUE, 100000000000) != VK_SUCCESS) { + printf("vkWaitForFences failed.\n"); + status = -1; + goto _cleanup; + } + } + + // Read output buffer + { + u32 *p = NULL; + + if (vkMapMemory(device, buffer_memory, 0, BUFFER_SIZE, 0, (void **) &p) != VK_SUCCESS) { + printf("vkMapMemory failed.\n"); + status = -1; + goto _cleanup; + } + + printf("Data:"); + for (u32 i = 0; i < BUFFER_SIZE / 4; i++) + printf(" %2d", (int) p[i]); + printf("\n"); + + vkUnmapMemory(device, buffer_memory); + } + +_cleanup: + { + if (fence != NULL) + vkDestroyFence(device, fence, NULL); + if (buffer_memory != NULL) + vkFreeMemory(device, buffer_memory, NULL); + if (buffer != NULL) + vkDestroyBuffer(device, buffer, NULL); + if (compute_module != NULL) + vkDestroyShaderModule(device, compute_module, NULL); + if (descriptor_pool != NULL) + vkDestroyDescriptorPool(device, descriptor_pool, NULL); + if (descriptor_set_layout != NULL) + vkDestroyDescriptorSetLayout(device, descriptor_set_layout, NULL); + if (pipeline_layout != NULL) + vkDestroyPipelineLayout(device, pipeline_layout, NULL); + if (pipeline != NULL) + vkDestroyPipeline(device, pipeline, NULL); + if (command_pool != NULL) + vkDestroyCommandPool(device, command_pool, NULL); + if (device != NULL) + vkDestroyDevice(device, NULL); + if (instance != NULL) + vkDestroyInstance(instance, NULL); + + instance = NULL; + physical_device = NULL; + device = NULL; + queue = NULL; + buffer = NULL; + buffer_memory = NULL; + descriptor_set_layout = NULL; + descriptor_pool = NULL; + descriptor_set = NULL; + compute_module = NULL; + pipeline_layout = NULL; + pipeline = NULL; + command_pool = NULL; + command_buffer = NULL; + fence = NULL; + } + + printf("OK\n"); + return status; +} -- cgit v1.2.3