From 4ec57bbd7953d3a9a1aa464844ba2d43198e386a Mon Sep 17 00:00:00 2001 From: Robear Selwans Date: Mon, 9 Dec 2024 12:51:29 +0200 Subject: [PATCH] Starting Out Signed-off-by: Robear Selwans --- .gitignore | 3 + evk/evk.h | 16 + evk/evkAllocator.c | 70 +++ evk/evkAllocator.h | 6 + evk/evkBuffer.c | 29 ++ evk/evkBuffer.h | 6 + evk/evkCommand.c | 51 ++ evk/evkCommand.h | 15 + evk/evkCommon.h | 13 + evk/evkDescriptor.c | 43 ++ evk/evkDescriptor.h | 5 + evk/evkDevice.c | 176 +++++++ evk/evkDevice.h | 9 + evk/evkImage.c | 67 +++ evk/evkImage.h | 8 + evk/evkInstance.c | 43 ++ evk/evkInstance.h | 7 + evk/evkMemory.c | 84 ++++ evk/evkMemory.h | 12 + evk/evkPipeline.c | 163 +++++++ evk/evkPipeline.h | 10 + evk/evkRender.c | 1 + evk/evkRender.h | 3 + evk/evkShader.c | 138 ++++++ evk/evkShader.h | 18 + evk/evkSwapChain.c | 93 ++++ evk/evkSwapChain.h | 8 + evk/evkSync.c | 20 + evk/evkSync.h | 10 + evk/evkTypes.h | 448 ++++++++++++++++++ main.c | 249 ++++++++++ meson-native-clang | 8 + meson.build | 69 +++ shaders/tri.frag | 9 + shaders/tri.vert | 15 + subprojects/evol-headers.wrap | 7 + subprojects/glfw.wrap | 13 + .../packagefiles/libshaderc/meson.build | 13 + subprojects/packagefiles/spvref/meson.build | 22 + subprojects/packagefiles/vma/meson.build | 22 + subprojects/packagefiles/vma/vk_mem_alloc.cpp | 4 + subprojects/packagefiles/volk/meson.build | 26 + subprojects/packagefiles/volk/volk_impl.c | 2 + subprojects/shaderc.wrap | 12 + subprojects/spvref.wrap | 7 + subprojects/vma.wrap | 7 + subprojects/volk.wrap | 9 + 47 files changed, 2069 insertions(+) create mode 100644 evk/evk.h create mode 100644 evk/evkAllocator.c create mode 100644 evk/evkAllocator.h create mode 100644 evk/evkBuffer.c create mode 100644 evk/evkBuffer.h create mode 100644 evk/evkCommand.c create mode 100644 evk/evkCommand.h create mode 100644 evk/evkCommon.h create mode 100644 evk/evkDescriptor.c create mode 100644 evk/evkDescriptor.h create mode 100644 evk/evkDevice.c create mode 100644 evk/evkDevice.h create mode 100644 evk/evkImage.c create mode 100644 evk/evkImage.h create mode 100644 evk/evkInstance.c create mode 100644 evk/evkInstance.h create mode 100644 evk/evkMemory.c create mode 100644 evk/evkMemory.h create mode 100644 evk/evkPipeline.c create mode 100644 evk/evkPipeline.h create mode 100644 evk/evkRender.c create mode 100644 evk/evkRender.h create mode 100644 evk/evkShader.c create mode 100644 evk/evkShader.h create mode 100644 evk/evkSwapChain.c create mode 100644 evk/evkSwapChain.h create mode 100644 evk/evkSync.c create mode 100644 evk/evkSync.h create mode 100644 evk/evkTypes.h create mode 100644 main.c create mode 100644 meson-native-clang create mode 100644 meson.build create mode 100644 shaders/tri.frag create mode 100644 shaders/tri.vert create mode 100644 subprojects/evol-headers.wrap create mode 100644 subprojects/glfw.wrap create mode 100644 subprojects/packagefiles/libshaderc/meson.build create mode 100644 subprojects/packagefiles/spvref/meson.build create mode 100644 subprojects/packagefiles/vma/meson.build create mode 100644 subprojects/packagefiles/vma/vk_mem_alloc.cpp create mode 100644 subprojects/packagefiles/volk/meson.build create mode 100644 subprojects/packagefiles/volk/volk_impl.c create mode 100644 subprojects/shaderc.wrap create mode 100644 subprojects/spvref.wrap create mode 100644 subprojects/vma.wrap create mode 100644 subprojects/volk.wrap diff --git a/.gitignore b/.gitignore index c6127b3..df4390a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ modules.order Module.symvers Mkfile.old dkms.conf + +**/build +**/.cache diff --git a/evk/evk.h b/evk/evk.h new file mode 100644 index 0000000..d8c7a2d --- /dev/null +++ b/evk/evk.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "evkTypes.h" + +#include "evkAllocator.h" + +#include "evkInstance.h" +#include "evkDevice.h" +#include "evkSync.h" +#include "evkShader.h" +#include "evkSwapChain.h" +#include "evkCommand.h" +#include "evkPipeline.h" +#include "evkImage.h" diff --git a/evk/evkAllocator.c b/evk/evkAllocator.c new file mode 100644 index 0000000..534f1f9 --- /dev/null +++ b/evk/evkAllocator.c @@ -0,0 +1,70 @@ +#include "evkAllocator.h" + +#include +#include + +// PFN_vkAllocationFunction +void* evkAllocationFunctionCallback(void* pUserData, size_t allocationSize, size_t allocationAlignment, VkSystemAllocationScope allocationScope) +{ + /* puts("evkAllocationFunctionCallback"); */ + void* alloc = _aligned_malloc(allocationSize, allocationAlignment); + if(alloc == NULL) + { + puts("Allocation Failed"); + } + return alloc; +} + +// PFN_vkReallocationFunction +void* evkReallocationFunctionCallback(void* pUserData, void* pOriginal, size_t allocationSize, size_t allocationAlignment, VkSystemAllocationScope allocationScope) +{ + /* puts("evkReallocationFunctionCallback"); */ + return _aligned_realloc(pOriginal, allocationSize, allocationAlignment); +} + +// PFN_vkFreeFucntion +void evkFreeFunctionCallback(void* pUserData, void* pMemory) +{ + /* puts("evkFreeFunctionCallback"); */ + _aligned_free(pMemory); +} + +// PFN_vkInternalAllocaitonNotification +void evkInternalAllocationCallback(void* pUserData, size_t allocationSize, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) +{ + puts("evkInternalAllocationCallback"); +} + +// PFN_vkInternalFreeNotification +void evkInternalFreeCallback(void* pUserData, size_t allocationSize, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) +{ + puts("evkInternalFreeCallback"); +} + +TYPEDATA_GEN( + VkAllocationCallbacks, + DEFAULT( + .pUserData = &(evkAllocationUserData){ 0 }, // EV_INVALID(evkAllocationUserData) + .pfnAllocation = evkAllocationFunctionCallback, + .pfnReallocation = evkReallocationFunctionCallback, + .pfnFree = evkFreeFunctionCallback, + .pfnInternalAllocation = evkInternalAllocationCallback, + .pfnInternalFree = evkInternalFreeCallback + ) +); + +VkAllocationCallbacks def = { + .pUserData = NULL, // EV_INVALID(evkAllocationUserData) + .pfnAllocation = evkAllocationFunctionCallback, + .pfnReallocation = evkReallocationFunctionCallback, + .pfnFree = evkFreeFunctionCallback, + .pfnInternalAllocation = evkInternalAllocationCallback, + .pfnInternalFree = evkInternalFreeCallback +}; + +VkAllocationCallbacks* evkGetAllocationCallbacks() +{ + /* return &EV_DEFAULT(VkAllocationCallbacks); */ + /* return &def; */ + return NULL; +} diff --git a/evk/evkAllocator.h b/evk/evkAllocator.h new file mode 100644 index 0000000..6ffd617 --- /dev/null +++ b/evk/evkAllocator.h @@ -0,0 +1,6 @@ +#pragma once + +#include "evkCommon.h" + +VkAllocationCallbacks* evkGetAllocationCallbacks(); + diff --git a/evk/evkBuffer.c b/evk/evkBuffer.c new file mode 100644 index 0000000..5c84c05 --- /dev/null +++ b/evk/evkBuffer.c @@ -0,0 +1,29 @@ +#include "evkImage.h" +#include "evk/evkMemory.h" + +evkBuffer evkCreateBuffer(evkBufferCreateInfo createInfo) +{ + VkBufferCreateInfo bufferCreateInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = NULL, + .flags = createInfo.flags, + .usage = createInfo.usage, + .sharingMode = createInfo.exclusive?VK_SHARING_MODE_EXCLUSIVE:VK_SHARING_MODE_CONCURRENT, + .queueFamilyIndexCount = vec_len(&createInfo.queueFamilyIndices), + .pQueueFamilyIndices = createInfo.queueFamilyIndices, + }; + + evkBuffer buffer = evkGPUCreateBuffer(createInfo.allocationCreateInfo, &bufferCreateInfo); + + if(buffer.vk != VK_NULL_HANDLE) + { + buffer.sizeInBytes = createInfo.sizeInBytes; + } + + return buffer; +} + +void evkDestroyBuffer(evkBuffer buf) +{ + evkGPUDestroyBuffer(buf); +} diff --git a/evk/evkBuffer.h b/evk/evkBuffer.h new file mode 100644 index 0000000..e8b8607 --- /dev/null +++ b/evk/evkBuffer.h @@ -0,0 +1,6 @@ +#pragma once + +#include "evkCommon.h" + +evkBuffer evkCreateBuffer(evkBufferCreateInfo createInfo); +void evkDestroyBuffer(evkBuffer buf); diff --git a/evk/evkCommand.c b/evk/evkCommand.c new file mode 100644 index 0000000..eaacff5 --- /dev/null +++ b/evk/evkCommand.c @@ -0,0 +1,51 @@ +#include "evkCommand.h" + +evkCommandPool evkCreateCommandPool(evkCommandPoolCreateInfo createInfo) +{ + evkCommandPool commandPool; + commandPool.queueFamily = createInfo.device.queueFamilies[createInfo.queueFlags]; + + VkCommandPoolCreateInfo vkCreateInfo = (VkCommandPoolCreateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = createInfo.poolFlags, + .queueFamilyIndex = commandPool.queueFamily.familyIndex, + }; + + vkCreateCommandPool(createInfo.device.vk, &vkCreateInfo, NULL, &commandPool.vk); + return commandPool; +} + +void evkDestroyCommandPool(evkDevice device, evkCommandPool commandPool) +{ + vkDestroyCommandPool(device.vk, commandPool.vk, NULL); +} + +vec(VkCommandBuffer) evkAllocateCommandBuffers(evkDevice device, evkCommandPool commandPool, u32 count, bool primary) +{ + vec(VkCommandBuffer) buffers = vec_init(VkCommandBuffer); + vec_setlen(&buffers, count); + + VkCommandBufferAllocateInfo allocateInfo = (VkCommandBufferAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = commandPool.vk, + .commandBufferCount = count, + .level = primary?VK_COMMAND_BUFFER_LEVEL_PRIMARY:VK_COMMAND_BUFFER_LEVEL_SECONDARY, + }; + + vkAllocateCommandBuffers(device.vk, &allocateInfo, buffers); + + return buffers; +} + +void evkBeginPrimaryCommandBuffer(VkCommandBuffer cmdBuf) +{ + VkCommandBufferBeginInfo beginInfo = (VkCommandBufferBeginInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + }; + vkBeginCommandBuffer(cmdBuf, &beginInfo); +} + +void evkEndCommandBuffer(VkCommandBuffer cmdBuf) +{ + vkEndCommandBuffer(cmdBuf); +} diff --git a/evk/evkCommand.h b/evk/evkCommand.h new file mode 100644 index 0000000..0ec0618 --- /dev/null +++ b/evk/evkCommand.h @@ -0,0 +1,15 @@ +#pragma once + +#include "evk.h" +#include "evkDevice.h" + + +[[nodiscard("Leaking VkCommandPool")]] +evkCommandPool evkCreateCommandPool(evkCommandPoolCreateInfo createInfo); +void evkDestroyCommandPool(evkDevice device, evkCommandPool commandPool); + +[[nodiscard("Leaking allocated CommandBuffers")]] +vec(VkCommandBuffer) evkAllocateCommandBuffers(evkDevice device, evkCommandPool commandPool, u32 count, bool primary); + +void evkBeginPrimaryCommandBuffer(VkCommandBuffer cmdBuf); +void evkEndCommandBuffer(VkCommandBuffer cmdBuf); diff --git a/evk/evkCommon.h b/evk/evkCommon.h new file mode 100644 index 0000000..929b7f9 --- /dev/null +++ b/evk/evkCommon.h @@ -0,0 +1,13 @@ +#pragma once + +#include "evk_buildconfig.h" +#include +#include "vk_mem_alloc.h" + +#include +#include + +#define EV_VEC_SHORTNAMES +#include + +#include "evkTypes.h" diff --git a/evk/evkDescriptor.c b/evk/evkDescriptor.c new file mode 100644 index 0000000..e05d7a2 --- /dev/null +++ b/evk/evkDescriptor.c @@ -0,0 +1,43 @@ +#pragma once + +#include "evkDescriptor.h" + +typedef struct { + VkDescriptorSetLayout descriptorSetLayouts[4]; +} evkSetLayout; + +evkSetLayout evkCreateDescriptorSetLayouts(vec(evkShader) shaders) +{ + VkDescriptorSetLayoutCreateInfo createInfos[4]; + for(int i = 0; i < 4; i++) + { + createInfos[i] = EV_DEFAULT( + VkDescriptorSetLayoutCreateInfo, + pBindings = vec_init(VkDescriptorSetLayoutBinding) + ); + } + + for(int i = 0; i < vec_len(&shaders); i++) + { + vec(evkDescriptorBinding) bindings = shaders[i].reflect.bindings; + for(int bi = 0; bi < vec_len(&bindings); bi++) + { + evkDescriptorBinding b = bindings[bi]; + if(b.binding >= vec_len(&createInfos[b.set].pBindings)) + { + u32 old_len = vec_len(&createInfos[b.set].pBindings); + vec_setlen(&createInfos[b.set].pBindings, b.binding+1); + for(int cs = old_len; cs < b.binding+1; cs++) + { + /* createInfos[b.set].pBindings[cs].binding */ + } + } + + } + } + + for(int i = 0; i < 4; i++) + { + createInfos[i].bindingCount = vec_len(&createInfos[i].pBindings); + } +} diff --git a/evk/evkDescriptor.h b/evk/evkDescriptor.h new file mode 100644 index 0000000..3c3dadc --- /dev/null +++ b/evk/evkDescriptor.h @@ -0,0 +1,5 @@ +#pragma once + +#include "evkCommon.h" + +// void evkFillSetLayoutCreateInfos(vec(evkShader) shaders, VkDescriptorSetLayoutCreateInfo layoutCreateInfos[4]); diff --git a/evk/evkDevice.c b/evk/evkDevice.c new file mode 100644 index 0000000..13ea134 --- /dev/null +++ b/evk/evkDevice.c @@ -0,0 +1,176 @@ +#include "evkDevice.h" +#include "evkTypes.h" +#include "evkAllocator.h" + +evstring VkDynamicRenderingExtName = evstr("VK_KHR_dynamic_rendering"); +evstring VkSync2ExtName = evstr("VK_KHR_synchronization2"); + +VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = (VkPhysicalDeviceDynamicRenderingFeaturesKHR) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR, + .dynamicRendering = VK_TRUE, +}; + +VkPhysicalDeviceSynchronization2Features sync2Features = (VkPhysicalDeviceSynchronization2Features) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + .synchronization2 = VK_TRUE, +}; + +VkPhysicalDevice evkDetectPhysicalDevice(evkInstance instance, VkPhysicalDeviceType deviceType) +{ + VkPhysicalDevice chosenDevice = EV_INVALID(VkPhysicalDevice); + u32 deviceCount = 0; + + vec(VkPhysicalDevice) physicalDevices = vec_init(VkPhysicalDevice); + vkEnumeratePhysicalDevices(instance.vk, &deviceCount, NULL); + vec_setlen(&physicalDevices, deviceCount); + vkEnumeratePhysicalDevices(instance.vk, &deviceCount, physicalDevices); + + // TODO Add a more robust/flexible way to pick a GPU + for(u32 i = 0; i < deviceCount; i++) + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProperties); + if(deviceProperties.deviceType == deviceType) + { + chosenDevice = physicalDevices[i]; + break; + } + } + + vec_fini(&physicalDevices); + + return chosenDevice; +} + +evkDevice evkCreateDevice(evkDeviceCreateInfo createInfo) +{ + evkDevice device; + device._physicalDevice = evkDetectPhysicalDevice(createInfo.instance, createInfo.physicalDeviceType); + device._instance = createInfo.instance; + + VkPhysicalDeviceProperties physicalDeviceProperties; + vkGetPhysicalDeviceProperties(device._physicalDevice, &physicalDeviceProperties); + device.limits = physicalDeviceProperties.limits; + + for (u32 i = 0; i < MAX_QUEUE_FAMILIES; i++) { + device.queueFamilies[i] = (evkDeviceQueueFamily){ + .familyIndex = -1, + .allocatedQueueCount = 0 + }; + } + + VkDeviceCreateInfo vkDeviceCreateInfo = __EV_VEC_EMPTY_ARRAY; + vkDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + void** pNext = &vkDeviceCreateInfo.pNext; + + if(ev_vec_find(&createInfo.deviceExtensions, &VkDynamicRenderingExtName) != -1) + { + *pNext = &dynamicRenderingFeature; + pNext = &dynamicRenderingFeature.pNext; + } + + if(ev_vec_find(&createInfo.deviceExtensions, &VkSync2ExtName) != -1) + { + *pNext = &sync2Features; + pNext = &sync2Features.pNext; + } + + vkDeviceCreateInfo.enabledExtensionCount = vec_len(&createInfo.deviceExtensions); + vkDeviceCreateInfo.ppEnabledExtensionNames = createInfo.deviceExtensions; + + // Getting the total number of queues requested from the device + u32 totalQueueCount = 0; + for (u32 i = 0; i < vec_len(&createInfo.queueRequirements); i++) { + totalQueueCount += createInfo.queueRequirements[i].count; + } + + const f32 DEFAULT_PRIORITY = 1.0f; + // A priorities list that can be passed with any VkQueueCreteInfo (doable since we have no custom priorities) + vec(f32) priorities = vec_init(f32); + vec_setlen(&priorities, totalQueueCount); + for (u32 i = 0; i < totalQueueCount; i++) { + priorities[i] = DEFAULT_PRIORITY; + } + + vec(VkDeviceQueueCreateInfo) queueCreateInfoList = vec_init(VkDeviceQueueCreateInfo); + if (createInfo.queueRequirements) { + // Retrieving the queue family properties into a vector + u32 queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device._physicalDevice, &queueFamilyCount, NULL); + vec(VkQueueFamilyProperties) queueFamilyProperties = vec_init(VkQueueFamilyProperties); + vec_setlen(&queueFamilyProperties, queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device._physicalDevice, &queueFamilyCount, queueFamilyProperties); + + // Creating VkDeviceQueueCreateInfos from requirements and family properties + // NOTICE: Currently, there can be no queues of a specific type in 2 different + // families. While that can be considered a limitation, the application targeted + // by this abstraction aren't expected to be complex enough to need it. + for (u32 familyIndex = 0; familyIndex < queueFamilyCount; familyIndex++) { + VkQueueFamilyProperties currentFamilyProperties = queueFamilyProperties[familyIndex]; + u32 availableQueuesInFamily = currentFamilyProperties.queueCount; + + for (u32 reqIndex = 0; reqIndex < vec_len(&createInfo.queueRequirements); reqIndex++) { + evkDeviceQueueRequirement currentRequirement = createInfo.queueRequirements[reqIndex]; + + // Testing compatibility of queue family and requirement + if (currentRequirement.flags & currentFamilyProperties.queueFlags) { + + u32 requiredCount = currentRequirement.count; + while (availableQueuesInFamily > 0 && requiredCount > 0) { + // If the queue family is not already in the vector, push it. + if (vec_len(&queueCreateInfoList) <= familyIndex) { + VkDeviceQueueCreateInfo newQueueCreateInfo = { 0 }; + newQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + newQueueCreateInfo.queueFamilyIndex = familyIndex; + newQueueCreateInfo.pQueuePriorities = priorities; + vec_push(&queueCreateInfoList, &newQueueCreateInfo); + } + + u32 allocationCount = (availableQueuesInFamily>requiredCount)?requiredCount:availableQueuesInFamily; + queueCreateInfoList[familyIndex].queueCount += allocationCount; + device.queueFamilies[currentRequirement.flags].familyIndex = familyIndex; + device.queueFamilies[currentRequirement.flags].allocatedQueueCount += allocationCount; + + // Allowing retrieval of families according to their actual flags + device.queueFamilies[currentFamilyProperties.queueFlags].familyIndex = familyIndex; + device.queueFamilies[currentFamilyProperties.queueFlags].allocatedQueueCount += allocationCount; + + // Updating the loop condition variables + availableQueuesInFamily -= allocationCount; + requiredCount -= allocationCount; + } + // Updating the requirement's count after allocation + createInfo.queueRequirements[reqIndex].count = requiredCount; + } + } + } + + vkDeviceCreateInfo.pQueueCreateInfos = queueCreateInfoList; + vkDeviceCreateInfo.queueCreateInfoCount = (u32)vec_len(&queueCreateInfoList); + + vec_fini(&queueFamilyProperties); + } + + // Allowing the retrieval of non-requested, but allocated, queues is possible. + // Example: Allocating COMPUTE | GRAPHICS then requesting GRAPHICS should + // return the already allocated queue + for (i32 i = MAX_QUEUE_FAMILIES - 1; i >= 0; i--) { + for (i32 j = i - 1; j >= 0; j--) { + if (device.queueFamilies[j].familyIndex == -1 && (i & j) == j) { + device.queueFamilies[j] = device.queueFamilies[i]; + } + } + } + + vkCreateDevice(device._physicalDevice, &vkDeviceCreateInfo, evkGetAllocationCallbacks(), &device.vk); + + vec_fini(&queueCreateInfoList); + vec_fini(&priorities); + + return device; +} + +void evkDestroyDevice(evkDevice device) +{ + vkDestroyDevice(device.vk, evkGetAllocationCallbacks()); +} diff --git a/evk/evkDevice.h b/evk/evkDevice.h new file mode 100644 index 0000000..cbda5a3 --- /dev/null +++ b/evk/evkDevice.h @@ -0,0 +1,9 @@ +#pragma once + +#include "evkCommon.h" + +VkPhysicalDevice evkDetectPhysicalDevice(evkInstance instance, VkPhysicalDeviceType deviceType); + +[[nodiscard("Leaking VkDevice")]] +evkDevice evkCreateDevice(evkDeviceCreateInfo createInfo); +void evkDestroyDevice(evkDevice device); diff --git a/evk/evkImage.c b/evk/evkImage.c new file mode 100644 index 0000000..2d44f2b --- /dev/null +++ b/evk/evkImage.c @@ -0,0 +1,67 @@ +#include "evkImage.h" +#include "evk/evkMemory.h" + +void evkDestroyImage(evkDevice device, evkImage img) +{ + evkGPUDestroyImage(img); +} + +evkImageView evkCreateImageView(evkDevice device, evkImage img, evkImageViewCreateInfo createInfo) +{ + evkImageView imgView; + VkImageViewCreateInfo imageViewCreateInfo = (VkImageViewCreateInfo) { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = img.vk, + .viewType = VK_IMAGE_VIEW_TYPE_2D, // TODO Get from evkImage or allow overriding? + .format = img.format, + .components = {0,0,0,0}, // No swizzling use cases yet. + .subresourceRange = { + .aspectMask = createInfo.viewAspect, + .baseMipLevel = createInfo.mipBase, + .levelCount = createInfo.mipCount, + .baseArrayLayer = createInfo.baseLayer, + .layerCount = createInfo.layerCount, + } + }; + + vkCreateImageView(device.vk, &imageViewCreateInfo, NULL, &imgView.vk); + return imgView; +} + +void evkDestroyImageView(evkDevice device, evkImageView imgv) +{ + vkDestroyImageView(device.vk, imgv.vk, NULL); +} + +evkImage evkCreateImage(evkImageCreateInfo createInfo) +{ + VkImageCreateInfo vkImageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = createInfo.flags, + .imageType = createInfo.type, + .format = createInfo.format, + .extent = createInfo.extent, + .mipLevels = createInfo.mipCount, + .arrayLayers = createInfo.layerCount, + .samples = createInfo.sampleCount, + .tiling = createInfo.tiling, + .usage = createInfo.usage, + .sharingMode = createInfo.exclusive?VK_SHARING_MODE_EXCLUSIVE:VK_SHARING_MODE_CONCURRENT, + .initialLayout = createInfo.layout, + }; + if(!createInfo.exclusive) + { + vkImageCreateInfo.queueFamilyIndexCount = vec_len(&createInfo.queueFamilyIndices); + vkImageCreateInfo.pQueueFamilyIndices = createInfo.queueFamilyIndices; + } + + evkImage img = evkGPUCreateImage(createInfo.allocationCreateInfo, &vkImageCreateInfo); + if(img.vk != VK_NULL_HANDLE) + { + img.format = createInfo.format; + img.width = createInfo.extent.width; + img.height = createInfo.extent.height; + } + + return img; +} diff --git a/evk/evkImage.h b/evk/evkImage.h new file mode 100644 index 0000000..b9bf3d7 --- /dev/null +++ b/evk/evkImage.h @@ -0,0 +1,8 @@ +#pragma once + +#include "evkCommon.h" + +evkImageView evkCreateImageView(evkDevice device, evkImage img, evkImageViewCreateInfo createInfo); +void evkDestroyImageView(evkDevice device, evkImageView imgv); + +void evkDestroyImage(evkDevice device, evkImage img); diff --git a/evk/evkInstance.c b/evk/evkInstance.c new file mode 100644 index 0000000..1118e92 --- /dev/null +++ b/evk/evkInstance.c @@ -0,0 +1,43 @@ +#include "evkInstance.h" + +evkInstance evkCreateInstance(evkInstanceCreateInfo instanceCreateInfo) +{ + evkInstance res = EV_INVALID(evkInstance); + if(volkInitialize() != VK_SUCCESS) + return res; + + VkApplicationInfo vkAppInfo = __EV_VEC_EMPTY_ARRAY; + vkAppInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + vkAppInfo.pNext = 0; + vkAppInfo.pEngineName = instanceCreateInfo.applicationInfo.engineName; + vkAppInfo.pApplicationName = instanceCreateInfo.applicationInfo.applicationName; + vkAppInfo.engineVersion = instanceCreateInfo.applicationInfo.engineVersion; + vkAppInfo.applicationVersion = instanceCreateInfo.applicationInfo.applicationVersion; + vkAppInfo.apiVersion = instanceCreateInfo.applicationInfo.apiVersion; + + VkInstanceCreateInfo vkInstanceCreateInfo = __EV_VEC_EMPTY_ARRAY; + vkInstanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + vkInstanceCreateInfo.pApplicationInfo = &vkAppInfo; + vkInstanceCreateInfo.enabledLayerCount = (u32)vec_len(&instanceCreateInfo.layers); + vkInstanceCreateInfo.ppEnabledLayerNames = instanceCreateInfo.layers; + vkInstanceCreateInfo.enabledExtensionCount = (u32)vec_len(&instanceCreateInfo.extensions); + vkInstanceCreateInfo.ppEnabledExtensionNames = instanceCreateInfo.extensions; + + VkResult instanceCreationResult = vkCreateInstance(&vkInstanceCreateInfo, NULL, &res.vk); + /* VkResult instanceCreationResult = vkCreateInstance(&vkInstanceCreateInfo, evkGetAllocationCallbacks(), &res); */ + /* printf("InstanceCreationResult: %d\n", instanceCreationResult); */ + + if(instanceCreationResult == VK_SUCCESS) + { + volkLoadInstance(res.vk); + res.apiVersion = vkAppInfo.apiVersion; + } + + return res; +} + +void evkDestroyInstance(evkInstance instance) +{ + vkDestroyInstance(instance.vk, NULL); + /* vkDestroyInstance(vkInstance, evkGetAllocationCallbacks()); */ +} diff --git a/evk/evkInstance.h b/evk/evkInstance.h new file mode 100644 index 0000000..1e9fae1 --- /dev/null +++ b/evk/evkInstance.h @@ -0,0 +1,7 @@ +#pragma once + +#include "evk.h" + +[[nodiscard("Leaking VkInstance")]] +evkInstance evkCreateInstance(evkInstanceCreateInfo); +void evkDestroyInstance(evkInstance); diff --git a/evk/evkMemory.c b/evk/evkMemory.c new file mode 100644 index 0000000..af41dc5 --- /dev/null +++ b/evk/evkMemory.c @@ -0,0 +1,84 @@ +#pragma once + +#include "evkMemory.h" + +evkGPUAllocator evkGPUCreateAllocator(evkDevice device) +{ + VmaVulkanFunctions vmaFunctions = { + .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, + .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, + .vkAllocateMemory = vkAllocateMemory, + .vkFreeMemory = vkFreeMemory, + .vkMapMemory = vkMapMemory, + .vkUnmapMemory = vkUnmapMemory, + .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, + .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, + .vkBindBufferMemory = vkBindBufferMemory, + .vkBindImageMemory = vkBindImageMemory, + .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, + .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, + .vkCreateBuffer = vkCreateBuffer, + .vkDestroyBuffer = vkDestroyBuffer, + .vkCreateImage = vkCreateImage, + .vkDestroyImage = vkDestroyImage, + .vkCmdCopyBuffer = vkCmdCopyBuffer, + }; + + VmaAllocatorCreateInfo createInfo = { + .physicalDevice = device._physicalDevice, + .device = device.vk, + .instance = device._instance.vk, + .vulkanApiVersion = device._instance.apiVersion, + .pVulkanFunctions = &vmaFunctions, + }; + + evkGPUAllocator alloc; + vmaCreateAllocator(&createInfo, &alloc.vma); + + return alloc; +} + +void evkGPUDestroyAllocator(evkGPUAllocator alloc) +{ + vmaDestroyAllocator(alloc.vma); +} + +evkImage evkGPUCreateImage(evkGPUAllocationCreateInfo allocationCreateInfo, VkImageCreateInfo* imageCreateInfo) +{ + evkImage img; + + VmaAllocationCreateInfo vmaAllocCreateInfo = { + .usage = allocationCreateInfo.memoryUsage, + .flags = allocationCreateInfo.allocationFlags, + .pool = allocationCreateInfo.pool.vma, + }; + + vmaCreateImage(allocationCreateInfo.allocator.vma, imageCreateInfo, &vmaAllocCreateInfo, &img.vk, &img.allocData.allocation.vma, &img.allocData.allocationInfo.vma); + + return img; +} + +void evkGPUDestroyImage(evkImage img) +{ + vmaDestroyImage(img.allocData.allocator.vma, img.vk, img.allocData.allocation.vma); +} + +evkBuffer evkGPUCreateBuffer(evkGPUAllocationCreateInfo allocationCreateInfo, VkBufferCreateInfo* imageCreateInfo) +{ + evkBuffer buf; + + VmaAllocationCreateInfo vmaAllocCreateInfo = { + .usage = allocationCreateInfo.memoryUsage, + .flags = allocationCreateInfo.allocationFlags, + .pool = allocationCreateInfo.pool.vma, + }; + + vmaCreateBuffer(allocationCreateInfo.allocator.vma, imageCreateInfo, &vmaAllocCreateInfo, &buf.vk, &buf.allocData.allocation.vma, &buf.allocData.allocationInfo.vma); + + return buf; +} + +void evkGPUDestroyBuffer(evkBuffer buf) +{ + vmaDestroyBuffer(buf.allocData.allocator.vma, buf.vk, buf.allocData.allocation.vma); +} diff --git a/evk/evkMemory.h b/evk/evkMemory.h new file mode 100644 index 0000000..20fb69b --- /dev/null +++ b/evk/evkMemory.h @@ -0,0 +1,12 @@ +#pragma once + +#include "evkCommon.h" + +evkImage evkGPUCreateImage(evkGPUAllocationCreateInfo, VkImageCreateInfo*); +void evkGPUDestroyImage(evkImage); + +evkBuffer evkGPUCreateBuffer(evkGPUAllocationCreateInfo, VkBufferCreateInfo*); +void evkGPUDestroyBuffer(evkBuffer); + +evkGPUAllocator evkGPUCreateAllocator(evkDevice device); +void evkGPUDestroyAllocator(evkGPUAllocator alloc); diff --git a/evk/evkPipeline.c b/evk/evkPipeline.c new file mode 100644 index 0000000..fa152c5 --- /dev/null +++ b/evk/evkPipeline.c @@ -0,0 +1,163 @@ +#include "evkPipeline.h" + +#include "evk/evkTypes.h" +#include "evkShader.h" + +const u32 DESCRIPTOR_SET_LAYOUT_COUNT = 4; + +evkPipelineLayout evkCreatePipelineLayout(evkDevice device, evkPipelineLayoutCreateInfo createInfo) +{ + evkPipelineLayout layout; + + VkPipelineLayoutCreateInfo vkCreateInfo = (VkPipelineLayoutCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + }; + + vkCreatePipelineLayout(device.vk, &vkCreateInfo, NULL, &layout.vk); + + return layout; +} + +void evkDestroyPipelineLayout(evkDevice device, evkPipelineLayout layout) +{ + vkDestroyPipelineLayout(device.vk, layout.vk, NULL); +} + + +evkPipeline evkCreatePipeline(evkDevice device, evkPipelineCreateInfo createInfo) +{ + u32 shaderStageCount = vec_len(&createInfo.shaderStages); + u32 colorAttachmentCount = vec_len(&createInfo.colorAttachments); + u32 dynamicStateCount = vec_len(&createInfo.dynamicStates); + + evkPipeline res; + + VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = dynamicStateCount, + .pDynamicStates = createInfo.dynamicStates, + }; + + VkFormat colorAttachmentFormats[colorAttachmentCount]; + for(int i = 0; i < colorAttachmentCount; i++) + colorAttachmentFormats[i] = createInfo.colorAttachments[i].format; + + VkPipelineRenderingCreateInfoKHR pipelineRenderingCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, + .colorAttachmentCount = colorAttachmentCount, + .pColorAttachmentFormats = colorAttachmentFormats, + .depthAttachmentFormat = createInfo.depthAttachmentFormat, + .stencilAttachmentFormat = createInfo.stencilAttachmentFormat, + .viewMask = createInfo.viewMask, + }; + + VkPipelineColorBlendAttachmentState colorAttachmentBlendStates[colorAttachmentCount]; + for(int i = 0; i < colorAttachmentCount; i++) + colorAttachmentBlendStates[i] = createInfo.colorAttachments[i].blendState; + + VkPipelineColorBlendStateCreateInfo colorBlending = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, // Update if VK_EXT_rasterization_order_attachment_access is supported + .logicOpEnable = createInfo.blendingOp != VK_LOGIC_OP_NO_OP, + .logicOp = createInfo.blendingOp, + + .attachmentCount = colorAttachmentCount, + .pAttachments = colorAttachmentBlendStates, + }; + memcpy(colorBlending.blendConstants, createInfo.blendConstants, sizeof(f32) * 4); + + u32 viewportCount = createInfo.viewportCountOverride; + if(viewportCount == 0 && createInfo.viewports) + viewportCount = vec_len(&createInfo.viewports); + + VkViewport viewports[viewportCount]; + VkRect2D scissors[viewportCount]; + + if(createInfo.viewports) + { + for(int i = 0; i < viewportCount; i++) + { + viewports[i] = createInfo.viewports[i].vkViewport; + + VkRect2D currScis = createInfo.viewports[i].vkScissor; + scissors[i].offset.x = max(currScis.offset.x, viewports[i].x); + scissors[i].offset.y = max(currScis.offset.y, viewports[i].y); + scissors[i].extent.width = currScis.extent.width == 0? viewports[i].width : currScis.extent.width; + scissors[i].extent.height = currScis.extent.height == 0? viewports[i].height : currScis.extent.height; + } + } + + VkPipelineViewportStateCreateInfo viewportStateCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = viewportCount, + .scissorCount = viewportCount, + .pViewports = viewports, + .pScissors = scissors, + }; + + VkPipelineShaderStageCreateInfo shaderStageCreateInfos[shaderStageCount]; + for(int i = 0; i < vec_len(&createInfo.shaderStages); i++) + shaderStageCreateInfos[i] = evkGetShaderStageCreateInfo(createInfo.shaderStages[i]); + + VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 0, + .vertexAttributeDescriptionCount = 0, + }; + + /* VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfos[DESCRIPTOR_SET_LAYOUT_COUNT] = {}; */ + + /* for(u32 di = 0; di < DESCRIPTOR_SET_LAYOUT_COUNT; di++) */ + /* { */ + /* descriptorSetLayoutCreateInfos[di].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; */ + /* for(u32 si = 0; si < vec_len(&createInfo.shaderStages); si++) */ + /* { */ + /**/ + /* } */ + /* } */ + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfos[4]; + + svec(VkDescriptorSetLayout) descriptorSetLayouts = svec_init_w_len(VkDescriptorSetLayout, 4); + + for(int i = 0; i < 4; i++) + { + VkDescriptorSetLayoutCreateInfo dsCreateInfo = {}; + + // Fill descriptorSetLayoutCreateInfos[i] from shader reflection data + Create descriptorSetLayouts[i] + } + + res.layout = evkCreatePipelineLayout(device, + EV_DEFAULT(evkPipelineLayoutCreateInfo, + descriptorSetLayouts = descriptorSetLayouts + ) + ); + + VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &pipelineRenderingCreateInfo, + .stageCount = shaderStageCount, + .pStages = shaderStageCreateInfos, + .pVertexInputState = &vertexInputStateCreateInfo, + .pInputAssemblyState = &EV_DEFAULT(VkPipelineInputAssemblyStateCreateInfo), + .pViewportState = &viewportStateCreateInfo, + .pRasterizationState = &EV_DEFAULT(VkPipelineRasterizationStateCreateInfo), + .pMultisampleState = &EV_DEFAULT(VkPipelineMultisampleStateCreateInfo), + .pColorBlendState = &colorBlending, + .pDynamicState = &dynamicStateCreateInfo, + .layout = res.layout.vk, + }; + + vkCreateGraphicsPipelines(device.vk, VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, NULL, &res.vk); + + res._device = device; + + return res; +} + +void evkDestroyPipeline(evkPipeline pipeline) +{ + vkDestroyPipeline(pipeline._device.vk, pipeline.vk, NULL); + evkDestroyPipelineLayout(pipeline._device, pipeline.layout); +} diff --git a/evk/evkPipeline.h b/evk/evkPipeline.h new file mode 100644 index 0000000..a74af80 --- /dev/null +++ b/evk/evkPipeline.h @@ -0,0 +1,10 @@ +#pragma once + +#include "evkCommon.h" + +[[nodiscard("Leaking VkPipelineLayout")]] +evkPipelineLayout evkCreatePipelineLayout(evkDevice device, evkPipelineLayoutCreateInfo createInfo); +void evkDestroyPipelineLayout(evkDevice device, evkPipelineLayout layout); + +evkPipeline evkCreatePipeline(evkDevice device, evkPipelineCreateInfo createInfo); +void evkDestroyPipeline(evkPipeline pipeline); diff --git a/evk/evkRender.c b/evk/evkRender.c new file mode 100644 index 0000000..bab4004 --- /dev/null +++ b/evk/evkRender.c @@ -0,0 +1 @@ +#include "evkRender.h" diff --git a/evk/evkRender.h b/evk/evkRender.h new file mode 100644 index 0000000..86ea35c --- /dev/null +++ b/evk/evkRender.h @@ -0,0 +1,3 @@ +#pragma once + +#include "evkCommon.h" diff --git a/evk/evkShader.c b/evk/evkShader.c new file mode 100644 index 0000000..2494480 --- /dev/null +++ b/evk/evkShader.c @@ -0,0 +1,138 @@ +#include "evk/evkShader.h" +#include "shaderc/shaderc.h" +#include "ev_helpers.h" + +evkShader evkInitShaderFromBytes(evkDevice device, const u8* shaderBytes, u32 shaderLen) +{ + evkShader shader; + VkShaderModuleCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pCode = (u32*)shaderBytes, + .codeSize = shaderLen, + }; + vkCreateShaderModule(device.vk, &createInfo, NULL, &shader.vk); + + shader.reflect = evkGenerateShaderReflectionData(shaderBytes, shaderLen); + + return shader; +} + +evkShaderReflectionData evkGenerateShaderReflectionData(const u8* shaderBytes, u32 shaderLen) +{ + evkShaderReflectionData res; + + SpvReflectShaderModule spvref; + SpvReflectResult result = spvReflectCreateShaderModule(shaderLen, shaderBytes, &spvref); + assert(result == SPV_REFLECT_RESULT_SUCCESS); + + // Fill evkShaderReflectionData + res.stage = spvref.shader_stage; + res.bindings = vec_init(evkDescriptorBinding); + + + uint32_t descSetCount = 0; + SpvReflectResult descSetResult = spvReflectEnumerateDescriptorSets(&spvref, &descSetCount, NULL); + if(descSetResult == SPV_REFLECT_RESULT_SUCCESS) + { + SpvReflectDescriptorSet* refDescriptorSets[descSetCount] = {}; + spvReflectEnumerateDescriptorSets(&spvref, &descSetCount, refDescriptorSets); + + for(int setIdx = 0; setIdx < descSetCount; setIdx++) + { + SpvReflectDescriptorSet* currentSet = refDescriptorSets[setIdx]; + for(int bindingIdx = 0; bindingIdx < currentSet->binding_count; bindingIdx++) + { + evkDescriptorBinding binding = { + .stageFlags = res.stage, + .binding = currentSet->bindings[bindingIdx]->binding, + .descriptorType = (VkDescriptorType) currentSet->bindings[bindingIdx]->descriptor_type, + .descriptorCount = currentSet->bindings[bindingIdx]->count, + .set = currentSet->set, + }; + + vec_push(&res.bindings, &binding); + } + } + } + + spvReflectDestroyShaderModule(&spvref); + return res; +} + +void evkDestroyShaderReflectionData(evkShaderReflectionData data) +{ + vec_fini(&data.bindings); +} + +void evkDestroyShader(evkDevice device, evkShader shader) +{ + vkDestroyShaderModule(device.vk, shader.vk, NULL); + evkDestroyShaderReflectionData(shader.reflect); +} + +shaderc_include_result* _shader_include_resolve(void* user_data, const char* requested_source, int type, const char* requesting_source, size_t include_depth) +{ + shaderc_include_result* result = malloc(sizeof(shaderc_include_result)); + result->source_name = evstring_new(requested_source); + result->source_name_length = evstring_getLength((evstring)result->source_name); + + result->content = evstring_readFile((evstring)result->source_name); + result->content_length = evstring_getLength((evstring)result->content); + + return result; +} + +void _shader_include_release(void* user_data, shaderc_include_result* include_result) +{ + evstring_free((evstring)include_result->content); + evstring_free((evstring)include_result->source_name); + free(include_result); +} + +evkShaderCompiler evkCreateShaderCompiler() +{ + evkShaderCompiler compiler; + compiler.sc = shaderc_compiler_initialize(); + compiler.scopt = shaderc_compile_options_initialize(); + shaderc_compile_options_set_include_callbacks(compiler.scopt, _shader_include_resolve, _shader_include_release, NULL); + return compiler; +} + +void evkDestroyShaderCompiler(evkShaderCompiler compiler) +{ + shaderc_compile_options_release(compiler.scopt); + shaderc_compiler_release(compiler.sc); +} + +evkShader evkInitShaderFromFile(evkDevice device, evkShaderCompiler compiler, evstring shaderPath) +{ + evstring shaderText = evstring_readFile(shaderPath); + + shaderc_compilation_result_t compilation_result = shaderc_compile_into_spv(compiler.sc, shaderText, evstring_getLength(shaderText), shaderc_glsl_infer_from_source, shaderPath, "main", compiler.scopt); + + shaderc_compilation_status status = shaderc_result_get_compilation_status(compilation_result); + + u32 errorCount = shaderc_result_get_num_errors(compilation_result); + u32 warnCount = shaderc_result_get_num_warnings(compilation_result); + printf("[[evkShader]] %s Compilation Status: %d ( %d Errors, %d Warnings )\n", shaderPath, status, errorCount, warnCount); + if(errorCount + warnCount > 0) + { + printf("Errors:\n%s\n", shaderc_result_get_error_message(compilation_result)); + } + + evkShader shader = evkInitShaderFromBytes(device, (u8*)shaderc_result_get_bytes(compilation_result), shaderc_result_get_length(compilation_result)); + + evstring_free(shaderText); + + return shader; +} + +VkPipelineShaderStageCreateInfo evkGetShaderStageCreateInfo(evkShader shader) +{ + return (VkPipelineShaderStageCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = shader.reflect.stage, + .module = shader.vk, + .pName = "main", + }; +} diff --git a/evk/evkShader.h b/evk/evkShader.h new file mode 100644 index 0000000..0ae0982 --- /dev/null +++ b/evk/evkShader.h @@ -0,0 +1,18 @@ +#pragma once + +#include "evk/evkCommon.h" + +[[nodiscard("Leaking VkShaderModule")]] +evkShader evkInitShaderFromBytes(evkDevice device, const u8* shaderBytes, u32 shaderLen); +evkShader evkInitShaderFromFile(evkDevice device, evkShaderCompiler compiler, evstring shaderPath); +void evkDestroyShader(evkDevice device, evkShader shader); + +[[nodiscard("Leaking Shader Compiler")]] +evkShaderCompiler evkCreateShaderCompiler(); +void evkDestroyShaderCompiler(evkShaderCompiler compiler); + +VkPipelineShaderStageCreateInfo evkGetShaderStageCreateInfo(evkShader shader); + +[[nodiscard("Leaking Shader Reflection Data")]] +evkShaderReflectionData evkGenerateShaderReflectionData(const u8* shaderBytes, u32 shaderLen); +void evkDestroyShaderReflectionData(evkShaderReflectionData data); diff --git a/evk/evkSwapChain.c b/evk/evkSwapChain.c new file mode 100644 index 0000000..0cac496 --- /dev/null +++ b/evk/evkSwapChain.c @@ -0,0 +1,93 @@ +#include "evkSwapChain.h" +#include "evk.h" + +evkSwapChain evkCreateSwapChain(evkSwapChainCreateInfo createInfo) +{ + evkSwapChain swapChain; + swapChain.surface = createInfo.surface; + + VkSurfaceCapabilitiesKHR surfaceCaps; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(createInfo.device._physicalDevice, createInfo.surface, &surfaceCaps); + + VkCompositeAlphaFlagBitsKHR compositeAlpha = + (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) + ? VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR + : (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + ? VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR + : (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + ? VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR + : VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + + u32 surfaceFormatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(createInfo.device._physicalDevice, createInfo.surface, &surfaceFormatCount, NULL); + + VkSurfaceFormatKHR surfaceFormats[surfaceFormatCount]; + vkGetPhysicalDeviceSurfaceFormatsKHR(createInfo.device._physicalDevice, createInfo.surface, &surfaceFormatCount, surfaceFormats); + + // TODO Add format picking logic + swapChain.surfaceFormat = surfaceFormats[0]; + + u32 buffering = min(max(createInfo.imageCount, surfaceCaps.minImageCount), surfaceCaps.maxImageCount); + + VkExtent2D imageExtent = surfaceCaps.currentExtent; + if(imageExtent.width == UInt32.MAX) + imageExtent.width = createInfo.width; + if(imageExtent.height == UInt32.MAX) + imageExtent.height = createInfo.width; + + VkSwapchainCreateInfoKHR swapChainCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = swapChain.surface, + .minImageCount = buffering, + .imageFormat = swapChain.surfaceFormat.format, + .imageColorSpace = swapChain.surfaceFormat.colorSpace, + .imageExtent = imageExtent, + + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .preTransform = surfaceCaps.currentTransform, + .compositeAlpha = compositeAlpha, + .presentMode = VK_PRESENT_MODE_MAILBOX_KHR, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + vkCreateSwapchainKHR(createInfo.device.vk, &swapChainCreateInfo, NULL, &swapChain.vk); + + VkImage swapChainVkImages[buffering]; + vkGetSwapchainImagesKHR(createInfo.device.vk, swapChain.vk, &buffering, swapChainVkImages); + + swapChain.images = vec_init(evkImage); + swapChain.imageViews = vec_init(evkImageView); + vec_setlen(&swapChain.images, buffering); + vec_setlen(&swapChain.imageViews, buffering); + for(u32 i = 0; i < buffering; i++) + { + swapChain.images[i] = (evkImage) { + .vk = swapChainVkImages[i], + .width = imageExtent.width, + .height = imageExtent.height, + .format = swapChain.surfaceFormat.format + }; + swapChain.imageViews[i] = evkCreateImageView(createInfo.device, swapChain.images[i], + EV_DEFAULT(evkImageViewCreateInfo, + viewAspect = VK_IMAGE_ASPECT_COLOR_BIT + ) + ); + } + + return swapChain; +} + +void evkDestroySwapChain(evkDevice device, evkSwapChain swapChain) +{ + for(i32 i = 0; i < vec_len(&swapChain.imageViews); i++) + { + vkDestroyImageView(device.vk, swapChain.imageViews[i].vk, NULL); + } + + vec_fini(&swapChain.imageViews); + vec_fini(&swapChain.images); + vkDestroySwapchainKHR(device.vk, swapChain.vk, NULL); +} diff --git a/evk/evkSwapChain.h b/evk/evkSwapChain.h new file mode 100644 index 0000000..22b6a16 --- /dev/null +++ b/evk/evkSwapChain.h @@ -0,0 +1,8 @@ +#pragma once + +#include "evkCommon.h" + +[[nodiscard("Leaking VkSwapChain")]] +evkSwapChain evkCreateSwapChain(evkSwapChainCreateInfo createInfo); + +void evkDestroySwapChain(evkDevice device, evkSwapChain swapChain); diff --git a/evk/evkSync.c b/evk/evkSync.c new file mode 100644 index 0000000..6cf2ff9 --- /dev/null +++ b/evk/evkSync.c @@ -0,0 +1,20 @@ +#include "evk/evkSync.h" + +VkSemaphore evkCreateSemaphore(evkDevice device) +{ + VkSemaphore semaphore; + VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + vkCreateSemaphore(device.vk, &semaphoreCreateInfo, NULL, &semaphore); + return semaphore; +} + +VkFence evkCreateFence(evkDevice device, bool signaled) +{ + VkFence fence; + VkFenceCreateInfo fenceCreateInfo = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = signaled?VK_FENCE_CREATE_SIGNALED_BIT:0, + }; + vkCreateFence(device.vk, &fenceCreateInfo, NULL, &fence); + return fence; +} diff --git a/evk/evkSync.h b/evk/evkSync.h new file mode 100644 index 0000000..7dacb0e --- /dev/null +++ b/evk/evkSync.h @@ -0,0 +1,10 @@ +#pragma once + +#include "evk.h" +#include "evkDevice.h" + +[[nodiscard("Leaking VkSemaphore")]] +VkSemaphore evkCreateSemaphore(evkDevice device); + +[[nodiscard("Leaking VkFence")]] +VkFence evkCreateFence(evkDevice device, bool signaled); diff --git a/evk/evkTypes.h b/evk/evkTypes.h new file mode 100644 index 0000000..c46cad8 --- /dev/null +++ b/evk/evkTypes.h @@ -0,0 +1,448 @@ +#pragma once + +#include "evkCommon.h" +#include "shaderc/shaderc.h" +#include "spirv_reflect.h" + +TYPEDATA_GEN(VkInstance, INVALID(VK_NULL_HANDLE)); +TYPEDATA_GEN(VkDevice, INVALID(VK_NULL_HANDLE)); +TYPEDATA_GEN(VkPhysicalDevice); +TYPEDATA_GEN(VkDeviceQueueCreateInfo); +TYPEDATA_GEN(VkQueueFamilyProperties); +TYPEDATA_GEN(VkImage); +TYPEDATA_GEN(VkCommandBufferBeginInfo); +TYPEDATA_GEN(VkCommandBuffer); +TYPEDATA_GEN(VkDynamicState); +TYPEDATA_GEN(VkSurfaceFormatKHR); +TYPEDATA_GEN(VkFormat); +TYPEDATA_GEN(VkClearValue, DEFAULT(0.f,0.f,0.f,1.f)); +TYPEDATA_GEN(VkRenderingAttachmentInfoKHR, + DEFAULT( + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, + .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, // Requires VK_KHR_synchronization2 + .loadOp = VK_ATTACHMENT_LOAD_OP_NONE_KHR, + .storeOp = VK_ATTACHMENT_STORE_OP_NONE, + .clearValue = (VkClearValue){0.f, 0.f, 0.f, 1.f}, + ) +); +TYPEDATA_GEN(VkViewport, + DEFAULT( + .x = 0, + .y = 0, + .maxDepth = 1.0f, + .minDepth = 0.f, + .width = 0, + .height = 0 + ) +) + +TYPEDATA_GEN(VkRect2D, + DEFAULT(0) +) + +TYPEDATA_GEN(VkRenderingInfo, + DEFAULT( + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .flags = 0, + .renderArea = {0, 0, 0, 0}, + .layerCount = 1, + .viewMask = 0, + .colorAttachmentCount = 0, + .pColorAttachments = VK_NULL_HANDLE, + .pDepthAttachment = VK_NULL_HANDLE, + .pStencilAttachment = VK_NULL_HANDLE, + ) +) + +TYPEDATA_GEN(VkPipelineInputAssemblyStateCreateInfo, + DEFAULT( + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + ) +); + +TYPEDATA_GEN(VkPipelineRasterizationStateCreateInfo, + DEFAULT( + .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, + .depthBiasEnable = VK_FALSE, + ) +); + +TYPEDATA_GEN(VkPipelineColorBlendAttachmentState, + DEFAULT( + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + .blendEnable = VK_FALSE, + ) +); + +TYPEDATA_GEN(VkPipelineMultisampleStateCreateInfo, + DEFAULT( + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .sampleShadingEnable = VK_FALSE, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + ) +); + +TYPEDATA_GEN(VkPipelineShaderStageCreateInfo); + +TYPEDATA_GEN(VkDescriptorSetLayout); +TYPEDATA_GEN(VkDescriptorSetLayoutBinding); +TYPEDATA_GEN(VkDescriptorSetLayoutCreateInfo, + DEFAULT( + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + ) +); + +// ========================================================================================================= // +// =================================evk Types=============================================================== // +// ========================================================================================================= // + +typedef u32 evkApiVersion; + +typedef struct { + VkInstance vk; + evkApiVersion apiVersion; +} evkInstance; + +typedef struct { + VkQueueFlags flags; + u32 count; +} evkDeviceQueueRequirement; + +typedef struct { + evkInstance instance; + VkPhysicalDeviceType physicalDeviceType; + ev_vec(evkDeviceQueueRequirement) queueRequirements; + ev_vec(evstring) deviceExtensions; +} evkDeviceCreateInfo; + +typedef struct { + i32 familyIndex; + u32 allocatedQueueCount; +} evkDeviceQueueFamily; + +typedef struct { + bool dynamicRendering; + bool multiViewport; +} evkPhysicalDeviceFeatures; + +#define MAX_QUEUE_FAMILIES ((VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_SPARSE_BINDING_BIT) + 1) +typedef struct { + VkDevice vk; + VkPhysicalDevice _physicalDevice; + evkInstance _instance; + evkDeviceQueueFamily queueFamilies[MAX_QUEUE_FAMILIES]; + VkPhysicalDeviceLimits limits; + evkPhysicalDeviceFeatures enabledFeatures; +} evkDevice; + +typedef struct { + VkCommandPool vk; + evkDeviceQueueFamily queueFamily; +} evkCommandPool; + +typedef struct { + evkDevice device; + VkCommandPoolCreateFlags poolFlags; + VkQueueFlags queueFlags; +} evkCommandPoolCreateInfo; + +typedef u32 evkAPIVersion; + +typedef struct { + evkAPIVersion apiVersion; + u32 applicationVersion; + u32 engineVersion; + evstring applicationName; + evstring engineName; +} evkApplicationInfo; + +typedef struct { + evkApplicationInfo applicationInfo; + ev_vec(evstring) extensions; + ev_vec(evstring) layers; +} evkInstanceCreateInfo; + +typedef struct { + VkPipelineLayout vk; +} evkPipelineLayout; + +typedef struct { + vec(VkDescriptorSetLayout) descriptorSetLayouts; + vec(VkPushConstantRange) pushConstantRanges; +} evkPipelineLayoutCreateInfo; + +typedef struct { + VkOffset2D renderOffset; + VkExtent2D renderExtents; + u32 layerCount; + u32 viewMask; + VkRenderingFlags renderingFlags; + + vec(VkRenderingAttachmentInfoKHR) colorAttachments; + VkRenderingAttachmentInfoKHR depthAttachment; + VkRenderingAttachmentInfoKHR stencilAttachment; +} evkRenderingInfo; + +typedef union { + struct { + VkViewport; + VkRect2D scissor; + }; + struct { + VkViewport vkViewport; + VkRect2D vkScissor; + }; +} evkViewport; + +typedef union { + VkDescriptorSetLayoutBinding vk; + struct { + VkDescriptorSetLayoutBinding; + u32 set; + }; +} evkDescriptorBinding; + +typedef struct { + VkShaderStageFlags stage; + vec(evkDescriptorBinding) bindings; +} evkShaderReflectionData; + +typedef struct { + VkShaderModule vk; + evkShaderReflectionData reflect; +} evkShader; + +typedef struct { + shaderc_compiler_t sc; + shaderc_compile_options_t scopt; +} evkShaderCompiler; + +typedef struct { + u32 dummy; +} evkAllocationUserData; + +typedef struct { + VmaAllocator vma; +} evkGPUAllocator; + +typedef struct { + VmaAllocation vma; +} evkGPUAllocation; + +typedef struct { + VmaAllocationInfo vma; +} evkGPUAllocationInfo; + +typedef struct { + evkGPUAllocator allocator; + evkGPUAllocation allocation; + evkGPUAllocationInfo allocationInfo; +} evkGPUAllocationData; + +typedef enum { + EVK_GPU_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, + EVK_GPU_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT, + EVK_GPU_ALLOCATION_CREATE_MAPPED_BIT = VMA_ALLOCATION_CREATE_MAPPED_BIT, + EVK_GPU_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT, + EVK_GPU_ALLOCATION_CREATE_DONT_BIND_BIT = VMA_ALLOCATION_CREATE_DONT_BIND_BIT, + EVK_GPU_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, + EVK_GPU_ALLOCATION_CREATE_CAN_ALIAS_BIT = VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT, + EVK_GPU_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + EVK_GPU_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + EVK_GPU_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT, + EVK_GPU_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, +} evkGPUAllocationFlags; + +typedef enum evkGPUMemoryUsage +{ + EVK_GPU_MEMORY_USAGE_UNKNOWN = VMA_MEMORY_USAGE_UNKNOWN, + EVK_GPU_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED, + EVK_GPU_MEMORY_USAGE_AUTO = VMA_MEMORY_USAGE_AUTO, + EVK_GPU_MEMORY_USAGE_AUTO_PREFER_DEVICE = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, + EVK_GPU_MEMORY_USAGE_AUTO_PREFER_HOST = VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + EVK_GPU_MEMORY_USAGE_MAX_ENUM = VMA_MEMORY_USAGE_MAX_ENUM +} evkGPUMemoryUsage; + +typedef struct { + VmaPool vma; +} evkGPUMemoryPool; + +typedef struct { + evkGPUAllocationFlags allocationFlags; + evkGPUMemoryUsage memoryUsage; + evkGPUAllocator allocator; + evkGPUMemoryPool pool; +} evkGPUAllocationCreateInfo; + +typedef struct { + VkImage vk; + u32 width; + u32 height; + VkFormat format; + evkGPUAllocationData allocData; +} evkImage; + +typedef struct { + VkImageAspectFlags viewAspect; + u32 mipBase; + u32 mipCount; + u32 baseLayer; + u32 layerCount; +} evkImageViewCreateInfo; + +typedef struct { + VkImageView vk; +} evkImageView; + +typedef struct { + evkGPUAllocationCreateInfo allocationCreateInfo; + VkBufferCreateFlags flags; + VkDeviceSize sizeInBytes; + VkBufferUsageFlags usage; + bool exclusive; + vec(u32) queueFamilyIndices; +} evkBufferCreateInfo; + +typedef struct { + VkBuffer vk; + evkGPUAllocationData allocData; + u32 sizeInBytes; +} evkBuffer; + +typedef struct { + evkGPUAllocationCreateInfo allocationCreateInfo; + VkImageCreateFlags flags; + VkImageType type; + VkFormat format; + VkExtent3D extent; + u32 mipCount; + u32 layerCount; + u32 sampleCount; + VkImageTiling tiling; + VkImageUsageFlags usage; + bool exclusive; + vec(u32) queueFamilyIndices; + VkImageLayout layout; +} evkImageCreateInfo; + +typedef struct { + VkSurfaceFormatKHR surfaceFormat; + VkSurfaceKHR surface; + VkSwapchainKHR vk; + vec(evkImage) images; + vec(evkImageView) imageViews; +} evkSwapChain; + +typedef struct { + evkDevice device; + VkSurfaceKHR surface; + u32 width; + u32 height; + u32 imageCount; + // Add more stuff when needed +} evkSwapChainCreateInfo; + +typedef struct { + VkFormat format; + VkPipelineColorBlendAttachmentState blendState; +} evkColorAttachment; + +typedef struct { + vec(VkDynamicState) dynamicStates; + vec(evkShader) shaderStages; + + vec(evkColorAttachment) colorAttachments; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; + u32 viewMask; + + VkLogicOp blendingOp; + f32 blendConstants[4]; + + u32 viewportCountOverride; + vec(evkViewport) viewports; +} evkPipelineCreateInfo; + +typedef struct { + VkPipeline vk; + evkPipelineLayout layout; + evkDevice _device; +} evkPipeline; + +TYPEDATA_GEN(evkInstance, + INVALID( + .vk = VK_NULL_HANDLE, + ) +); + +TYPEDATA_GEN(evkShader); + +TYPEDATA_GEN(evkPipelineLayoutCreateInfo, + DEFAULT( + .descriptorSetLayouts = EV_VEC_EMPTY, + .pushConstantRanges = EV_VEC_EMPTY, + ) +); + +TYPEDATA_GEN(evkApplicationInfo, + DEFAULT( + .apiVersion = VK_API_VERSION_1_1, + .applicationVersion = 0, + .engineVersion = 0, + .applicationName = evstr("Test Application"), + .engineName = evstr("evk") + ) +); + +TYPEDATA_GEN(evkImage); +TYPEDATA_GEN(evkImageView); +TYPEDATA_GEN(evkImageViewCreateInfo, + DEFAULT( + .layerCount = 1, + .baseLayer = 0, + .mipCount = 1, + .mipBase = 0, + .viewAspect = VK_IMAGE_ASPECT_NONE, + ) +); +TYPEDATA_GEN(evkAllocationUserData, INVALID(__EV_VEC_EMPTY_ARRAY)); +TYPEDATA_GEN(evkDeviceQueueRequirement); + +TYPEDATA_GEN(evkViewport, + DEFAULT( + .x = 0, + .y = 0, + .maxDepth = 1.0f, + .minDepth = 0.f, + .width = 0, + .height = 0, + .scissor.offset.x = 0, + .scissor.offset.y = 0, + .scissor.extent.width = 0, + .scissor.extent.height = 0, + ) +); + +TYPEDATA_GEN(evkColorAttachment); + +TYPEDATA_GEN(evkPipelineCreateInfo, + DEFAULT( + .dynamicStates = EV_VEC_EMPTY, + .shaderStages = EV_VEC_EMPTY, + + .colorAttachments = EV_VEC_EMPTY, + .viewMask = 0, + + .blendingOp = VK_LOGIC_OP_NO_OP + ) +); + +TYPEDATA_GEN(evkDescriptorBinding); diff --git a/main.c b/main.c new file mode 100644 index 0000000..a0b646e --- /dev/null +++ b/main.c @@ -0,0 +1,249 @@ +#include +#include +#include + +#define GLFW_INCLUDE_NONE +#include + +evstring PROJECT_NAME = evstr("evk"); + +float vertexPositions[] = { + 0.f,-.5f, + .5f, .5f, + -.5f, .5f +}; + +int main(void) +{ + u32 width = 1024; + u32 height = 1024; + + evkInstance instance = evkCreateInstance((evkInstanceCreateInfo){ + .applicationInfo = EV_DEFAULT(evkApplicationInfo), + .layers = svec_init(evstring, { + evstr("VK_LAYER_KHRONOS_validation"), + }), + .extensions = svec_init(evstring, { + evstr("VK_KHR_surface"), + evstr("VK_KHR_win32_surface"), + }), + }); + + if(instance.vk == EV_INVALID(VkInstance)) + { + puts("Instance creation failed."); + goto InstanceCreationFailed; + } + puts("Instance was created successfully."); + + evkDevice device = evkCreateDevice((evkDeviceCreateInfo) { + .instance = instance, + .physicalDeviceType = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, + .queueRequirements = svec_init(evkDeviceQueueRequirement, { + { VK_QUEUE_GRAPHICS_BIT, 1 }, + { VK_QUEUE_COMPUTE_BIT , 1 }, + }), + .deviceExtensions = svec_init(evstring, { + evstr("VK_KHR_swapchain"), + evstr("VK_KHR_dynamic_rendering"), + evstr("VK_KHR_depth_stencil_resolve"), + evstr("VK_KHR_create_renderpass2"), + evstr("VK_KHR_synchronization2"), + evstr("VK_KHR_buffer_device_address"), + evstr("VK_EXT_descriptor_indexing"), + }), + }); + + if(device.vk == EV_INVALID(VkDevice)) + { + puts("Couldn't create a VkDevice"); + goto DeviceCreationFailed; + } + puts("Logical Device created successfully."); + VkQueue graphicsQueue; + vkGetDeviceQueue(device.vk, device.queueFamilies[VK_QUEUE_GRAPHICS_BIT].familyIndex, 0, &graphicsQueue); + + if (!glfwInit()) + { + puts("GLFW Initialization failed."); + goto GLFWInitFailed; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + GLFWwindow* window = glfwCreateWindow(1024,1024, "evk", NULL, NULL); + if(!window) + { + puts("Window Creation Failed."); + goto WindowCreationFailed; + } + + VkSurfaceKHR surface; + VkResult err = glfwCreateWindowSurface(instance.vk, window, NULL, &surface); + if (err) + { + goto VKSurfaceCreationFailed; + puts("Surface creation failed."); + } + + evkSwapChain swapChain = evkCreateSwapChain((evkSwapChainCreateInfo){ + .device = device, + .surface = surface, + .width = width, + .height = height, + .imageCount = 3, + }); + + evkCommandPool commandPool = evkCreateCommandPool((evkCommandPoolCreateInfo) { + .device = device, + .queueFlags = VK_QUEUE_GRAPHICS_BIT, + .poolFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT + }); + + vec(VkCommandBuffer) commandBuffers = evkAllocateCommandBuffers(device, commandPool, vec_len(&swapChain.images), true); + + evkShaderCompiler compiler = evkCreateShaderCompiler(); + + evkShader vertShader = evkInitShaderFromFile(device, compiler, "shaders/tri.vert"); + evkShader fragShader = evkInitShaderFromFile(device, compiler, "shaders/tri.frag"); + + evkDestroyShaderCompiler(compiler); + + evkColorAttachment colorAttachment0 = { + swapChain.surfaceFormat.format, + EV_DEFAULT(VkPipelineColorBlendAttachmentState) + }; + + evkPipelineCreateInfo pipelineCreateInfo = EV_DEFAULT(evkPipelineCreateInfo, + dynamicStates = svec_init(VkDynamicState, { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }), + shaderStages = svec_init(evkShader, { + vertShader, + fragShader, + }), + colorAttachments = svec_init(evkColorAttachment, { + colorAttachment0, + }), + viewportCountOverride = 1, + ); + + VkViewport viewport = EV_DEFAULT(VkViewport, width=width, height=height); + VkRect2D scissor = EV_DEFAULT(VkRect2D,extent.width=width, extent.height=height); + + evkPipeline graphicsPipeline = evkCreatePipeline(device, pipelineCreateInfo); + + VkFence drawFence = evkCreateFence(device, false); + VkSemaphore imageAcquiredSemaphore = evkCreateSemaphore(device); + VkSemaphore drawFinishedSemaphore = evkCreateSemaphore(device); + + while(!glfwWindowShouldClose(window)) + { + u32 imageIdx; + vkAcquireNextImageKHR(device.vk, swapChain.vk, UInt64.MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &imageIdx); + + VkCommandBuffer cmdbuf = commandBuffers[imageIdx]; + + VkRenderingAttachmentInfoKHR colorAttachment = EV_DEFAULT(VkRenderingAttachmentInfoKHR, + imageView = swapChain.imageViews[imageIdx].vk, + loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + storeOp= VK_ATTACHMENT_STORE_OP_STORE, + ); + + VkRenderingInfo renderingInfo = EV_DEFAULT(VkRenderingInfo, + renderArea.extent = ((VkExtent2D){width, height}), + pColorAttachments = &colorAttachment, + colorAttachmentCount = 1, + ); + + evkBeginPrimaryCommandBuffer(cmdbuf); + + vkCmdSetScissor(cmdbuf, 0, 1, &scissor); + vkCmdSetViewport(cmdbuf, 0, 1, &viewport); + + vkCmdBeginRenderingKHR(cmdbuf, &renderingInfo); + + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline.vk); + vkCmdDraw(cmdbuf, 3, 1, 0, 0); + + vkCmdEndRenderingKHR(cmdbuf); + + VkImageMemoryBarrier imageMemoryBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .image = swapChain.images[imageIdx].vk, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1, + .levelCount = 1, + }, + }; + + vkCmdPipelineBarrier(cmdbuf, 0, 0, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier); + + evkEndCommandBuffer(cmdbuf); + + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + + VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pCommandBuffers = &cmdbuf, + .commandBufferCount = 1, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &imageAcquiredSemaphore, + .pWaitDstStageMask = waitStages, + .signalSemaphoreCount = 1, + .pSignalSemaphores = &drawFinishedSemaphore, + }; + + vkQueueSubmit(graphicsQueue, 1, &submitInfo, drawFence); + vkWaitForFences(device.vk, 1, &drawFence, VK_TRUE, UInt64.MAX); + vkResetFences(device.vk, 1, &drawFence); + + VkPresentInfoKHR presentInfo = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &drawFinishedSemaphore, + .swapchainCount = 1, + .pSwapchains = &swapChain.vk, + .pImageIndices = &imageIdx, + }; + vkQueuePresentKHR(graphicsQueue, &presentInfo); + + vkResetCommandBuffer(cmdbuf,VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + + // Main Loop + glfwPollEvents(); + } + + vkQueueWaitIdle(graphicsQueue); + + vkDestroyFence(device.vk, drawFence, NULL); + vkDestroySemaphore(device.vk, imageAcquiredSemaphore, NULL); + vkDestroySemaphore(device.vk, drawFinishedSemaphore, NULL); + + evkDestroyPipeline(graphicsPipeline); + + evkDestroyShader(device, vertShader); + evkDestroyShader(device, fragShader); + + vkFreeCommandBuffers(device.vk, commandPool.vk, vec_len(&commandBuffers), commandBuffers); + evkDestroyCommandPool(device, commandPool); + + evkDestroySwapChain(device, swapChain); + +SwapchainCreationFailed: + vkDestroySurfaceKHR(instance.vk, swapChain.surface, NULL); + +VKSurfaceCreationFailed: +WindowCreationFailed: + glfwTerminate(); + +GLFWInitFailed: + evkDestroyDevice(device); + +DeviceCreationFailed: + evkDestroyInstance(instance); +InstanceCreationFailed: + return 0; +} diff --git a/meson-native-clang b/meson-native-clang new file mode 100644 index 0000000..2bd8f74 --- /dev/null +++ b/meson-native-clang @@ -0,0 +1,8 @@ +[binaries] +c = 'clang.exe' +c_ld = 'lld' +cpp = 'clang++.exe' +cpp_ld = 'lld' + +[properties] +c_args = ['-DEV_CC_CLANG=1'] diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..d8afbf2 --- /dev/null +++ b/meson.build @@ -0,0 +1,69 @@ +project('evk', ['c','cpp'], + version : '0.1', + default_options : ['c_std=gnu23']) + +build_config = configuration_data() + +buildtype = get_option('buildtype') +if buildtype == 'debug' + build_config.set('EV_BUILDTYPE_DEBUG', 1) +elif buildtype == 'debugoptimized' + build_config.set('EV_BUILDTYPE_DEBUGOPT', 1) +else + build_config.set('EV_BUILDTYPE_RELEASE', 1) +endif + +configure_file(output: 'evk_buildconfig.h', configuration: build_config) + +subproject('evol-headers') + +evh_c_args = [] + +cc = meson.get_compiler('c') +if cc.get_id() == 'msvc' + evh_c_args += '/Zc:preprocessor' +elif cc.get_id() == 'clang' + evh_c_args += '-DEV_CC_CLANG=1' +endif + +evk_incdir = [ + '.', +] + +evk_src = [ + 'main.c', + + 'evk/evkInstance.c', + 'evk/evkDevice.c', + 'evk/evkAllocator.c', + 'evk/evkSync.c', + 'evk/evkShader.c', + 'evk/evkSwapChain.c', + 'evk/evkAllocator.c', + 'evk/evkCommand.c', + 'evk/evkPipeline.c', + 'evk/evkDescriptor.c', + 'evk/evkRender.c', + 'evk/evkImage.c', + 'evk/evkMemory.c', +] + +executable( + 'evk', + evk_src, + include_directories: include_directories(evk_incdir), + dependencies: [ + dependency('ev_vec'), + dependency('ev_str'), + dependency('ev_helpers'), + + dependency('vma'), + + dependency('volk'), + dependency('glfw3'), + + dependency('shaderc'), + dependency('spvref'), + ], + c_args: evh_c_args, +) diff --git a/shaders/tri.frag b/shaders/tri.frag new file mode 100644 index 0000000..4407c21 --- /dev/null +++ b/shaders/tri.frag @@ -0,0 +1,9 @@ +#version 450 + +#pragma shader_stage(fragment) + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(0.f, 1.f, 0.f, 1.f); +} diff --git a/shaders/tri.vert b/shaders/tri.vert new file mode 100644 index 0000000..331b5a1 --- /dev/null +++ b/shaders/tri.vert @@ -0,0 +1,15 @@ +#version 450 +#pragma shader_stage(vertex) + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +// in layout(location=0) vec2 position; + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + // gl_Position = vec4(position, 0.0, 1.0); +} diff --git a/subprojects/evol-headers.wrap b/subprojects/evol-headers.wrap new file mode 100644 index 0000000..86aa6fe --- /dev/null +++ b/subprojects/evol-headers.wrap @@ -0,0 +1,7 @@ +[wrap-git] +directory = evol-headers +url = https://github.com/evol3d/evol-headers +revision = master + +[provide] +dependency_names = ev_str, ev_vec, evol-headers diff --git a/subprojects/glfw.wrap b/subprojects/glfw.wrap new file mode 100644 index 0000000..c5a9751 --- /dev/null +++ b/subprojects/glfw.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = glfw-3.3.10 +source_url = https://github.com/glfw/glfw/archive/refs/tags/3.3.10.tar.gz +source_filename = glfw-3.3.10.tar.gz +source_hash = 4ff18a3377da465386374d8127e7b7349b685288cb8e17122f7e1179f73769d5 +patch_filename = glfw_3.3.10-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/glfw_3.3.10-1/get_patch +patch_hash = 3567f96c2576a5fc8c9cafd9059f919d7da404f6c22450c6c2ce3f9938909b8b +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/glfw_3.3.10-1/glfw-3.3.10.tar.gz +wrapdb_version = 3.3.10-1 + +[provide] +glfw3 = glfw_dep diff --git a/subprojects/packagefiles/libshaderc/meson.build b/subprojects/packagefiles/libshaderc/meson.build new file mode 100644 index 0000000..b71f4df --- /dev/null +++ b/subprojects/packagefiles/libshaderc/meson.build @@ -0,0 +1,13 @@ +project('libshaderc', 'cpp', default_options: ['buildtype=release']) + +fs = import('fs') +cxxc = meson.get_compiler('cpp') + +shaderc_inc = include_directories('install/include') + +shaderc_dep = declare_dependency( + dependencies: cxxc.find_library('shaderc_combined', dirs: meson.current_source_dir()/'install/lib'), + include_directories: shaderc_inc, +) + +meson.override_dependency('shaderc', shaderc_dep) diff --git a/subprojects/packagefiles/spvref/meson.build b/subprojects/packagefiles/spvref/meson.build new file mode 100644 index 0000000..d090797 --- /dev/null +++ b/subprojects/packagefiles/spvref/meson.build @@ -0,0 +1,22 @@ +project('spvref', 'c') + +vulkan_dep = dependency('vulkan').partial_dependency(compile_args : true) + +spvref_inc = include_directories('.') + +spvref_src = files('spirv_reflect.c') + +spvref_lib = static_library( + 'spvref', + spvref_src, + include_directories: spvref_inc, + dependencies: vulkan_dep +) + +spvref_dep = declare_dependency( + link_with: spvref_lib, + include_directories: spvref_inc, + dependencies: vulkan_dep +) + +meson.override_dependency('spvref', spvref_dep) diff --git a/subprojects/packagefiles/vma/meson.build b/subprojects/packagefiles/vma/meson.build new file mode 100644 index 0000000..f0ceb45 --- /dev/null +++ b/subprojects/packagefiles/vma/meson.build @@ -0,0 +1,22 @@ +project('vma', ['c','cpp']) + +vulkan_dep = dependency('vulkan').partial_dependency(compile_args : true) + +vma_inc = include_directories('include') + +vma_src = files('vk_mem_alloc.cpp') + +vma_lib = static_library( + 'vma', + vma_src, + include_directories: vma_inc, + dependencies: vulkan_dep +) + +vma_dep = declare_dependency( + link_with: vma_lib, + include_directories: vma_inc, + dependencies: vulkan_dep +) + +meson.override_dependency('vma', vma_dep) diff --git a/subprojects/packagefiles/vma/vk_mem_alloc.cpp b/subprojects/packagefiles/vma/vk_mem_alloc.cpp new file mode 100644 index 0000000..9c32887 --- /dev/null +++ b/subprojects/packagefiles/vma/vk_mem_alloc.cpp @@ -0,0 +1,4 @@ +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 +#define VMA_IMPLEMENTATION +#include diff --git a/subprojects/packagefiles/volk/meson.build b/subprojects/packagefiles/volk/meson.build new file mode 100644 index 0000000..d6e6745 --- /dev/null +++ b/subprojects/packagefiles/volk/meson.build @@ -0,0 +1,26 @@ +project('volk', 'c', + default_options: [ + 'default_library=static', + ], +) + +vulkan_dep = dependency('vulkan').partial_dependency(compile_args : true) + +volk_inc = include_directories('.') + +volk_src = files('volk_impl.c') + +volk_lib = library( + 'volk', + volk_src, + include_directories: volk_inc, + dependencies: vulkan_dep +) + +volk_dep = declare_dependency( + link_with: volk_lib, + include_directories: volk_inc, + dependencies: vulkan_dep +) + +meson.override_dependency('volk', volk_dep) diff --git a/subprojects/packagefiles/volk/volk_impl.c b/subprojects/packagefiles/volk/volk_impl.c new file mode 100644 index 0000000..2a7e037 --- /dev/null +++ b/subprojects/packagefiles/volk/volk_impl.c @@ -0,0 +1,2 @@ +#define VOLK_IMPLEMENTATION +#include "volk.h" diff --git a/subprojects/shaderc.wrap b/subprojects/shaderc.wrap new file mode 100644 index 0000000..e17f54b --- /dev/null +++ b/subprojects/shaderc.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = libshaderc + +source_url = https://storage.googleapis.com/shaderc/artifacts/prod/graphics_shader_compiler/shaderc/windows/continuous_release_2019/64/20241106-090939/install.zip +source_filename = libshaderc-upstream-msvc.zip +source_hash = a6879869e580d5991ebd9909f3665d1f6fe39cdb9830adf4f48b35dfd3782a78 +lead_directory_missing = libshaderc + +patch_directory = libshaderc + +[provide] +dependency_names = shaderc diff --git a/subprojects/spvref.wrap b/subprojects/spvref.wrap new file mode 100644 index 0000000..4ce74ca --- /dev/null +++ b/subprojects/spvref.wrap @@ -0,0 +1,7 @@ +[wrap-git] +directory = spvref +url = https://github.com/KhronosGroup/SPIRV-Reflect +revision = main +depth = 1 + +patch_directory = spvref diff --git a/subprojects/vma.wrap b/subprojects/vma.wrap new file mode 100644 index 0000000..86462a4 --- /dev/null +++ b/subprojects/vma.wrap @@ -0,0 +1,7 @@ +[wrap-git] +directory = vma +url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator +revision = master +depth = 1 + +patch_directory = vma diff --git a/subprojects/volk.wrap b/subprojects/volk.wrap new file mode 100644 index 0000000..778f55f --- /dev/null +++ b/subprojects/volk.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory = volk +url = https://github.com/zeux/volk +revision = master + +patch_directory = volk + +[provide] +dependency_names = volk