From a6486714ff8c8805d064ed57038094e8a5e62e94 Mon Sep 17 00:00:00 2001 From: miri Date: Thu, 5 Sep 2024 18:48:18 +0200 Subject: [PATCH 01/22] initial commit --- .gitignore | 3 + include/svulc.h | 28 +++++++++ include/swapchain.h | 30 ++++++++++ src/instance.c | 131 +++++++++++++++++++++++++++++++++++++++++ src/swapchain.c | 104 ++++++++++++++++++++++++++++++++ test/device-creation.c | 120 +++++++++++++++++++++++++++++++++++++ 6 files changed, 416 insertions(+) create mode 100644 .gitignore create mode 100644 include/svulc.h create mode 100644 include/swapchain.h create mode 100644 src/instance.c create mode 100644 src/swapchain.c create mode 100644 test/device-creation.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8483e07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +#output files +a.out +main \ No newline at end of file diff --git a/include/svulc.h b/include/svulc.h new file mode 100644 index 0000000..8a61ea4 --- /dev/null +++ b/include/svulc.h @@ -0,0 +1,28 @@ +#ifndef __SVULC_H__ +#define __SVULC_H__ + +#include + +VkResult svlk_createIinstance(const char **layers, int layersNum, const char **extensions, int extensionsNum, VkInstance *instance); + +VkResult svlk_getPhysDeviceList(VkInstance instance, unsigned int *num, VkPhysicalDevice **devicesPointer); + +int svlk_getQueueFamilyIndex(VkPhysicalDevice physDevice, VkQueueFlags queueFlag, int *error); + +int svlk_getPresentQueueFamilyIndex(VkPhysicalDevice physDevice, VkSurfaceKHR KHRsurface, int *error); + +int svlk_deviceCompatable(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int queueFamilies, const char **deviceExtensions, int deviceExtensionsNum); + +VkDeviceQueueCreateInfo svlk_createQueueInfo(int queueIndex, int count, float *priority); + +VkResult svlk_createLogicalDevice( + VkPhysicalDevice physDevice, + VkSurfaceKHR surface, + VkDeviceQueueCreateInfo *queueFamilies, + unsigned int queueFamiliesNum, + VkPhysicalDeviceFeatures deviceFeatures, + const char **deviceExtensions, + int deviceExtensionsNum, + VkDevice *device); + +#endif \ No newline at end of file diff --git a/include/swapchain.h b/include/swapchain.h new file mode 100644 index 0000000..0022c8c --- /dev/null +++ b/include/swapchain.h @@ -0,0 +1,30 @@ +#ifndef __SWAPCHAIN_H__ +#define __SWAPCHAIN_H__ +#include + +VkSurfaceFormatKHR *svlk_getSurfaceFormats(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkPresentModeKHR *svlk_getPresentModes(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfaceFormats, VkFormat format, VkColorSpaceKHR colorSpace, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace); + +VkExtent2D svlk_checkSwapchainExtent(const VkSurfaceCapabilitiesKHR *capabilities, int w, int h); + +VkSwapchainCreateInfoKHR svlk_createSwapchainCreateinfo( + unsigned int imageCount, + VkSurfaceKHR surface, + VkSurfaceFormatKHR format, + VkPresentModeKHR presentMode, + VkExtent2D extent, + int arrayLayers, + unsigned int usage, + VkSurfaceTransformFlagBitsKHR pretransform + ); + +void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent, unsigned int *queueFamilies, int queueFamiliesNum); + +VkResult svlk_createSwapchain(); + +#endif \ No newline at end of file diff --git a/src/instance.c b/src/instance.c new file mode 100644 index 0000000..a184880 --- /dev/null +++ b/src/instance.c @@ -0,0 +1,131 @@ +#include "../include/svulc.h" +#include +#include +#include + +VkResult svlk_createIinstance(const char **layers, int layersNum, const char **extensions, int extensionsNum, VkInstance *instance){ + VkInstanceCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.pApplicationInfo = NULL; + createInfo.enabledLayerCount = layersNum; + createInfo.ppEnabledLayerNames = layers; + createInfo.enabledExtensionCount = extensionsNum; + createInfo.ppEnabledExtensionNames = extensions; + + return vkCreateInstance(&createInfo, NULL, instance); +} + +VkResult svlk_getPhysDeviceList(VkInstance instace, unsigned int *num, VkPhysicalDevice **devicesPointer){ + VkResult result = vkEnumeratePhysicalDevices(instace, num, NULL); + if(result != VK_SUCCESS){ + return result; + } + *devicesPointer = malloc(*num * sizeof(VkPhysicalDevice)); + result = vkEnumeratePhysicalDevices(instace, num, *devicesPointer); + if(result != VK_SUCCESS){ + return result; + } + return result; +} + +int svlk_getQueueFamilyIndex(VkPhysicalDevice physDevice, VkQueueFlags queueFlag, int *error){ + unsigned int len = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &len, NULL); + VkQueueFamilyProperties queueFamilies[len]; + vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &len, queueFamilies); + if(error) *error=0; + + for (int i=0; i>= 1) { + if(queueFamilies & mask){ + int error=0; + svlk_getQueueFamilyIndex(physDevice, queueFamilies & mask, &error); + if(error) ret=0; + } + } + // not all queue families were found + if (!ret) return 0; + + if (surface) { + // check if rendering to the surface is possible + unsigned int num; + vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, &num, NULL); + if(!num) return 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, &num, NULL); + if(!num) return 0; + + // check for present family + int error = 0; + svlk_getPresentQueueFamilyIndex(physDevice, surface, &error); + if(!error) return 1; + else return 0; + } + + return 1; +} + +VkDeviceQueueCreateInfo svlk_createQueueInfo(int queueIndex, int count, float *priority){ + VkDeviceQueueCreateInfo ret={0}; + ret.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + ret.pNext = NULL; + ret.flags = 0; + ret.queueFamilyIndex = queueIndex; + ret.queueCount = 1; + ret.pQueuePriorities = priority; + return ret; +} + +VkResult svlk_createLogicalDevice( + VkPhysicalDevice physDevice, + VkSurfaceKHR surface, + VkDeviceQueueCreateInfo *queueFamilies, + unsigned int queueFamiliesNum, + VkPhysicalDeviceFeatures deviceFeatures, + const char **deviceExtensions, + int deviceExtensionsNum, + VkDevice *device + ){ + + VkDeviceCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.queueCreateInfoCount = queueFamiliesNum; + createInfo.pQueueCreateInfos = queueFamilies; + createInfo.enabledLayerCount = 0; + createInfo.enabledExtensionCount = deviceExtensionsNum; + createInfo.ppEnabledExtensionNames = deviceExtensions; + createInfo.pEnabledFeatures = &deviceFeatures; + + return vkCreateDevice(physDevice, &createInfo, NULL, device); +} \ No newline at end of file diff --git a/src/swapchain.c b/src/swapchain.c new file mode 100644 index 0000000..2ec8a01 --- /dev/null +++ b/src/swapchain.c @@ -0,0 +1,104 @@ +#ifndef __SWAPCHAIN_C__ +#define __SWAPCHAIN_C__ +#include "../include/swapchain.h" +#include +#include +#include + +VkSurfaceFormatKHR *svlk_getSurfaceFormats(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len){ + vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, len, NULL); + + if(len == 0) return NULL; + + VkSurfaceFormatKHR *ret = malloc(*len * sizeof(VkSurfaceFormatKHR)); + + vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, len, ret); + return ret; +} + +VkPresentModeKHR *svlk_getPresentModes(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len){ + vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, len, NULL); + + if(len == 0) return NULL; + + VkPresentModeKHR *ret = malloc(*len * sizeof(VkPresentModeKHR)); + + vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, len, ret); + return ret; +} + +VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfaceFormats, VkFormat format, VkColorSpaceKHR colorSpace, unsigned int *len){ + for (unsigned int i = 0; i < *len; i++) { + if (surfaceFormats[i].format == format && surfaceFormats->colorSpace == colorSpace) { + return surfaceFormats[i]; + } + } + return surfaceFormats[0]; +} + +VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace){ + unsigned int *len; + VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, len); + VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, len); + free(surfaceFormats); + return surfaceFormat; +} + +VkExtent2D svlk_checkSwapchainExtent(const VkSurfaceCapabilitiesKHR *capabilities, int w, int h){ + if (capabilities->currentExtent.width != UINT32_MAX) { + return capabilities->currentExtent; + } else { + VkExtent2D extent = { + w, + h + }; + if(w < capabilities->minImageExtent.width) extent.width = capabilities->minImageExtent.width; + if(w > capabilities->maxImageExtent.width) extent.width = capabilities->maxImageExtent.width; + + if(h < capabilities->minImageExtent.height) extent.height = capabilities->minImageExtent.height; + if(h > capabilities->maxImageExtent.height) extent.height = capabilities->maxImageExtent.height; + + return extent; + } +} + +VkSwapchainCreateInfoKHR svlk_createSwapchainCreateinfo( + unsigned int imageCount, + VkSurfaceKHR surface, + VkSurfaceFormatKHR format, + VkPresentModeKHR presentMode, + VkExtent2D extent, + int arrayLayers, + unsigned int usage, + VkSurfaceTransformFlagBitsKHR pretransform + ){ + VkSwapchainCreateInfoKHR createinfo={0}; + createinfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createinfo.surface = surface; + createinfo.minImageCount = imageCount; + createinfo.imageFormat = format.format; + createinfo.imageColorSpace = format.colorSpace; + createinfo.imageExtent = extent; + createinfo.imageArrayLayers = arrayLayers; + createinfo.imageUsage = usage; + // createinfo.imageSharingMode &co is handled in select image sharing mode + createinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createinfo.presentMode = presentMode; + createinfo.clipped = VK_TRUE; + createinfo.oldSwapchain = NULL; + createinfo.preTransform = pretransform; + + return createinfo; +} + +void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent, unsigned int *queueFamilies, int queueFamiliesNum){ + if(concurrent){ + createinfo->imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createinfo->queueFamilyIndexCount = queueFamiliesNum; + createinfo->pQueueFamilyIndices = queueFamilies; + } else { + createinfo->imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } +} + +#endif \ No newline at end of file diff --git a/test/device-creation.c b/test/device-creation.c new file mode 100644 index 0000000..6bf5268 --- /dev/null +++ b/test/device-creation.c @@ -0,0 +1,120 @@ +#include "../include/svulc.h" +#include "../include/swapchain.h" +#include +#include +#include +#include +#include + +int main(){ + + //------------------------GLFW---------------------- + glfwInit(); + + GLFWwindow *window; + VkSurfaceKHR surface; + + int window_width = 100, + window_height = 100; + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + // create the window + window = glfwCreateWindow(window_width, window_height, "device-creation.c", NULL, NULL); + + // get required extensions + unsigned int numExtensions; + + const char **extensions = glfwGetRequiredInstanceExtensions(&numExtensions); + const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + + + + //----------------------INSTANCE------------------- + VkInstance instace; + VkResult result; + result = svlk_createIinstance(layers, 1, extensions, numExtensions, &instace); + if (result != VK_SUCCESS){printf("createinstance failed with code %i\n", result); return 0;} + + result = glfwCreateWindowSurface(instace, window, NULL, &surface); + if (result != VK_SUCCESS){printf("createwindowsurface failed with code %i\n", result); return 0;} + + //--------------------PHYS-DEVICES----------------- + unsigned int physDeviceNum; + VkPhysicalDevice *physDevices; + + result = svlk_getPhysDeviceList(instace, &physDeviceNum, &physDevices); + + VkPhysicalDevice toUsePhysDevice; + + for (int i=0; i capabilities.maxImageCount && capabilities.maxImageCount != 0) numimgs = capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR swcreateinfo = svlk_createSwapchainCreateinfo( + numimgs, + surface, + format, + VK_PRESENT_MODE_FIFO_KHR, + svlk_checkSwapchainExtent(&capabilities, w, h), + 1, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + capabilities.currentTransform + ); + + svlk_selectSharingMode(&swcreateinfo, graphicsQueue != presentQueue, queueFamilies, 2); + + VkSwapchainKHR swapchain; + + result = vkCreateSwapchainKHR(device, &swcreateinfo, NULL, &swapchain); + +} From 1db8d3624dd700cbd05c2a5526cc7f24e857315d Mon Sep 17 00:00:00 2001 From: miri Date: Thu, 5 Sep 2024 18:48:18 +0200 Subject: [PATCH 02/22] initial commit --- .gitignore | 3 + include/svulc.h | 28 +++++++++ include/swapchain.h | 30 ++++++++++ src/instance.c | 131 +++++++++++++++++++++++++++++++++++++++++ src/swapchain.c | 104 ++++++++++++++++++++++++++++++++ test/device-creation.c | 120 +++++++++++++++++++++++++++++++++++++ 6 files changed, 416 insertions(+) create mode 100644 .gitignore create mode 100644 include/svulc.h create mode 100644 include/swapchain.h create mode 100644 src/instance.c create mode 100644 src/swapchain.c create mode 100644 test/device-creation.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8483e07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +#output files +a.out +main \ No newline at end of file diff --git a/include/svulc.h b/include/svulc.h new file mode 100644 index 0000000..8a61ea4 --- /dev/null +++ b/include/svulc.h @@ -0,0 +1,28 @@ +#ifndef __SVULC_H__ +#define __SVULC_H__ + +#include + +VkResult svlk_createIinstance(const char **layers, int layersNum, const char **extensions, int extensionsNum, VkInstance *instance); + +VkResult svlk_getPhysDeviceList(VkInstance instance, unsigned int *num, VkPhysicalDevice **devicesPointer); + +int svlk_getQueueFamilyIndex(VkPhysicalDevice physDevice, VkQueueFlags queueFlag, int *error); + +int svlk_getPresentQueueFamilyIndex(VkPhysicalDevice physDevice, VkSurfaceKHR KHRsurface, int *error); + +int svlk_deviceCompatable(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int queueFamilies, const char **deviceExtensions, int deviceExtensionsNum); + +VkDeviceQueueCreateInfo svlk_createQueueInfo(int queueIndex, int count, float *priority); + +VkResult svlk_createLogicalDevice( + VkPhysicalDevice physDevice, + VkSurfaceKHR surface, + VkDeviceQueueCreateInfo *queueFamilies, + unsigned int queueFamiliesNum, + VkPhysicalDeviceFeatures deviceFeatures, + const char **deviceExtensions, + int deviceExtensionsNum, + VkDevice *device); + +#endif \ No newline at end of file diff --git a/include/swapchain.h b/include/swapchain.h new file mode 100644 index 0000000..0022c8c --- /dev/null +++ b/include/swapchain.h @@ -0,0 +1,30 @@ +#ifndef __SWAPCHAIN_H__ +#define __SWAPCHAIN_H__ +#include + +VkSurfaceFormatKHR *svlk_getSurfaceFormats(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkPresentModeKHR *svlk_getPresentModes(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfaceFormats, VkFormat format, VkColorSpaceKHR colorSpace, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace); + +VkExtent2D svlk_checkSwapchainExtent(const VkSurfaceCapabilitiesKHR *capabilities, int w, int h); + +VkSwapchainCreateInfoKHR svlk_createSwapchainCreateinfo( + unsigned int imageCount, + VkSurfaceKHR surface, + VkSurfaceFormatKHR format, + VkPresentModeKHR presentMode, + VkExtent2D extent, + int arrayLayers, + unsigned int usage, + VkSurfaceTransformFlagBitsKHR pretransform + ); + +void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent, unsigned int *queueFamilies, int queueFamiliesNum); + +VkResult svlk_createSwapchain(); + +#endif \ No newline at end of file diff --git a/src/instance.c b/src/instance.c new file mode 100644 index 0000000..a184880 --- /dev/null +++ b/src/instance.c @@ -0,0 +1,131 @@ +#include "../include/svulc.h" +#include +#include +#include + +VkResult svlk_createIinstance(const char **layers, int layersNum, const char **extensions, int extensionsNum, VkInstance *instance){ + VkInstanceCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.pApplicationInfo = NULL; + createInfo.enabledLayerCount = layersNum; + createInfo.ppEnabledLayerNames = layers; + createInfo.enabledExtensionCount = extensionsNum; + createInfo.ppEnabledExtensionNames = extensions; + + return vkCreateInstance(&createInfo, NULL, instance); +} + +VkResult svlk_getPhysDeviceList(VkInstance instace, unsigned int *num, VkPhysicalDevice **devicesPointer){ + VkResult result = vkEnumeratePhysicalDevices(instace, num, NULL); + if(result != VK_SUCCESS){ + return result; + } + *devicesPointer = malloc(*num * sizeof(VkPhysicalDevice)); + result = vkEnumeratePhysicalDevices(instace, num, *devicesPointer); + if(result != VK_SUCCESS){ + return result; + } + return result; +} + +int svlk_getQueueFamilyIndex(VkPhysicalDevice physDevice, VkQueueFlags queueFlag, int *error){ + unsigned int len = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &len, NULL); + VkQueueFamilyProperties queueFamilies[len]; + vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &len, queueFamilies); + if(error) *error=0; + + for (int i=0; i>= 1) { + if(queueFamilies & mask){ + int error=0; + svlk_getQueueFamilyIndex(physDevice, queueFamilies & mask, &error); + if(error) ret=0; + } + } + // not all queue families were found + if (!ret) return 0; + + if (surface) { + // check if rendering to the surface is possible + unsigned int num; + vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, &num, NULL); + if(!num) return 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, &num, NULL); + if(!num) return 0; + + // check for present family + int error = 0; + svlk_getPresentQueueFamilyIndex(physDevice, surface, &error); + if(!error) return 1; + else return 0; + } + + return 1; +} + +VkDeviceQueueCreateInfo svlk_createQueueInfo(int queueIndex, int count, float *priority){ + VkDeviceQueueCreateInfo ret={0}; + ret.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + ret.pNext = NULL; + ret.flags = 0; + ret.queueFamilyIndex = queueIndex; + ret.queueCount = 1; + ret.pQueuePriorities = priority; + return ret; +} + +VkResult svlk_createLogicalDevice( + VkPhysicalDevice physDevice, + VkSurfaceKHR surface, + VkDeviceQueueCreateInfo *queueFamilies, + unsigned int queueFamiliesNum, + VkPhysicalDeviceFeatures deviceFeatures, + const char **deviceExtensions, + int deviceExtensionsNum, + VkDevice *device + ){ + + VkDeviceCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.queueCreateInfoCount = queueFamiliesNum; + createInfo.pQueueCreateInfos = queueFamilies; + createInfo.enabledLayerCount = 0; + createInfo.enabledExtensionCount = deviceExtensionsNum; + createInfo.ppEnabledExtensionNames = deviceExtensions; + createInfo.pEnabledFeatures = &deviceFeatures; + + return vkCreateDevice(physDevice, &createInfo, NULL, device); +} \ No newline at end of file diff --git a/src/swapchain.c b/src/swapchain.c new file mode 100644 index 0000000..2ec8a01 --- /dev/null +++ b/src/swapchain.c @@ -0,0 +1,104 @@ +#ifndef __SWAPCHAIN_C__ +#define __SWAPCHAIN_C__ +#include "../include/swapchain.h" +#include +#include +#include + +VkSurfaceFormatKHR *svlk_getSurfaceFormats(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len){ + vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, len, NULL); + + if(len == 0) return NULL; + + VkSurfaceFormatKHR *ret = malloc(*len * sizeof(VkSurfaceFormatKHR)); + + vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, len, ret); + return ret; +} + +VkPresentModeKHR *svlk_getPresentModes(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len){ + vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, len, NULL); + + if(len == 0) return NULL; + + VkPresentModeKHR *ret = malloc(*len * sizeof(VkPresentModeKHR)); + + vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, len, ret); + return ret; +} + +VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfaceFormats, VkFormat format, VkColorSpaceKHR colorSpace, unsigned int *len){ + for (unsigned int i = 0; i < *len; i++) { + if (surfaceFormats[i].format == format && surfaceFormats->colorSpace == colorSpace) { + return surfaceFormats[i]; + } + } + return surfaceFormats[0]; +} + +VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace){ + unsigned int *len; + VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, len); + VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, len); + free(surfaceFormats); + return surfaceFormat; +} + +VkExtent2D svlk_checkSwapchainExtent(const VkSurfaceCapabilitiesKHR *capabilities, int w, int h){ + if (capabilities->currentExtent.width != UINT32_MAX) { + return capabilities->currentExtent; + } else { + VkExtent2D extent = { + w, + h + }; + if(w < capabilities->minImageExtent.width) extent.width = capabilities->minImageExtent.width; + if(w > capabilities->maxImageExtent.width) extent.width = capabilities->maxImageExtent.width; + + if(h < capabilities->minImageExtent.height) extent.height = capabilities->minImageExtent.height; + if(h > capabilities->maxImageExtent.height) extent.height = capabilities->maxImageExtent.height; + + return extent; + } +} + +VkSwapchainCreateInfoKHR svlk_createSwapchainCreateinfo( + unsigned int imageCount, + VkSurfaceKHR surface, + VkSurfaceFormatKHR format, + VkPresentModeKHR presentMode, + VkExtent2D extent, + int arrayLayers, + unsigned int usage, + VkSurfaceTransformFlagBitsKHR pretransform + ){ + VkSwapchainCreateInfoKHR createinfo={0}; + createinfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createinfo.surface = surface; + createinfo.minImageCount = imageCount; + createinfo.imageFormat = format.format; + createinfo.imageColorSpace = format.colorSpace; + createinfo.imageExtent = extent; + createinfo.imageArrayLayers = arrayLayers; + createinfo.imageUsage = usage; + // createinfo.imageSharingMode &co is handled in select image sharing mode + createinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createinfo.presentMode = presentMode; + createinfo.clipped = VK_TRUE; + createinfo.oldSwapchain = NULL; + createinfo.preTransform = pretransform; + + return createinfo; +} + +void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent, unsigned int *queueFamilies, int queueFamiliesNum){ + if(concurrent){ + createinfo->imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createinfo->queueFamilyIndexCount = queueFamiliesNum; + createinfo->pQueueFamilyIndices = queueFamilies; + } else { + createinfo->imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } +} + +#endif \ No newline at end of file diff --git a/test/device-creation.c b/test/device-creation.c new file mode 100644 index 0000000..6bf5268 --- /dev/null +++ b/test/device-creation.c @@ -0,0 +1,120 @@ +#include "../include/svulc.h" +#include "../include/swapchain.h" +#include +#include +#include +#include +#include + +int main(){ + + //------------------------GLFW---------------------- + glfwInit(); + + GLFWwindow *window; + VkSurfaceKHR surface; + + int window_width = 100, + window_height = 100; + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + // create the window + window = glfwCreateWindow(window_width, window_height, "device-creation.c", NULL, NULL); + + // get required extensions + unsigned int numExtensions; + + const char **extensions = glfwGetRequiredInstanceExtensions(&numExtensions); + const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + + + + //----------------------INSTANCE------------------- + VkInstance instace; + VkResult result; + result = svlk_createIinstance(layers, 1, extensions, numExtensions, &instace); + if (result != VK_SUCCESS){printf("createinstance failed with code %i\n", result); return 0;} + + result = glfwCreateWindowSurface(instace, window, NULL, &surface); + if (result != VK_SUCCESS){printf("createwindowsurface failed with code %i\n", result); return 0;} + + //--------------------PHYS-DEVICES----------------- + unsigned int physDeviceNum; + VkPhysicalDevice *physDevices; + + result = svlk_getPhysDeviceList(instace, &physDeviceNum, &physDevices); + + VkPhysicalDevice toUsePhysDevice; + + for (int i=0; i capabilities.maxImageCount && capabilities.maxImageCount != 0) numimgs = capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR swcreateinfo = svlk_createSwapchainCreateinfo( + numimgs, + surface, + format, + VK_PRESENT_MODE_FIFO_KHR, + svlk_checkSwapchainExtent(&capabilities, w, h), + 1, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + capabilities.currentTransform + ); + + svlk_selectSharingMode(&swcreateinfo, graphicsQueue != presentQueue, queueFamilies, 2); + + VkSwapchainKHR swapchain; + + result = vkCreateSwapchainKHR(device, &swcreateinfo, NULL, &swapchain); + +} From e5add5c246ceba62fbfb9fa317321ee3345652e1 Mon Sep 17 00:00:00 2001 From: miri Date: Fri, 6 Sep 2024 17:49:19 +0200 Subject: [PATCH 03/22] added image views to swapchain.* --- include/swapchain.h | 5 +++++ src/swapchain.c | 32 ++++++++++++++++++++++++++++++++ test/device-creation.c | 18 ++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/include/swapchain.h b/include/swapchain.h index 0022c8c..a95309f 100644 --- a/include/swapchain.h +++ b/include/swapchain.h @@ -27,4 +27,9 @@ void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent VkResult svlk_createSwapchain(); +VkImage *svlk_getSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, unsigned int *imageCount); + +VkImageViewCreateInfo svlk_createVkImageViewCreateInfo(VkImage image, VkImageViewType viewtype, VkFormat format); +VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType viewtype, VkFormat format, VkImageView *imageView); + #endif \ No newline at end of file diff --git a/src/swapchain.c b/src/swapchain.c index 2ec8a01..4dc7a42 100644 --- a/src/swapchain.c +++ b/src/swapchain.c @@ -101,4 +101,36 @@ void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent } } +VkImageViewCreateInfo svlk_createVkImageViewCreateInfo(VkImage image, VkImageViewType viewtype, VkFormat format){ + VkImageViewCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = image; + createInfo.viewType = viewtype; + createInfo.format = format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + return createInfo; +} + +VkImage *svlk_getSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, unsigned int *imageCount){ + vkGetSwapchainImagesKHR(device, swapchain, imageCount, NULL); + VkImage *images = malloc(*imageCount * sizeof(VkImage)); + vkGetSwapchainImagesKHR(device, swapchain, imageCount, images); + return images; +} + +VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType viewtype, VkFormat format, VkImageView *imageView){ + VkImageViewCreateInfo createInfo = svlk_createVkImageViewCreateInfo(image, viewtype, format); + + return vkCreateImageView(device, &createInfo, NULL, imageView); +} + #endif \ No newline at end of file diff --git a/test/device-creation.c b/test/device-creation.c index 6bf5268..10d1836 100644 --- a/test/device-creation.c +++ b/test/device-creation.c @@ -70,6 +70,7 @@ int main(){ //-------------------LOGIC-DEVICES----------------- + // Create the logical device and the queues float queuePriority = 1.f; VkDeviceQueueCreateInfo deviceQueues[] = { @@ -117,4 +118,21 @@ int main(){ result = vkCreateSwapchainKHR(device, &swcreateinfo, NULL, &swapchain); + + unsigned int imageCount = 0; + VkImage *images = svlk_getSwapchainImages(device, swapchain, &imageCount); + + //------------------IMAGE-VIEWS------------------- + + unsigned int numImageViews = imageCount; + VkImageView *imageViews = malloc(numImageViews * sizeof(VkImageView)); + + for (int i = 0; i < numImageViews; i++){ + if(svlk_createVkImageView(device, images[i], VK_IMAGE_VIEW_TYPE_2D, format.format, &imageViews[i]) != VK_SUCCESS){ + printf("failed to create image view.\n"); + exit(EXIT_FAILURE); + } + } + + } From 083c440a70945e023f3dceae9916fc9e7120c80c Mon Sep 17 00:00:00 2001 From: miri Date: Fri, 6 Sep 2024 17:49:19 +0200 Subject: [PATCH 04/22] added image views to swapchain.* --- include/swapchain.h | 5 +++++ src/swapchain.c | 32 ++++++++++++++++++++++++++++++++ test/device-creation.c | 18 ++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/include/swapchain.h b/include/swapchain.h index 0022c8c..a95309f 100644 --- a/include/swapchain.h +++ b/include/swapchain.h @@ -27,4 +27,9 @@ void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent VkResult svlk_createSwapchain(); +VkImage *svlk_getSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, unsigned int *imageCount); + +VkImageViewCreateInfo svlk_createVkImageViewCreateInfo(VkImage image, VkImageViewType viewtype, VkFormat format); +VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType viewtype, VkFormat format, VkImageView *imageView); + #endif \ No newline at end of file diff --git a/src/swapchain.c b/src/swapchain.c index 2ec8a01..4dc7a42 100644 --- a/src/swapchain.c +++ b/src/swapchain.c @@ -101,4 +101,36 @@ void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent } } +VkImageViewCreateInfo svlk_createVkImageViewCreateInfo(VkImage image, VkImageViewType viewtype, VkFormat format){ + VkImageViewCreateInfo createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = image; + createInfo.viewType = viewtype; + createInfo.format = format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + return createInfo; +} + +VkImage *svlk_getSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, unsigned int *imageCount){ + vkGetSwapchainImagesKHR(device, swapchain, imageCount, NULL); + VkImage *images = malloc(*imageCount * sizeof(VkImage)); + vkGetSwapchainImagesKHR(device, swapchain, imageCount, images); + return images; +} + +VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType viewtype, VkFormat format, VkImageView *imageView){ + VkImageViewCreateInfo createInfo = svlk_createVkImageViewCreateInfo(image, viewtype, format); + + return vkCreateImageView(device, &createInfo, NULL, imageView); +} + #endif \ No newline at end of file diff --git a/test/device-creation.c b/test/device-creation.c index 6bf5268..10d1836 100644 --- a/test/device-creation.c +++ b/test/device-creation.c @@ -70,6 +70,7 @@ int main(){ //-------------------LOGIC-DEVICES----------------- + // Create the logical device and the queues float queuePriority = 1.f; VkDeviceQueueCreateInfo deviceQueues[] = { @@ -117,4 +118,21 @@ int main(){ result = vkCreateSwapchainKHR(device, &swcreateinfo, NULL, &swapchain); + + unsigned int imageCount = 0; + VkImage *images = svlk_getSwapchainImages(device, swapchain, &imageCount); + + //------------------IMAGE-VIEWS------------------- + + unsigned int numImageViews = imageCount; + VkImageView *imageViews = malloc(numImageViews * sizeof(VkImageView)); + + for (int i = 0; i < numImageViews; i++){ + if(svlk_createVkImageView(device, images[i], VK_IMAGE_VIEW_TYPE_2D, format.format, &imageViews[i]) != VK_SUCCESS){ + printf("failed to create image view.\n"); + exit(EXIT_FAILURE); + } + } + + } From b14cc558eefc1bea48dd0f511ea0356ba374d4ef Mon Sep 17 00:00:00 2001 From: miri Date: Sun, 13 Oct 2024 19:33:04 +0200 Subject: [PATCH 05/22] started adding command pool things. TODO: easier RP creation --- include/renderingpipeline.h | 72 +++++++++++++++++++++++++++++++++++++ src/swapchain.c | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 include/renderingpipeline.h diff --git a/include/renderingpipeline.h b/include/renderingpipeline.h new file mode 100644 index 0000000..1dd3043 --- /dev/null +++ b/include/renderingpipeline.h @@ -0,0 +1,72 @@ +#ifndef __RENDERING_PIPELINE_H__ +#define __RENDERING_PIPELINE_H__ +#include + +const VkPipelineMultisampleStateCreateInfo svlk_noMultisample{ + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + NULL, + 0, + VK_SAMPLE_COUNT_1_BIT, + VK_FALSE, + 1.0f, + NULL, + VK_FALSE, + VK_FALSE +}; + +const VkPipelineVertexInputStateCreateInfo svlk_noVertexData{ + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL +}; + +const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate{ + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + NULL, + 0, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_FALSE +}; + +const VkViewport svlk_viewportTemplate { + 0.0f, + 0.0f, + 0.0f, // width and height must be set by user + 0.0f, // width and height must be set by user + 0.0f, + 1.0f +}; + +const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate{ + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_FALSE, + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_CLOCKWISE, + VK_FALSE, + 0.0f, + 0.0f, + 0.0f, + 0.0f +}; + +const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate{ + VK_FALSE, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD +}; + +#endif \ No newline at end of file diff --git a/src/swapchain.c b/src/swapchain.c index 4dc7a42..64d6599 100644 --- a/src/swapchain.c +++ b/src/swapchain.c @@ -37,7 +37,7 @@ VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfac } VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace){ - unsigned int *len; + unsigned int *len = 0; VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, len); VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, len); free(surfaceFormats); From b394343843a701c56ab0baf3fb4c581b841b67a7 Mon Sep 17 00:00:00 2001 From: miri Date: Sun, 13 Oct 2024 19:33:04 +0200 Subject: [PATCH 06/22] started adding command pool things. TODO: easier RP creation --- include/renderingpipeline.h | 72 +++++++++++++++++++++++++++++++++++++ src/swapchain.c | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 include/renderingpipeline.h diff --git a/include/renderingpipeline.h b/include/renderingpipeline.h new file mode 100644 index 0000000..1dd3043 --- /dev/null +++ b/include/renderingpipeline.h @@ -0,0 +1,72 @@ +#ifndef __RENDERING_PIPELINE_H__ +#define __RENDERING_PIPELINE_H__ +#include + +const VkPipelineMultisampleStateCreateInfo svlk_noMultisample{ + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + NULL, + 0, + VK_SAMPLE_COUNT_1_BIT, + VK_FALSE, + 1.0f, + NULL, + VK_FALSE, + VK_FALSE +}; + +const VkPipelineVertexInputStateCreateInfo svlk_noVertexData{ + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL +}; + +const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate{ + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + NULL, + 0, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_FALSE +}; + +const VkViewport svlk_viewportTemplate { + 0.0f, + 0.0f, + 0.0f, // width and height must be set by user + 0.0f, // width and height must be set by user + 0.0f, + 1.0f +}; + +const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate{ + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_FALSE, + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_CLOCKWISE, + VK_FALSE, + 0.0f, + 0.0f, + 0.0f, + 0.0f +}; + +const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate{ + VK_FALSE, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD +}; + +#endif \ No newline at end of file diff --git a/src/swapchain.c b/src/swapchain.c index 4dc7a42..64d6599 100644 --- a/src/swapchain.c +++ b/src/swapchain.c @@ -37,7 +37,7 @@ VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfac } VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace){ - unsigned int *len; + unsigned int *len = 0; VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, len); VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, len); free(surfaceFormats); From de1483b516243fb50525edfc7209f245f5e78ed2 Mon Sep 17 00:00:00 2001 From: miri Date: Sun, 13 Oct 2024 20:46:46 +0200 Subject: [PATCH 07/22] actually commit, forgor to stage --- include/commandpools.h | 8 ++ include/renderingpipeline.h | 61 +++++++++-- shd/triangle.frag | 9 ++ shd/triangle.vert | 20 ++++ src/commandpools.c | 23 +++++ src/instance.c | 2 +- src/swapchain.c | 6 +- test/device-creation.c | 197 +++++++++++++++++++++++++++++++++++- test/helpers.c | 88 ++++++++++++++++ test/helpers.h | 82 +++++++++++++++ 10 files changed, 482 insertions(+), 14 deletions(-) create mode 100644 include/commandpools.h create mode 100644 shd/triangle.frag create mode 100644 shd/triangle.vert create mode 100644 src/commandpools.c create mode 100644 test/helpers.c create mode 100644 test/helpers.h diff --git a/include/commandpools.h b/include/commandpools.h new file mode 100644 index 0000000..1ed723d --- /dev/null +++ b/include/commandpools.h @@ -0,0 +1,8 @@ +#ifndef __COMMADPOOLS_H__ +#define __COMMADPOOLS_H__ + +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool); +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); +#endif \ No newline at end of file diff --git a/include/renderingpipeline.h b/include/renderingpipeline.h index 1dd3043..a28b22c 100644 --- a/include/renderingpipeline.h +++ b/include/renderingpipeline.h @@ -2,7 +2,27 @@ #define __RENDERING_PIPELINE_H__ #include -const VkPipelineMultisampleStateCreateInfo svlk_noMultisample{ +const VkPipelineShaderStageCreateInfo svlk_vertexShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_VERTEX_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineShaderStageCreateInfo svlk_fragmentShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_FRAGMENT_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineMultisampleStateCreateInfo svlk_noMultisample = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, NULL, 0, @@ -14,7 +34,7 @@ const VkPipelineMultisampleStateCreateInfo svlk_noMultisample{ VK_FALSE }; -const VkPipelineVertexInputStateCreateInfo svlk_noVertexData{ +const VkPipelineVertexInputStateCreateInfo svlk_noVertexData = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL, 0, @@ -24,7 +44,7 @@ const VkPipelineVertexInputStateCreateInfo svlk_noVertexData{ NULL }; -const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate{ +const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, NULL, 0, @@ -32,7 +52,7 @@ const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate{ VK_FALSE }; -const VkViewport svlk_viewportTemplate { +const VkViewport svlk_viewportTemplate = { 0.0f, 0.0f, 0.0f, // width and height must be set by user @@ -41,7 +61,7 @@ const VkViewport svlk_viewportTemplate { 1.0f }; -const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate{ +const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, NULL, 0, @@ -54,10 +74,10 @@ const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate{ 0.0f, 0.0f, 0.0f, - 0.0f + 1.0f }; -const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate{ +const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate = { VK_FALSE, VK_BLEND_FACTOR_ONE, @@ -66,7 +86,32 @@ const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate{ VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, - VK_BLEND_OP_ADD + VK_BLEND_OP_ADD, + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT +}; + +const VkPipelineColorBlendStateCreateInfo svlk_colorBlendCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_LOGIC_OP_COPY, + 0, // must be set by user + NULL, // must be set by user + 0.f, + 0.f, + 0.f, + 0.f +}; + +const VkPipelineLayoutCreateInfo svlk_pipelineLayoutCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL }; #endif \ No newline at end of file diff --git a/shd/triangle.frag b/shd/triangle.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/shd/triangle.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/shd/triangle.vert b/shd/triangle.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/shd/triangle.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} diff --git a/src/commandpools.c b/src/commandpools.c new file mode 100644 index 0000000..8d76ba0 --- /dev/null +++ b/src/commandpools.c @@ -0,0 +1,23 @@ +#include "../include/commandpools.h" +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool){ + VkCommandPoolCreateInfo createinfo = { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + NULL, + flags, + familyindex + }; + return vkCreateCommandPool(device, &createinfo, NULL, commandpool); +} + +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers){ + VkCommandBufferAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + NULL, + cmdpool, + level, + count + }; + return vkAllocateCommandBuffers(device, &allocinfo, cmdbuffers); +} \ No newline at end of file diff --git a/src/instance.c b/src/instance.c index a184880..0ba9244 100644 --- a/src/instance.c +++ b/src/instance.c @@ -78,7 +78,7 @@ int svlk_deviceCompatable(VkPhysicalDevice physDevice, VkSurfaceKHR surface, uns if (surface) { // check if rendering to the surface is possible - unsigned int num; + unsigned int num = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, &num, NULL); if(!num) return 0; vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, &num, NULL); diff --git a/src/swapchain.c b/src/swapchain.c index 64d6599..e1afbaf 100644 --- a/src/swapchain.c +++ b/src/swapchain.c @@ -37,9 +37,9 @@ VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfac } VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace){ - unsigned int *len = 0; - VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, len); - VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, len); + unsigned int len = 0; + VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, &len); + VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, &len); free(surfaceFormats); return surfaceFormat; } diff --git a/test/device-creation.c b/test/device-creation.c index 10d1836..2830aea 100644 --- a/test/device-creation.c +++ b/test/device-creation.c @@ -1,10 +1,30 @@ #include "../include/svulc.h" #include "../include/swapchain.h" +#include "../include/renderingpipeline.h" +#include "../include/commandpools.h" +#include #include #include #include #include #include +#include "helpers.h" + +VkShaderModule createShaderModule(sizedData shadercode, VkDevice device){ + VkShaderModuleCreateInfo createinfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + NULL, + 0, + shadercode.length, + shadercode.data + }; + VkShaderModule module; + if(vkCreateShaderModule(device, &createinfo, NULL, &module)!= VK_SUCCESS){ + printf("Couldn't create shader module!"); + exit(EXIT_FAILURE); + } + return module; +} int main(){ @@ -94,7 +114,7 @@ int main(){ glfwGetFramebufferSize(window, &w, &h); - VkExtent2D extent = svlk_checkSwapchainExtent(&capabilities, w, h); + VkExtent2D swapchainExtent = svlk_checkSwapchainExtent(&capabilities, w, h); // find optimum number of images for the swapchain @@ -106,7 +126,7 @@ int main(){ surface, format, VK_PRESENT_MODE_FIFO_KHR, - svlk_checkSwapchainExtent(&capabilities, w, h), + swapchainExtent, 1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, capabilities.currentTransform @@ -134,5 +154,178 @@ int main(){ } } + //------------------RENDER-PIPELINE--------------- + sizedData vertshdcode = readFileR("shd/triangle.vert.spv"); + sizedData fragshdcode = readFileR("shd/triangle.frag.spv"); + + VkShaderModule vertshdmod = createShaderModule(vertshdcode, device); + VkShaderModule fragshdmod = createShaderModule(fragshdcode, device); + + VkPipelineShaderStageCreateInfo stageInfoVert = svlk_vertexShaderTemplate; + stageInfoVert.module = vertshdmod; + + VkPipelineShaderStageCreateInfo stageInfoFrag = svlk_fragmentShaderTemplate; + stageInfoFrag.module = fragshdmod; + + VkPipelineShaderStageCreateInfo shaderStages[] = {stageInfoVert, stageInfoFrag}; + + VkViewport viewport = svlk_viewportTemplate; + viewport.width = swapchainExtent.width; + viewport.height = swapchainExtent.height; + + VkRect2D scissor = { + {0,0}, + swapchainExtent + }; + + VkPipelineViewportStateCreateInfo viewportState = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + NULL, + 0, + 1, + &viewport, + 1, + &scissor + }; + + VkPipelineRasterizationStateCreateInfo rasterizer = svlk_rasterizationTemplate; + VkPipelineMultisampleStateCreateInfo multisample = svlk_noMultisample; + // no depth testing + VkPipelineColorBlendAttachmentState colorblendatt = svlk_colorBlendTemplate; + VkPipelineColorBlendStateCreateInfo colorblend = svlk_colorBlendCreateInfoTemplate; + colorblend.attachmentCount = 1; + colorblend.pAttachments = &colorblendatt; + VkPipelineLayoutCreateInfo pipelinelayoutcrinfo = svlk_pipelineLayoutCreateInfoTemplate; + + VkPipelineLayout pipelinelayout; + + if(vkCreatePipelineLayout(device, &pipelinelayoutcrinfo, NULL, &pipelinelayout) != VK_SUCCESS){ + printf("couldn't create pipeline layout!\n"); + exit(EXIT_FAILURE); + } + + + VkAttachmentDescription colorattachment = { + 0, + format.format, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + }; + + VkAttachmentReference colorattachmentRef = { + 0, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + VkSubpassDescription subpass = { + 0, + VK_PIPELINE_BIND_POINT_GRAPHICS, + 0, + NULL, + 1, + &colorattachmentRef, + NULL, + NULL, + 0, + NULL + }; + + VkRenderPassCreateInfo renderPassCreateinfo = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + NULL, + 0, + 1, + &colorattachment, + 1, + &subpass, + 0, + NULL + }; + + VkRenderPass renderPass; + + result = vkCreateRenderPass(device, &renderPassCreateinfo, NULL, &renderPass); + if(result != VK_SUCCESS){ + printf("vkCreateRenderPass failed\n"); + exit(EXIT_FAILURE); + } + + VkGraphicsPipelineCreateInfo pipelineCreateinfo = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + NULL, + 0, + 2, + shaderStages, + &svlk_noVertexData, + &svlk_inputAssemblyTemplate, + NULL, + &viewportState, + &rasterizer, + &multisample, + NULL, + &colorblend, + NULL, + pipelinelayout, + renderPass, + 0, + NULL, + -1 + }; + + VkPipeline graphicsPipeline; + + result = vkCreateGraphicsPipelines(device, NULL, 1, &pipelineCreateinfo, NULL, &graphicsPipeline); + if(result != VK_SUCCESS){ + printf("vkCreateGraphicsPipeline failed\n"); + exit(EXIT_FAILURE); + } + + VkFramebuffer swapchainFramebuffers[numImageViews]; + unsigned int swapchainFramebuffersNum; + for(int i = 0; i < swapchainFramebuffersNum; i++){ + VkImageView attachment [] = { + imageViews[i] + }; + + VkFramebufferCreateInfo createInfo = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + NULL, + 0, + renderPass, + 1, + attachment, + swapchainExtent.width, + swapchainExtent.height, + 1 + }; + + result = vkCreateFramebuffer(device, &createInfo, NULL, &swapchainFramebuffers[i]); + if(result != VK_SUCCESS){ + printf("vkCreateFramebuffer failed\n"); + exit(EXIT_FAILURE); + } + } + + //------------------COMMAND-POOLS----------------- + VkCommandPool commandPool; + result = svlk_createCommandPool(device, graphicsQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &commandPool); + if(result != VK_SUCCESS){ + printf("createCommandPool failed\n"); + exit(EXIT_FAILURE); + } + + VkCommandBuffer commandBuffer; + result = svlk_allocateCommandBuffer(device, commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &commandBuffer); + if(result != VK_SUCCESS){ + printf("failed to allocate command buffer\n"); + exit(EXIT_FAILURE); + } + + } diff --git a/test/helpers.c b/test/helpers.c new file mode 100644 index 0000000..ad23b3f --- /dev/null +++ b/test/helpers.c @@ -0,0 +1,88 @@ +#include "helpers.h" +#include +#include + +char *readFileC(const char *filename){ + //open file + FILE *fp = fopen(filename, "r+"); + + if(!fp) return NULL; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the string on the heap + char *ret = (char*)malloc(len); + + //go back to the beginning and read everything to the string + fseek(fp, 0, SEEK_SET); + fread(ret, 1, len, fp); + + //close the stream + fclose(fp); + + return ret; +} + + +sizedData readFileR(const char *filename){ + sizedData ret = {NULL,0}; + + //open file + FILE *fp = fopen(filename, "r+"); + if(!fp) return ret; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the data on the heap + ret.data = malloc(len); + + //go back to the beginning and read everything + fseek(fp, 0, SEEK_SET); + fread(ret.data, 1, len, fp); + + //set length of data + ret.length=len; + + //close the stream + fclose(fp); + + return ret; +} + +int writeFileC(const char *filename, char *data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data, sizeof(char), strlen(data), fp); + + //close the stream + fclose(fp); + + return 0; +} + + +int writeFileR(const char *filename, sizedData data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data.data, 1, data.length, fp); + + //close the stream + fclose(fp); + + return 0; +} + +void crash(char *reason){ + PRINTE(reason); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/test/helpers.h b/test/helpers.h new file mode 100644 index 0000000..0b80d11 --- /dev/null +++ b/test/helpers.h @@ -0,0 +1,82 @@ +// just some random helpers and function I use. + +#ifndef __M_HELPERS_H__ +#define __M_HELPERS_H__ + + +#ifndef __cplusplus +#include +#define PRINT(message) printf("%s",message) +#define PRINTE(error) printf("%s",error) +#else +#include +#define PRINT(message) std::cout< Date: Sun, 13 Oct 2024 20:46:46 +0200 Subject: [PATCH 08/22] actually commit, forgor to stage --- include/commandpools.h | 8 ++ include/renderingpipeline.h | 61 +++++++++-- shd/triangle.frag | 9 ++ shd/triangle.vert | 20 ++++ src/commandpools.c | 23 +++++ src/instance.c | 2 +- src/swapchain.c | 6 +- test/device-creation.c | 197 +++++++++++++++++++++++++++++++++++- test/helpers.c | 88 ++++++++++++++++ test/helpers.h | 82 +++++++++++++++ 10 files changed, 482 insertions(+), 14 deletions(-) create mode 100644 include/commandpools.h create mode 100644 shd/triangle.frag create mode 100644 shd/triangle.vert create mode 100644 src/commandpools.c create mode 100644 test/helpers.c create mode 100644 test/helpers.h diff --git a/include/commandpools.h b/include/commandpools.h new file mode 100644 index 0000000..1ed723d --- /dev/null +++ b/include/commandpools.h @@ -0,0 +1,8 @@ +#ifndef __COMMADPOOLS_H__ +#define __COMMADPOOLS_H__ + +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool); +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); +#endif \ No newline at end of file diff --git a/include/renderingpipeline.h b/include/renderingpipeline.h index 1dd3043..a28b22c 100644 --- a/include/renderingpipeline.h +++ b/include/renderingpipeline.h @@ -2,7 +2,27 @@ #define __RENDERING_PIPELINE_H__ #include -const VkPipelineMultisampleStateCreateInfo svlk_noMultisample{ +const VkPipelineShaderStageCreateInfo svlk_vertexShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_VERTEX_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineShaderStageCreateInfo svlk_fragmentShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_FRAGMENT_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineMultisampleStateCreateInfo svlk_noMultisample = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, NULL, 0, @@ -14,7 +34,7 @@ const VkPipelineMultisampleStateCreateInfo svlk_noMultisample{ VK_FALSE }; -const VkPipelineVertexInputStateCreateInfo svlk_noVertexData{ +const VkPipelineVertexInputStateCreateInfo svlk_noVertexData = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL, 0, @@ -24,7 +44,7 @@ const VkPipelineVertexInputStateCreateInfo svlk_noVertexData{ NULL }; -const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate{ +const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, NULL, 0, @@ -32,7 +52,7 @@ const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate{ VK_FALSE }; -const VkViewport svlk_viewportTemplate { +const VkViewport svlk_viewportTemplate = { 0.0f, 0.0f, 0.0f, // width and height must be set by user @@ -41,7 +61,7 @@ const VkViewport svlk_viewportTemplate { 1.0f }; -const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate{ +const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, NULL, 0, @@ -54,10 +74,10 @@ const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate{ 0.0f, 0.0f, 0.0f, - 0.0f + 1.0f }; -const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate{ +const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate = { VK_FALSE, VK_BLEND_FACTOR_ONE, @@ -66,7 +86,32 @@ const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate{ VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, - VK_BLEND_OP_ADD + VK_BLEND_OP_ADD, + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT +}; + +const VkPipelineColorBlendStateCreateInfo svlk_colorBlendCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_LOGIC_OP_COPY, + 0, // must be set by user + NULL, // must be set by user + 0.f, + 0.f, + 0.f, + 0.f +}; + +const VkPipelineLayoutCreateInfo svlk_pipelineLayoutCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL }; #endif \ No newline at end of file diff --git a/shd/triangle.frag b/shd/triangle.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/shd/triangle.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/shd/triangle.vert b/shd/triangle.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/shd/triangle.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} diff --git a/src/commandpools.c b/src/commandpools.c new file mode 100644 index 0000000..8d76ba0 --- /dev/null +++ b/src/commandpools.c @@ -0,0 +1,23 @@ +#include "../include/commandpools.h" +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool){ + VkCommandPoolCreateInfo createinfo = { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + NULL, + flags, + familyindex + }; + return vkCreateCommandPool(device, &createinfo, NULL, commandpool); +} + +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers){ + VkCommandBufferAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + NULL, + cmdpool, + level, + count + }; + return vkAllocateCommandBuffers(device, &allocinfo, cmdbuffers); +} \ No newline at end of file diff --git a/src/instance.c b/src/instance.c index a184880..0ba9244 100644 --- a/src/instance.c +++ b/src/instance.c @@ -78,7 +78,7 @@ int svlk_deviceCompatable(VkPhysicalDevice physDevice, VkSurfaceKHR surface, uns if (surface) { // check if rendering to the surface is possible - unsigned int num; + unsigned int num = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(physDevice, surface, &num, NULL); if(!num) return 0; vkGetPhysicalDeviceSurfacePresentModesKHR(physDevice, surface, &num, NULL); diff --git a/src/swapchain.c b/src/swapchain.c index 64d6599..e1afbaf 100644 --- a/src/swapchain.c +++ b/src/swapchain.c @@ -37,9 +37,9 @@ VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfac } VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace){ - unsigned int *len = 0; - VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, len); - VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, len); + unsigned int len = 0; + VkSurfaceFormatKHR *surfaceFormats = svlk_getSurfaceFormats(physDevice, surface, &len); + VkSurfaceFormatKHR surfaceFormat = svlk_selectSwapchainFormatFromList(surfaceFormats, format, colorSpace, &len); free(surfaceFormats); return surfaceFormat; } diff --git a/test/device-creation.c b/test/device-creation.c index 10d1836..2830aea 100644 --- a/test/device-creation.c +++ b/test/device-creation.c @@ -1,10 +1,30 @@ #include "../include/svulc.h" #include "../include/swapchain.h" +#include "../include/renderingpipeline.h" +#include "../include/commandpools.h" +#include #include #include #include #include #include +#include "helpers.h" + +VkShaderModule createShaderModule(sizedData shadercode, VkDevice device){ + VkShaderModuleCreateInfo createinfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + NULL, + 0, + shadercode.length, + shadercode.data + }; + VkShaderModule module; + if(vkCreateShaderModule(device, &createinfo, NULL, &module)!= VK_SUCCESS){ + printf("Couldn't create shader module!"); + exit(EXIT_FAILURE); + } + return module; +} int main(){ @@ -94,7 +114,7 @@ int main(){ glfwGetFramebufferSize(window, &w, &h); - VkExtent2D extent = svlk_checkSwapchainExtent(&capabilities, w, h); + VkExtent2D swapchainExtent = svlk_checkSwapchainExtent(&capabilities, w, h); // find optimum number of images for the swapchain @@ -106,7 +126,7 @@ int main(){ surface, format, VK_PRESENT_MODE_FIFO_KHR, - svlk_checkSwapchainExtent(&capabilities, w, h), + swapchainExtent, 1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, capabilities.currentTransform @@ -134,5 +154,178 @@ int main(){ } } + //------------------RENDER-PIPELINE--------------- + sizedData vertshdcode = readFileR("shd/triangle.vert.spv"); + sizedData fragshdcode = readFileR("shd/triangle.frag.spv"); + + VkShaderModule vertshdmod = createShaderModule(vertshdcode, device); + VkShaderModule fragshdmod = createShaderModule(fragshdcode, device); + + VkPipelineShaderStageCreateInfo stageInfoVert = svlk_vertexShaderTemplate; + stageInfoVert.module = vertshdmod; + + VkPipelineShaderStageCreateInfo stageInfoFrag = svlk_fragmentShaderTemplate; + stageInfoFrag.module = fragshdmod; + + VkPipelineShaderStageCreateInfo shaderStages[] = {stageInfoVert, stageInfoFrag}; + + VkViewport viewport = svlk_viewportTemplate; + viewport.width = swapchainExtent.width; + viewport.height = swapchainExtent.height; + + VkRect2D scissor = { + {0,0}, + swapchainExtent + }; + + VkPipelineViewportStateCreateInfo viewportState = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + NULL, + 0, + 1, + &viewport, + 1, + &scissor + }; + + VkPipelineRasterizationStateCreateInfo rasterizer = svlk_rasterizationTemplate; + VkPipelineMultisampleStateCreateInfo multisample = svlk_noMultisample; + // no depth testing + VkPipelineColorBlendAttachmentState colorblendatt = svlk_colorBlendTemplate; + VkPipelineColorBlendStateCreateInfo colorblend = svlk_colorBlendCreateInfoTemplate; + colorblend.attachmentCount = 1; + colorblend.pAttachments = &colorblendatt; + VkPipelineLayoutCreateInfo pipelinelayoutcrinfo = svlk_pipelineLayoutCreateInfoTemplate; + + VkPipelineLayout pipelinelayout; + + if(vkCreatePipelineLayout(device, &pipelinelayoutcrinfo, NULL, &pipelinelayout) != VK_SUCCESS){ + printf("couldn't create pipeline layout!\n"); + exit(EXIT_FAILURE); + } + + + VkAttachmentDescription colorattachment = { + 0, + format.format, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + }; + + VkAttachmentReference colorattachmentRef = { + 0, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + + VkSubpassDescription subpass = { + 0, + VK_PIPELINE_BIND_POINT_GRAPHICS, + 0, + NULL, + 1, + &colorattachmentRef, + NULL, + NULL, + 0, + NULL + }; + + VkRenderPassCreateInfo renderPassCreateinfo = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + NULL, + 0, + 1, + &colorattachment, + 1, + &subpass, + 0, + NULL + }; + + VkRenderPass renderPass; + + result = vkCreateRenderPass(device, &renderPassCreateinfo, NULL, &renderPass); + if(result != VK_SUCCESS){ + printf("vkCreateRenderPass failed\n"); + exit(EXIT_FAILURE); + } + + VkGraphicsPipelineCreateInfo pipelineCreateinfo = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + NULL, + 0, + 2, + shaderStages, + &svlk_noVertexData, + &svlk_inputAssemblyTemplate, + NULL, + &viewportState, + &rasterizer, + &multisample, + NULL, + &colorblend, + NULL, + pipelinelayout, + renderPass, + 0, + NULL, + -1 + }; + + VkPipeline graphicsPipeline; + + result = vkCreateGraphicsPipelines(device, NULL, 1, &pipelineCreateinfo, NULL, &graphicsPipeline); + if(result != VK_SUCCESS){ + printf("vkCreateGraphicsPipeline failed\n"); + exit(EXIT_FAILURE); + } + + VkFramebuffer swapchainFramebuffers[numImageViews]; + unsigned int swapchainFramebuffersNum; + for(int i = 0; i < swapchainFramebuffersNum; i++){ + VkImageView attachment [] = { + imageViews[i] + }; + + VkFramebufferCreateInfo createInfo = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + NULL, + 0, + renderPass, + 1, + attachment, + swapchainExtent.width, + swapchainExtent.height, + 1 + }; + + result = vkCreateFramebuffer(device, &createInfo, NULL, &swapchainFramebuffers[i]); + if(result != VK_SUCCESS){ + printf("vkCreateFramebuffer failed\n"); + exit(EXIT_FAILURE); + } + } + + //------------------COMMAND-POOLS----------------- + VkCommandPool commandPool; + result = svlk_createCommandPool(device, graphicsQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &commandPool); + if(result != VK_SUCCESS){ + printf("createCommandPool failed\n"); + exit(EXIT_FAILURE); + } + + VkCommandBuffer commandBuffer; + result = svlk_allocateCommandBuffer(device, commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &commandBuffer); + if(result != VK_SUCCESS){ + printf("failed to allocate command buffer\n"); + exit(EXIT_FAILURE); + } + + } diff --git a/test/helpers.c b/test/helpers.c new file mode 100644 index 0000000..ad23b3f --- /dev/null +++ b/test/helpers.c @@ -0,0 +1,88 @@ +#include "helpers.h" +#include +#include + +char *readFileC(const char *filename){ + //open file + FILE *fp = fopen(filename, "r+"); + + if(!fp) return NULL; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the string on the heap + char *ret = (char*)malloc(len); + + //go back to the beginning and read everything to the string + fseek(fp, 0, SEEK_SET); + fread(ret, 1, len, fp); + + //close the stream + fclose(fp); + + return ret; +} + + +sizedData readFileR(const char *filename){ + sizedData ret = {NULL,0}; + + //open file + FILE *fp = fopen(filename, "r+"); + if(!fp) return ret; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the data on the heap + ret.data = malloc(len); + + //go back to the beginning and read everything + fseek(fp, 0, SEEK_SET); + fread(ret.data, 1, len, fp); + + //set length of data + ret.length=len; + + //close the stream + fclose(fp); + + return ret; +} + +int writeFileC(const char *filename, char *data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data, sizeof(char), strlen(data), fp); + + //close the stream + fclose(fp); + + return 0; +} + + +int writeFileR(const char *filename, sizedData data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data.data, 1, data.length, fp); + + //close the stream + fclose(fp); + + return 0; +} + +void crash(char *reason){ + PRINTE(reason); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/test/helpers.h b/test/helpers.h new file mode 100644 index 0000000..0b80d11 --- /dev/null +++ b/test/helpers.h @@ -0,0 +1,82 @@ +// just some random helpers and function I use. + +#ifndef __M_HELPERS_H__ +#define __M_HELPERS_H__ + + +#ifndef __cplusplus +#include +#define PRINT(message) printf("%s",message) +#define PRINTE(error) printf("%s",error) +#else +#include +#define PRINT(message) std::cout< Date: Thu, 26 Dec 2024 23:51:12 +0100 Subject: [PATCH 09/22] small restructuring --- .gitignore | 4 +- include/commandpools.h | 1 + makefile | 6 + src/commandpools.c | 23 -- src/helpers.c | 88 +++++++ src/helpers.h | 82 +++++++ src/main.c | 43 ++++ src/rendering/init.c | 200 ++++++++++++++++ src/rendering/init.h | 35 +++ src/rendering/pipeline3d/pipeline.h | 17 ++ src/rendering/pipeline3d/setup.c | 144 ++++++++++++ src/rendering/pipeline3d/setup.h | 4 + src/rendering/struct.h | 77 +++++++ src/rendering/vulkan/commandpools.c | 44 ++++ src/rendering/vulkan/commandpools.h | 12 + src/{ => rendering/vulkan}/instance.c | 2 +- src/rendering/vulkan/renderingpipeline.h | 279 +++++++++++++++++++++++ src/rendering/vulkan/svulc.h | 28 +++ src/{ => rendering/vulkan}/swapchain.c | 12 +- src/rendering/vulkan/swapchain.h | 36 +++ src/shd/triangle.frag | 9 + src/shd/triangle.vert | 20 ++ test/device-creation.c | 52 ++++- 23 files changed, 1183 insertions(+), 35 deletions(-) create mode 100644 makefile delete mode 100644 src/commandpools.c create mode 100644 src/helpers.c create mode 100644 src/helpers.h create mode 100644 src/main.c create mode 100644 src/rendering/init.c create mode 100644 src/rendering/init.h create mode 100644 src/rendering/pipeline3d/pipeline.h create mode 100644 src/rendering/pipeline3d/setup.c create mode 100644 src/rendering/pipeline3d/setup.h create mode 100644 src/rendering/struct.h create mode 100644 src/rendering/vulkan/commandpools.c create mode 100644 src/rendering/vulkan/commandpools.h rename src/{ => rendering/vulkan}/instance.c (99%) create mode 100644 src/rendering/vulkan/renderingpipeline.h create mode 100644 src/rendering/vulkan/svulc.h rename src/{ => rendering/vulkan}/swapchain.c (91%) create mode 100644 src/rendering/vulkan/swapchain.h create mode 100644 src/shd/triangle.frag create mode 100644 src/shd/triangle.vert diff --git a/.gitignore b/.gitignore index 8483e07..16c0376 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ #output files a.out -main \ No newline at end of file +main +valgrind* +*.spv \ No newline at end of file diff --git a/include/commandpools.h b/include/commandpools.h index 1ed723d..e1e3793 100644 --- a/include/commandpools.h +++ b/include/commandpools.h @@ -5,4 +5,5 @@ VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool); VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); +VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); #endif \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..5ce9cd2 --- /dev/null +++ b/makefile @@ -0,0 +1,6 @@ +CC = clang +CLIBS = -lglfw -lvulkan + + +all: + $(CC) $(CLIBS) src/*.c src/rendering/*.c src/rendering/pipeline3d/*.c src/rendering/vulkan/*.c \ No newline at end of file diff --git a/src/commandpools.c b/src/commandpools.c deleted file mode 100644 index 8d76ba0..0000000 --- a/src/commandpools.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "../include/commandpools.h" -#include - -VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool){ - VkCommandPoolCreateInfo createinfo = { - VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - NULL, - flags, - familyindex - }; - return vkCreateCommandPool(device, &createinfo, NULL, commandpool); -} - -VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers){ - VkCommandBufferAllocateInfo allocinfo = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - NULL, - cmdpool, - level, - count - }; - return vkAllocateCommandBuffers(device, &allocinfo, cmdbuffers); -} \ No newline at end of file diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..ad23b3f --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,88 @@ +#include "helpers.h" +#include +#include + +char *readFileC(const char *filename){ + //open file + FILE *fp = fopen(filename, "r+"); + + if(!fp) return NULL; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the string on the heap + char *ret = (char*)malloc(len); + + //go back to the beginning and read everything to the string + fseek(fp, 0, SEEK_SET); + fread(ret, 1, len, fp); + + //close the stream + fclose(fp); + + return ret; +} + + +sizedData readFileR(const char *filename){ + sizedData ret = {NULL,0}; + + //open file + FILE *fp = fopen(filename, "r+"); + if(!fp) return ret; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the data on the heap + ret.data = malloc(len); + + //go back to the beginning and read everything + fseek(fp, 0, SEEK_SET); + fread(ret.data, 1, len, fp); + + //set length of data + ret.length=len; + + //close the stream + fclose(fp); + + return ret; +} + +int writeFileC(const char *filename, char *data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data, sizeof(char), strlen(data), fp); + + //close the stream + fclose(fp); + + return 0; +} + + +int writeFileR(const char *filename, sizedData data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data.data, 1, data.length, fp); + + //close the stream + fclose(fp); + + return 0; +} + +void crash(char *reason){ + PRINTE(reason); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/src/helpers.h b/src/helpers.h new file mode 100644 index 0000000..0b80d11 --- /dev/null +++ b/src/helpers.h @@ -0,0 +1,82 @@ +// just some random helpers and function I use. + +#ifndef __M_HELPERS_H__ +#define __M_HELPERS_H__ + + +#ifndef __cplusplus +#include +#define PRINT(message) printf("%s",message) +#define PRINTE(error) printf("%s",error) +#else +#include +#define PRINT(message) std::cout< +#include "rendering/init.h" +#include "rendering/vulkan/renderingpipeline.h" +#include "rendering/vulkan/svulc.h" +#include "helpers.h" + +int main(int argc, const char **argv){ + struct rendering_state s = {0}; + s.window_title = "Hi!"; + s.window_width = 100; + s.window_height = 100; + + rendering_initGLFW(&s); + rendering_vkinstance(&s); + + // TODO: make a function that selects the best, most suitable device + unsigned int physDeviceNum; + VkPhysicalDevice *physDevices; + + VkResult result = svlk_getPhysDeviceList(s.instance, &physDeviceNum, &physDevices); + + for (int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include "../helpers.h" +#include "init.h" + +VkShaderModule rendering_createShaderModule(sizedData shadercode, VkDevice device){ + VkShaderModuleCreateInfo createinfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + NULL, + 0, + shadercode.length, + shadercode.data + }; + VkShaderModule module; + if(vkCreateShaderModule(device, &createinfo, NULL, &module)!= VK_SUCCESS){ + printf("Couldn't create shader module!"); + exit(EXIT_FAILURE); + } + return module; +} + +void rendering_initGLFW(struct rendering_state *s){ + glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + s->window = glfwCreateWindow(s->window_width, s->window_height, s->window_title, NULL, NULL); + + s->extensions = glfwGetRequiredInstanceExtensions(&s->numextensions); +} + +void rendering_vkinstance(struct rendering_state *s){ + const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + s->error = svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance); + if(s->error != VK_SUCCESS) return; + s->error = glfwCreateWindowSurface(s->instance, s->window, NULL, &s->surface); + if(s->error != VK_SUCCESS) return; +} + +void rendering_vkgetfamilies(struct rendering_state *s){ + int error = 0; + s->queues.graphicsQFI = svlk_getQueueFamilyIndex(s->physdevice, VK_QUEUE_GRAPHICS_BIT, &error); + if(error){return;} + s->queues.presentationQFI = svlk_getPresentQueueFamilyIndex(s->physdevice, s->surface, &error); + if(error){return;} +} + +void rendering_vkdevice(struct rendering_state *s){ + float queuePriority = 1.f; + + VkDeviceQueueCreateInfo deviceQueues[] = { + svlk_createQueueInfo(s->queues.graphicsQFI, 1, &queuePriority) + }; + + VkPhysicalDeviceFeatures devFeatures = {0}; + + s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); +}; + +void rendering_vkswapchain(struct rendering_state *s){ + s->error = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities); + if(s->error != VK_SUCCESS) return; + + s->surfaceformat = svlk_selectSwapchainFormat(s->physdevice, s->surface, VK_FORMAT_B8G8R8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); + + s->swapchainExtent = svlk_checkSwapchainExtent(&s->surfaceCapabilities, s->window_width, s->window_height); + //TODO + s->swapchainNumimgs = s->surfaceCapabilities.minImageCount + 1; + if(s->swapchainNumimgs > s->surfaceCapabilities.maxImageCount && s->surfaceCapabilities.maxImageCount != 0) s->swapchainNumimgs = s->surfaceCapabilities.maxImageCount; + + VkSwapchainCreateInfoKHR swcreateinfo = svlk_createSwapchainCreateinfo( + s->swapchainNumimgs, + s->surface, + s->surfaceformat, + VK_PRESENT_MODE_FIFO_KHR, + s->swapchainExtent, + 1, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + s->surfaceCapabilities.currentTransform + ); + unsigned int queueFamilies[] = {s->queues.graphicsQFI, s->queues.presentationQFI}; + svlk_selectSharingMode(&swcreateinfo, s->queues.graphicsQFI != s->queues.presentationQFI, queueFamilies, 2); + + s->error = vkCreateSwapchainKHR(s->device, &swcreateinfo, NULL, &s->swapchain); + if(s->error != VK_SUCCESS) return; + + s->swapchainImages = svlk_getSwapchainImages(s->device, s->swapchain, &s->numswapchainImages); + + s->error = svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView); +} + +void rendering_createframebuffers(struct rendering_state *s){ + s->framebuffers = calloc(s->numswapchainImageView, sizeof(VkFramebuffer)); + s->numframebuffers = s->numswapchainImageView; + + for(int i = 0; i < s->numswapchainImageView; i++){ + VkImageView attachments[] = { + s->swapchainImageView[i] + }; + + VkFramebufferCreateInfo fbCI = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + NULL, + 0, + s->pipeline.renderpass, + 1, + attachments, + s->window_width, + s->window_height, + 1 + }; + vkCreateFramebuffer(s->device, &fbCI, NULL, &s->framebuffers[i]); + } +} + +/* Now a relic of the old wor-- I mean relic of dumbness +void rendering_createtemplate(struct rendering_state *s, struct rendering_settings *st){ + VkGraphicsPipelineCreateInfo *c = s->gptemplate = calloc(1, sizeof(VkGraphicsPipelineCreateInfo)); + c->sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + c->stageCount = 0; // must be filled out + c->pStages = NULL; // must be filled out + VkPipelineVertexInputStateCreateInfo *vkpvis; + c->pVertexInputState = vkpvis = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); + vkpvis->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vkpvis->vertexBindingDescriptionCount = 1; + VkVertexInputBindingDescription *bindes; + vkpvis->pVertexBindingDescriptions = bindes = calloc(1, sizeof(VkVertexInputBindingDescription)); + bindes->binding = 0; + bindes->inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindes->stride = sizeof(struct vertex); + vkpvis->vertexAttributeDescriptionCount = 2; + VkVertexInputAttributeDescription *atrdes; + vkpvis->pVertexAttributeDescriptions = atrdes = calloc(2, sizeof(VkVertexInputAttributeDescription)); + atrdes[0].location = 0; + atrdes[0].binding = 0; + atrdes[0].format = VK_FORMAT_R32G32B32_SFLOAT; + atrdes[0].offset = 0; + atrdes[1].location = 1; + atrdes[1].binding = 0; + atrdes[1].format = VK_FORMAT_R32G32_SFLOAT; + atrdes[1].offset = sizeof(float) * 3; + VkPipelineInputAssemblyStateCreateInfo *vkpias; + c->pInputAssemblyState = vkpias = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); + vkpias->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + vkpias->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + vkpias->primitiveRestartEnable = VK_FALSE; + c->pTessellationState = NULL; + VkPipelineViewportStateCreateInfo *vkpvs; + c->pViewportState = vkpvs = calloc(1, sizeof(VkPipelineViewportStateCreateInfo)); + vkpvs->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vkpvs->viewportCount = 1; + vkpvs->scissorCount = 1; + VkPipelineRasterizationStateCreateInfo *vkrs; + c->pRasterizationState = vkrs = calloc(1, sizeof(VkPipelineRasterizationStateCreateInfo)); + vkrs->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + vkrs->depthClampEnable = VK_FALSE; + vkrs->rasterizerDiscardEnable = VK_TRUE; + vkrs->polygonMode = VK_POLYGON_MODE_FILL; + vkrs->cullMode = VK_CULL_MODE_FRONT_BIT; + vkrs->frontFace = VK_FRONT_FACE_CLOCKWISE; + vkrs->depthBiasEnable = VK_FALSE; + //c->pMultisampleState = &svlk_noMultisample; + VkPipelineDepthStencilStateCreateInfo *vkds; + c->pDepthStencilState = vkds = calloc(1, sizeof(VkPipelineDepthStencilStateCreateInfo)); + vkds->sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + vkds->depthTestEnable = VK_TRUE; + vkds->depthWriteEnable = VK_FALSE; + vkds->depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; + vkds->depthBoundsTestEnable = VK_FALSE; + vkds->stencilTestEnable = VK_TRUE; + // TODO:front and back + vkds->minDepthBounds = 0; + vkds->maxDepthBounds = 1; + VkPipelineColorBlendStateCreateInfo *vkcbs; + c->pColorBlendState = vkcbs = calloc(1, sizeof(VkPipelineColorBlendStateCreateInfo)); + vkcbs->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + vkcbs->logicOpEnable = VK_FALSE; + vkcbs->logicOp = VK_LOGIC_OP_COPY; + vkcbs->attachmentCount = 1; + // vkcbs->pAttachments = &svlk_colorBlendTemplate; + VkPipelineDynamicStateCreateInfo *vkdys; + vkdys->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + vkdys->dynamicStateCount = 2; + VkDynamicState *vkdynst = calloc(2, sizeof(VkDynamicState)); + vkdys->pDynamicStates = vkdynst; + vkdynst[0] = VK_DYNAMIC_STATE_VIEWPORT; + vkdynst[1] = VK_DYNAMIC_STATE_SCISSOR; + +} +*/ \ No newline at end of file diff --git a/src/rendering/init.h b/src/rendering/init.h new file mode 100644 index 0000000..5d41c8d --- /dev/null +++ b/src/rendering/init.h @@ -0,0 +1,35 @@ +#ifndef __RENDERING_INIT_H__ +#define __RENDERING_INIT_H__ + +#include "struct.h" +#include "../helpers.h" +// s = the state struct + + +// init the glfw window +// requires: none +// produces: s.window, s.extensions, s.devextensions +void rendering_initGLFW(struct rendering_state *s); + +// requires: s.window +// produces: s.instance, s.surface, s.extensions(may be null), s.layers(may be null) +void rendering_vkinstance(struct rendering_state *s); + +// requires: everything untill s.physdevice +// produces: s.queues +void rendering_vkgetfamilies(struct rendering_state *s); + +// requires: s.queues, s.physdevice, s.surface, s.deviceextensions +// produces: s.device +void rendering_vkdevice(struct rendering_state *s); + +// requires: vkdevice, and all of it's requirements +// produces: s.swapchain +void rendering_vkswapchain(struct rendering_state *s); + +VkShaderModule rendering_createShaderModule(sizedData shadercode, VkDevice device); + +// requires: s.pipeline.renderpass +void rendering_createframebuffers(struct rendering_state *s); + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h new file mode 100644 index 0000000..2ea17ca --- /dev/null +++ b/src/rendering/pipeline3d/pipeline.h @@ -0,0 +1,17 @@ +#ifndef __PIPELINE3D_PIPELINE_H__ +#define __PIPELINE3D_PIPELINE_H__ + +#include + +struct pipeline3dSubpass { + VkPipelineLayout layout; + VkPipeline pipeline; + +}; + +struct pipeline3d { + VkRenderPass renderpass; + struct pipeline3dSubpass geometryPass; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c new file mode 100644 index 0000000..e680e52 --- /dev/null +++ b/src/rendering/pipeline3d/setup.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include "../../helpers.h" +#include "../init.h" +#include "../vulkan/renderingpipeline.h" +#include "../vulkan/svulc.h" + +#include "pipeline.h" +#include "setup.h" + +void createGeometryPassSD(VkSubpassDescription *desc){ + VkAttachmentReference *refrences = calloc(1, sizeof(VkAttachmentReference)); + VkAttachmentReference colorattachmentref = { + 0, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + refrences[0] = colorattachmentref; + + VkSubpassDescription gpdesc = { + 0, + VK_PIPELINE_BIND_POINT_GRAPHICS, + 0, + NULL, + 1, + &colorattachmentref, + NULL, + NULL, + 0, + NULL + }; + *desc = gpdesc; +} + +void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum){ + // Shader Code + sizedData vertshdcode = readFileR("shd/triangle.vert.spv"); + sizedData fragshdcode = readFileR("shd/triangle.frag.spv"); + + VkPipelineShaderStageCreateInfo vert = loadshader(vertshdcode, VK_SHADER_STAGE_VERTEX_BIT, s); + VkPipelineShaderStageCreateInfo frag = loadshader(vertshdcode, VK_SHADER_STAGE_FRAGMENT_BIT, s); + + VkPipelineShaderStageCreateInfo stages[] = {vert, frag}; + + + // Vertex Input + VkVertexInputBindingDescription bindes[1]; + bindes[0].binding = 0; + bindes[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindes[0].stride = sizeof(struct vertex); + + VkVertexInputAttributeDescription atdes[2]; + atdes[0].location = 0; + atdes[0].binding = 0; + atdes[0].format = VK_FORMAT_R32G32B32_SFLOAT; + atdes[0].offset = 0; + + atdes[1].location = 1; + atdes[1].binding = 0; + atdes[1].format = VK_FORMAT_R32G32_SFLOAT; + atdes[1].offset = sizeof(float) * 3; + + + // Fixed Function States + VkPipelineVertexInputStateCreateInfo inputState = svlk_createPipelineVertexInputStateCI(bindes, 1, atdes, 2, 0); + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = svlk_createPipelineInputAssemblyStateCI(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineViewportStateCreateInfo viewportState = svlk_createPipelineViewportStateCI(1, 1, 0); + VkPipelineRasterizationStateCreateInfo rasterizerState = svlk_createPipelineRasterizationStateCI(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, 0); + VkPipelineMultisampleStateCreateInfo multisampleState = svlk_createPipelineMultisampleStateCI(VK_SAMPLE_COUNT_1_BIT, 0); + VkPipelineDepthStencilStateCreateInfo depthState = svlk_createDepthStencilStateCI(VK_TRUE, VK_FALSE, VK_COMPARE_OP_GREATER_OR_EQUAL); + VkPipelineColorBlendAttachmentState colorblendatt = svlk_createColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorblendState = svlk_createColorBlendStateCI(1, &colorblendatt); + VkDynamicState dynstates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamicState = svlk_createDynamicStateCI(dynstates, 2, 0); + + // Pipeline Layout + VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(NULL, 0); + vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); + + + VkGraphicsPipelineCreateInfo pipelineCI = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + NULL, + 0, + 2, + stages, + &inputState, + &inputAssemblyState, + NULL, + &viewportState, + &rasterizerState, + &multisampleState, + &depthState, + &colorblendState, + &dynamicState, + p->geometryPass.layout, + p->renderpass, + subpassnum, + NULL, + -1 + }; + + vkCreateGraphicsPipelines(s->device, NULL, 1, &pipelineCI, NULL, &p->geometryPass.pipeline); +} + +VkRenderPassCreateInfo createRenderPass(uint32_t attachmentcount, VkAttachmentDescription *attdes, uint32_t subpasscount, VkSubpassDescription *subpasses){ + VkRenderPassCreateInfo renderpassCI = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + NULL, + 0, + attachmentcount, + attdes, + subpasscount, + subpasses, + }; + + return renderpassCI; +} + +void createPipeline3d(struct rendering_state *s){ + struct pipeline3d p; + + VkAttachmentDescription attdescs[1] = {0}; + + VkAttachmentDescription colordesc = { + 0, + s->surfaceformat.format, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + }; attdescs[0] = colordesc; + + VkSubpassDescription subpasses[1] = {0}; + createGeometryPassSD(&subpasses[0]); + + VkRenderPassCreateInfo rpCI = createRenderPass(1, attdescs, 1, subpasses); + vkCreateRenderPass(s->device, &rpCI, NULL, &p.renderpass); + + createGeometryPass(s, &p, 0); +} \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.h b/src/rendering/pipeline3d/setup.h new file mode 100644 index 0000000..fe938c7 --- /dev/null +++ b/src/rendering/pipeline3d/setup.h @@ -0,0 +1,4 @@ +#include + +void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum); +void createPipeline3d(struct rendering_state *s); \ No newline at end of file diff --git a/src/rendering/struct.h b/src/rendering/struct.h new file mode 100644 index 0000000..0f1906b --- /dev/null +++ b/src/rendering/struct.h @@ -0,0 +1,77 @@ +#ifndef __RENDERING_STRUCT_H__ +#define __RENDERING_STRUCT_H__ + +#include "pipeline3d/pipeline.h" +#include +#include +#include + +struct rendering_queueFamilies{ + int graphicsQFI; + VkQueue graphicsQueue; + int presentationQFI; + VkQueue presentationQueue; + + // if this struct is initialised yet + int present; +}; + +//general rendering settings (eg multisampling etc.) +struct rendering_settings{ + +}; + +struct vertex{ + // position + float x; + float y; + float z; + // texture + float u; + float v; +}; + +struct rendering_state{ + // must be filled out prior to initialising glfw + int window_width; + int window_height; + char *window_title; + + // the last error that occured + VkResult error; + + GLFWwindow *window; + + const char **extensions; + uint32_t numextensions; + + const char **deviceextensions; + uint32_t numdeviceextensions; + + const char **layers; + uint32_t numlayers; + + VkInstance instance; + VkSurfaceKHR surface; + VkPhysicalDevice physdevice; + struct rendering_queueFamilies queues; + VkDevice device; + + VkSurfaceFormatKHR surfaceformat; + VkSurfaceCapabilitiesKHR surfaceCapabilities; + VkExtent2D swapchainExtent; + VkSwapchainKHR swapchain; + int swapchainNumimgs; + VkImage *swapchainImages; + uint32_t numswapchainImages; + VkImageView *swapchainImageView; + uint32_t numswapchainImageView; + VkFramebuffer *framebuffers; + uint32_t numframebuffers; + + VkGraphicsPipelineCreateInfo *gptemplate; + + struct pipeline3d pipeline; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.c b/src/rendering/vulkan/commandpools.c new file mode 100644 index 0000000..7b88c52 --- /dev/null +++ b/src/rendering/vulkan/commandpools.c @@ -0,0 +1,44 @@ +#include "commandpools.h" +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool){ + VkCommandPoolCreateInfo createinfo = { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + NULL, + flags, + familyindex + }; + return vkCreateCommandPool(device, &createinfo, NULL, commandpool); +} + +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers){ + VkCommandBufferAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + NULL, + cmdpool, + level, + count + }; + return vkAllocateCommandBuffers(device, &allocinfo, cmdbuffers); +} + +VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + flags, + NULL + }; + + return vkBeginCommandBuffer(cmdbuf, &begininfo); +} + +VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore){ + VkSemaphoreCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, NULL, 0}; + return vkCreateSemaphore(device, &semaphoreCreateinfo, NULL, semaphore); +} + +VkResult svlk_createFence(VkDevice device, VkFence *fence){ + VkFenceCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, 0}; + return vkCreateFence(device, &semaphoreCreateinfo, NULL, fence); +} \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.h b/src/rendering/vulkan/commandpools.h new file mode 100644 index 0000000..fc83e8b --- /dev/null +++ b/src/rendering/vulkan/commandpools.h @@ -0,0 +1,12 @@ +#ifndef __COMMADPOOLS_H__ +#define __COMMADPOOLS_H__ + +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool); +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); +VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); + +VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore); +VkResult svlk_createFence(VkDevice device, VkFence *fence); +#endif \ No newline at end of file diff --git a/src/instance.c b/src/rendering/vulkan/instance.c similarity index 99% rename from src/instance.c rename to src/rendering/vulkan/instance.c index 0ba9244..946b9b6 100644 --- a/src/instance.c +++ b/src/rendering/vulkan/instance.c @@ -1,4 +1,4 @@ -#include "../include/svulc.h" +#include "svulc.h" #include #include #include diff --git a/src/rendering/vulkan/renderingpipeline.h b/src/rendering/vulkan/renderingpipeline.h new file mode 100644 index 0000000..69ca789 --- /dev/null +++ b/src/rendering/vulkan/renderingpipeline.h @@ -0,0 +1,279 @@ +#ifndef __RENDERING_PIPELINE_H__ +#define __RENDERING_PIPELINE_H__ +#include +#include "../struct.h" +#include "../../helpers.h" +#include "../init.h" + +static inline VkPipelineShaderStageCreateInfo loadshader(sizedData code, VkShaderStageFlagBits flags, struct rendering_state *s){ + VkShaderModule mod = rendering_createShaderModule(code, s->device); + + VkPipelineShaderStageCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + flags, + mod, + "main", + NULL + }; + return CI; +}; + +static inline VkPipelineVertexInputStateCreateInfo svlk_createPipelineVertexInputStateCI(VkVertexInputBindingDescription *bindes, uint32_t bindescount, VkVertexInputAttributeDescription *atdes, uint32_t atdescount, VkPipelineVertexInputStateCreateFlags flags){ + VkPipelineVertexInputStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + NULL, + flags, + bindescount, + bindes, + atdescount, + atdes + }; + return CI; +} +static inline VkPipelineInputAssemblyStateCreateInfo svlk_createPipelineInputAssemblyStateCI(VkPrimitiveTopology ptop, VkPipelineInputAssemblyStateCreateFlags flags, VkBool32 primitiveRestart){ + VkPipelineInputAssemblyStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + NULL, + flags, + ptop, + primitiveRestart + }; + return CI; +} +static inline VkPipelineViewportStateCreateInfo svlk_createPipelineViewportStateCI(uint32_t viewportCount, uint32_t scissorCount, VkPipelineViewportStateCreateFlags flags){ + VkPipelineViewportStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + NULL, + flags, + viewportCount, + NULL, + scissorCount, + NULL + }; + return CI; +} +static inline VkPipelineRasterizationStateCreateInfo svlk_createPipelineRasterizationStateCI(VkPolygonMode pmode, VkCullModeFlags cmf, VkFrontFace ff, VkPipelineRasterizationStateCreateFlags flags){ + VkPipelineRasterizationStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + NULL, + flags, + 0, + 0, + pmode, + cmf, + ff, + 0, + 0.f, + 0.f, + 0.f, + 0.f + }; + return CI; +} +static inline VkPipelineMultisampleStateCreateInfo svlk_createPipelineMultisampleStateCI(VkSampleCountFlagBits scf, VkPipelineMultisampleStateCreateFlags flags){ + VkPipelineMultisampleStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + NULL, + flags, + scf, + 0, + 0.f, + NULL, + 0, + 0 + }; + return CI; +} +static inline VkPipelineDepthStencilStateCreateInfo svlk_createDepthStencilStateCI(VkBool32 depthTest, VkBool32 depthWrite, VkCompareOp depthop){ + VkPipelineDepthStencilStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + NULL, + 0, + depthTest, + depthWrite, + depthop, + 0, + 0, + {}, + {.compareOp=VK_COMPARE_OP_ALWAYS}, + 0, + 0 + }; + return CI; +} +static inline VkPipelineColorBlendAttachmentState svlk_createColorBlendAttachmentState(VkColorComponentFlags colorWriteMask, VkBool32 blend){ + // vulkan dev, aka full time struct filler + VkPipelineColorBlendAttachmentState state = { + blend, + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + colorWriteMask + }; + return state; +} +static inline VkPipelineColorBlendStateCreateInfo svlk_createColorBlendStateCI(uint32_t attcount, VkPipelineColorBlendAttachmentState *atts){ + VkPipelineColorBlendStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + NULL, + 0, + 0, + VK_LOGIC_OP_OR, + attcount, + atts, + 0, + 0, + 0, + 0 + }; + return CI; +} +static inline VkPipelineDynamicStateCreateInfo svlk_createDynamicStateCI(VkDynamicState *states, uint32_t count, VkPipelineDynamicStateCreateFlags flags){ + VkPipelineDynamicStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + NULL, + flags, + count, + states + }; + return CI; +} +/*inline VkRenderPassCreateInfo svlk_createRenderPassCI(VkAttachmentDescription *att, uint32_t attcount, VkSubpassDescription *sub, uint32_t supcount, VkSubpassDependency *sud, uint32_t sudcount, VkRenderPassCreateFlags flags){ + +}*/ + +static inline VkPipelineLayoutCreateInfo svlk_createPipelineLayoutCI(VkDescriptorSetLayout *layouts, uint32_t count){ + VkPipelineLayoutCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + NULL, + 0, + count, + layouts, + 0, + NULL + }; + return CI; +} + + + + +/* +const VkPipelineShaderStageCreateInfo svlk_vertexShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_VERTEX_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineShaderStageCreateInfo svlk_fragmentShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_FRAGMENT_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineMultisampleStateCreateInfo svlk_noMultisample = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + NULL, + 0, + VK_SAMPLE_COUNT_1_BIT, + VK_FALSE, + 1.0f, + NULL, + VK_FALSE, + VK_FALSE +}; + +const VkPipelineVertexInputStateCreateInfo svlk_noVertexData = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL +}; + +const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + NULL, + 0, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_FALSE +}; + +const VkViewport svlk_viewportTemplate = { + 0.0f, + 0.0f, + 0.0f, // width and height must be set by user + 0.0f, // width and height must be set by user + 0.0f, + 1.0f +}; + +const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_FALSE, + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_CLOCKWISE, + VK_FALSE, + 0.0f, + 0.0f, + 0.0f, + 1.0f +}; + +const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate = { + VK_FALSE, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT +}; + +const VkPipelineColorBlendStateCreateInfo svlk_colorBlendCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_LOGIC_OP_COPY, + 0, // must be set by user + NULL, // must be set by user + 0.f, + 0.f, + 0.f, + 0.f +}; + +const VkPipelineLayoutCreateInfo svlk_pipelineLayoutCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL +}; +*/ +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/svulc.h b/src/rendering/vulkan/svulc.h new file mode 100644 index 0000000..8a61ea4 --- /dev/null +++ b/src/rendering/vulkan/svulc.h @@ -0,0 +1,28 @@ +#ifndef __SVULC_H__ +#define __SVULC_H__ + +#include + +VkResult svlk_createIinstance(const char **layers, int layersNum, const char **extensions, int extensionsNum, VkInstance *instance); + +VkResult svlk_getPhysDeviceList(VkInstance instance, unsigned int *num, VkPhysicalDevice **devicesPointer); + +int svlk_getQueueFamilyIndex(VkPhysicalDevice physDevice, VkQueueFlags queueFlag, int *error); + +int svlk_getPresentQueueFamilyIndex(VkPhysicalDevice physDevice, VkSurfaceKHR KHRsurface, int *error); + +int svlk_deviceCompatable(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int queueFamilies, const char **deviceExtensions, int deviceExtensionsNum); + +VkDeviceQueueCreateInfo svlk_createQueueInfo(int queueIndex, int count, float *priority); + +VkResult svlk_createLogicalDevice( + VkPhysicalDevice physDevice, + VkSurfaceKHR surface, + VkDeviceQueueCreateInfo *queueFamilies, + unsigned int queueFamiliesNum, + VkPhysicalDeviceFeatures deviceFeatures, + const char **deviceExtensions, + int deviceExtensionsNum, + VkDevice *device); + +#endif \ No newline at end of file diff --git a/src/swapchain.c b/src/rendering/vulkan/swapchain.c similarity index 91% rename from src/swapchain.c rename to src/rendering/vulkan/swapchain.c index e1afbaf..62e458f 100644 --- a/src/swapchain.c +++ b/src/rendering/vulkan/swapchain.c @@ -1,6 +1,6 @@ #ifndef __SWAPCHAIN_C__ #define __SWAPCHAIN_C__ -#include "../include/swapchain.h" +#include "swapchain.h" #include #include #include @@ -133,4 +133,14 @@ VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType return vkCreateImageView(device, &createInfo, NULL, imageView); } +VkResult svlk_createVkImageViews(VkDevice device, VkImage *images, VkImageViewType viewtype, VkFormat format, unsigned int numimgs, VkImageView **imageView){ + *imageView = malloc(numimgs * sizeof(VkImageView)); + VkResult result; + for(int i = 0; i < numimgs; i++){ + result = svlk_createVkImageView(device, images[i], viewtype, format, &(*imageView)[i]); + if(result != VK_SUCCESS) return result; + } + return VK_SUCCESS; +} + #endif \ No newline at end of file diff --git a/src/rendering/vulkan/swapchain.h b/src/rendering/vulkan/swapchain.h new file mode 100644 index 0000000..7051c80 --- /dev/null +++ b/src/rendering/vulkan/swapchain.h @@ -0,0 +1,36 @@ +#ifndef __SWAPCHAIN_H__ +#define __SWAPCHAIN_H__ +#include + +VkSurfaceFormatKHR *svlk_getSurfaceFormats(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkPresentModeKHR *svlk_getPresentModes(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfaceFormats, VkFormat format, VkColorSpaceKHR colorSpace, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace); + +VkExtent2D svlk_checkSwapchainExtent(const VkSurfaceCapabilitiesKHR *capabilities, int w, int h); + +VkSwapchainCreateInfoKHR svlk_createSwapchainCreateinfo( + unsigned int imageCount, + VkSurfaceKHR surface, + VkSurfaceFormatKHR format, + VkPresentModeKHR presentMode, + VkExtent2D extent, + int arrayLayers, + unsigned int usage, + VkSurfaceTransformFlagBitsKHR pretransform + ); + +void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent, unsigned int *queueFamilies, int queueFamiliesNum); + +VkResult svlk_createSwapchain(); + +VkImage *svlk_getSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, unsigned int *imageCount); + +VkImageViewCreateInfo svlk_createVkImageViewCreateInfo(VkImage image, VkImageViewType viewtype, VkFormat format); +VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType viewtype, VkFormat format, VkImageView *imageView); +VkResult svlk_createVkImageViews(VkDevice device, VkImage *images, VkImageViewType viewtype, VkFormat format, unsigned int numimgs, VkImageView **imageView); + +#endif \ No newline at end of file diff --git a/src/shd/triangle.frag b/src/shd/triangle.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/src/shd/triangle.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/src/shd/triangle.vert b/src/shd/triangle.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/src/shd/triangle.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} diff --git a/test/device-creation.c b/test/device-creation.c index 2830aea..a7cd1ab 100644 --- a/test/device-creation.c +++ b/test/device-creation.c @@ -169,13 +169,17 @@ int main(){ VkPipelineShaderStageCreateInfo shaderStages[] = {stageInfoVert, stageInfoFrag}; - VkViewport viewport = svlk_viewportTemplate; - viewport.width = swapchainExtent.width; - viewport.height = swapchainExtent.height; + VkDynamicState dynamicStates[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; - VkRect2D scissor = { - {0,0}, - swapchainExtent + VkPipelineDynamicStateCreateInfo dynamicState = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + NULL, + 0, + 2, + dynamicStates }; VkPipelineViewportStateCreateInfo viewportState = { @@ -183,9 +187,9 @@ int main(){ NULL, 0, 1, - &viewport, + NULL, 1, - &scissor + NULL }; VkPipelineRasterizationStateCreateInfo rasterizer = svlk_rasterizationTemplate; @@ -269,7 +273,7 @@ int main(){ &multisample, NULL, &colorblend, - NULL, + &dynamicState, pipelinelayout, renderPass, 0, @@ -327,5 +331,35 @@ int main(){ } + //---------------RENDERING------------------------ + int imageindex = 0; + VkClearValue clearcolor = {{{0.f,0.f,0.f,1.f}}}; + VkRenderPassBeginInfo rpbinfo = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + NULL, + renderPass, + swapchainFramebuffers[imageindex], + {{0,0}, swapchainExtent}, + 1, + &clearcolor + }; + vkCmdBeginRenderPass(commandBuffer, &rpbinfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + VkViewport viewport = svlk_viewportTemplate; + viewport.width = swapchainExtent.width; + viewport.height = swapchainExtent.height; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor = { + {0,0}, + swapchainExtent + }; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); } From b2bb85e17a88e412dd8702eb4af524b3cdbd7946 Mon Sep 17 00:00:00 2001 From: miri Date: Thu, 26 Dec 2024 23:51:12 +0100 Subject: [PATCH 10/22] small restructuring --- .gitignore | 4 +- include/commandpools.h | 1 + makefile | 6 + src/commandpools.c | 23 -- src/helpers.c | 88 +++++++ src/helpers.h | 82 +++++++ src/main.c | 43 ++++ src/rendering/init.c | 200 ++++++++++++++++ src/rendering/init.h | 35 +++ src/rendering/pipeline3d/pipeline.h | 17 ++ src/rendering/pipeline3d/setup.c | 144 ++++++++++++ src/rendering/pipeline3d/setup.h | 4 + src/rendering/struct.h | 77 +++++++ src/rendering/vulkan/commandpools.c | 44 ++++ src/rendering/vulkan/commandpools.h | 12 + src/{ => rendering/vulkan}/instance.c | 2 +- src/rendering/vulkan/renderingpipeline.h | 279 +++++++++++++++++++++++ src/rendering/vulkan/svulc.h | 28 +++ src/{ => rendering/vulkan}/swapchain.c | 12 +- src/rendering/vulkan/swapchain.h | 36 +++ src/shd/triangle.frag | 9 + src/shd/triangle.vert | 20 ++ test/device-creation.c | 52 ++++- 23 files changed, 1183 insertions(+), 35 deletions(-) create mode 100644 makefile delete mode 100644 src/commandpools.c create mode 100644 src/helpers.c create mode 100644 src/helpers.h create mode 100644 src/main.c create mode 100644 src/rendering/init.c create mode 100644 src/rendering/init.h create mode 100644 src/rendering/pipeline3d/pipeline.h create mode 100644 src/rendering/pipeline3d/setup.c create mode 100644 src/rendering/pipeline3d/setup.h create mode 100644 src/rendering/struct.h create mode 100644 src/rendering/vulkan/commandpools.c create mode 100644 src/rendering/vulkan/commandpools.h rename src/{ => rendering/vulkan}/instance.c (99%) create mode 100644 src/rendering/vulkan/renderingpipeline.h create mode 100644 src/rendering/vulkan/svulc.h rename src/{ => rendering/vulkan}/swapchain.c (91%) create mode 100644 src/rendering/vulkan/swapchain.h create mode 100644 src/shd/triangle.frag create mode 100644 src/shd/triangle.vert diff --git a/.gitignore b/.gitignore index 8483e07..16c0376 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ #output files a.out -main \ No newline at end of file +main +valgrind* +*.spv \ No newline at end of file diff --git a/include/commandpools.h b/include/commandpools.h index 1ed723d..e1e3793 100644 --- a/include/commandpools.h +++ b/include/commandpools.h @@ -5,4 +5,5 @@ VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool); VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); +VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); #endif \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..5ce9cd2 --- /dev/null +++ b/makefile @@ -0,0 +1,6 @@ +CC = clang +CLIBS = -lglfw -lvulkan + + +all: + $(CC) $(CLIBS) src/*.c src/rendering/*.c src/rendering/pipeline3d/*.c src/rendering/vulkan/*.c \ No newline at end of file diff --git a/src/commandpools.c b/src/commandpools.c deleted file mode 100644 index 8d76ba0..0000000 --- a/src/commandpools.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "../include/commandpools.h" -#include - -VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool){ - VkCommandPoolCreateInfo createinfo = { - VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - NULL, - flags, - familyindex - }; - return vkCreateCommandPool(device, &createinfo, NULL, commandpool); -} - -VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers){ - VkCommandBufferAllocateInfo allocinfo = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - NULL, - cmdpool, - level, - count - }; - return vkAllocateCommandBuffers(device, &allocinfo, cmdbuffers); -} \ No newline at end of file diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..ad23b3f --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,88 @@ +#include "helpers.h" +#include +#include + +char *readFileC(const char *filename){ + //open file + FILE *fp = fopen(filename, "r+"); + + if(!fp) return NULL; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the string on the heap + char *ret = (char*)malloc(len); + + //go back to the beginning and read everything to the string + fseek(fp, 0, SEEK_SET); + fread(ret, 1, len, fp); + + //close the stream + fclose(fp); + + return ret; +} + + +sizedData readFileR(const char *filename){ + sizedData ret = {NULL,0}; + + //open file + FILE *fp = fopen(filename, "r+"); + if(!fp) return ret; + + //go to end and get length + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + + //allocate enough memory for the data on the heap + ret.data = malloc(len); + + //go back to the beginning and read everything + fseek(fp, 0, SEEK_SET); + fread(ret.data, 1, len, fp); + + //set length of data + ret.length=len; + + //close the stream + fclose(fp); + + return ret; +} + +int writeFileC(const char *filename, char *data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data, sizeof(char), strlen(data), fp); + + //close the stream + fclose(fp); + + return 0; +} + + +int writeFileR(const char *filename, sizedData data){ + //open file + FILE *fp = fopen(filename, "w+"); + if(!fp) return -1; + + //write the data + fwrite(data.data, 1, data.length, fp); + + //close the stream + fclose(fp); + + return 0; +} + +void crash(char *reason){ + PRINTE(reason); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/src/helpers.h b/src/helpers.h new file mode 100644 index 0000000..0b80d11 --- /dev/null +++ b/src/helpers.h @@ -0,0 +1,82 @@ +// just some random helpers and function I use. + +#ifndef __M_HELPERS_H__ +#define __M_HELPERS_H__ + + +#ifndef __cplusplus +#include +#define PRINT(message) printf("%s",message) +#define PRINTE(error) printf("%s",error) +#else +#include +#define PRINT(message) std::cout< +#include "rendering/init.h" +#include "rendering/vulkan/renderingpipeline.h" +#include "rendering/vulkan/svulc.h" +#include "helpers.h" + +int main(int argc, const char **argv){ + struct rendering_state s = {0}; + s.window_title = "Hi!"; + s.window_width = 100; + s.window_height = 100; + + rendering_initGLFW(&s); + rendering_vkinstance(&s); + + // TODO: make a function that selects the best, most suitable device + unsigned int physDeviceNum; + VkPhysicalDevice *physDevices; + + VkResult result = svlk_getPhysDeviceList(s.instance, &physDeviceNum, &physDevices); + + for (int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include "../helpers.h" +#include "init.h" + +VkShaderModule rendering_createShaderModule(sizedData shadercode, VkDevice device){ + VkShaderModuleCreateInfo createinfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + NULL, + 0, + shadercode.length, + shadercode.data + }; + VkShaderModule module; + if(vkCreateShaderModule(device, &createinfo, NULL, &module)!= VK_SUCCESS){ + printf("Couldn't create shader module!"); + exit(EXIT_FAILURE); + } + return module; +} + +void rendering_initGLFW(struct rendering_state *s){ + glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + s->window = glfwCreateWindow(s->window_width, s->window_height, s->window_title, NULL, NULL); + + s->extensions = glfwGetRequiredInstanceExtensions(&s->numextensions); +} + +void rendering_vkinstance(struct rendering_state *s){ + const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + s->error = svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance); + if(s->error != VK_SUCCESS) return; + s->error = glfwCreateWindowSurface(s->instance, s->window, NULL, &s->surface); + if(s->error != VK_SUCCESS) return; +} + +void rendering_vkgetfamilies(struct rendering_state *s){ + int error = 0; + s->queues.graphicsQFI = svlk_getQueueFamilyIndex(s->physdevice, VK_QUEUE_GRAPHICS_BIT, &error); + if(error){return;} + s->queues.presentationQFI = svlk_getPresentQueueFamilyIndex(s->physdevice, s->surface, &error); + if(error){return;} +} + +void rendering_vkdevice(struct rendering_state *s){ + float queuePriority = 1.f; + + VkDeviceQueueCreateInfo deviceQueues[] = { + svlk_createQueueInfo(s->queues.graphicsQFI, 1, &queuePriority) + }; + + VkPhysicalDeviceFeatures devFeatures = {0}; + + s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); +}; + +void rendering_vkswapchain(struct rendering_state *s){ + s->error = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities); + if(s->error != VK_SUCCESS) return; + + s->surfaceformat = svlk_selectSwapchainFormat(s->physdevice, s->surface, VK_FORMAT_B8G8R8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); + + s->swapchainExtent = svlk_checkSwapchainExtent(&s->surfaceCapabilities, s->window_width, s->window_height); + //TODO + s->swapchainNumimgs = s->surfaceCapabilities.minImageCount + 1; + if(s->swapchainNumimgs > s->surfaceCapabilities.maxImageCount && s->surfaceCapabilities.maxImageCount != 0) s->swapchainNumimgs = s->surfaceCapabilities.maxImageCount; + + VkSwapchainCreateInfoKHR swcreateinfo = svlk_createSwapchainCreateinfo( + s->swapchainNumimgs, + s->surface, + s->surfaceformat, + VK_PRESENT_MODE_FIFO_KHR, + s->swapchainExtent, + 1, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + s->surfaceCapabilities.currentTransform + ); + unsigned int queueFamilies[] = {s->queues.graphicsQFI, s->queues.presentationQFI}; + svlk_selectSharingMode(&swcreateinfo, s->queues.graphicsQFI != s->queues.presentationQFI, queueFamilies, 2); + + s->error = vkCreateSwapchainKHR(s->device, &swcreateinfo, NULL, &s->swapchain); + if(s->error != VK_SUCCESS) return; + + s->swapchainImages = svlk_getSwapchainImages(s->device, s->swapchain, &s->numswapchainImages); + + s->error = svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView); +} + +void rendering_createframebuffers(struct rendering_state *s){ + s->framebuffers = calloc(s->numswapchainImageView, sizeof(VkFramebuffer)); + s->numframebuffers = s->numswapchainImageView; + + for(int i = 0; i < s->numswapchainImageView; i++){ + VkImageView attachments[] = { + s->swapchainImageView[i] + }; + + VkFramebufferCreateInfo fbCI = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + NULL, + 0, + s->pipeline.renderpass, + 1, + attachments, + s->window_width, + s->window_height, + 1 + }; + vkCreateFramebuffer(s->device, &fbCI, NULL, &s->framebuffers[i]); + } +} + +/* Now a relic of the old wor-- I mean relic of dumbness +void rendering_createtemplate(struct rendering_state *s, struct rendering_settings *st){ + VkGraphicsPipelineCreateInfo *c = s->gptemplate = calloc(1, sizeof(VkGraphicsPipelineCreateInfo)); + c->sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + c->stageCount = 0; // must be filled out + c->pStages = NULL; // must be filled out + VkPipelineVertexInputStateCreateInfo *vkpvis; + c->pVertexInputState = vkpvis = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); + vkpvis->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vkpvis->vertexBindingDescriptionCount = 1; + VkVertexInputBindingDescription *bindes; + vkpvis->pVertexBindingDescriptions = bindes = calloc(1, sizeof(VkVertexInputBindingDescription)); + bindes->binding = 0; + bindes->inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindes->stride = sizeof(struct vertex); + vkpvis->vertexAttributeDescriptionCount = 2; + VkVertexInputAttributeDescription *atrdes; + vkpvis->pVertexAttributeDescriptions = atrdes = calloc(2, sizeof(VkVertexInputAttributeDescription)); + atrdes[0].location = 0; + atrdes[0].binding = 0; + atrdes[0].format = VK_FORMAT_R32G32B32_SFLOAT; + atrdes[0].offset = 0; + atrdes[1].location = 1; + atrdes[1].binding = 0; + atrdes[1].format = VK_FORMAT_R32G32_SFLOAT; + atrdes[1].offset = sizeof(float) * 3; + VkPipelineInputAssemblyStateCreateInfo *vkpias; + c->pInputAssemblyState = vkpias = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); + vkpias->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + vkpias->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + vkpias->primitiveRestartEnable = VK_FALSE; + c->pTessellationState = NULL; + VkPipelineViewportStateCreateInfo *vkpvs; + c->pViewportState = vkpvs = calloc(1, sizeof(VkPipelineViewportStateCreateInfo)); + vkpvs->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vkpvs->viewportCount = 1; + vkpvs->scissorCount = 1; + VkPipelineRasterizationStateCreateInfo *vkrs; + c->pRasterizationState = vkrs = calloc(1, sizeof(VkPipelineRasterizationStateCreateInfo)); + vkrs->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + vkrs->depthClampEnable = VK_FALSE; + vkrs->rasterizerDiscardEnable = VK_TRUE; + vkrs->polygonMode = VK_POLYGON_MODE_FILL; + vkrs->cullMode = VK_CULL_MODE_FRONT_BIT; + vkrs->frontFace = VK_FRONT_FACE_CLOCKWISE; + vkrs->depthBiasEnable = VK_FALSE; + //c->pMultisampleState = &svlk_noMultisample; + VkPipelineDepthStencilStateCreateInfo *vkds; + c->pDepthStencilState = vkds = calloc(1, sizeof(VkPipelineDepthStencilStateCreateInfo)); + vkds->sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + vkds->depthTestEnable = VK_TRUE; + vkds->depthWriteEnable = VK_FALSE; + vkds->depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; + vkds->depthBoundsTestEnable = VK_FALSE; + vkds->stencilTestEnable = VK_TRUE; + // TODO:front and back + vkds->minDepthBounds = 0; + vkds->maxDepthBounds = 1; + VkPipelineColorBlendStateCreateInfo *vkcbs; + c->pColorBlendState = vkcbs = calloc(1, sizeof(VkPipelineColorBlendStateCreateInfo)); + vkcbs->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + vkcbs->logicOpEnable = VK_FALSE; + vkcbs->logicOp = VK_LOGIC_OP_COPY; + vkcbs->attachmentCount = 1; + // vkcbs->pAttachments = &svlk_colorBlendTemplate; + VkPipelineDynamicStateCreateInfo *vkdys; + vkdys->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + vkdys->dynamicStateCount = 2; + VkDynamicState *vkdynst = calloc(2, sizeof(VkDynamicState)); + vkdys->pDynamicStates = vkdynst; + vkdynst[0] = VK_DYNAMIC_STATE_VIEWPORT; + vkdynst[1] = VK_DYNAMIC_STATE_SCISSOR; + +} +*/ \ No newline at end of file diff --git a/src/rendering/init.h b/src/rendering/init.h new file mode 100644 index 0000000..5d41c8d --- /dev/null +++ b/src/rendering/init.h @@ -0,0 +1,35 @@ +#ifndef __RENDERING_INIT_H__ +#define __RENDERING_INIT_H__ + +#include "struct.h" +#include "../helpers.h" +// s = the state struct + + +// init the glfw window +// requires: none +// produces: s.window, s.extensions, s.devextensions +void rendering_initGLFW(struct rendering_state *s); + +// requires: s.window +// produces: s.instance, s.surface, s.extensions(may be null), s.layers(may be null) +void rendering_vkinstance(struct rendering_state *s); + +// requires: everything untill s.physdevice +// produces: s.queues +void rendering_vkgetfamilies(struct rendering_state *s); + +// requires: s.queues, s.physdevice, s.surface, s.deviceextensions +// produces: s.device +void rendering_vkdevice(struct rendering_state *s); + +// requires: vkdevice, and all of it's requirements +// produces: s.swapchain +void rendering_vkswapchain(struct rendering_state *s); + +VkShaderModule rendering_createShaderModule(sizedData shadercode, VkDevice device); + +// requires: s.pipeline.renderpass +void rendering_createframebuffers(struct rendering_state *s); + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h new file mode 100644 index 0000000..2ea17ca --- /dev/null +++ b/src/rendering/pipeline3d/pipeline.h @@ -0,0 +1,17 @@ +#ifndef __PIPELINE3D_PIPELINE_H__ +#define __PIPELINE3D_PIPELINE_H__ + +#include + +struct pipeline3dSubpass { + VkPipelineLayout layout; + VkPipeline pipeline; + +}; + +struct pipeline3d { + VkRenderPass renderpass; + struct pipeline3dSubpass geometryPass; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c new file mode 100644 index 0000000..e680e52 --- /dev/null +++ b/src/rendering/pipeline3d/setup.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include "../../helpers.h" +#include "../init.h" +#include "../vulkan/renderingpipeline.h" +#include "../vulkan/svulc.h" + +#include "pipeline.h" +#include "setup.h" + +void createGeometryPassSD(VkSubpassDescription *desc){ + VkAttachmentReference *refrences = calloc(1, sizeof(VkAttachmentReference)); + VkAttachmentReference colorattachmentref = { + 0, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + refrences[0] = colorattachmentref; + + VkSubpassDescription gpdesc = { + 0, + VK_PIPELINE_BIND_POINT_GRAPHICS, + 0, + NULL, + 1, + &colorattachmentref, + NULL, + NULL, + 0, + NULL + }; + *desc = gpdesc; +} + +void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum){ + // Shader Code + sizedData vertshdcode = readFileR("shd/triangle.vert.spv"); + sizedData fragshdcode = readFileR("shd/triangle.frag.spv"); + + VkPipelineShaderStageCreateInfo vert = loadshader(vertshdcode, VK_SHADER_STAGE_VERTEX_BIT, s); + VkPipelineShaderStageCreateInfo frag = loadshader(vertshdcode, VK_SHADER_STAGE_FRAGMENT_BIT, s); + + VkPipelineShaderStageCreateInfo stages[] = {vert, frag}; + + + // Vertex Input + VkVertexInputBindingDescription bindes[1]; + bindes[0].binding = 0; + bindes[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindes[0].stride = sizeof(struct vertex); + + VkVertexInputAttributeDescription atdes[2]; + atdes[0].location = 0; + atdes[0].binding = 0; + atdes[0].format = VK_FORMAT_R32G32B32_SFLOAT; + atdes[0].offset = 0; + + atdes[1].location = 1; + atdes[1].binding = 0; + atdes[1].format = VK_FORMAT_R32G32_SFLOAT; + atdes[1].offset = sizeof(float) * 3; + + + // Fixed Function States + VkPipelineVertexInputStateCreateInfo inputState = svlk_createPipelineVertexInputStateCI(bindes, 1, atdes, 2, 0); + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = svlk_createPipelineInputAssemblyStateCI(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineViewportStateCreateInfo viewportState = svlk_createPipelineViewportStateCI(1, 1, 0); + VkPipelineRasterizationStateCreateInfo rasterizerState = svlk_createPipelineRasterizationStateCI(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, 0); + VkPipelineMultisampleStateCreateInfo multisampleState = svlk_createPipelineMultisampleStateCI(VK_SAMPLE_COUNT_1_BIT, 0); + VkPipelineDepthStencilStateCreateInfo depthState = svlk_createDepthStencilStateCI(VK_TRUE, VK_FALSE, VK_COMPARE_OP_GREATER_OR_EQUAL); + VkPipelineColorBlendAttachmentState colorblendatt = svlk_createColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorblendState = svlk_createColorBlendStateCI(1, &colorblendatt); + VkDynamicState dynstates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamicState = svlk_createDynamicStateCI(dynstates, 2, 0); + + // Pipeline Layout + VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(NULL, 0); + vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); + + + VkGraphicsPipelineCreateInfo pipelineCI = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + NULL, + 0, + 2, + stages, + &inputState, + &inputAssemblyState, + NULL, + &viewportState, + &rasterizerState, + &multisampleState, + &depthState, + &colorblendState, + &dynamicState, + p->geometryPass.layout, + p->renderpass, + subpassnum, + NULL, + -1 + }; + + vkCreateGraphicsPipelines(s->device, NULL, 1, &pipelineCI, NULL, &p->geometryPass.pipeline); +} + +VkRenderPassCreateInfo createRenderPass(uint32_t attachmentcount, VkAttachmentDescription *attdes, uint32_t subpasscount, VkSubpassDescription *subpasses){ + VkRenderPassCreateInfo renderpassCI = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + NULL, + 0, + attachmentcount, + attdes, + subpasscount, + subpasses, + }; + + return renderpassCI; +} + +void createPipeline3d(struct rendering_state *s){ + struct pipeline3d p; + + VkAttachmentDescription attdescs[1] = {0}; + + VkAttachmentDescription colordesc = { + 0, + s->surfaceformat.format, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + }; attdescs[0] = colordesc; + + VkSubpassDescription subpasses[1] = {0}; + createGeometryPassSD(&subpasses[0]); + + VkRenderPassCreateInfo rpCI = createRenderPass(1, attdescs, 1, subpasses); + vkCreateRenderPass(s->device, &rpCI, NULL, &p.renderpass); + + createGeometryPass(s, &p, 0); +} \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.h b/src/rendering/pipeline3d/setup.h new file mode 100644 index 0000000..fe938c7 --- /dev/null +++ b/src/rendering/pipeline3d/setup.h @@ -0,0 +1,4 @@ +#include + +void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum); +void createPipeline3d(struct rendering_state *s); \ No newline at end of file diff --git a/src/rendering/struct.h b/src/rendering/struct.h new file mode 100644 index 0000000..0f1906b --- /dev/null +++ b/src/rendering/struct.h @@ -0,0 +1,77 @@ +#ifndef __RENDERING_STRUCT_H__ +#define __RENDERING_STRUCT_H__ + +#include "pipeline3d/pipeline.h" +#include +#include +#include + +struct rendering_queueFamilies{ + int graphicsQFI; + VkQueue graphicsQueue; + int presentationQFI; + VkQueue presentationQueue; + + // if this struct is initialised yet + int present; +}; + +//general rendering settings (eg multisampling etc.) +struct rendering_settings{ + +}; + +struct vertex{ + // position + float x; + float y; + float z; + // texture + float u; + float v; +}; + +struct rendering_state{ + // must be filled out prior to initialising glfw + int window_width; + int window_height; + char *window_title; + + // the last error that occured + VkResult error; + + GLFWwindow *window; + + const char **extensions; + uint32_t numextensions; + + const char **deviceextensions; + uint32_t numdeviceextensions; + + const char **layers; + uint32_t numlayers; + + VkInstance instance; + VkSurfaceKHR surface; + VkPhysicalDevice physdevice; + struct rendering_queueFamilies queues; + VkDevice device; + + VkSurfaceFormatKHR surfaceformat; + VkSurfaceCapabilitiesKHR surfaceCapabilities; + VkExtent2D swapchainExtent; + VkSwapchainKHR swapchain; + int swapchainNumimgs; + VkImage *swapchainImages; + uint32_t numswapchainImages; + VkImageView *swapchainImageView; + uint32_t numswapchainImageView; + VkFramebuffer *framebuffers; + uint32_t numframebuffers; + + VkGraphicsPipelineCreateInfo *gptemplate; + + struct pipeline3d pipeline; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.c b/src/rendering/vulkan/commandpools.c new file mode 100644 index 0000000..7b88c52 --- /dev/null +++ b/src/rendering/vulkan/commandpools.c @@ -0,0 +1,44 @@ +#include "commandpools.h" +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool){ + VkCommandPoolCreateInfo createinfo = { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + NULL, + flags, + familyindex + }; + return vkCreateCommandPool(device, &createinfo, NULL, commandpool); +} + +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers){ + VkCommandBufferAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + NULL, + cmdpool, + level, + count + }; + return vkAllocateCommandBuffers(device, &allocinfo, cmdbuffers); +} + +VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + flags, + NULL + }; + + return vkBeginCommandBuffer(cmdbuf, &begininfo); +} + +VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore){ + VkSemaphoreCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, NULL, 0}; + return vkCreateSemaphore(device, &semaphoreCreateinfo, NULL, semaphore); +} + +VkResult svlk_createFence(VkDevice device, VkFence *fence){ + VkFenceCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, 0}; + return vkCreateFence(device, &semaphoreCreateinfo, NULL, fence); +} \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.h b/src/rendering/vulkan/commandpools.h new file mode 100644 index 0000000..fc83e8b --- /dev/null +++ b/src/rendering/vulkan/commandpools.h @@ -0,0 +1,12 @@ +#ifndef __COMMADPOOLS_H__ +#define __COMMADPOOLS_H__ + +#include + +VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkCommandPool *commandpool); +VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); +VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); + +VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore); +VkResult svlk_createFence(VkDevice device, VkFence *fence); +#endif \ No newline at end of file diff --git a/src/instance.c b/src/rendering/vulkan/instance.c similarity index 99% rename from src/instance.c rename to src/rendering/vulkan/instance.c index 0ba9244..946b9b6 100644 --- a/src/instance.c +++ b/src/rendering/vulkan/instance.c @@ -1,4 +1,4 @@ -#include "../include/svulc.h" +#include "svulc.h" #include #include #include diff --git a/src/rendering/vulkan/renderingpipeline.h b/src/rendering/vulkan/renderingpipeline.h new file mode 100644 index 0000000..69ca789 --- /dev/null +++ b/src/rendering/vulkan/renderingpipeline.h @@ -0,0 +1,279 @@ +#ifndef __RENDERING_PIPELINE_H__ +#define __RENDERING_PIPELINE_H__ +#include +#include "../struct.h" +#include "../../helpers.h" +#include "../init.h" + +static inline VkPipelineShaderStageCreateInfo loadshader(sizedData code, VkShaderStageFlagBits flags, struct rendering_state *s){ + VkShaderModule mod = rendering_createShaderModule(code, s->device); + + VkPipelineShaderStageCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + flags, + mod, + "main", + NULL + }; + return CI; +}; + +static inline VkPipelineVertexInputStateCreateInfo svlk_createPipelineVertexInputStateCI(VkVertexInputBindingDescription *bindes, uint32_t bindescount, VkVertexInputAttributeDescription *atdes, uint32_t atdescount, VkPipelineVertexInputStateCreateFlags flags){ + VkPipelineVertexInputStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + NULL, + flags, + bindescount, + bindes, + atdescount, + atdes + }; + return CI; +} +static inline VkPipelineInputAssemblyStateCreateInfo svlk_createPipelineInputAssemblyStateCI(VkPrimitiveTopology ptop, VkPipelineInputAssemblyStateCreateFlags flags, VkBool32 primitiveRestart){ + VkPipelineInputAssemblyStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + NULL, + flags, + ptop, + primitiveRestart + }; + return CI; +} +static inline VkPipelineViewportStateCreateInfo svlk_createPipelineViewportStateCI(uint32_t viewportCount, uint32_t scissorCount, VkPipelineViewportStateCreateFlags flags){ + VkPipelineViewportStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + NULL, + flags, + viewportCount, + NULL, + scissorCount, + NULL + }; + return CI; +} +static inline VkPipelineRasterizationStateCreateInfo svlk_createPipelineRasterizationStateCI(VkPolygonMode pmode, VkCullModeFlags cmf, VkFrontFace ff, VkPipelineRasterizationStateCreateFlags flags){ + VkPipelineRasterizationStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + NULL, + flags, + 0, + 0, + pmode, + cmf, + ff, + 0, + 0.f, + 0.f, + 0.f, + 0.f + }; + return CI; +} +static inline VkPipelineMultisampleStateCreateInfo svlk_createPipelineMultisampleStateCI(VkSampleCountFlagBits scf, VkPipelineMultisampleStateCreateFlags flags){ + VkPipelineMultisampleStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + NULL, + flags, + scf, + 0, + 0.f, + NULL, + 0, + 0 + }; + return CI; +} +static inline VkPipelineDepthStencilStateCreateInfo svlk_createDepthStencilStateCI(VkBool32 depthTest, VkBool32 depthWrite, VkCompareOp depthop){ + VkPipelineDepthStencilStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + NULL, + 0, + depthTest, + depthWrite, + depthop, + 0, + 0, + {}, + {.compareOp=VK_COMPARE_OP_ALWAYS}, + 0, + 0 + }; + return CI; +} +static inline VkPipelineColorBlendAttachmentState svlk_createColorBlendAttachmentState(VkColorComponentFlags colorWriteMask, VkBool32 blend){ + // vulkan dev, aka full time struct filler + VkPipelineColorBlendAttachmentState state = { + blend, + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + colorWriteMask + }; + return state; +} +static inline VkPipelineColorBlendStateCreateInfo svlk_createColorBlendStateCI(uint32_t attcount, VkPipelineColorBlendAttachmentState *atts){ + VkPipelineColorBlendStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + NULL, + 0, + 0, + VK_LOGIC_OP_OR, + attcount, + atts, + 0, + 0, + 0, + 0 + }; + return CI; +} +static inline VkPipelineDynamicStateCreateInfo svlk_createDynamicStateCI(VkDynamicState *states, uint32_t count, VkPipelineDynamicStateCreateFlags flags){ + VkPipelineDynamicStateCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + NULL, + flags, + count, + states + }; + return CI; +} +/*inline VkRenderPassCreateInfo svlk_createRenderPassCI(VkAttachmentDescription *att, uint32_t attcount, VkSubpassDescription *sub, uint32_t supcount, VkSubpassDependency *sud, uint32_t sudcount, VkRenderPassCreateFlags flags){ + +}*/ + +static inline VkPipelineLayoutCreateInfo svlk_createPipelineLayoutCI(VkDescriptorSetLayout *layouts, uint32_t count){ + VkPipelineLayoutCreateInfo CI = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + NULL, + 0, + count, + layouts, + 0, + NULL + }; + return CI; +} + + + + +/* +const VkPipelineShaderStageCreateInfo svlk_vertexShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_VERTEX_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineShaderStageCreateInfo svlk_fragmentShaderTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + NULL, + 0, + VK_SHADER_STAGE_FRAGMENT_BIT, + NULL, // must be changed by user + "main", + NULL +}; + +const VkPipelineMultisampleStateCreateInfo svlk_noMultisample = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + NULL, + 0, + VK_SAMPLE_COUNT_1_BIT, + VK_FALSE, + 1.0f, + NULL, + VK_FALSE, + VK_FALSE +}; + +const VkPipelineVertexInputStateCreateInfo svlk_noVertexData = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL +}; + +const VkPipelineInputAssemblyStateCreateInfo svlk_inputAssemblyTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + NULL, + 0, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_FALSE +}; + +const VkViewport svlk_viewportTemplate = { + 0.0f, + 0.0f, + 0.0f, // width and height must be set by user + 0.0f, // width and height must be set by user + 0.0f, + 1.0f +}; + +const VkPipelineRasterizationStateCreateInfo svlk_rasterizationTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_FALSE, + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_CLOCKWISE, + VK_FALSE, + 0.0f, + 0.0f, + 0.0f, + 1.0f +}; + +const VkPipelineColorBlendAttachmentState svlk_colorBlendTemplate = { + VK_FALSE, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + VK_BLEND_OP_ADD, + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT +}; + +const VkPipelineColorBlendStateCreateInfo svlk_colorBlendCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + NULL, + 0, + VK_FALSE, + VK_LOGIC_OP_COPY, + 0, // must be set by user + NULL, // must be set by user + 0.f, + 0.f, + 0.f, + 0.f +}; + +const VkPipelineLayoutCreateInfo svlk_pipelineLayoutCreateInfoTemplate = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + NULL, + 0, + 0, + NULL, + 0, + NULL +}; +*/ +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/svulc.h b/src/rendering/vulkan/svulc.h new file mode 100644 index 0000000..8a61ea4 --- /dev/null +++ b/src/rendering/vulkan/svulc.h @@ -0,0 +1,28 @@ +#ifndef __SVULC_H__ +#define __SVULC_H__ + +#include + +VkResult svlk_createIinstance(const char **layers, int layersNum, const char **extensions, int extensionsNum, VkInstance *instance); + +VkResult svlk_getPhysDeviceList(VkInstance instance, unsigned int *num, VkPhysicalDevice **devicesPointer); + +int svlk_getQueueFamilyIndex(VkPhysicalDevice physDevice, VkQueueFlags queueFlag, int *error); + +int svlk_getPresentQueueFamilyIndex(VkPhysicalDevice physDevice, VkSurfaceKHR KHRsurface, int *error); + +int svlk_deviceCompatable(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int queueFamilies, const char **deviceExtensions, int deviceExtensionsNum); + +VkDeviceQueueCreateInfo svlk_createQueueInfo(int queueIndex, int count, float *priority); + +VkResult svlk_createLogicalDevice( + VkPhysicalDevice physDevice, + VkSurfaceKHR surface, + VkDeviceQueueCreateInfo *queueFamilies, + unsigned int queueFamiliesNum, + VkPhysicalDeviceFeatures deviceFeatures, + const char **deviceExtensions, + int deviceExtensionsNum, + VkDevice *device); + +#endif \ No newline at end of file diff --git a/src/swapchain.c b/src/rendering/vulkan/swapchain.c similarity index 91% rename from src/swapchain.c rename to src/rendering/vulkan/swapchain.c index e1afbaf..62e458f 100644 --- a/src/swapchain.c +++ b/src/rendering/vulkan/swapchain.c @@ -1,6 +1,6 @@ #ifndef __SWAPCHAIN_C__ #define __SWAPCHAIN_C__ -#include "../include/swapchain.h" +#include "swapchain.h" #include #include #include @@ -133,4 +133,14 @@ VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType return vkCreateImageView(device, &createInfo, NULL, imageView); } +VkResult svlk_createVkImageViews(VkDevice device, VkImage *images, VkImageViewType viewtype, VkFormat format, unsigned int numimgs, VkImageView **imageView){ + *imageView = malloc(numimgs * sizeof(VkImageView)); + VkResult result; + for(int i = 0; i < numimgs; i++){ + result = svlk_createVkImageView(device, images[i], viewtype, format, &(*imageView)[i]); + if(result != VK_SUCCESS) return result; + } + return VK_SUCCESS; +} + #endif \ No newline at end of file diff --git a/src/rendering/vulkan/swapchain.h b/src/rendering/vulkan/swapchain.h new file mode 100644 index 0000000..7051c80 --- /dev/null +++ b/src/rendering/vulkan/swapchain.h @@ -0,0 +1,36 @@ +#ifndef __SWAPCHAIN_H__ +#define __SWAPCHAIN_H__ +#include + +VkSurfaceFormatKHR *svlk_getSurfaceFormats(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkPresentModeKHR *svlk_getPresentModes(VkPhysicalDevice physDevice, VkSurfaceKHR surface, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormatFromList(VkSurfaceFormatKHR *surfaceFormats, VkFormat format, VkColorSpaceKHR colorSpace, unsigned int *len); + +VkSurfaceFormatKHR svlk_selectSwapchainFormat(VkPhysicalDevice physDevice, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorSpace); + +VkExtent2D svlk_checkSwapchainExtent(const VkSurfaceCapabilitiesKHR *capabilities, int w, int h); + +VkSwapchainCreateInfoKHR svlk_createSwapchainCreateinfo( + unsigned int imageCount, + VkSurfaceKHR surface, + VkSurfaceFormatKHR format, + VkPresentModeKHR presentMode, + VkExtent2D extent, + int arrayLayers, + unsigned int usage, + VkSurfaceTransformFlagBitsKHR pretransform + ); + +void svlk_selectSharingMode(VkSwapchainCreateInfoKHR *createinfo, int concurrent, unsigned int *queueFamilies, int queueFamiliesNum); + +VkResult svlk_createSwapchain(); + +VkImage *svlk_getSwapchainImages(VkDevice device, VkSwapchainKHR swapchain, unsigned int *imageCount); + +VkImageViewCreateInfo svlk_createVkImageViewCreateInfo(VkImage image, VkImageViewType viewtype, VkFormat format); +VkResult svlk_createVkImageView(VkDevice device, VkImage image, VkImageViewType viewtype, VkFormat format, VkImageView *imageView); +VkResult svlk_createVkImageViews(VkDevice device, VkImage *images, VkImageViewType viewtype, VkFormat format, unsigned int numimgs, VkImageView **imageView); + +#endif \ No newline at end of file diff --git a/src/shd/triangle.frag b/src/shd/triangle.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/src/shd/triangle.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/src/shd/triangle.vert b/src/shd/triangle.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/src/shd/triangle.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} diff --git a/test/device-creation.c b/test/device-creation.c index 2830aea..a7cd1ab 100644 --- a/test/device-creation.c +++ b/test/device-creation.c @@ -169,13 +169,17 @@ int main(){ VkPipelineShaderStageCreateInfo shaderStages[] = {stageInfoVert, stageInfoFrag}; - VkViewport viewport = svlk_viewportTemplate; - viewport.width = swapchainExtent.width; - viewport.height = swapchainExtent.height; + VkDynamicState dynamicStates[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; - VkRect2D scissor = { - {0,0}, - swapchainExtent + VkPipelineDynamicStateCreateInfo dynamicState = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + NULL, + 0, + 2, + dynamicStates }; VkPipelineViewportStateCreateInfo viewportState = { @@ -183,9 +187,9 @@ int main(){ NULL, 0, 1, - &viewport, + NULL, 1, - &scissor + NULL }; VkPipelineRasterizationStateCreateInfo rasterizer = svlk_rasterizationTemplate; @@ -269,7 +273,7 @@ int main(){ &multisample, NULL, &colorblend, - NULL, + &dynamicState, pipelinelayout, renderPass, 0, @@ -327,5 +331,35 @@ int main(){ } + //---------------RENDERING------------------------ + int imageindex = 0; + VkClearValue clearcolor = {{{0.f,0.f,0.f,1.f}}}; + VkRenderPassBeginInfo rpbinfo = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + NULL, + renderPass, + swapchainFramebuffers[imageindex], + {{0,0}, swapchainExtent}, + 1, + &clearcolor + }; + vkCmdBeginRenderPass(commandBuffer, &rpbinfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + VkViewport viewport = svlk_viewportTemplate; + viewport.width = swapchainExtent.width; + viewport.height = swapchainExtent.height; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor = { + {0,0}, + swapchainExtent + }; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); } From 5757ba501bdab8bf93a2b46d31a7333fdf609235 Mon Sep 17 00:00:00 2001 From: miri Date: Fri, 27 Dec 2024 17:28:36 +0100 Subject: [PATCH 11/22] rendering functional --- shd/triangle.vert | 9 ++- src/main.c | 41 ++++++++++++ src/rendering/init.c | 38 ++++++++++- src/rendering/init.h | 6 ++ src/rendering/pipeline3d/render.c | 37 +++++++++++ src/rendering/pipeline3d/render.h | 9 +++ src/rendering/pipeline3d/setup.c | 11 ++-- src/rendering/pipeline3d/vertex.h | 24 +++++++ src/rendering/rendering.c | 80 ++++++++++++++++++++++++ src/rendering/rendering.h | 10 +++ src/rendering/struct.h | 26 +++++++- src/rendering/vulkan/commandpools.c | 4 +- src/rendering/vulkan/commandpools.h | 2 +- src/rendering/vulkan/gpumem.c | 66 +++++++++++++++++++ src/rendering/vulkan/gpumem.h | 23 +++++++ src/rendering/vulkan/renderingpipeline.h | 2 +- src/rendering/vulkan/swapchain.c | 1 + src/shd/triangle.frag | 9 --- src/shd/triangle.vert | 20 ------ 19 files changed, 375 insertions(+), 43 deletions(-) create mode 100644 src/rendering/pipeline3d/render.c create mode 100644 src/rendering/pipeline3d/render.h create mode 100644 src/rendering/pipeline3d/vertex.h create mode 100644 src/rendering/rendering.c create mode 100644 src/rendering/rendering.h create mode 100644 src/rendering/vulkan/gpumem.c create mode 100644 src/rendering/vulkan/gpumem.h delete mode 100644 src/shd/triangle.frag delete mode 100644 src/shd/triangle.vert diff --git a/shd/triangle.vert b/shd/triangle.vert index f5b2f8d..f9194b7 100644 --- a/shd/triangle.vert +++ b/shd/triangle.vert @@ -1,7 +1,11 @@ #version 450 +layout(location = 0) in vec3 pos; +layout(location = 1) in vec2 uv; + layout(location = 0) out vec3 fragColor; +/* vec2 positions[3] = vec2[]( vec2(0.0, -0.5), vec2(0.5, 0.5), @@ -13,8 +17,9 @@ vec3 colors[3] = vec3[]( vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0) ); +*/ void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = vec4(pos, 1.0); + fragColor = vec3(uv, 1.0); } diff --git a/src/main.c b/src/main.c index 0ae526d..d3869ff 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,22 @@ +#include #include #include "rendering/init.h" +#include "rendering/pipeline3d/vertex.h" +#include "rendering/struct.h" +#include "rendering/vulkan/gpumem.h" #include "rendering/vulkan/renderingpipeline.h" #include "rendering/vulkan/svulc.h" #include "helpers.h" +#include "rendering/pipeline3d/setup.h" +#include "rendering/rendering.h" + +void fbResizeCb(GLFWwindow *window, int width, int height){ + struct rendering_state *s = glfwGetWindowUserPointer(window); + s->shouldRecreateswapchainFlags = RNDR_RECREATE_SWAPCHAIN | RNDR_RECREATE_SWAPCHAIN_USE_TMP; + s->tmp_window_width = width; + s->tmp_window_height = height; +} + int main(int argc, const char **argv){ struct rendering_state s = {0}; @@ -11,6 +25,10 @@ int main(int argc, const char **argv){ s.window_height = 100; rendering_initGLFW(&s); + + glfwSetWindowUserPointer(s.window, &s); + glfwSetFramebufferSizeCallback(s.window, fbResizeCb); + rendering_vkinstance(&s); // TODO: make a function that selects the best, most suitable device @@ -40,4 +58,27 @@ int main(int argc, const char **argv){ // load a shader + createPipeline3d(&s); + + rendering_createframebuffers(&s); + rendering_createCommandPools(&s); + rendering_createSyncObjects(&s); + + struct renderData triangle; + struct vertex triangleverts[] = { + {0.f, -0.5f, 0.f, 0.f, 1.f}, + {0.5f, 0.5f, 0.f, 0.f, 0.f}, + {-0.5f, 0.5f, 0.f, 1.f, 0.f}, + }; + triangle.vertexBuffer.size = sizeof(triangleverts); + + rendering_makeBufferForRender(&triangle.vertexBuffer, triangleverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); + + triangle.vertexcount = 3; + + while (!glfwWindowShouldClose(s.window)){ + glfwPollEvents(); + render(&s, &triangle, 1); + } + } \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index 8cda64d..e36808d 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -65,6 +65,9 @@ void rendering_vkdevice(struct rendering_state *s){ VkPhysicalDeviceFeatures devFeatures = {0}; s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); + + vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.graphicsQueue); + vkGetDeviceQueue(s->device, s->queues.presentationQFI, 0, &s->queues.presentationQueue); }; void rendering_vkswapchain(struct rendering_state *s){ @@ -97,13 +100,17 @@ void rendering_vkswapchain(struct rendering_state *s){ s->swapchainImages = svlk_getSwapchainImages(s->device, s->swapchain, &s->numswapchainImages); s->error = svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView); + s->numswapchainImageView = s->numswapchainImages; + if(s->error != VK_SUCCESS){ + printf("nööööööö!\n"); + } } void rendering_createframebuffers(struct rendering_state *s){ s->framebuffers = calloc(s->numswapchainImageView, sizeof(VkFramebuffer)); s->numframebuffers = s->numswapchainImageView; - for(int i = 0; i < s->numswapchainImageView; i++){ + for(uint32_t i = 0; i < s->numswapchainImageView; i++){ VkImageView attachments[] = { s->swapchainImageView[i] }; @@ -123,6 +130,16 @@ void rendering_createframebuffers(struct rendering_state *s){ } } +void rendering_cleanupSwapchain(struct rendering_state *s){ + for(uint32_t i = 0; i < s->numframebuffers; i++){ + vkDestroyFramebuffer(s->device, s->framebuffers[i], NULL); + } + for(uint32_t i = 0; i < s->numswapchainImageView; i++){ + vkDestroyImageView(s->device, s->swapchainImageView[i], NULL); + } + vkDestroySwapchainKHR(s->device, s->swapchain, NULL); +} + /* Now a relic of the old wor-- I mean relic of dumbness void rendering_createtemplate(struct rendering_state *s, struct rendering_settings *st){ VkGraphicsPipelineCreateInfo *c = s->gptemplate = calloc(1, sizeof(VkGraphicsPipelineCreateInfo)); @@ -197,4 +214,21 @@ void rendering_createtemplate(struct rendering_state *s, struct rendering_settin vkdynst[1] = VK_DYNAMIC_STATE_SCISSOR; } -*/ \ No newline at end of file +*/ + +void rendering_createCommandPools(struct rendering_state *s){ + VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); + if(result != VK_SUCCESS){ + printf("Creating the Graphics Pool Failed"); + } + result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer); + if(result != VK_SUCCESS){ + printf("Creatingt the Graphics Buffer Failed"); + } +} + +void rendering_createSyncObjects(struct rendering_state *s){ + svlk_createFence(s->device, &s->sync.frameFinishFence, VK_FENCE_CREATE_SIGNALED_BIT); + svlk_createSemaphore(s->device, &s->sync.presentReadySemaphore); + svlk_createSemaphore(s->device, &s->sync.renderFinishSemaphore); +} \ No newline at end of file diff --git a/src/rendering/init.h b/src/rendering/init.h index 5d41c8d..63c6aec 100644 --- a/src/rendering/init.h +++ b/src/rendering/init.h @@ -32,4 +32,10 @@ VkShaderModule rendering_createShaderModule(sizedData shadercode, VkDevice devic // requires: s.pipeline.renderpass void rendering_createframebuffers(struct rendering_state *s); +void rendering_cleanupSwapchain(struct rendering_state *s); + +void rendering_createCommandPools(struct rendering_state *s); + +void rendering_createSyncObjects(struct rendering_state *s); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c new file mode 100644 index 0000000..64da1cd --- /dev/null +++ b/src/rendering/pipeline3d/render.c @@ -0,0 +1,37 @@ +#include "render.h" +#include "pipeline.h" +#include + +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount){ + VkClearValue clearcolor = {0.4f,0.f,0.f,1.f}; + VkRenderPassBeginInfo rpbi = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + NULL, + rp, + framebuffer, + {{0,0}, {s->window_width, s->window_height}}, + 1, + &clearcolor + }; + vkCmdBeginRenderPass(cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.pipeline); + + VkViewport vp = { + 0, 0, + s->window_width, s->window_height, + 0.f, 1.f + }; + vkCmdSetViewport(cmdbuf, 0, 1, &vp); + + VkRect2D sc = {{0,0}, {s->window_width, s->window_height}}; + vkCmdSetScissor(cmdbuf, 0, 1, &sc); + + for (uint32_t i = 0; i < objcount; i++) { + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, offsets); + vkCmdDraw(cmdbuf, obj[i].vertexcount, 1, 0, 0); + } + + vkCmdEndRenderPass(cmdbuf); +} \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h new file mode 100644 index 0000000..7c8c201 --- /dev/null +++ b/src/rendering/pipeline3d/render.h @@ -0,0 +1,9 @@ +#ifndef __PIPELINE3D_RENDER_H__ +#define __PIPELINE3D_RENDER_H__ + +#include +#include "vertex.h" +#include "../struct.h" +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount); + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index e680e52..7643e52 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -5,6 +5,7 @@ #include "../init.h" #include "../vulkan/renderingpipeline.h" #include "../vulkan/svulc.h" +#include "vertex.h" #include "pipeline.h" #include "setup.h" @@ -23,7 +24,7 @@ void createGeometryPassSD(VkSubpassDescription *desc){ 0, NULL, 1, - &colorattachmentref, + refrences, NULL, NULL, 0, @@ -38,7 +39,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ sizedData fragshdcode = readFileR("shd/triangle.frag.spv"); VkPipelineShaderStageCreateInfo vert = loadshader(vertshdcode, VK_SHADER_STAGE_VERTEX_BIT, s); - VkPipelineShaderStageCreateInfo frag = loadshader(vertshdcode, VK_SHADER_STAGE_FRAGMENT_BIT, s); + VkPipelineShaderStageCreateInfo frag = loadshader(fragshdcode, VK_SHADER_STAGE_FRAGMENT_BIT, s); VkPipelineShaderStageCreateInfo stages[] = {vert, frag}; @@ -118,7 +119,7 @@ VkRenderPassCreateInfo createRenderPass(uint32_t attachmentcount, VkAttachmentDe } void createPipeline3d(struct rendering_state *s){ - struct pipeline3d p; + struct pipeline3d *p = &s->pipeline; VkAttachmentDescription attdescs[1] = {0}; @@ -138,7 +139,7 @@ void createPipeline3d(struct rendering_state *s){ createGeometryPassSD(&subpasses[0]); VkRenderPassCreateInfo rpCI = createRenderPass(1, attdescs, 1, subpasses); - vkCreateRenderPass(s->device, &rpCI, NULL, &p.renderpass); + vkCreateRenderPass(s->device, &rpCI, NULL, &p->renderpass); - createGeometryPass(s, &p, 0); + createGeometryPass(s, p, 0); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/vertex.h b/src/rendering/pipeline3d/vertex.h new file mode 100644 index 0000000..b627d35 --- /dev/null +++ b/src/rendering/pipeline3d/vertex.h @@ -0,0 +1,24 @@ +#ifndef __VERTEX_H__ +#define __VERTEX_H__ +#include +#include "../vulkan/gpumem.h" + +struct vertex { + //position + float x; + float y; + float z; + + // texture + float u; + float v; +}; + +// data for generic meshes (not things like heightmaps or other exotic things) +struct renderData { + struct gpubuffer vertexBuffer; + struct gpubuffer indexBuffer; + uint32_t vertexcount; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c new file mode 100644 index 0000000..11a7687 --- /dev/null +++ b/src/rendering/rendering.c @@ -0,0 +1,80 @@ +#include "rendering.h" +#include "pipeline3d/render.h" +#include "pipeline3d/vertex.h" +#include +#include +#include +#include "init.h" +#include "struct.h" + +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount){ + vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); + + uint32_t imageindex; + + + VkResult nextImageResult = vkAcquireNextImageKHR(s->device, s->swapchain, UINT64_MAX, s->sync.presentReadySemaphore, NULL, &imageindex); + if(nextImageResult == VK_ERROR_OUT_OF_DATE_KHR){ + recreateSwapchain(s); + }else if(nextImageResult != VK_SUCCESS && nextImageResult != VK_SUBOPTIMAL_KHR){ + printf("error getting next image!\n"); + } + vkResetFences(s->device, 1, &s->sync.frameFinishFence); + + vkResetCommandBuffer(s->graphicsBuffer, 0); + + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + VkResult result = vkBeginCommandBuffer(s->graphicsBuffer, &begininfo); + + recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount); + + vkEndCommandBuffer(s->graphicsBuffer); + + VkPipelineStageFlags waitstages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; + VkSubmitInfo submitInfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 1, + &s->sync.presentReadySemaphore, + waitstages, + 1, + &s->graphicsBuffer, + 1, + &s->sync.renderFinishSemaphore + }; + result = vkQueueSubmit(s->queues.graphicsQueue, 1, &submitInfo, s->sync.frameFinishFence); + + VkPresentInfoKHR presentI = { + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + NULL, + 1, + &s->sync.renderFinishSemaphore, + 1, + &s->swapchain, + &imageindex, + NULL + }; + vkQueuePresentKHR(s->queues.presentationQueue, &presentI); + + if(nextImageResult == VK_SUBOPTIMAL_KHR || s->shouldRecreateswapchainFlags){ + recreateSwapchain(s); + s->shouldRecreateswapchainFlags = 0; + } +} + +void recreateSwapchain(struct rendering_state *s){ + if(s->shouldRecreateswapchainFlags & RNDR_RECREATE_SWAPCHAIN_USE_TMP){ + s->window_width = s->tmp_window_width; + s->window_height = s->tmp_window_height; + } + + vkDeviceWaitIdle(s->device); + rendering_cleanupSwapchain(s); + rendering_vkswapchain(s); + rendering_createframebuffers(s); +} \ No newline at end of file diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h new file mode 100644 index 0000000..e7d111b --- /dev/null +++ b/src/rendering/rendering.h @@ -0,0 +1,10 @@ +#ifndef __RENDERING_RENDERING_H__ +#define __RENDERING_RENDERING_H__ +#include "struct.h" +#include "pipeline3d/vertex.h" + +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount); + +void recreateSwapchain(struct rendering_state *s); + +#endif \ No newline at end of file diff --git a/src/rendering/struct.h b/src/rendering/struct.h index 0f1906b..a722d5a 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -21,6 +21,14 @@ struct rendering_settings{ }; +// semaphores and fences +struct rendering_sync{ + VkFence frameFinishFence; + VkSemaphore presentReadySemaphore; + VkSemaphore renderFinishSemaphore; +}; + +/* moved to rendering/pipeline3d/vertex.h struct vertex{ // position float x; @@ -29,7 +37,7 @@ struct vertex{ // texture float u; float v; -}; +};*/ struct rendering_state{ // must be filled out prior to initialising glfw @@ -72,6 +80,22 @@ struct rendering_state{ VkGraphicsPipelineCreateInfo *gptemplate; struct pipeline3d pipeline; + + VkCommandPool graphicsPool; + VkCommandBuffer graphicsBuffer; + VkCommandPool memoryPool; + + struct rendering_sync sync; + + uint32_t shouldRecreateswapchainFlags; + // filled in when framebuffer size changes + int tmp_window_width; + int tmp_window_height; +}; + +enum rendering_SwapchainRecreateFlags{ + RNDR_RECREATE_SWAPCHAIN = 1, + RNDR_RECREATE_SWAPCHAIN_USE_TMP = 2 // use tmp_window_width & height }; #endif \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.c b/src/rendering/vulkan/commandpools.c index 7b88c52..a827a31 100644 --- a/src/rendering/vulkan/commandpools.c +++ b/src/rendering/vulkan/commandpools.c @@ -38,7 +38,7 @@ VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore){ return vkCreateSemaphore(device, &semaphoreCreateinfo, NULL, semaphore); } -VkResult svlk_createFence(VkDevice device, VkFence *fence){ - VkFenceCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, 0}; +VkResult svlk_createFence(VkDevice device, VkFence *fence, VkFenceCreateFlags flags){ + VkFenceCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, flags}; return vkCreateFence(device, &semaphoreCreateinfo, NULL, fence); } \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.h b/src/rendering/vulkan/commandpools.h index fc83e8b..8ba6021 100644 --- a/src/rendering/vulkan/commandpools.h +++ b/src/rendering/vulkan/commandpools.h @@ -8,5 +8,5 @@ VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore); -VkResult svlk_createFence(VkDevice device, VkFence *fence); +VkResult svlk_createFence(VkDevice device, VkFence *fence, VkFenceCreateFlags flags); #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c new file mode 100644 index 0000000..4c4e8f2 --- /dev/null +++ b/src/rendering/vulkan/gpumem.c @@ -0,0 +1,66 @@ +#include "gpumem.h" +#include +#include +#include +#include + +void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, VkSharingMode shareMode, VkMemoryPropertyFlagBits properties, struct rendering_state *s){ + VkBufferCreateInfo bufferCI = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + NULL, + 0, + buf->size, + usage, + shareMode, + 0, + NULL + }; + + vkCreateBuffer(s->device, &bufferCI, NULL, &buf->buffer); + + VkMemoryRequirements memreq; + vkGetBufferMemoryRequirements(s->device, buf->buffer, &memreq); + + VkMemoryAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + NULL, + memreq.size, + rendering_findMemoryType(memreq.memoryTypeBits, properties, s) + }; + + VkResult result = vkAllocateMemory(s->device, &allocinfo, NULL, &buf->memory); + if(result != VK_SUCCESS){ + printf("Failed to allocate memory on the GPU. error: %i", result); + exit(EXIT_FAILURE); + } + vkBindBufferMemory(s->device, buf->buffer, buf->memory, 0); +} + +void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s){ + void *mapadr; + vkMapMemory(s->device, buf->memory, 0, buf->size, 0, &mapadr); + memcpy(mapadr, data, buf->size); + vkUnmapMemory(s->device, buf->memory); +} + +void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s){ + printf("a\n"); + rendering_createBuffer(buf, usage, shareMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); + printf("b\n"); + rendering_putBuffer(buf, data, s); + printf("c\n"); + + // TODO: Staging buffer stuff. (waiting on copy buffer) +} + +uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s){ + VkPhysicalDeviceMemoryProperties memproperties; + vkGetPhysicalDeviceMemoryProperties(s->physdevice, &memproperties); + for(uint32_t i = 0; i < memproperties.memoryTypeCount; i++){ + if((type & (1 << i)) && (memproperties.memoryTypes[i].propertyFlags & properties) == properties){ + return i; + } + } + printf("no suitable memory type!\n"); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h new file mode 100644 index 0000000..d3fc203 --- /dev/null +++ b/src/rendering/vulkan/gpumem.h @@ -0,0 +1,23 @@ +#ifndef __GPUMEM_H__ +#define __GPUMEM_H__ +#include "../struct.h" +#include + +struct gpubuffer { + VkBuffer buffer; + VkDeviceMemory memory; + uint64_t size; +}; + +void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, VkSharingMode shareMode, VkMemoryPropertyFlagBits properties, struct rendering_state *s); + +uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s); + +// TODO: IMPLEMENT ME +void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size); + +void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s); + +void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s); + +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/renderingpipeline.h b/src/rendering/vulkan/renderingpipeline.h index 69ca789..4ec33d0 100644 --- a/src/rendering/vulkan/renderingpipeline.h +++ b/src/rendering/vulkan/renderingpipeline.h @@ -68,7 +68,7 @@ static inline VkPipelineRasterizationStateCreateInfo svlk_createPipelineRasteriz 0.f, 0.f, 0.f, - 0.f + 1.f }; return CI; } diff --git a/src/rendering/vulkan/swapchain.c b/src/rendering/vulkan/swapchain.c index 62e458f..48dc9b7 100644 --- a/src/rendering/vulkan/swapchain.c +++ b/src/rendering/vulkan/swapchain.c @@ -2,6 +2,7 @@ #define __SWAPCHAIN_C__ #include "swapchain.h" #include +#include #include #include diff --git a/src/shd/triangle.frag b/src/shd/triangle.frag deleted file mode 100644 index 13009da..0000000 --- a/src/shd/triangle.frag +++ /dev/null @@ -1,9 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 fragColor; - -layout(location = 0) out vec4 outColor; - -void main() { - outColor = vec4(fragColor, 1.0); -} \ No newline at end of file diff --git a/src/shd/triangle.vert b/src/shd/triangle.vert deleted file mode 100644 index f5b2f8d..0000000 --- a/src/shd/triangle.vert +++ /dev/null @@ -1,20 +0,0 @@ -#version 450 - -layout(location = 0) out vec3 fragColor; - -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - -void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; -} From 07a8fc56d896aa7cd5c08aeb68b0304f020abbab Mon Sep 17 00:00:00 2001 From: miri Date: Fri, 27 Dec 2024 17:28:36 +0100 Subject: [PATCH 12/22] rendering functional --- shd/triangle.vert | 9 ++- src/main.c | 41 ++++++++++++ src/rendering/init.c | 38 ++++++++++- src/rendering/init.h | 6 ++ src/rendering/pipeline3d/render.c | 37 +++++++++++ src/rendering/pipeline3d/render.h | 9 +++ src/rendering/pipeline3d/setup.c | 11 ++-- src/rendering/pipeline3d/vertex.h | 24 +++++++ src/rendering/rendering.c | 80 ++++++++++++++++++++++++ src/rendering/rendering.h | 10 +++ src/rendering/struct.h | 26 +++++++- src/rendering/vulkan/commandpools.c | 4 +- src/rendering/vulkan/commandpools.h | 2 +- src/rendering/vulkan/gpumem.c | 66 +++++++++++++++++++ src/rendering/vulkan/gpumem.h | 23 +++++++ src/rendering/vulkan/renderingpipeline.h | 2 +- src/rendering/vulkan/swapchain.c | 1 + src/shd/triangle.frag | 9 --- src/shd/triangle.vert | 20 ------ 19 files changed, 375 insertions(+), 43 deletions(-) create mode 100644 src/rendering/pipeline3d/render.c create mode 100644 src/rendering/pipeline3d/render.h create mode 100644 src/rendering/pipeline3d/vertex.h create mode 100644 src/rendering/rendering.c create mode 100644 src/rendering/rendering.h create mode 100644 src/rendering/vulkan/gpumem.c create mode 100644 src/rendering/vulkan/gpumem.h delete mode 100644 src/shd/triangle.frag delete mode 100644 src/shd/triangle.vert diff --git a/shd/triangle.vert b/shd/triangle.vert index f5b2f8d..f9194b7 100644 --- a/shd/triangle.vert +++ b/shd/triangle.vert @@ -1,7 +1,11 @@ #version 450 +layout(location = 0) in vec3 pos; +layout(location = 1) in vec2 uv; + layout(location = 0) out vec3 fragColor; +/* vec2 positions[3] = vec2[]( vec2(0.0, -0.5), vec2(0.5, 0.5), @@ -13,8 +17,9 @@ vec3 colors[3] = vec3[]( vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0) ); +*/ void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = vec4(pos, 1.0); + fragColor = vec3(uv, 1.0); } diff --git a/src/main.c b/src/main.c index 0ae526d..d3869ff 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,22 @@ +#include #include #include "rendering/init.h" +#include "rendering/pipeline3d/vertex.h" +#include "rendering/struct.h" +#include "rendering/vulkan/gpumem.h" #include "rendering/vulkan/renderingpipeline.h" #include "rendering/vulkan/svulc.h" #include "helpers.h" +#include "rendering/pipeline3d/setup.h" +#include "rendering/rendering.h" + +void fbResizeCb(GLFWwindow *window, int width, int height){ + struct rendering_state *s = glfwGetWindowUserPointer(window); + s->shouldRecreateswapchainFlags = RNDR_RECREATE_SWAPCHAIN | RNDR_RECREATE_SWAPCHAIN_USE_TMP; + s->tmp_window_width = width; + s->tmp_window_height = height; +} + int main(int argc, const char **argv){ struct rendering_state s = {0}; @@ -11,6 +25,10 @@ int main(int argc, const char **argv){ s.window_height = 100; rendering_initGLFW(&s); + + glfwSetWindowUserPointer(s.window, &s); + glfwSetFramebufferSizeCallback(s.window, fbResizeCb); + rendering_vkinstance(&s); // TODO: make a function that selects the best, most suitable device @@ -40,4 +58,27 @@ int main(int argc, const char **argv){ // load a shader + createPipeline3d(&s); + + rendering_createframebuffers(&s); + rendering_createCommandPools(&s); + rendering_createSyncObjects(&s); + + struct renderData triangle; + struct vertex triangleverts[] = { + {0.f, -0.5f, 0.f, 0.f, 1.f}, + {0.5f, 0.5f, 0.f, 0.f, 0.f}, + {-0.5f, 0.5f, 0.f, 1.f, 0.f}, + }; + triangle.vertexBuffer.size = sizeof(triangleverts); + + rendering_makeBufferForRender(&triangle.vertexBuffer, triangleverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); + + triangle.vertexcount = 3; + + while (!glfwWindowShouldClose(s.window)){ + glfwPollEvents(); + render(&s, &triangle, 1); + } + } \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index 8cda64d..e36808d 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -65,6 +65,9 @@ void rendering_vkdevice(struct rendering_state *s){ VkPhysicalDeviceFeatures devFeatures = {0}; s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); + + vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.graphicsQueue); + vkGetDeviceQueue(s->device, s->queues.presentationQFI, 0, &s->queues.presentationQueue); }; void rendering_vkswapchain(struct rendering_state *s){ @@ -97,13 +100,17 @@ void rendering_vkswapchain(struct rendering_state *s){ s->swapchainImages = svlk_getSwapchainImages(s->device, s->swapchain, &s->numswapchainImages); s->error = svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView); + s->numswapchainImageView = s->numswapchainImages; + if(s->error != VK_SUCCESS){ + printf("nööööööö!\n"); + } } void rendering_createframebuffers(struct rendering_state *s){ s->framebuffers = calloc(s->numswapchainImageView, sizeof(VkFramebuffer)); s->numframebuffers = s->numswapchainImageView; - for(int i = 0; i < s->numswapchainImageView; i++){ + for(uint32_t i = 0; i < s->numswapchainImageView; i++){ VkImageView attachments[] = { s->swapchainImageView[i] }; @@ -123,6 +130,16 @@ void rendering_createframebuffers(struct rendering_state *s){ } } +void rendering_cleanupSwapchain(struct rendering_state *s){ + for(uint32_t i = 0; i < s->numframebuffers; i++){ + vkDestroyFramebuffer(s->device, s->framebuffers[i], NULL); + } + for(uint32_t i = 0; i < s->numswapchainImageView; i++){ + vkDestroyImageView(s->device, s->swapchainImageView[i], NULL); + } + vkDestroySwapchainKHR(s->device, s->swapchain, NULL); +} + /* Now a relic of the old wor-- I mean relic of dumbness void rendering_createtemplate(struct rendering_state *s, struct rendering_settings *st){ VkGraphicsPipelineCreateInfo *c = s->gptemplate = calloc(1, sizeof(VkGraphicsPipelineCreateInfo)); @@ -197,4 +214,21 @@ void rendering_createtemplate(struct rendering_state *s, struct rendering_settin vkdynst[1] = VK_DYNAMIC_STATE_SCISSOR; } -*/ \ No newline at end of file +*/ + +void rendering_createCommandPools(struct rendering_state *s){ + VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); + if(result != VK_SUCCESS){ + printf("Creating the Graphics Pool Failed"); + } + result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer); + if(result != VK_SUCCESS){ + printf("Creatingt the Graphics Buffer Failed"); + } +} + +void rendering_createSyncObjects(struct rendering_state *s){ + svlk_createFence(s->device, &s->sync.frameFinishFence, VK_FENCE_CREATE_SIGNALED_BIT); + svlk_createSemaphore(s->device, &s->sync.presentReadySemaphore); + svlk_createSemaphore(s->device, &s->sync.renderFinishSemaphore); +} \ No newline at end of file diff --git a/src/rendering/init.h b/src/rendering/init.h index 5d41c8d..63c6aec 100644 --- a/src/rendering/init.h +++ b/src/rendering/init.h @@ -32,4 +32,10 @@ VkShaderModule rendering_createShaderModule(sizedData shadercode, VkDevice devic // requires: s.pipeline.renderpass void rendering_createframebuffers(struct rendering_state *s); +void rendering_cleanupSwapchain(struct rendering_state *s); + +void rendering_createCommandPools(struct rendering_state *s); + +void rendering_createSyncObjects(struct rendering_state *s); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c new file mode 100644 index 0000000..64da1cd --- /dev/null +++ b/src/rendering/pipeline3d/render.c @@ -0,0 +1,37 @@ +#include "render.h" +#include "pipeline.h" +#include + +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount){ + VkClearValue clearcolor = {0.4f,0.f,0.f,1.f}; + VkRenderPassBeginInfo rpbi = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + NULL, + rp, + framebuffer, + {{0,0}, {s->window_width, s->window_height}}, + 1, + &clearcolor + }; + vkCmdBeginRenderPass(cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.pipeline); + + VkViewport vp = { + 0, 0, + s->window_width, s->window_height, + 0.f, 1.f + }; + vkCmdSetViewport(cmdbuf, 0, 1, &vp); + + VkRect2D sc = {{0,0}, {s->window_width, s->window_height}}; + vkCmdSetScissor(cmdbuf, 0, 1, &sc); + + for (uint32_t i = 0; i < objcount; i++) { + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, offsets); + vkCmdDraw(cmdbuf, obj[i].vertexcount, 1, 0, 0); + } + + vkCmdEndRenderPass(cmdbuf); +} \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h new file mode 100644 index 0000000..7c8c201 --- /dev/null +++ b/src/rendering/pipeline3d/render.h @@ -0,0 +1,9 @@ +#ifndef __PIPELINE3D_RENDER_H__ +#define __PIPELINE3D_RENDER_H__ + +#include +#include "vertex.h" +#include "../struct.h" +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount); + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index e680e52..7643e52 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -5,6 +5,7 @@ #include "../init.h" #include "../vulkan/renderingpipeline.h" #include "../vulkan/svulc.h" +#include "vertex.h" #include "pipeline.h" #include "setup.h" @@ -23,7 +24,7 @@ void createGeometryPassSD(VkSubpassDescription *desc){ 0, NULL, 1, - &colorattachmentref, + refrences, NULL, NULL, 0, @@ -38,7 +39,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ sizedData fragshdcode = readFileR("shd/triangle.frag.spv"); VkPipelineShaderStageCreateInfo vert = loadshader(vertshdcode, VK_SHADER_STAGE_VERTEX_BIT, s); - VkPipelineShaderStageCreateInfo frag = loadshader(vertshdcode, VK_SHADER_STAGE_FRAGMENT_BIT, s); + VkPipelineShaderStageCreateInfo frag = loadshader(fragshdcode, VK_SHADER_STAGE_FRAGMENT_BIT, s); VkPipelineShaderStageCreateInfo stages[] = {vert, frag}; @@ -118,7 +119,7 @@ VkRenderPassCreateInfo createRenderPass(uint32_t attachmentcount, VkAttachmentDe } void createPipeline3d(struct rendering_state *s){ - struct pipeline3d p; + struct pipeline3d *p = &s->pipeline; VkAttachmentDescription attdescs[1] = {0}; @@ -138,7 +139,7 @@ void createPipeline3d(struct rendering_state *s){ createGeometryPassSD(&subpasses[0]); VkRenderPassCreateInfo rpCI = createRenderPass(1, attdescs, 1, subpasses); - vkCreateRenderPass(s->device, &rpCI, NULL, &p.renderpass); + vkCreateRenderPass(s->device, &rpCI, NULL, &p->renderpass); - createGeometryPass(s, &p, 0); + createGeometryPass(s, p, 0); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/vertex.h b/src/rendering/pipeline3d/vertex.h new file mode 100644 index 0000000..b627d35 --- /dev/null +++ b/src/rendering/pipeline3d/vertex.h @@ -0,0 +1,24 @@ +#ifndef __VERTEX_H__ +#define __VERTEX_H__ +#include +#include "../vulkan/gpumem.h" + +struct vertex { + //position + float x; + float y; + float z; + + // texture + float u; + float v; +}; + +// data for generic meshes (not things like heightmaps or other exotic things) +struct renderData { + struct gpubuffer vertexBuffer; + struct gpubuffer indexBuffer; + uint32_t vertexcount; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c new file mode 100644 index 0000000..11a7687 --- /dev/null +++ b/src/rendering/rendering.c @@ -0,0 +1,80 @@ +#include "rendering.h" +#include "pipeline3d/render.h" +#include "pipeline3d/vertex.h" +#include +#include +#include +#include "init.h" +#include "struct.h" + +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount){ + vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); + + uint32_t imageindex; + + + VkResult nextImageResult = vkAcquireNextImageKHR(s->device, s->swapchain, UINT64_MAX, s->sync.presentReadySemaphore, NULL, &imageindex); + if(nextImageResult == VK_ERROR_OUT_OF_DATE_KHR){ + recreateSwapchain(s); + }else if(nextImageResult != VK_SUCCESS && nextImageResult != VK_SUBOPTIMAL_KHR){ + printf("error getting next image!\n"); + } + vkResetFences(s->device, 1, &s->sync.frameFinishFence); + + vkResetCommandBuffer(s->graphicsBuffer, 0); + + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + VkResult result = vkBeginCommandBuffer(s->graphicsBuffer, &begininfo); + + recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount); + + vkEndCommandBuffer(s->graphicsBuffer); + + VkPipelineStageFlags waitstages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; + VkSubmitInfo submitInfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 1, + &s->sync.presentReadySemaphore, + waitstages, + 1, + &s->graphicsBuffer, + 1, + &s->sync.renderFinishSemaphore + }; + result = vkQueueSubmit(s->queues.graphicsQueue, 1, &submitInfo, s->sync.frameFinishFence); + + VkPresentInfoKHR presentI = { + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + NULL, + 1, + &s->sync.renderFinishSemaphore, + 1, + &s->swapchain, + &imageindex, + NULL + }; + vkQueuePresentKHR(s->queues.presentationQueue, &presentI); + + if(nextImageResult == VK_SUBOPTIMAL_KHR || s->shouldRecreateswapchainFlags){ + recreateSwapchain(s); + s->shouldRecreateswapchainFlags = 0; + } +} + +void recreateSwapchain(struct rendering_state *s){ + if(s->shouldRecreateswapchainFlags & RNDR_RECREATE_SWAPCHAIN_USE_TMP){ + s->window_width = s->tmp_window_width; + s->window_height = s->tmp_window_height; + } + + vkDeviceWaitIdle(s->device); + rendering_cleanupSwapchain(s); + rendering_vkswapchain(s); + rendering_createframebuffers(s); +} \ No newline at end of file diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h new file mode 100644 index 0000000..e7d111b --- /dev/null +++ b/src/rendering/rendering.h @@ -0,0 +1,10 @@ +#ifndef __RENDERING_RENDERING_H__ +#define __RENDERING_RENDERING_H__ +#include "struct.h" +#include "pipeline3d/vertex.h" + +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount); + +void recreateSwapchain(struct rendering_state *s); + +#endif \ No newline at end of file diff --git a/src/rendering/struct.h b/src/rendering/struct.h index 0f1906b..a722d5a 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -21,6 +21,14 @@ struct rendering_settings{ }; +// semaphores and fences +struct rendering_sync{ + VkFence frameFinishFence; + VkSemaphore presentReadySemaphore; + VkSemaphore renderFinishSemaphore; +}; + +/* moved to rendering/pipeline3d/vertex.h struct vertex{ // position float x; @@ -29,7 +37,7 @@ struct vertex{ // texture float u; float v; -}; +};*/ struct rendering_state{ // must be filled out prior to initialising glfw @@ -72,6 +80,22 @@ struct rendering_state{ VkGraphicsPipelineCreateInfo *gptemplate; struct pipeline3d pipeline; + + VkCommandPool graphicsPool; + VkCommandBuffer graphicsBuffer; + VkCommandPool memoryPool; + + struct rendering_sync sync; + + uint32_t shouldRecreateswapchainFlags; + // filled in when framebuffer size changes + int tmp_window_width; + int tmp_window_height; +}; + +enum rendering_SwapchainRecreateFlags{ + RNDR_RECREATE_SWAPCHAIN = 1, + RNDR_RECREATE_SWAPCHAIN_USE_TMP = 2 // use tmp_window_width & height }; #endif \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.c b/src/rendering/vulkan/commandpools.c index 7b88c52..a827a31 100644 --- a/src/rendering/vulkan/commandpools.c +++ b/src/rendering/vulkan/commandpools.c @@ -38,7 +38,7 @@ VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore){ return vkCreateSemaphore(device, &semaphoreCreateinfo, NULL, semaphore); } -VkResult svlk_createFence(VkDevice device, VkFence *fence){ - VkFenceCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, 0}; +VkResult svlk_createFence(VkDevice device, VkFence *fence, VkFenceCreateFlags flags){ + VkFenceCreateInfo semaphoreCreateinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, flags}; return vkCreateFence(device, &semaphoreCreateinfo, NULL, fence); } \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.h b/src/rendering/vulkan/commandpools.h index fc83e8b..8ba6021 100644 --- a/src/rendering/vulkan/commandpools.h +++ b/src/rendering/vulkan/commandpools.h @@ -8,5 +8,5 @@ VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore); -VkResult svlk_createFence(VkDevice device, VkFence *fence); +VkResult svlk_createFence(VkDevice device, VkFence *fence, VkFenceCreateFlags flags); #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c new file mode 100644 index 0000000..4c4e8f2 --- /dev/null +++ b/src/rendering/vulkan/gpumem.c @@ -0,0 +1,66 @@ +#include "gpumem.h" +#include +#include +#include +#include + +void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, VkSharingMode shareMode, VkMemoryPropertyFlagBits properties, struct rendering_state *s){ + VkBufferCreateInfo bufferCI = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + NULL, + 0, + buf->size, + usage, + shareMode, + 0, + NULL + }; + + vkCreateBuffer(s->device, &bufferCI, NULL, &buf->buffer); + + VkMemoryRequirements memreq; + vkGetBufferMemoryRequirements(s->device, buf->buffer, &memreq); + + VkMemoryAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + NULL, + memreq.size, + rendering_findMemoryType(memreq.memoryTypeBits, properties, s) + }; + + VkResult result = vkAllocateMemory(s->device, &allocinfo, NULL, &buf->memory); + if(result != VK_SUCCESS){ + printf("Failed to allocate memory on the GPU. error: %i", result); + exit(EXIT_FAILURE); + } + vkBindBufferMemory(s->device, buf->buffer, buf->memory, 0); +} + +void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s){ + void *mapadr; + vkMapMemory(s->device, buf->memory, 0, buf->size, 0, &mapadr); + memcpy(mapadr, data, buf->size); + vkUnmapMemory(s->device, buf->memory); +} + +void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s){ + printf("a\n"); + rendering_createBuffer(buf, usage, shareMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); + printf("b\n"); + rendering_putBuffer(buf, data, s); + printf("c\n"); + + // TODO: Staging buffer stuff. (waiting on copy buffer) +} + +uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s){ + VkPhysicalDeviceMemoryProperties memproperties; + vkGetPhysicalDeviceMemoryProperties(s->physdevice, &memproperties); + for(uint32_t i = 0; i < memproperties.memoryTypeCount; i++){ + if((type & (1 << i)) && (memproperties.memoryTypes[i].propertyFlags & properties) == properties){ + return i; + } + } + printf("no suitable memory type!\n"); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h new file mode 100644 index 0000000..d3fc203 --- /dev/null +++ b/src/rendering/vulkan/gpumem.h @@ -0,0 +1,23 @@ +#ifndef __GPUMEM_H__ +#define __GPUMEM_H__ +#include "../struct.h" +#include + +struct gpubuffer { + VkBuffer buffer; + VkDeviceMemory memory; + uint64_t size; +}; + +void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, VkSharingMode shareMode, VkMemoryPropertyFlagBits properties, struct rendering_state *s); + +uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s); + +// TODO: IMPLEMENT ME +void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size); + +void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s); + +void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s); + +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/renderingpipeline.h b/src/rendering/vulkan/renderingpipeline.h index 69ca789..4ec33d0 100644 --- a/src/rendering/vulkan/renderingpipeline.h +++ b/src/rendering/vulkan/renderingpipeline.h @@ -68,7 +68,7 @@ static inline VkPipelineRasterizationStateCreateInfo svlk_createPipelineRasteriz 0.f, 0.f, 0.f, - 0.f + 1.f }; return CI; } diff --git a/src/rendering/vulkan/swapchain.c b/src/rendering/vulkan/swapchain.c index 62e458f..48dc9b7 100644 --- a/src/rendering/vulkan/swapchain.c +++ b/src/rendering/vulkan/swapchain.c @@ -2,6 +2,7 @@ #define __SWAPCHAIN_C__ #include "swapchain.h" #include +#include #include #include diff --git a/src/shd/triangle.frag b/src/shd/triangle.frag deleted file mode 100644 index 13009da..0000000 --- a/src/shd/triangle.frag +++ /dev/null @@ -1,9 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 fragColor; - -layout(location = 0) out vec4 outColor; - -void main() { - outColor = vec4(fragColor, 1.0); -} \ No newline at end of file diff --git a/src/shd/triangle.vert b/src/shd/triangle.vert deleted file mode 100644 index f5b2f8d..0000000 --- a/src/shd/triangle.vert +++ /dev/null @@ -1,20 +0,0 @@ -#version 450 - -layout(location = 0) out vec3 fragColor; - -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - -void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; -} From acbef1159555ca3cdb70a4453706e1ac255aa0d3 Mon Sep 17 00:00:00 2001 From: miri Date: Tue, 11 Mar 2025 19:57:48 +0100 Subject: [PATCH 13/22] added uniforms support needs some debugging work and stuff --- .gitignore | 3 +- makefile | 5 +- shd/triangle.vert | 6 +- src/main.c | 36 ++++++- src/rendering/init.c | 11 ++- src/rendering/management/elementHeap.c | 52 ++++++++++ src/rendering/management/elementHeap.h | 48 +++++++++ src/rendering/pipeline3d/data.c | 6 ++ src/rendering/pipeline3d/{vertex.h => data.h} | 15 +++ src/rendering/pipeline3d/pipeline.h | 9 +- src/rendering/pipeline3d/render.c | 13 ++- src/rendering/pipeline3d/render.h | 2 +- src/rendering/pipeline3d/setup.c | 98 ++++++++++++++++++- src/rendering/pipeline3d/setup.h | 6 +- src/rendering/rendering.c | 2 +- src/rendering/rendering.h | 2 +- src/rendering/struct.h | 3 +- src/rendering/vulkan/gpumem.c | 43 ++++++-- src/rendering/vulkan/gpumem.h | 15 ++- src/rendering/vulkan/gpumem_types.h | 11 +++ 20 files changed, 351 insertions(+), 35 deletions(-) create mode 100644 src/rendering/management/elementHeap.c create mode 100644 src/rendering/management/elementHeap.h create mode 100644 src/rendering/pipeline3d/data.c rename src/rendering/pipeline3d/{vertex.h => data.h} (56%) create mode 100644 src/rendering/vulkan/gpumem_types.h diff --git a/.gitignore b/.gitignore index 16c0376..7f94f65 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ a.out main valgrind* -*.spv \ No newline at end of file +*.spv +vgcore.* \ No newline at end of file diff --git a/makefile b/makefile index 5ce9cd2..84db5b3 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,7 @@ CC = clang -CLIBS = -lglfw -lvulkan +CLIBS = -lglfw -lvulkan -lcglm -lm +CFLAGS = -g all: - $(CC) $(CLIBS) src/*.c src/rendering/*.c src/rendering/pipeline3d/*.c src/rendering/vulkan/*.c \ No newline at end of file + $(CC) $(CLIBS) $(CFLAGS) src/*.c src/rendering/*.c src/rendering/pipeline3d/*.c src/rendering/vulkan/*.c src/rendering/management/*.c \ No newline at end of file diff --git a/shd/triangle.vert b/shd/triangle.vert index f9194b7..dfe7f7c 100644 --- a/shd/triangle.vert +++ b/shd/triangle.vert @@ -1,5 +1,9 @@ #version 450 +layout(binding = 0) uniform UniformBufferObject { + mat4 matrix; // do all the view matrix stuff on the cpu during queue submission +} ubo; + layout(location = 0) in vec3 pos; layout(location = 1) in vec2 uv; @@ -20,6 +24,6 @@ vec3 colors[3] = vec3[]( */ void main() { - gl_Position = vec4(pos, 1.0); + gl_Position = ubo.matrix * vec4(pos, 1.0); fragColor = vec3(uv, 1.0); } diff --git a/src/main.c b/src/main.c index d3869ff..4ff7e8d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,15 @@ #include +#include +#include +#include +#include +#include +#include +#include #include #include "rendering/init.h" -#include "rendering/pipeline3d/vertex.h" +#include "rendering/management/elementHeap.h" +#include "rendering/pipeline3d/data.h" #include "rendering/struct.h" #include "rendering/vulkan/gpumem.h" #include "rendering/vulkan/renderingpipeline.h" @@ -64,21 +72,47 @@ int main(int argc, const char **argv){ rendering_createCommandPools(&s); rendering_createSyncObjects(&s); + // create a triangle renderdata struct renderData triangle; struct vertex triangleverts[] = { {0.f, -0.5f, 0.f, 0.f, 1.f}, {0.5f, 0.5f, 0.f, 0.f, 0.f}, {-0.5f, 0.5f, 0.f, 1.f, 0.f}, }; + uint32_t triangleindices[] = {0,1,2}; triangle.vertexBuffer.size = sizeof(triangleverts); + triangle.indexBuffer.size = sizeof(triangleindices); rendering_makeBufferForRender(&triangle.vertexBuffer, triangleverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); + rendering_makeBufferForRender(&triangle.indexBuffer, triangleindices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); triangle.vertexcount = 3; + triangle.uniforms.heap = &s.pipeline.uniformHeap; + triangle.uniforms.index = elementHeap_getFreeIndexAndReserve(triangle.uniforms.heap); + printf("index = %i\n", triangle.uniforms.index); + triangle.uniforms.ubo = triangle.uniforms.heap->buffer + triangle.uniforms.heap->objsize * triangle.uniforms.index; + + //glm_mat4_copy(GLM_MAT4_IDENTITY, triangle.uniforms.ubo->matrix); + mat4 proj; + mat4 view; + glm_perspective(glm_rad(50.0), 1.0, 0.1, 100.0, proj); + vec3 eye = {0.f,0.f,1.f}; + vec3 center = {0.f,0.f,0.f}; + vec3 up = {0.f,1.f,0.f}; + glm_lookat(eye, center, up, view); + glm_mat4_mul(proj, view, triangle.uniforms.ubo->matrix); + glm_mat4_print(view, stdout); + + uint32_t first = 1; + while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); render(&s, &triangle, 1); + if(first){ + first = 0; + glm_mat4_print(triangle.uniforms.ubo->matrix, stdout); + } } } \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index e36808d..af49d89 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -59,7 +59,7 @@ void rendering_vkdevice(struct rendering_state *s){ float queuePriority = 1.f; VkDeviceQueueCreateInfo deviceQueues[] = { - svlk_createQueueInfo(s->queues.graphicsQFI, 1, &queuePriority) + svlk_createQueueInfo(s->queues.graphicsQFI, 2, &queuePriority) }; VkPhysicalDeviceFeatures devFeatures = {0}; @@ -67,6 +67,7 @@ void rendering_vkdevice(struct rendering_state *s){ s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.graphicsQueue); + vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.transferQueue); vkGetDeviceQueue(s->device, s->queues.presentationQFI, 0, &s->queues.presentationQueue); }; @@ -219,11 +220,15 @@ void rendering_createtemplate(struct rendering_state *s, struct rendering_settin void rendering_createCommandPools(struct rendering_state *s){ VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); if(result != VK_SUCCESS){ - printf("Creating the Graphics Pool Failed"); + printf("Creating the Graphics Pool Failed\n"); } result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer); if(result != VK_SUCCESS){ - printf("Creatingt the Graphics Buffer Failed"); + printf("Creating the Graphics Buffer Failed\n"); + } + result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->transferBuffer); + if(result != VK_SUCCESS){ + printf("Creating the Transfer Buffer Failed\n"); } } diff --git a/src/rendering/management/elementHeap.c b/src/rendering/management/elementHeap.c new file mode 100644 index 0000000..af75b74 --- /dev/null +++ b/src/rendering/management/elementHeap.c @@ -0,0 +1,52 @@ +#include "elementHeap.h" +#include +#include +#include + +#if __amd64__ +//#define ASM_BSR_AVAIL +#endif + +void *elementHeap_getFreeElement(struct elementHeap *h){ + uint32_t index = elementHeap_getFreeIndex(h); + if(index != UINT32_MAX) + return h->buffer + h->objsize * index; + else + return NULL; +} +uint32_t elementHeap_getFreeIndex(struct elementHeap *h){ + uint32_t avblen = h->len / 32 + (h->len % 32 != 0); // get the lenth of the availability bitmap + for (uint32_t i = 0; i < avblen; i++) { + uint32_t j = (i + h->lkg) % avblen; // start searching from the lkg + if(h->availabilitybitmap[j] != UINT32_MAX){ + #ifdef ASM_BSR_AVAIL + // potential future stuff + #else + + uint32_t x = h->availabilitybitmap[j]; + uint32_t lzc = 0 + + ((x & 0xFFFF0000) ? 16 : 0) + + ((x & 0xFF00FF00) ? 8 : 0) + + ((x & 0xF0F0F0F0) ? 4 : 0) + + ((x & 0xCCCCCCCC) ? 2 : 0) + + ((x & 0xAAAAAAAA) ? 1 : 0); // get the specific bit of the unset element + + h->availabilitybitmap[j] |= 1 << lzc; // set the bit for the reserved element + h->lkg = j; // update the lkg + + if(j * 32 + lzc >= h->len) return UINT32_MAX; + + return j * 32 + lzc; + + #endif + } + } + + // return uint32 maximum if no free UBO is found. + return UINT32_MAX; +} + +void elementHeap_init(struct elementHeap *h){ + uint32_t avblen = h->len / 32 + (h->len % 32 != 0); // get the lenth of the availability bitmap + h->availabilitybitmap = calloc(1, avblen * sizeof(uint32_t)); +} \ No newline at end of file diff --git a/src/rendering/management/elementHeap.h b/src/rendering/management/elementHeap.h new file mode 100644 index 0000000..cee8fe8 --- /dev/null +++ b/src/rendering/management/elementHeap.h @@ -0,0 +1,48 @@ +#ifndef __UNIFORMS_H__ +#define __UNIFORMS_H__ + +// a Bundle is data for multiple objects put into a buffer +#include + +struct uboBufinfo{ + struct uniformTable *ut; + uint32_t id; +}; + +// a heap of elements with a bitmap for availability +struct elementHeap{ + uint32_t *availabilitybitmap; // bit field specifying which uniforms are available + uint32_t len; // length of the heap in elements + uint32_t objsize; // the size of one of the objects + uint32_t lkg; // last known good index to start searching from in elements of availabilitybitmap + void *buffer; // the data (on gpu, expect write only) +}; + +// set an element of the heap as reserved (does not check) (avoid using this unless a specific element is required) +static inline void elementHeap_setReserved(struct elementHeap *h, uint32_t index){ + h->availabilitybitmap[index/32] |= 1 << (index % 32); +} + +// set an element of the heap as free (does not check) +static inline void elementHeap_setFree(struct elementHeap *h, uint32_t index){ + h->availabilitybitmap[index/32] &= ~(1 << (index % 32)); + h->lkg = index/32; +} + +// get the index of the first free element without setting it as used (avoid using this) +uint32_t elementHeap_getFreeIndex(struct elementHeap *h); + +// get the index of the first free element without setting it as used (avoid using this) +static inline uint32_t elementHeap_getFreeIndexAndReserve(struct elementHeap *h){ + uint32_t i = elementHeap_getFreeIndex(h); + elementHeap_setReserved(h, i); + return i; +} + +// get the first free element and return a pointer to it whilst setting it as used +void *elementHeap_getFreeElement(struct elementHeap *h); + +// initialise the heap, BUFFER IS NOT CREATED, _MUST_ BE CREATED SEPERATLEY +void elementHeap_init(struct elementHeap *h); + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/data.c b/src/rendering/pipeline3d/data.c new file mode 100644 index 0000000..a93e74b --- /dev/null +++ b/src/rendering/pipeline3d/data.c @@ -0,0 +1,6 @@ +#include "data.h" + +void uniformData_init(struct uniformData *ud, struct elementHeap *heap){ + ud->heap = heap; + ud->index = elementHeap_getFreeIndexAndReserve(heap); +} \ No newline at end of file diff --git a/src/rendering/pipeline3d/vertex.h b/src/rendering/pipeline3d/data.h similarity index 56% rename from src/rendering/pipeline3d/vertex.h rename to src/rendering/pipeline3d/data.h index b627d35..888177d 100644 --- a/src/rendering/pipeline3d/vertex.h +++ b/src/rendering/pipeline3d/data.h @@ -1,7 +1,9 @@ #ifndef __VERTEX_H__ #define __VERTEX_H__ +#include #include #include "../vulkan/gpumem.h" +#include "../management/elementHeap.h" struct vertex { //position @@ -14,11 +16,24 @@ struct vertex { float v; }; +struct UniformBufferObject{ + mat4 matrix; +}; + +struct uniformData { + struct elementHeap *heap; + uint32_t index; + struct UniformBufferObject *ubo; +}; + // data for generic meshes (not things like heightmaps or other exotic things) struct renderData { struct gpubuffer vertexBuffer; struct gpubuffer indexBuffer; uint32_t vertexcount; + struct uniformData uniforms; }; +void uniformData_init(struct uniformData *ud, struct elementHeap *heap); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h index 2ea17ca..edb2163 100644 --- a/src/rendering/pipeline3d/pipeline.h +++ b/src/rendering/pipeline3d/pipeline.h @@ -2,16 +2,23 @@ #define __PIPELINE3D_PIPELINE_H__ #include +#include "../management/elementHeap.h" +#include "../vulkan/gpumem_types.h" struct pipeline3dSubpass { VkPipelineLayout layout; VkPipeline pipeline; - + VkDescriptorSetLayout desclayout; }; struct pipeline3d { VkRenderPass renderpass; struct pipeline3dSubpass geometryPass; + VkDescriptorPool descriptorPool; + // potentially wrap the uniform heap in a linked list + struct elementHeap uniformHeap; + VkDescriptorSet descriptorSet; + struct gpubuffer uniformbuffer; }; #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index 64da1cd..e10fae3 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -1,8 +1,11 @@ #include "render.h" #include "pipeline.h" +#include "setup.h" +#include #include void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount){ + updateDescriptorSet(s); VkClearValue clearcolor = {0.4f,0.f,0.f,1.f}; VkRenderPassBeginInfo rpbi = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, @@ -28,9 +31,13 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer vkCmdSetScissor(cmdbuf, 0, 1, &sc); for (uint32_t i = 0; i < objcount; i++) { - VkDeviceSize offsets[] = {0}; - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, offsets); - vkCmdDraw(cmdbuf, obj[i].vertexcount, 1, 0, 0); + VkDeviceSize vtxoffsets[] = {0}; + uint32_t unioffsets[] = {obj[i].uniforms.index}; + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, vtxoffsets); + vkCmdBindIndexBuffer(cmdbuf, obj[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 0, + 1, &p->descriptorSet, 1, unioffsets); + vkCmdDrawIndexed(cmdbuf, obj[i].vertexcount, 1, 0, 0, 0); } vkCmdEndRenderPass(cmdbuf); diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h index 7c8c201..ca1efa1 100644 --- a/src/rendering/pipeline3d/render.h +++ b/src/rendering/pipeline3d/render.h @@ -2,7 +2,7 @@ #define __PIPELINE3D_RENDER_H__ #include -#include "vertex.h" +#include "data.h" #include "../struct.h" void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount); diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index 7643e52..775d31b 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -1,11 +1,12 @@ #include +#include #include #include #include "../../helpers.h" #include "../init.h" #include "../vulkan/renderingpipeline.h" #include "../vulkan/svulc.h" -#include "vertex.h" +#include "data.h" #include "pipeline.h" #include "setup.h" @@ -75,7 +76,25 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineDynamicStateCreateInfo dynamicState = svlk_createDynamicStateCI(dynstates, 2, 0); // Pipeline Layout - VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(NULL, 0); + VkDescriptorSetLayoutBinding lb = { + 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + 1, + VK_SHADER_STAGE_VERTEX_BIT, + NULL + }; + + VkDescriptorSetLayoutCreateInfo dslCI = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + NULL, + 0, + 1, + &lb + }; + + vkCreateDescriptorSetLayout(s->device, &dslCI, NULL, &p->geometryPass.desclayout); + + VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(&p->geometryPass.desclayout, 1); vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); @@ -83,7 +102,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, NULL, 0, - 2, + 2, stages, &inputState, &inputAssemblyState, @@ -142,4 +161,77 @@ void createPipeline3d(struct rendering_state *s){ vkCreateRenderPass(s->device, &rpCI, NULL, &p->renderpass); createGeometryPass(s, p, 0); + + // uniform stuff + p->uniformHeap.len = 16; + p->uniformHeap.objsize = sizeof(struct uniformData); + elementHeap_init(&p->uniformHeap); + + p->uniformbuffer.size = p->uniformHeap.len * p->uniformHeap.objsize; + rendering_createBuffer(&p->uniformbuffer, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_SHARING_MODE_EXCLUSIVE, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + s + ); + vkMapMemory(s->device, p->uniformbuffer.memory, 0, p->uniformbuffer.size, 0, &p->uniformHeap.buffer); + + // why descriptors, why. why museth you exist + VkDescriptorPoolSize poolsize = { + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // offsets are cool + 1 // no linked lists yet so 1 + }; + + VkDescriptorPoolCreateInfo dpCI = { + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + NULL, + 0, + 1, + 1, + &poolsize + }; + + VkResult result = vkCreateDescriptorPool(s->device, &dpCI, NULL, &p->descriptorPool); + if(result != VK_SUCCESS){ + printf("Could not create descriptor pool. result=%i\n", result); + } + + VkDescriptorSetAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + NULL, + p->descriptorPool, + 1, + &p->geometryPass.desclayout + }; + result = vkAllocateDescriptorSets(s->device, &allocInfo, &p->descriptorSet); + if(result != VK_SUCCESS){ + printf("Could not create descriptor set. result=%i\n", result); + } + updateDescriptorSet(s); +} + +void updateDescriptorSet(struct rendering_state *s){ + struct pipeline3d *p = &s->pipeline; + VkDescriptorBufferInfo bufferInfo = { + p->uniformbuffer.buffer, + 0, + sizeof(struct UniformBufferObject) + }; + + VkWriteDescriptorSet descriptorWrite = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + NULL, + p->descriptorSet, + 0, + 0, + 1, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + NULL, + &bufferInfo, + NULL + }; + + vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); + // pain. + } \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.h b/src/rendering/pipeline3d/setup.h index fe938c7..61b8126 100644 --- a/src/rendering/pipeline3d/setup.h +++ b/src/rendering/pipeline3d/setup.h @@ -1,4 +1,8 @@ +#include #include +#include +#include "../management/elementHeap.h" void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum); -void createPipeline3d(struct rendering_state *s); \ No newline at end of file +void createPipeline3d(struct rendering_state *s); +void updateDescriptorSet(struct rendering_state *s); \ No newline at end of file diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c index 11a7687..95cc717 100644 --- a/src/rendering/rendering.c +++ b/src/rendering/rendering.c @@ -1,6 +1,6 @@ #include "rendering.h" #include "pipeline3d/render.h" -#include "pipeline3d/vertex.h" +#include "pipeline3d/data.h" #include #include #include diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h index e7d111b..cbcae41 100644 --- a/src/rendering/rendering.h +++ b/src/rendering/rendering.h @@ -1,7 +1,7 @@ #ifndef __RENDERING_RENDERING_H__ #define __RENDERING_RENDERING_H__ #include "struct.h" -#include "pipeline3d/vertex.h" +#include "pipeline3d/data.h" void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount); diff --git a/src/rendering/struct.h b/src/rendering/struct.h index a722d5a..dc30b1c 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -9,6 +9,7 @@ struct rendering_queueFamilies{ int graphicsQFI; VkQueue graphicsQueue; + VkQueue transferQueue; // for staging buffers and such int presentationQFI; VkQueue presentationQueue; @@ -83,7 +84,7 @@ struct rendering_state{ VkCommandPool graphicsPool; VkCommandBuffer graphicsBuffer; - VkCommandPool memoryPool; + VkCommandBuffer transferBuffer; struct rendering_sync sync; diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c index 4c4e8f2..a632731 100644 --- a/src/rendering/vulkan/gpumem.c +++ b/src/rendering/vulkan/gpumem.c @@ -44,13 +44,13 @@ void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_sta } void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s){ - printf("a\n"); - rendering_createBuffer(buf, usage, shareMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); - printf("b\n"); - rendering_putBuffer(buf, data, s); - printf("c\n"); - - // TODO: Staging buffer stuff. (waiting on copy buffer) + struct gpubuffer stagebuf = {}; + stagebuf.size = buf->size; + rendering_createBuffer(&stagebuf, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | usage, shareMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); + rendering_putBuffer(&stagebuf, data, s); + rendering_createBuffer(buf, VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage, shareMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, s); + rendering_copyBuffer(stagebuf.buffer, buf->buffer, buf->size, s); + rendering_destroyBuffer(&stagebuf, s); } uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s){ @@ -63,4 +63,33 @@ uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags propertie } printf("no suitable memory type!\n"); exit(EXIT_FAILURE); +} + +void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + VkBufferCopy copyregion = { + 0, 0, + size + }; + vkCmdCopyBuffer(s->transferBuffer, src, dst, 1, ©region); + vkEndCommandBuffer(s->transferBuffer); + VkSubmitInfo submitinfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 0, + NULL, + 0, + 1, + &s->transferBuffer, + 0, + NULL + }; + vkQueueSubmit(s->queues.transferQueue, 1, &submitinfo, NULL); + vkQueueWaitIdle(s->queues.transferQueue); } \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h index d3fc203..701aefc 100644 --- a/src/rendering/vulkan/gpumem.h +++ b/src/rendering/vulkan/gpumem.h @@ -1,23 +1,22 @@ #ifndef __GPUMEM_H__ #define __GPUMEM_H__ #include "../struct.h" +#include "gpumem_types.h" #include -struct gpubuffer { - VkBuffer buffer; - VkDeviceMemory memory; - uint64_t size; -}; - void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, VkSharingMode shareMode, VkMemoryPropertyFlagBits properties, struct rendering_state *s); uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s); -// TODO: IMPLEMENT ME -void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size); +void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct rendering_state *s); void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s); void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s); +static inline void rendering_destroyBuffer(struct gpubuffer *b, struct rendering_state *s){ + vkDestroyBuffer(s->device, b->buffer, NULL); + vkFreeMemory(s->device, b->memory, NULL); +} + #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem_types.h b/src/rendering/vulkan/gpumem_types.h new file mode 100644 index 0000000..ff7a61d --- /dev/null +++ b/src/rendering/vulkan/gpumem_types.h @@ -0,0 +1,11 @@ +#ifndef __GPUMEM_TYPES_H__ +#define __GPUMEM_TYPES_H__ +#include + +struct gpubuffer { + VkBuffer buffer; + VkDeviceMemory memory; + uint64_t size; +}; + +#endif \ No newline at end of file From a808a46a866518c0717c5c98b1c51c2dabfab666 Mon Sep 17 00:00:00 2001 From: miri Date: Tue, 11 Mar 2025 19:57:48 +0100 Subject: [PATCH 14/22] added uniforms support needs some debugging work and stuff --- .gitignore | 3 +- makefile | 5 +- shd/triangle.vert | 6 +- src/main.c | 36 ++++++- src/rendering/init.c | 11 ++- src/rendering/management/elementHeap.c | 52 ++++++++++ src/rendering/management/elementHeap.h | 48 +++++++++ src/rendering/pipeline3d/data.c | 6 ++ src/rendering/pipeline3d/{vertex.h => data.h} | 15 +++ src/rendering/pipeline3d/pipeline.h | 9 +- src/rendering/pipeline3d/render.c | 13 ++- src/rendering/pipeline3d/render.h | 2 +- src/rendering/pipeline3d/setup.c | 98 ++++++++++++++++++- src/rendering/pipeline3d/setup.h | 6 +- src/rendering/rendering.c | 2 +- src/rendering/rendering.h | 2 +- src/rendering/struct.h | 3 +- src/rendering/vulkan/gpumem.c | 43 ++++++-- src/rendering/vulkan/gpumem.h | 15 ++- src/rendering/vulkan/gpumem_types.h | 11 +++ 20 files changed, 351 insertions(+), 35 deletions(-) create mode 100644 src/rendering/management/elementHeap.c create mode 100644 src/rendering/management/elementHeap.h create mode 100644 src/rendering/pipeline3d/data.c rename src/rendering/pipeline3d/{vertex.h => data.h} (56%) create mode 100644 src/rendering/vulkan/gpumem_types.h diff --git a/.gitignore b/.gitignore index 16c0376..7f94f65 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ a.out main valgrind* -*.spv \ No newline at end of file +*.spv +vgcore.* \ No newline at end of file diff --git a/makefile b/makefile index 5ce9cd2..84db5b3 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,7 @@ CC = clang -CLIBS = -lglfw -lvulkan +CLIBS = -lglfw -lvulkan -lcglm -lm +CFLAGS = -g all: - $(CC) $(CLIBS) src/*.c src/rendering/*.c src/rendering/pipeline3d/*.c src/rendering/vulkan/*.c \ No newline at end of file + $(CC) $(CLIBS) $(CFLAGS) src/*.c src/rendering/*.c src/rendering/pipeline3d/*.c src/rendering/vulkan/*.c src/rendering/management/*.c \ No newline at end of file diff --git a/shd/triangle.vert b/shd/triangle.vert index f9194b7..dfe7f7c 100644 --- a/shd/triangle.vert +++ b/shd/triangle.vert @@ -1,5 +1,9 @@ #version 450 +layout(binding = 0) uniform UniformBufferObject { + mat4 matrix; // do all the view matrix stuff on the cpu during queue submission +} ubo; + layout(location = 0) in vec3 pos; layout(location = 1) in vec2 uv; @@ -20,6 +24,6 @@ vec3 colors[3] = vec3[]( */ void main() { - gl_Position = vec4(pos, 1.0); + gl_Position = ubo.matrix * vec4(pos, 1.0); fragColor = vec3(uv, 1.0); } diff --git a/src/main.c b/src/main.c index d3869ff..4ff7e8d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,15 @@ #include +#include +#include +#include +#include +#include +#include +#include #include #include "rendering/init.h" -#include "rendering/pipeline3d/vertex.h" +#include "rendering/management/elementHeap.h" +#include "rendering/pipeline3d/data.h" #include "rendering/struct.h" #include "rendering/vulkan/gpumem.h" #include "rendering/vulkan/renderingpipeline.h" @@ -64,21 +72,47 @@ int main(int argc, const char **argv){ rendering_createCommandPools(&s); rendering_createSyncObjects(&s); + // create a triangle renderdata struct renderData triangle; struct vertex triangleverts[] = { {0.f, -0.5f, 0.f, 0.f, 1.f}, {0.5f, 0.5f, 0.f, 0.f, 0.f}, {-0.5f, 0.5f, 0.f, 1.f, 0.f}, }; + uint32_t triangleindices[] = {0,1,2}; triangle.vertexBuffer.size = sizeof(triangleverts); + triangle.indexBuffer.size = sizeof(triangleindices); rendering_makeBufferForRender(&triangle.vertexBuffer, triangleverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); + rendering_makeBufferForRender(&triangle.indexBuffer, triangleindices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); triangle.vertexcount = 3; + triangle.uniforms.heap = &s.pipeline.uniformHeap; + triangle.uniforms.index = elementHeap_getFreeIndexAndReserve(triangle.uniforms.heap); + printf("index = %i\n", triangle.uniforms.index); + triangle.uniforms.ubo = triangle.uniforms.heap->buffer + triangle.uniforms.heap->objsize * triangle.uniforms.index; + + //glm_mat4_copy(GLM_MAT4_IDENTITY, triangle.uniforms.ubo->matrix); + mat4 proj; + mat4 view; + glm_perspective(glm_rad(50.0), 1.0, 0.1, 100.0, proj); + vec3 eye = {0.f,0.f,1.f}; + vec3 center = {0.f,0.f,0.f}; + vec3 up = {0.f,1.f,0.f}; + glm_lookat(eye, center, up, view); + glm_mat4_mul(proj, view, triangle.uniforms.ubo->matrix); + glm_mat4_print(view, stdout); + + uint32_t first = 1; + while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); render(&s, &triangle, 1); + if(first){ + first = 0; + glm_mat4_print(triangle.uniforms.ubo->matrix, stdout); + } } } \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index e36808d..af49d89 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -59,7 +59,7 @@ void rendering_vkdevice(struct rendering_state *s){ float queuePriority = 1.f; VkDeviceQueueCreateInfo deviceQueues[] = { - svlk_createQueueInfo(s->queues.graphicsQFI, 1, &queuePriority) + svlk_createQueueInfo(s->queues.graphicsQFI, 2, &queuePriority) }; VkPhysicalDeviceFeatures devFeatures = {0}; @@ -67,6 +67,7 @@ void rendering_vkdevice(struct rendering_state *s){ s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.graphicsQueue); + vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.transferQueue); vkGetDeviceQueue(s->device, s->queues.presentationQFI, 0, &s->queues.presentationQueue); }; @@ -219,11 +220,15 @@ void rendering_createtemplate(struct rendering_state *s, struct rendering_settin void rendering_createCommandPools(struct rendering_state *s){ VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); if(result != VK_SUCCESS){ - printf("Creating the Graphics Pool Failed"); + printf("Creating the Graphics Pool Failed\n"); } result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer); if(result != VK_SUCCESS){ - printf("Creatingt the Graphics Buffer Failed"); + printf("Creating the Graphics Buffer Failed\n"); + } + result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->transferBuffer); + if(result != VK_SUCCESS){ + printf("Creating the Transfer Buffer Failed\n"); } } diff --git a/src/rendering/management/elementHeap.c b/src/rendering/management/elementHeap.c new file mode 100644 index 0000000..af75b74 --- /dev/null +++ b/src/rendering/management/elementHeap.c @@ -0,0 +1,52 @@ +#include "elementHeap.h" +#include +#include +#include + +#if __amd64__ +//#define ASM_BSR_AVAIL +#endif + +void *elementHeap_getFreeElement(struct elementHeap *h){ + uint32_t index = elementHeap_getFreeIndex(h); + if(index != UINT32_MAX) + return h->buffer + h->objsize * index; + else + return NULL; +} +uint32_t elementHeap_getFreeIndex(struct elementHeap *h){ + uint32_t avblen = h->len / 32 + (h->len % 32 != 0); // get the lenth of the availability bitmap + for (uint32_t i = 0; i < avblen; i++) { + uint32_t j = (i + h->lkg) % avblen; // start searching from the lkg + if(h->availabilitybitmap[j] != UINT32_MAX){ + #ifdef ASM_BSR_AVAIL + // potential future stuff + #else + + uint32_t x = h->availabilitybitmap[j]; + uint32_t lzc = 0 + + ((x & 0xFFFF0000) ? 16 : 0) + + ((x & 0xFF00FF00) ? 8 : 0) + + ((x & 0xF0F0F0F0) ? 4 : 0) + + ((x & 0xCCCCCCCC) ? 2 : 0) + + ((x & 0xAAAAAAAA) ? 1 : 0); // get the specific bit of the unset element + + h->availabilitybitmap[j] |= 1 << lzc; // set the bit for the reserved element + h->lkg = j; // update the lkg + + if(j * 32 + lzc >= h->len) return UINT32_MAX; + + return j * 32 + lzc; + + #endif + } + } + + // return uint32 maximum if no free UBO is found. + return UINT32_MAX; +} + +void elementHeap_init(struct elementHeap *h){ + uint32_t avblen = h->len / 32 + (h->len % 32 != 0); // get the lenth of the availability bitmap + h->availabilitybitmap = calloc(1, avblen * sizeof(uint32_t)); +} \ No newline at end of file diff --git a/src/rendering/management/elementHeap.h b/src/rendering/management/elementHeap.h new file mode 100644 index 0000000..cee8fe8 --- /dev/null +++ b/src/rendering/management/elementHeap.h @@ -0,0 +1,48 @@ +#ifndef __UNIFORMS_H__ +#define __UNIFORMS_H__ + +// a Bundle is data for multiple objects put into a buffer +#include + +struct uboBufinfo{ + struct uniformTable *ut; + uint32_t id; +}; + +// a heap of elements with a bitmap for availability +struct elementHeap{ + uint32_t *availabilitybitmap; // bit field specifying which uniforms are available + uint32_t len; // length of the heap in elements + uint32_t objsize; // the size of one of the objects + uint32_t lkg; // last known good index to start searching from in elements of availabilitybitmap + void *buffer; // the data (on gpu, expect write only) +}; + +// set an element of the heap as reserved (does not check) (avoid using this unless a specific element is required) +static inline void elementHeap_setReserved(struct elementHeap *h, uint32_t index){ + h->availabilitybitmap[index/32] |= 1 << (index % 32); +} + +// set an element of the heap as free (does not check) +static inline void elementHeap_setFree(struct elementHeap *h, uint32_t index){ + h->availabilitybitmap[index/32] &= ~(1 << (index % 32)); + h->lkg = index/32; +} + +// get the index of the first free element without setting it as used (avoid using this) +uint32_t elementHeap_getFreeIndex(struct elementHeap *h); + +// get the index of the first free element without setting it as used (avoid using this) +static inline uint32_t elementHeap_getFreeIndexAndReserve(struct elementHeap *h){ + uint32_t i = elementHeap_getFreeIndex(h); + elementHeap_setReserved(h, i); + return i; +} + +// get the first free element and return a pointer to it whilst setting it as used +void *elementHeap_getFreeElement(struct elementHeap *h); + +// initialise the heap, BUFFER IS NOT CREATED, _MUST_ BE CREATED SEPERATLEY +void elementHeap_init(struct elementHeap *h); + +#endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/data.c b/src/rendering/pipeline3d/data.c new file mode 100644 index 0000000..a93e74b --- /dev/null +++ b/src/rendering/pipeline3d/data.c @@ -0,0 +1,6 @@ +#include "data.h" + +void uniformData_init(struct uniformData *ud, struct elementHeap *heap){ + ud->heap = heap; + ud->index = elementHeap_getFreeIndexAndReserve(heap); +} \ No newline at end of file diff --git a/src/rendering/pipeline3d/vertex.h b/src/rendering/pipeline3d/data.h similarity index 56% rename from src/rendering/pipeline3d/vertex.h rename to src/rendering/pipeline3d/data.h index b627d35..888177d 100644 --- a/src/rendering/pipeline3d/vertex.h +++ b/src/rendering/pipeline3d/data.h @@ -1,7 +1,9 @@ #ifndef __VERTEX_H__ #define __VERTEX_H__ +#include #include #include "../vulkan/gpumem.h" +#include "../management/elementHeap.h" struct vertex { //position @@ -14,11 +16,24 @@ struct vertex { float v; }; +struct UniformBufferObject{ + mat4 matrix; +}; + +struct uniformData { + struct elementHeap *heap; + uint32_t index; + struct UniformBufferObject *ubo; +}; + // data for generic meshes (not things like heightmaps or other exotic things) struct renderData { struct gpubuffer vertexBuffer; struct gpubuffer indexBuffer; uint32_t vertexcount; + struct uniformData uniforms; }; +void uniformData_init(struct uniformData *ud, struct elementHeap *heap); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h index 2ea17ca..edb2163 100644 --- a/src/rendering/pipeline3d/pipeline.h +++ b/src/rendering/pipeline3d/pipeline.h @@ -2,16 +2,23 @@ #define __PIPELINE3D_PIPELINE_H__ #include +#include "../management/elementHeap.h" +#include "../vulkan/gpumem_types.h" struct pipeline3dSubpass { VkPipelineLayout layout; VkPipeline pipeline; - + VkDescriptorSetLayout desclayout; }; struct pipeline3d { VkRenderPass renderpass; struct pipeline3dSubpass geometryPass; + VkDescriptorPool descriptorPool; + // potentially wrap the uniform heap in a linked list + struct elementHeap uniformHeap; + VkDescriptorSet descriptorSet; + struct gpubuffer uniformbuffer; }; #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index 64da1cd..e10fae3 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -1,8 +1,11 @@ #include "render.h" #include "pipeline.h" +#include "setup.h" +#include #include void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount){ + updateDescriptorSet(s); VkClearValue clearcolor = {0.4f,0.f,0.f,1.f}; VkRenderPassBeginInfo rpbi = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, @@ -28,9 +31,13 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer vkCmdSetScissor(cmdbuf, 0, 1, &sc); for (uint32_t i = 0; i < objcount; i++) { - VkDeviceSize offsets[] = {0}; - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, offsets); - vkCmdDraw(cmdbuf, obj[i].vertexcount, 1, 0, 0); + VkDeviceSize vtxoffsets[] = {0}; + uint32_t unioffsets[] = {obj[i].uniforms.index}; + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, vtxoffsets); + vkCmdBindIndexBuffer(cmdbuf, obj[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 0, + 1, &p->descriptorSet, 1, unioffsets); + vkCmdDrawIndexed(cmdbuf, obj[i].vertexcount, 1, 0, 0, 0); } vkCmdEndRenderPass(cmdbuf); diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h index 7c8c201..ca1efa1 100644 --- a/src/rendering/pipeline3d/render.h +++ b/src/rendering/pipeline3d/render.h @@ -2,7 +2,7 @@ #define __PIPELINE3D_RENDER_H__ #include -#include "vertex.h" +#include "data.h" #include "../struct.h" void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount); diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index 7643e52..775d31b 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -1,11 +1,12 @@ #include +#include #include #include #include "../../helpers.h" #include "../init.h" #include "../vulkan/renderingpipeline.h" #include "../vulkan/svulc.h" -#include "vertex.h" +#include "data.h" #include "pipeline.h" #include "setup.h" @@ -75,7 +76,25 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineDynamicStateCreateInfo dynamicState = svlk_createDynamicStateCI(dynstates, 2, 0); // Pipeline Layout - VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(NULL, 0); + VkDescriptorSetLayoutBinding lb = { + 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + 1, + VK_SHADER_STAGE_VERTEX_BIT, + NULL + }; + + VkDescriptorSetLayoutCreateInfo dslCI = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + NULL, + 0, + 1, + &lb + }; + + vkCreateDescriptorSetLayout(s->device, &dslCI, NULL, &p->geometryPass.desclayout); + + VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(&p->geometryPass.desclayout, 1); vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); @@ -83,7 +102,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, NULL, 0, - 2, + 2, stages, &inputState, &inputAssemblyState, @@ -142,4 +161,77 @@ void createPipeline3d(struct rendering_state *s){ vkCreateRenderPass(s->device, &rpCI, NULL, &p->renderpass); createGeometryPass(s, p, 0); + + // uniform stuff + p->uniformHeap.len = 16; + p->uniformHeap.objsize = sizeof(struct uniformData); + elementHeap_init(&p->uniformHeap); + + p->uniformbuffer.size = p->uniformHeap.len * p->uniformHeap.objsize; + rendering_createBuffer(&p->uniformbuffer, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_SHARING_MODE_EXCLUSIVE, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + s + ); + vkMapMemory(s->device, p->uniformbuffer.memory, 0, p->uniformbuffer.size, 0, &p->uniformHeap.buffer); + + // why descriptors, why. why museth you exist + VkDescriptorPoolSize poolsize = { + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // offsets are cool + 1 // no linked lists yet so 1 + }; + + VkDescriptorPoolCreateInfo dpCI = { + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + NULL, + 0, + 1, + 1, + &poolsize + }; + + VkResult result = vkCreateDescriptorPool(s->device, &dpCI, NULL, &p->descriptorPool); + if(result != VK_SUCCESS){ + printf("Could not create descriptor pool. result=%i\n", result); + } + + VkDescriptorSetAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + NULL, + p->descriptorPool, + 1, + &p->geometryPass.desclayout + }; + result = vkAllocateDescriptorSets(s->device, &allocInfo, &p->descriptorSet); + if(result != VK_SUCCESS){ + printf("Could not create descriptor set. result=%i\n", result); + } + updateDescriptorSet(s); +} + +void updateDescriptorSet(struct rendering_state *s){ + struct pipeline3d *p = &s->pipeline; + VkDescriptorBufferInfo bufferInfo = { + p->uniformbuffer.buffer, + 0, + sizeof(struct UniformBufferObject) + }; + + VkWriteDescriptorSet descriptorWrite = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + NULL, + p->descriptorSet, + 0, + 0, + 1, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + NULL, + &bufferInfo, + NULL + }; + + vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); + // pain. + } \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.h b/src/rendering/pipeline3d/setup.h index fe938c7..61b8126 100644 --- a/src/rendering/pipeline3d/setup.h +++ b/src/rendering/pipeline3d/setup.h @@ -1,4 +1,8 @@ +#include #include +#include +#include "../management/elementHeap.h" void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum); -void createPipeline3d(struct rendering_state *s); \ No newline at end of file +void createPipeline3d(struct rendering_state *s); +void updateDescriptorSet(struct rendering_state *s); \ No newline at end of file diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c index 11a7687..95cc717 100644 --- a/src/rendering/rendering.c +++ b/src/rendering/rendering.c @@ -1,6 +1,6 @@ #include "rendering.h" #include "pipeline3d/render.h" -#include "pipeline3d/vertex.h" +#include "pipeline3d/data.h" #include #include #include diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h index e7d111b..cbcae41 100644 --- a/src/rendering/rendering.h +++ b/src/rendering/rendering.h @@ -1,7 +1,7 @@ #ifndef __RENDERING_RENDERING_H__ #define __RENDERING_RENDERING_H__ #include "struct.h" -#include "pipeline3d/vertex.h" +#include "pipeline3d/data.h" void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount); diff --git a/src/rendering/struct.h b/src/rendering/struct.h index a722d5a..dc30b1c 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -9,6 +9,7 @@ struct rendering_queueFamilies{ int graphicsQFI; VkQueue graphicsQueue; + VkQueue transferQueue; // for staging buffers and such int presentationQFI; VkQueue presentationQueue; @@ -83,7 +84,7 @@ struct rendering_state{ VkCommandPool graphicsPool; VkCommandBuffer graphicsBuffer; - VkCommandPool memoryPool; + VkCommandBuffer transferBuffer; struct rendering_sync sync; diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c index 4c4e8f2..a632731 100644 --- a/src/rendering/vulkan/gpumem.c +++ b/src/rendering/vulkan/gpumem.c @@ -44,13 +44,13 @@ void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_sta } void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s){ - printf("a\n"); - rendering_createBuffer(buf, usage, shareMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); - printf("b\n"); - rendering_putBuffer(buf, data, s); - printf("c\n"); - - // TODO: Staging buffer stuff. (waiting on copy buffer) + struct gpubuffer stagebuf = {}; + stagebuf.size = buf->size; + rendering_createBuffer(&stagebuf, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | usage, shareMode, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); + rendering_putBuffer(&stagebuf, data, s); + rendering_createBuffer(buf, VK_BUFFER_USAGE_TRANSFER_DST_BIT | usage, shareMode, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, s); + rendering_copyBuffer(stagebuf.buffer, buf->buffer, buf->size, s); + rendering_destroyBuffer(&stagebuf, s); } uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s){ @@ -63,4 +63,33 @@ uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags propertie } printf("no suitable memory type!\n"); exit(EXIT_FAILURE); +} + +void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + VkBufferCopy copyregion = { + 0, 0, + size + }; + vkCmdCopyBuffer(s->transferBuffer, src, dst, 1, ©region); + vkEndCommandBuffer(s->transferBuffer); + VkSubmitInfo submitinfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 0, + NULL, + 0, + 1, + &s->transferBuffer, + 0, + NULL + }; + vkQueueSubmit(s->queues.transferQueue, 1, &submitinfo, NULL); + vkQueueWaitIdle(s->queues.transferQueue); } \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h index d3fc203..701aefc 100644 --- a/src/rendering/vulkan/gpumem.h +++ b/src/rendering/vulkan/gpumem.h @@ -1,23 +1,22 @@ #ifndef __GPUMEM_H__ #define __GPUMEM_H__ #include "../struct.h" +#include "gpumem_types.h" #include -struct gpubuffer { - VkBuffer buffer; - VkDeviceMemory memory; - uint64_t size; -}; - void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, VkSharingMode shareMode, VkMemoryPropertyFlagBits properties, struct rendering_state *s); uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags properties, struct rendering_state *s); -// TODO: IMPLEMENT ME -void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size); +void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct rendering_state *s); void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s); void rendering_makeBufferForRender(struct gpubuffer *buf, void *data, VkBufferUsageFlagBits usage, VkSharingMode shareMode, struct rendering_state *s); +static inline void rendering_destroyBuffer(struct gpubuffer *b, struct rendering_state *s){ + vkDestroyBuffer(s->device, b->buffer, NULL); + vkFreeMemory(s->device, b->memory, NULL); +} + #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem_types.h b/src/rendering/vulkan/gpumem_types.h new file mode 100644 index 0000000..ff7a61d --- /dev/null +++ b/src/rendering/vulkan/gpumem_types.h @@ -0,0 +1,11 @@ +#ifndef __GPUMEM_TYPES_H__ +#define __GPUMEM_TYPES_H__ +#include + +struct gpubuffer { + VkBuffer buffer; + VkDeviceMemory memory; + uint64_t size; +}; + +#endif \ No newline at end of file From 0e0adae6b8c4df92fff6bf00393ab3e2b912278c Mon Sep 17 00:00:00 2001 From: miri Date: Wed, 12 Mar 2025 17:48:02 +0100 Subject: [PATCH 15/22] camera, and started cleaning up some code --- src/helpers.c | 10 +++ src/helpers.h | 3 + src/main.c | 134 ++++++++++++++++++++++-------- src/rendering/camera.h | 46 ++++++++++ src/rendering/init.c | 107 ++++++------------------ src/rendering/pipeline3d/data.c | 24 ++++++ src/rendering/pipeline3d/data.h | 7 +- src/rendering/pipeline3d/render.c | 29 +++++-- src/rendering/pipeline3d/render.h | 3 +- src/rendering/pipeline3d/setup.c | 2 +- src/rendering/rendering.c | 4 +- src/rendering/rendering.h | 3 +- src/rendering/struct.h | 8 ++ 13 files changed, 252 insertions(+), 128 deletions(-) create mode 100644 src/rendering/camera.h diff --git a/src/helpers.c b/src/helpers.c index ad23b3f..96a38bc 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -1,6 +1,9 @@ #include "helpers.h" +#include #include #include +#include +#include char *readFileC(const char *filename){ //open file @@ -85,4 +88,11 @@ int writeFileR(const char *filename, sizedData data){ void crash(char *reason){ PRINTE(reason); exit(EXIT_FAILURE); +} + +void checkErrorVk(char* func, VkResult res, void (*die)()){ + if(res == VK_SUCCESS) return; + fprintf(stdout, "FATAL: %s failed with code: %s (%i)\n", func, string_VkResult(res), res); + if(die) (die()); + return; } \ No newline at end of file diff --git a/src/helpers.h b/src/helpers.h index 0b80d11..e0f493c 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -4,6 +4,7 @@ #define __M_HELPERS_H__ +#include #ifndef __cplusplus #include #define PRINT(message) printf("%s",message) @@ -79,4 +80,6 @@ int writeFileR(const char* filename, sizedData data); */ void crash(char* reason); +void checkErrorVk(char* func, VkResult res, void (*die)()); + #endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index 4ff7e8d..5831c9a 100644 --- a/src/main.c +++ b/src/main.c @@ -2,11 +2,16 @@ #include #include #include +#include +#include #include +#include #include #include #include +#include #include +#include "rendering/camera.h" #include "rendering/init.h" #include "rendering/management/elementHeap.h" #include "rendering/pipeline3d/data.h" @@ -19,12 +24,62 @@ #include "rendering/rendering.h" void fbResizeCb(GLFWwindow *window, int width, int height){ - struct rendering_state *s = glfwGetWindowUserPointer(window); - s->shouldRecreateswapchainFlags = RNDR_RECREATE_SWAPCHAIN | RNDR_RECREATE_SWAPCHAIN_USE_TMP; - s->tmp_window_width = width; - s->tmp_window_height = height; + struct rendering_windowUserPointer *wup = glfwGetWindowUserPointer(window); + wup->s->shouldRecreateswapchainFlags = RNDR_RECREATE_SWAPCHAIN | RNDR_RECREATE_SWAPCHAIN_USE_TMP; + wup->s->tmp_window_width = width; + wup->s->tmp_window_height = height; } +void keyCb(GLFWwindow *window, int key, int scancode, int action, int mods){ + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){ + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } +} + +void keyboardUpdate(GLFWwindow *window, struct camera *cam){ + if(glfwGetKey(window, GLFW_KEY_W)) + glm_vec3_muladds(cam->forward, 0.1f, cam->position); + if(glfwGetKey(window, GLFW_KEY_S)) + glm_vec3_muladds(cam->forward, -0.1f, cam->position); + if(glfwGetKey(window, GLFW_KEY_A)) + glm_vec3_muladds(cam->right, -0.1f, cam->position); + if(glfwGetKey(window, GLFW_KEY_D)) + glm_vec3_muladds(cam->right, 0.1f, cam->position); + + vec3 xaxis = {1.f,0.f,0.f}; + vec3 zaxis = {0.f,0.f,1.f}; + + if(glfwGetKey(window, GLFW_KEY_UP)) + glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); + if(glfwGetKey(window, GLFW_KEY_DOWN)) + glm_vec3_rotate(cam->rotation, glm_rad(-1.f), cam->right); + if(glfwGetKey(window, GLFW_KEY_LEFT)) + glm_vec3_rotate(cam->rotation, glm_rad(-1.f), zaxis); + if(glfwGetKey(window, GLFW_KEY_RIGHT)) + glm_vec3_rotate(cam->rotation, glm_rad(1.f), zaxis); + + camera_update(cam); +} + +void cursorPosCb(GLFWwindow *window, double x, double y){ + struct rendering_windowUserPointer *wup = glfwGetWindowUserPointer(window); + struct camera *cam = wup->maincam; + vec3 xaxis = {1.f,0.f,0.f}; + vec3 zaxis = {0.f,0.f,1.f}; + + static double lastx; + static double lasty; + const double coef = 0.1f; + glm_vec3_rotate(cam->rotation, glm_rad(x-lastx) * coef, zaxis); + glm_vec3_rotate(cam->rotation, glm_rad(y-lasty) * coef, cam->right); + lastx = x; + lasty = y; +} + +// callback that will be called when the programm encounters a fatal error so that it can be cleaned up +void die(){ + exit(EXIT_FAILURE); +} int main(int argc, const char **argv){ struct rendering_state s = {0}; @@ -32,12 +87,22 @@ int main(int argc, const char **argv){ s.window_width = 100; s.window_height = 100; + struct rendering_windowUserPointer wup = { + NULL, + &s + }; + s.die = ¨ + rendering_initGLFW(&s); - glfwSetWindowUserPointer(s.window, &s); + glfwSetWindowUserPointer(s.window, &wup); glfwSetFramebufferSizeCallback(s.window, fbResizeCb); + glfwSetKeyCallback(s.window, keyCb); + glfwSetInputMode(s.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetCursorPosCallback(s.window, cursorPosCb); rendering_vkinstance(&s); + printf("vkerror = %i\n", s.error); // TODO: make a function that selects the best, most suitable device unsigned int physDeviceNum; @@ -63,6 +128,7 @@ int main(int argc, const char **argv){ rendering_vkswapchain(&s); + if(s.error != VK_SUCCESS) printf("fehler ): \n"); // load a shader @@ -73,45 +139,41 @@ int main(int argc, const char **argv){ rendering_createSyncObjects(&s); // create a triangle renderdata - struct renderData triangle; - struct vertex triangleverts[] = { - {0.f, -0.5f, 0.f, 0.f, 1.f}, - {0.5f, 0.5f, 0.f, 0.f, 0.f}, - {-0.5f, 0.5f, 0.f, 1.f, 0.f}, + struct renderData rects[2] = {0}; + struct vertex rectverts[] = { + {-0.5f, 0.f, 0.5f, 0.f, 0.f}, + {0.5f, 0.f, 0.5f, 1.f, 0.f}, + {-0.5f, 0.f, -0.5f, 0.f, 1.f}, + {0.5f, 0.f, -0.5f, 1.f, 1.f}, }; - uint32_t triangleindices[] = {0,1,2}; - triangle.vertexBuffer.size = sizeof(triangleverts); - triangle.indexBuffer.size = sizeof(triangleindices); - - rendering_makeBufferForRender(&triangle.vertexBuffer, triangleverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); - rendering_makeBufferForRender(&triangle.indexBuffer, triangleindices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); - - triangle.vertexcount = 3; - - triangle.uniforms.heap = &s.pipeline.uniformHeap; - triangle.uniforms.index = elementHeap_getFreeIndexAndReserve(triangle.uniforms.heap); - printf("index = %i\n", triangle.uniforms.index); - triangle.uniforms.ubo = triangle.uniforms.heap->buffer + triangle.uniforms.heap->objsize * triangle.uniforms.index; - - //glm_mat4_copy(GLM_MAT4_IDENTITY, triangle.uniforms.ubo->matrix); - mat4 proj; - mat4 view; - glm_perspective(glm_rad(50.0), 1.0, 0.1, 100.0, proj); - vec3 eye = {0.f,0.f,1.f}; - vec3 center = {0.f,0.f,0.f}; - vec3 up = {0.f,1.f,0.f}; - glm_lookat(eye, center, up, view); - glm_mat4_mul(proj, view, triangle.uniforms.ubo->matrix); - glm_mat4_print(view, stdout); + uint32_t triangleindices[] = {0,1,2, 1, 3, 2}; + renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[1]); + rectverts[1].x = 0.f; + rectverts[1].y = 1.f; + renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[0]); + //triangles[1].position[2] = 2.f; + // camera stuff uint32_t first = 1; + struct camera cam; + cam.far = 1000.f; + cam.near = 0.1f; + cam.fov = glm_rad(90.f); + cam.position[1] = -2.f; + cam.aspectRatio = 1.f; + vec4 quat = {0.f,1.f,0.f,0.f}; + glm_quat_copy(quat, cam.rotation); + camera_update(&cam); + wup.maincam = &cam; + while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); - render(&s, &triangle, 1); + keyboardUpdate(s.window, &cam); + render(&s, rects, 2, &cam); if(first){ first = 0; - glm_mat4_print(triangle.uniforms.ubo->matrix, stdout); + glm_mat4_print(rects[1].uniforms.ubo->matrix, stdout); } } diff --git a/src/rendering/camera.h b/src/rendering/camera.h new file mode 100644 index 0000000..e9e08ca --- /dev/null +++ b/src/rendering/camera.h @@ -0,0 +1,46 @@ +#ifndef __CAMERA_H__ +#define __CAMERA_H__ + +#include +#include +#include +#include +#include +#include +struct camera{ + mat4 view; + mat4 projection; + vec3 position; + vec4 rotation; // quaternion + float fov; + float aspectRatio; + float far; // far clipping plane + float near; // near clipping plane + + // up forward and right vectors + vec3 up; + vec3 forward; + vec3 right; +}; + +static inline void camera_update(struct camera *cam){ + glm_perspective(cam->fov, cam->aspectRatio, cam->near, cam->far, cam->projection); + + // correct the rotations to be righthanded, z-up and not upside down + vec3 angles = {glm_rad(90.0), 0.f, 0.f}; + vec4 rotation; + glm_euler_xyz_quat(angles, rotation); + glm_quat_mul(cam->rotation, rotation, rotation); + + glm_quat_look(cam->position, rotation, cam->view); + + vec3 up = {0.f,0.f,-1.f}; + vec3 forward = {0.f,1.f,0.f}; + vec3 right = {1.f,0.f,0.f}; + + glm_quat_rotatev(cam->rotation, up, cam->up); + glm_quat_rotatev(cam->rotation, forward, cam->forward); + glm_quat_rotatev(cam->rotation, right, cam->right); +} + +#endif \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index af49d89..0bb6ff4 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -34,6 +34,9 @@ void rendering_initGLFW(struct rendering_state *s){ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); s->window = glfwCreateWindow(s->window_width, s->window_height, s->window_title, NULL, NULL); + if(s->window == NULL){ + printf("oh noes, the window is null!\n"); + } s->extensions = glfwGetRequiredInstanceExtensions(&s->numextensions); } @@ -41,10 +44,15 @@ void rendering_initGLFW(struct rendering_state *s){ void rendering_vkinstance(struct rendering_state *s){ const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; - s->error = svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance); - if(s->error != VK_SUCCESS) return; - s->error = glfwCreateWindowSurface(s->instance, s->window, NULL, &s->surface); - if(s->error != VK_SUCCESS) return; + checkErrorVk("vkCreateInstance", + svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance), + s->die + ); + + checkErrorVk("glfwCreateWindowSurface", + glfwCreateWindowSurface(s->instance, s->window, NULL, &s->surface), + s->die + ); } void rendering_vkgetfamilies(struct rendering_state *s){ @@ -64,7 +72,14 @@ void rendering_vkdevice(struct rendering_state *s){ VkPhysicalDeviceFeatures devFeatures = {0}; - s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); + checkErrorVk("vkCreateDevice", + svlk_createLogicalDevice( + s->physdevice, s->surface, deviceQueues,1, + devFeatures, s->deviceextensions, + s->numdeviceextensions, &s->device + ), + s->die + ); vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.graphicsQueue); vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.transferQueue); @@ -72,7 +87,10 @@ void rendering_vkdevice(struct rendering_state *s){ }; void rendering_vkswapchain(struct rendering_state *s){ - s->error = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities); + checkErrorVk("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities), + s->die + ); if(s->error != VK_SUCCESS) return; s->surfaceformat = svlk_selectSwapchainFormat(s->physdevice, s->surface, VK_FORMAT_B8G8R8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); @@ -92,6 +110,7 @@ void rendering_vkswapchain(struct rendering_state *s){ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, s->surfaceCapabilities.currentTransform ); + unsigned int queueFamilies[] = {s->queues.graphicsQFI, s->queues.presentationQFI}; svlk_selectSharingMode(&swcreateinfo, s->queues.graphicsQFI != s->queues.presentationQFI, queueFamilies, 2); @@ -141,81 +160,7 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ vkDestroySwapchainKHR(s->device, s->swapchain, NULL); } -/* Now a relic of the old wor-- I mean relic of dumbness -void rendering_createtemplate(struct rendering_state *s, struct rendering_settings *st){ - VkGraphicsPipelineCreateInfo *c = s->gptemplate = calloc(1, sizeof(VkGraphicsPipelineCreateInfo)); - c->sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - c->stageCount = 0; // must be filled out - c->pStages = NULL; // must be filled out - VkPipelineVertexInputStateCreateInfo *vkpvis; - c->pVertexInputState = vkpvis = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); - vkpvis->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vkpvis->vertexBindingDescriptionCount = 1; - VkVertexInputBindingDescription *bindes; - vkpvis->pVertexBindingDescriptions = bindes = calloc(1, sizeof(VkVertexInputBindingDescription)); - bindes->binding = 0; - bindes->inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - bindes->stride = sizeof(struct vertex); - vkpvis->vertexAttributeDescriptionCount = 2; - VkVertexInputAttributeDescription *atrdes; - vkpvis->pVertexAttributeDescriptions = atrdes = calloc(2, sizeof(VkVertexInputAttributeDescription)); - atrdes[0].location = 0; - atrdes[0].binding = 0; - atrdes[0].format = VK_FORMAT_R32G32B32_SFLOAT; - atrdes[0].offset = 0; - atrdes[1].location = 1; - atrdes[1].binding = 0; - atrdes[1].format = VK_FORMAT_R32G32_SFLOAT; - atrdes[1].offset = sizeof(float) * 3; - VkPipelineInputAssemblyStateCreateInfo *vkpias; - c->pInputAssemblyState = vkpias = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); - vkpias->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - vkpias->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - vkpias->primitiveRestartEnable = VK_FALSE; - c->pTessellationState = NULL; - VkPipelineViewportStateCreateInfo *vkpvs; - c->pViewportState = vkpvs = calloc(1, sizeof(VkPipelineViewportStateCreateInfo)); - vkpvs->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - vkpvs->viewportCount = 1; - vkpvs->scissorCount = 1; - VkPipelineRasterizationStateCreateInfo *vkrs; - c->pRasterizationState = vkrs = calloc(1, sizeof(VkPipelineRasterizationStateCreateInfo)); - vkrs->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - vkrs->depthClampEnable = VK_FALSE; - vkrs->rasterizerDiscardEnable = VK_TRUE; - vkrs->polygonMode = VK_POLYGON_MODE_FILL; - vkrs->cullMode = VK_CULL_MODE_FRONT_BIT; - vkrs->frontFace = VK_FRONT_FACE_CLOCKWISE; - vkrs->depthBiasEnable = VK_FALSE; - //c->pMultisampleState = &svlk_noMultisample; - VkPipelineDepthStencilStateCreateInfo *vkds; - c->pDepthStencilState = vkds = calloc(1, sizeof(VkPipelineDepthStencilStateCreateInfo)); - vkds->sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - vkds->depthTestEnable = VK_TRUE; - vkds->depthWriteEnable = VK_FALSE; - vkds->depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; - vkds->depthBoundsTestEnable = VK_FALSE; - vkds->stencilTestEnable = VK_TRUE; - // TODO:front and back - vkds->minDepthBounds = 0; - vkds->maxDepthBounds = 1; - VkPipelineColorBlendStateCreateInfo *vkcbs; - c->pColorBlendState = vkcbs = calloc(1, sizeof(VkPipelineColorBlendStateCreateInfo)); - vkcbs->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - vkcbs->logicOpEnable = VK_FALSE; - vkcbs->logicOp = VK_LOGIC_OP_COPY; - vkcbs->attachmentCount = 1; - // vkcbs->pAttachments = &svlk_colorBlendTemplate; - VkPipelineDynamicStateCreateInfo *vkdys; - vkdys->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - vkdys->dynamicStateCount = 2; - VkDynamicState *vkdynst = calloc(2, sizeof(VkDynamicState)); - vkdys->pDynamicStates = vkdynst; - vkdynst[0] = VK_DYNAMIC_STATE_VIEWPORT; - vkdynst[1] = VK_DYNAMIC_STATE_SCISSOR; - -} -*/ +/* Here was a relic of the old wor-- I mean relic of dumbness */ void rendering_createCommandPools(struct rendering_state *s){ VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); diff --git a/src/rendering/pipeline3d/data.c b/src/rendering/pipeline3d/data.c index a93e74b..f1b153a 100644 --- a/src/rendering/pipeline3d/data.c +++ b/src/rendering/pipeline3d/data.c @@ -1,6 +1,30 @@ #include "data.h" +#include +#include +#include +#include void uniformData_init(struct uniformData *ud, struct elementHeap *heap){ ud->heap = heap; ud->index = elementHeap_getFreeIndexAndReserve(heap); +} +void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vertslen, uint32_t *indicies, uint32_t indiceslen, struct renderData *obj){ + obj->vertexBuffer.size = sizeof(struct vertex) * vertslen; + obj->indexBuffer.size = sizeof(uint32_t) * indiceslen; + + rendering_makeBufferForRender(&obj->vertexBuffer, verts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, s); + rendering_makeBufferForRender(&obj->indexBuffer, indicies, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, s); + + obj->indexCount = indiceslen; + obj->uniforms.heap = &s->pipeline.uniformHeap; + obj->uniforms.index = elementHeap_getFreeIndexAndReserve(obj->uniforms.heap); + printf("index = %i\n", obj->uniforms.index); + obj->uniforms.ubo = obj->uniforms.heap->buffer + obj->uniforms.heap->objsize * obj->uniforms.index; + + glm_quat_copy(GLM_QUAT_IDENTITY, obj->rotation); + vec3 scaleident = {1.f, 1.f, 1.f}; + glm_vec3_copy(scaleident, obj->scale); + vec3 origin = {0.f,0.f,0.f}; + glm_vec3_copy(origin, obj->position); + } \ No newline at end of file diff --git a/src/rendering/pipeline3d/data.h b/src/rendering/pipeline3d/data.h index 888177d..258a419 100644 --- a/src/rendering/pipeline3d/data.h +++ b/src/rendering/pipeline3d/data.h @@ -30,10 +30,15 @@ struct uniformData { struct renderData { struct gpubuffer vertexBuffer; struct gpubuffer indexBuffer; - uint32_t vertexcount; + uint32_t indexCount; struct uniformData uniforms; + vec3 position; + vec4 rotation; + vec3 scale; }; void uniformData_init(struct uniformData *ud, struct elementHeap *heap); +void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vertslen, uint32_t *indicies, uint32_t indiceslen, struct renderData *obj); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index e10fae3..186b798 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -1,12 +1,16 @@ #include "render.h" #include "pipeline.h" #include "setup.h" +#include +#include +#include +#include #include #include +#include "../camera.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount){ - updateDescriptorSet(s); - VkClearValue clearcolor = {0.4f,0.f,0.f,1.f}; +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam){ + VkClearValue clearcolor = {0.4f,0.f,0.f,0.5f}; VkRenderPassBeginInfo rpbi = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, @@ -30,14 +34,29 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer VkRect2D sc = {{0,0}, {s->window_width, s->window_height}}; vkCmdSetScissor(cmdbuf, 0, 1, &sc); + mat4 projview; + glm_mat4_mul(cam->projection, cam->view, projview); + for (uint32_t i = 0; i < objcount; i++) { + + // camera stuff + mat4 model; + glm_mat4_dup(GLM_MAT4_IDENTITY, model); + glm_scale(model, obj[i].scale); + glm_translate(model, obj[i].position); + glm_quat_rotate(model, obj[i].rotation, model); + + //glm_mat4_dup(projview, obj[i].uniforms.ubo->matrix); + glm_mat4_mul(projview, model, obj[i].uniforms.ubo->matrix); + + VkDeviceSize vtxoffsets[] = {0}; uint32_t unioffsets[] = {obj[i].uniforms.index}; vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, vtxoffsets); vkCmdBindIndexBuffer(cmdbuf, obj[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 0, - 1, &p->descriptorSet, 1, unioffsets); - vkCmdDrawIndexed(cmdbuf, obj[i].vertexcount, 1, 0, 0, 0); + 1, &p->descriptorSet, 1, unioffsets); + vkCmdDrawIndexed(cmdbuf, obj[i].indexCount, 1, 0, 0, 0); } vkCmdEndRenderPass(cmdbuf); diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h index ca1efa1..9cd65c4 100644 --- a/src/rendering/pipeline3d/render.h +++ b/src/rendering/pipeline3d/render.h @@ -4,6 +4,7 @@ #include #include "data.h" #include "../struct.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount); +#include "../camera.h" +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam); #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index 775d31b..98d9339 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -67,7 +67,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineVertexInputStateCreateInfo inputState = svlk_createPipelineVertexInputStateCI(bindes, 1, atdes, 2, 0); VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = svlk_createPipelineInputAssemblyStateCI(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineViewportStateCreateInfo viewportState = svlk_createPipelineViewportStateCI(1, 1, 0); - VkPipelineRasterizationStateCreateInfo rasterizerState = svlk_createPipelineRasterizationStateCI(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, 0); + VkPipelineRasterizationStateCreateInfo rasterizerState = svlk_createPipelineRasterizationStateCI(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0); VkPipelineMultisampleStateCreateInfo multisampleState = svlk_createPipelineMultisampleStateCI(VK_SAMPLE_COUNT_1_BIT, 0); VkPipelineDepthStencilStateCreateInfo depthState = svlk_createDepthStencilStateCI(VK_TRUE, VK_FALSE, VK_COMPARE_OP_GREATER_OR_EQUAL); VkPipelineColorBlendAttachmentState colorblendatt = svlk_createColorBlendAttachmentState(0xf, VK_FALSE); diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c index 95cc717..7cf6323 100644 --- a/src/rendering/rendering.c +++ b/src/rendering/rendering.c @@ -7,7 +7,7 @@ #include "init.h" #include "struct.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount){ +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam){ vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); uint32_t imageindex; @@ -31,7 +31,7 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount }; VkResult result = vkBeginCommandBuffer(s->graphicsBuffer, &begininfo); - recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount); + recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount, cam); vkEndCommandBuffer(s->graphicsBuffer); diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h index cbcae41..0a5bad8 100644 --- a/src/rendering/rendering.h +++ b/src/rendering/rendering.h @@ -2,8 +2,9 @@ #define __RENDERING_RENDERING_H__ #include "struct.h" #include "pipeline3d/data.h" +#include "camera.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount); +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam); void recreateSwapchain(struct rendering_state *s); diff --git a/src/rendering/struct.h b/src/rendering/struct.h index dc30b1c..8210ad8 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -1,6 +1,7 @@ #ifndef __RENDERING_STRUCT_H__ #define __RENDERING_STRUCT_H__ +#include "camera.h" #include "pipeline3d/pipeline.h" #include #include @@ -92,6 +93,8 @@ struct rendering_state{ // filled in when framebuffer size changes int tmp_window_width; int tmp_window_height; + + void (*die)(); }; enum rendering_SwapchainRecreateFlags{ @@ -99,4 +102,9 @@ enum rendering_SwapchainRecreateFlags{ RNDR_RECREATE_SWAPCHAIN_USE_TMP = 2 // use tmp_window_width & height }; +struct rendering_windowUserPointer{ + struct camera *maincam; + struct rendering_state *s; +}; + #endif \ No newline at end of file From fc3b008ab76192bc4664486904697adf88d98e82 Mon Sep 17 00:00:00 2001 From: miri Date: Wed, 12 Mar 2025 17:48:02 +0100 Subject: [PATCH 16/22] camera, and started cleaning up some code --- src/helpers.c | 10 +++ src/helpers.h | 3 + src/main.c | 134 ++++++++++++++++++++++-------- src/rendering/camera.h | 46 ++++++++++ src/rendering/init.c | 107 ++++++------------------ src/rendering/pipeline3d/data.c | 24 ++++++ src/rendering/pipeline3d/data.h | 7 +- src/rendering/pipeline3d/render.c | 29 +++++-- src/rendering/pipeline3d/render.h | 3 +- src/rendering/pipeline3d/setup.c | 2 +- src/rendering/rendering.c | 4 +- src/rendering/rendering.h | 3 +- src/rendering/struct.h | 8 ++ 13 files changed, 252 insertions(+), 128 deletions(-) create mode 100644 src/rendering/camera.h diff --git a/src/helpers.c b/src/helpers.c index ad23b3f..96a38bc 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -1,6 +1,9 @@ #include "helpers.h" +#include #include #include +#include +#include char *readFileC(const char *filename){ //open file @@ -85,4 +88,11 @@ int writeFileR(const char *filename, sizedData data){ void crash(char *reason){ PRINTE(reason); exit(EXIT_FAILURE); +} + +void checkErrorVk(char* func, VkResult res, void (*die)()){ + if(res == VK_SUCCESS) return; + fprintf(stdout, "FATAL: %s failed with code: %s (%i)\n", func, string_VkResult(res), res); + if(die) (die()); + return; } \ No newline at end of file diff --git a/src/helpers.h b/src/helpers.h index 0b80d11..e0f493c 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -4,6 +4,7 @@ #define __M_HELPERS_H__ +#include #ifndef __cplusplus #include #define PRINT(message) printf("%s",message) @@ -79,4 +80,6 @@ int writeFileR(const char* filename, sizedData data); */ void crash(char* reason); +void checkErrorVk(char* func, VkResult res, void (*die)()); + #endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index 4ff7e8d..5831c9a 100644 --- a/src/main.c +++ b/src/main.c @@ -2,11 +2,16 @@ #include #include #include +#include +#include #include +#include #include #include #include +#include #include +#include "rendering/camera.h" #include "rendering/init.h" #include "rendering/management/elementHeap.h" #include "rendering/pipeline3d/data.h" @@ -19,12 +24,62 @@ #include "rendering/rendering.h" void fbResizeCb(GLFWwindow *window, int width, int height){ - struct rendering_state *s = glfwGetWindowUserPointer(window); - s->shouldRecreateswapchainFlags = RNDR_RECREATE_SWAPCHAIN | RNDR_RECREATE_SWAPCHAIN_USE_TMP; - s->tmp_window_width = width; - s->tmp_window_height = height; + struct rendering_windowUserPointer *wup = glfwGetWindowUserPointer(window); + wup->s->shouldRecreateswapchainFlags = RNDR_RECREATE_SWAPCHAIN | RNDR_RECREATE_SWAPCHAIN_USE_TMP; + wup->s->tmp_window_width = width; + wup->s->tmp_window_height = height; } +void keyCb(GLFWwindow *window, int key, int scancode, int action, int mods){ + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){ + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } +} + +void keyboardUpdate(GLFWwindow *window, struct camera *cam){ + if(glfwGetKey(window, GLFW_KEY_W)) + glm_vec3_muladds(cam->forward, 0.1f, cam->position); + if(glfwGetKey(window, GLFW_KEY_S)) + glm_vec3_muladds(cam->forward, -0.1f, cam->position); + if(glfwGetKey(window, GLFW_KEY_A)) + glm_vec3_muladds(cam->right, -0.1f, cam->position); + if(glfwGetKey(window, GLFW_KEY_D)) + glm_vec3_muladds(cam->right, 0.1f, cam->position); + + vec3 xaxis = {1.f,0.f,0.f}; + vec3 zaxis = {0.f,0.f,1.f}; + + if(glfwGetKey(window, GLFW_KEY_UP)) + glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); + if(glfwGetKey(window, GLFW_KEY_DOWN)) + glm_vec3_rotate(cam->rotation, glm_rad(-1.f), cam->right); + if(glfwGetKey(window, GLFW_KEY_LEFT)) + glm_vec3_rotate(cam->rotation, glm_rad(-1.f), zaxis); + if(glfwGetKey(window, GLFW_KEY_RIGHT)) + glm_vec3_rotate(cam->rotation, glm_rad(1.f), zaxis); + + camera_update(cam); +} + +void cursorPosCb(GLFWwindow *window, double x, double y){ + struct rendering_windowUserPointer *wup = glfwGetWindowUserPointer(window); + struct camera *cam = wup->maincam; + vec3 xaxis = {1.f,0.f,0.f}; + vec3 zaxis = {0.f,0.f,1.f}; + + static double lastx; + static double lasty; + const double coef = 0.1f; + glm_vec3_rotate(cam->rotation, glm_rad(x-lastx) * coef, zaxis); + glm_vec3_rotate(cam->rotation, glm_rad(y-lasty) * coef, cam->right); + lastx = x; + lasty = y; +} + +// callback that will be called when the programm encounters a fatal error so that it can be cleaned up +void die(){ + exit(EXIT_FAILURE); +} int main(int argc, const char **argv){ struct rendering_state s = {0}; @@ -32,12 +87,22 @@ int main(int argc, const char **argv){ s.window_width = 100; s.window_height = 100; + struct rendering_windowUserPointer wup = { + NULL, + &s + }; + s.die = ¨ + rendering_initGLFW(&s); - glfwSetWindowUserPointer(s.window, &s); + glfwSetWindowUserPointer(s.window, &wup); glfwSetFramebufferSizeCallback(s.window, fbResizeCb); + glfwSetKeyCallback(s.window, keyCb); + glfwSetInputMode(s.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetCursorPosCallback(s.window, cursorPosCb); rendering_vkinstance(&s); + printf("vkerror = %i\n", s.error); // TODO: make a function that selects the best, most suitable device unsigned int physDeviceNum; @@ -63,6 +128,7 @@ int main(int argc, const char **argv){ rendering_vkswapchain(&s); + if(s.error != VK_SUCCESS) printf("fehler ): \n"); // load a shader @@ -73,45 +139,41 @@ int main(int argc, const char **argv){ rendering_createSyncObjects(&s); // create a triangle renderdata - struct renderData triangle; - struct vertex triangleverts[] = { - {0.f, -0.5f, 0.f, 0.f, 1.f}, - {0.5f, 0.5f, 0.f, 0.f, 0.f}, - {-0.5f, 0.5f, 0.f, 1.f, 0.f}, + struct renderData rects[2] = {0}; + struct vertex rectverts[] = { + {-0.5f, 0.f, 0.5f, 0.f, 0.f}, + {0.5f, 0.f, 0.5f, 1.f, 0.f}, + {-0.5f, 0.f, -0.5f, 0.f, 1.f}, + {0.5f, 0.f, -0.5f, 1.f, 1.f}, }; - uint32_t triangleindices[] = {0,1,2}; - triangle.vertexBuffer.size = sizeof(triangleverts); - triangle.indexBuffer.size = sizeof(triangleindices); - - rendering_makeBufferForRender(&triangle.vertexBuffer, triangleverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); - rendering_makeBufferForRender(&triangle.indexBuffer, triangleindices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, &s); - - triangle.vertexcount = 3; - - triangle.uniforms.heap = &s.pipeline.uniformHeap; - triangle.uniforms.index = elementHeap_getFreeIndexAndReserve(triangle.uniforms.heap); - printf("index = %i\n", triangle.uniforms.index); - triangle.uniforms.ubo = triangle.uniforms.heap->buffer + triangle.uniforms.heap->objsize * triangle.uniforms.index; - - //glm_mat4_copy(GLM_MAT4_IDENTITY, triangle.uniforms.ubo->matrix); - mat4 proj; - mat4 view; - glm_perspective(glm_rad(50.0), 1.0, 0.1, 100.0, proj); - vec3 eye = {0.f,0.f,1.f}; - vec3 center = {0.f,0.f,0.f}; - vec3 up = {0.f,1.f,0.f}; - glm_lookat(eye, center, up, view); - glm_mat4_mul(proj, view, triangle.uniforms.ubo->matrix); - glm_mat4_print(view, stdout); + uint32_t triangleindices[] = {0,1,2, 1, 3, 2}; + renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[1]); + rectverts[1].x = 0.f; + rectverts[1].y = 1.f; + renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[0]); + //triangles[1].position[2] = 2.f; + // camera stuff uint32_t first = 1; + struct camera cam; + cam.far = 1000.f; + cam.near = 0.1f; + cam.fov = glm_rad(90.f); + cam.position[1] = -2.f; + cam.aspectRatio = 1.f; + vec4 quat = {0.f,1.f,0.f,0.f}; + glm_quat_copy(quat, cam.rotation); + camera_update(&cam); + wup.maincam = &cam; + while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); - render(&s, &triangle, 1); + keyboardUpdate(s.window, &cam); + render(&s, rects, 2, &cam); if(first){ first = 0; - glm_mat4_print(triangle.uniforms.ubo->matrix, stdout); + glm_mat4_print(rects[1].uniforms.ubo->matrix, stdout); } } diff --git a/src/rendering/camera.h b/src/rendering/camera.h new file mode 100644 index 0000000..e9e08ca --- /dev/null +++ b/src/rendering/camera.h @@ -0,0 +1,46 @@ +#ifndef __CAMERA_H__ +#define __CAMERA_H__ + +#include +#include +#include +#include +#include +#include +struct camera{ + mat4 view; + mat4 projection; + vec3 position; + vec4 rotation; // quaternion + float fov; + float aspectRatio; + float far; // far clipping plane + float near; // near clipping plane + + // up forward and right vectors + vec3 up; + vec3 forward; + vec3 right; +}; + +static inline void camera_update(struct camera *cam){ + glm_perspective(cam->fov, cam->aspectRatio, cam->near, cam->far, cam->projection); + + // correct the rotations to be righthanded, z-up and not upside down + vec3 angles = {glm_rad(90.0), 0.f, 0.f}; + vec4 rotation; + glm_euler_xyz_quat(angles, rotation); + glm_quat_mul(cam->rotation, rotation, rotation); + + glm_quat_look(cam->position, rotation, cam->view); + + vec3 up = {0.f,0.f,-1.f}; + vec3 forward = {0.f,1.f,0.f}; + vec3 right = {1.f,0.f,0.f}; + + glm_quat_rotatev(cam->rotation, up, cam->up); + glm_quat_rotatev(cam->rotation, forward, cam->forward); + glm_quat_rotatev(cam->rotation, right, cam->right); +} + +#endif \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index af49d89..0bb6ff4 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -34,6 +34,9 @@ void rendering_initGLFW(struct rendering_state *s){ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); s->window = glfwCreateWindow(s->window_width, s->window_height, s->window_title, NULL, NULL); + if(s->window == NULL){ + printf("oh noes, the window is null!\n"); + } s->extensions = glfwGetRequiredInstanceExtensions(&s->numextensions); } @@ -41,10 +44,15 @@ void rendering_initGLFW(struct rendering_state *s){ void rendering_vkinstance(struct rendering_state *s){ const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; - s->error = svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance); - if(s->error != VK_SUCCESS) return; - s->error = glfwCreateWindowSurface(s->instance, s->window, NULL, &s->surface); - if(s->error != VK_SUCCESS) return; + checkErrorVk("vkCreateInstance", + svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance), + s->die + ); + + checkErrorVk("glfwCreateWindowSurface", + glfwCreateWindowSurface(s->instance, s->window, NULL, &s->surface), + s->die + ); } void rendering_vkgetfamilies(struct rendering_state *s){ @@ -64,7 +72,14 @@ void rendering_vkdevice(struct rendering_state *s){ VkPhysicalDeviceFeatures devFeatures = {0}; - s->error = svlk_createLogicalDevice(s->physdevice, s->surface, deviceQueues, 1, devFeatures, s->deviceextensions, s->numdeviceextensions, &s->device); + checkErrorVk("vkCreateDevice", + svlk_createLogicalDevice( + s->physdevice, s->surface, deviceQueues,1, + devFeatures, s->deviceextensions, + s->numdeviceextensions, &s->device + ), + s->die + ); vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.graphicsQueue); vkGetDeviceQueue(s->device, s->queues.graphicsQFI, 0, &s->queues.transferQueue); @@ -72,7 +87,10 @@ void rendering_vkdevice(struct rendering_state *s){ }; void rendering_vkswapchain(struct rendering_state *s){ - s->error = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities); + checkErrorVk("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities), + s->die + ); if(s->error != VK_SUCCESS) return; s->surfaceformat = svlk_selectSwapchainFormat(s->physdevice, s->surface, VK_FORMAT_B8G8R8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); @@ -92,6 +110,7 @@ void rendering_vkswapchain(struct rendering_state *s){ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, s->surfaceCapabilities.currentTransform ); + unsigned int queueFamilies[] = {s->queues.graphicsQFI, s->queues.presentationQFI}; svlk_selectSharingMode(&swcreateinfo, s->queues.graphicsQFI != s->queues.presentationQFI, queueFamilies, 2); @@ -141,81 +160,7 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ vkDestroySwapchainKHR(s->device, s->swapchain, NULL); } -/* Now a relic of the old wor-- I mean relic of dumbness -void rendering_createtemplate(struct rendering_state *s, struct rendering_settings *st){ - VkGraphicsPipelineCreateInfo *c = s->gptemplate = calloc(1, sizeof(VkGraphicsPipelineCreateInfo)); - c->sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - c->stageCount = 0; // must be filled out - c->pStages = NULL; // must be filled out - VkPipelineVertexInputStateCreateInfo *vkpvis; - c->pVertexInputState = vkpvis = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); - vkpvis->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vkpvis->vertexBindingDescriptionCount = 1; - VkVertexInputBindingDescription *bindes; - vkpvis->pVertexBindingDescriptions = bindes = calloc(1, sizeof(VkVertexInputBindingDescription)); - bindes->binding = 0; - bindes->inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - bindes->stride = sizeof(struct vertex); - vkpvis->vertexAttributeDescriptionCount = 2; - VkVertexInputAttributeDescription *atrdes; - vkpvis->pVertexAttributeDescriptions = atrdes = calloc(2, sizeof(VkVertexInputAttributeDescription)); - atrdes[0].location = 0; - atrdes[0].binding = 0; - atrdes[0].format = VK_FORMAT_R32G32B32_SFLOAT; - atrdes[0].offset = 0; - atrdes[1].location = 1; - atrdes[1].binding = 0; - atrdes[1].format = VK_FORMAT_R32G32_SFLOAT; - atrdes[1].offset = sizeof(float) * 3; - VkPipelineInputAssemblyStateCreateInfo *vkpias; - c->pInputAssemblyState = vkpias = calloc(1, sizeof(VkPipelineVertexInputStateCreateInfo)); - vkpias->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - vkpias->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - vkpias->primitiveRestartEnable = VK_FALSE; - c->pTessellationState = NULL; - VkPipelineViewportStateCreateInfo *vkpvs; - c->pViewportState = vkpvs = calloc(1, sizeof(VkPipelineViewportStateCreateInfo)); - vkpvs->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - vkpvs->viewportCount = 1; - vkpvs->scissorCount = 1; - VkPipelineRasterizationStateCreateInfo *vkrs; - c->pRasterizationState = vkrs = calloc(1, sizeof(VkPipelineRasterizationStateCreateInfo)); - vkrs->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - vkrs->depthClampEnable = VK_FALSE; - vkrs->rasterizerDiscardEnable = VK_TRUE; - vkrs->polygonMode = VK_POLYGON_MODE_FILL; - vkrs->cullMode = VK_CULL_MODE_FRONT_BIT; - vkrs->frontFace = VK_FRONT_FACE_CLOCKWISE; - vkrs->depthBiasEnable = VK_FALSE; - //c->pMultisampleState = &svlk_noMultisample; - VkPipelineDepthStencilStateCreateInfo *vkds; - c->pDepthStencilState = vkds = calloc(1, sizeof(VkPipelineDepthStencilStateCreateInfo)); - vkds->sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - vkds->depthTestEnable = VK_TRUE; - vkds->depthWriteEnable = VK_FALSE; - vkds->depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; - vkds->depthBoundsTestEnable = VK_FALSE; - vkds->stencilTestEnable = VK_TRUE; - // TODO:front and back - vkds->minDepthBounds = 0; - vkds->maxDepthBounds = 1; - VkPipelineColorBlendStateCreateInfo *vkcbs; - c->pColorBlendState = vkcbs = calloc(1, sizeof(VkPipelineColorBlendStateCreateInfo)); - vkcbs->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - vkcbs->logicOpEnable = VK_FALSE; - vkcbs->logicOp = VK_LOGIC_OP_COPY; - vkcbs->attachmentCount = 1; - // vkcbs->pAttachments = &svlk_colorBlendTemplate; - VkPipelineDynamicStateCreateInfo *vkdys; - vkdys->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - vkdys->dynamicStateCount = 2; - VkDynamicState *vkdynst = calloc(2, sizeof(VkDynamicState)); - vkdys->pDynamicStates = vkdynst; - vkdynst[0] = VK_DYNAMIC_STATE_VIEWPORT; - vkdynst[1] = VK_DYNAMIC_STATE_SCISSOR; - -} -*/ +/* Here was a relic of the old wor-- I mean relic of dumbness */ void rendering_createCommandPools(struct rendering_state *s){ VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); diff --git a/src/rendering/pipeline3d/data.c b/src/rendering/pipeline3d/data.c index a93e74b..f1b153a 100644 --- a/src/rendering/pipeline3d/data.c +++ b/src/rendering/pipeline3d/data.c @@ -1,6 +1,30 @@ #include "data.h" +#include +#include +#include +#include void uniformData_init(struct uniformData *ud, struct elementHeap *heap){ ud->heap = heap; ud->index = elementHeap_getFreeIndexAndReserve(heap); +} +void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vertslen, uint32_t *indicies, uint32_t indiceslen, struct renderData *obj){ + obj->vertexBuffer.size = sizeof(struct vertex) * vertslen; + obj->indexBuffer.size = sizeof(uint32_t) * indiceslen; + + rendering_makeBufferForRender(&obj->vertexBuffer, verts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, s); + rendering_makeBufferForRender(&obj->indexBuffer, indicies, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE, s); + + obj->indexCount = indiceslen; + obj->uniforms.heap = &s->pipeline.uniformHeap; + obj->uniforms.index = elementHeap_getFreeIndexAndReserve(obj->uniforms.heap); + printf("index = %i\n", obj->uniforms.index); + obj->uniforms.ubo = obj->uniforms.heap->buffer + obj->uniforms.heap->objsize * obj->uniforms.index; + + glm_quat_copy(GLM_QUAT_IDENTITY, obj->rotation); + vec3 scaleident = {1.f, 1.f, 1.f}; + glm_vec3_copy(scaleident, obj->scale); + vec3 origin = {0.f,0.f,0.f}; + glm_vec3_copy(origin, obj->position); + } \ No newline at end of file diff --git a/src/rendering/pipeline3d/data.h b/src/rendering/pipeline3d/data.h index 888177d..258a419 100644 --- a/src/rendering/pipeline3d/data.h +++ b/src/rendering/pipeline3d/data.h @@ -30,10 +30,15 @@ struct uniformData { struct renderData { struct gpubuffer vertexBuffer; struct gpubuffer indexBuffer; - uint32_t vertexcount; + uint32_t indexCount; struct uniformData uniforms; + vec3 position; + vec4 rotation; + vec3 scale; }; void uniformData_init(struct uniformData *ud, struct elementHeap *heap); +void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vertslen, uint32_t *indicies, uint32_t indiceslen, struct renderData *obj); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index e10fae3..186b798 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -1,12 +1,16 @@ #include "render.h" #include "pipeline.h" #include "setup.h" +#include +#include +#include +#include #include #include +#include "../camera.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount){ - updateDescriptorSet(s); - VkClearValue clearcolor = {0.4f,0.f,0.f,1.f}; +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam){ + VkClearValue clearcolor = {0.4f,0.f,0.f,0.5f}; VkRenderPassBeginInfo rpbi = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, @@ -30,14 +34,29 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer VkRect2D sc = {{0,0}, {s->window_width, s->window_height}}; vkCmdSetScissor(cmdbuf, 0, 1, &sc); + mat4 projview; + glm_mat4_mul(cam->projection, cam->view, projview); + for (uint32_t i = 0; i < objcount; i++) { + + // camera stuff + mat4 model; + glm_mat4_dup(GLM_MAT4_IDENTITY, model); + glm_scale(model, obj[i].scale); + glm_translate(model, obj[i].position); + glm_quat_rotate(model, obj[i].rotation, model); + + //glm_mat4_dup(projview, obj[i].uniforms.ubo->matrix); + glm_mat4_mul(projview, model, obj[i].uniforms.ubo->matrix); + + VkDeviceSize vtxoffsets[] = {0}; uint32_t unioffsets[] = {obj[i].uniforms.index}; vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, vtxoffsets); vkCmdBindIndexBuffer(cmdbuf, obj[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 0, - 1, &p->descriptorSet, 1, unioffsets); - vkCmdDrawIndexed(cmdbuf, obj[i].vertexcount, 1, 0, 0, 0); + 1, &p->descriptorSet, 1, unioffsets); + vkCmdDrawIndexed(cmdbuf, obj[i].indexCount, 1, 0, 0, 0); } vkCmdEndRenderPass(cmdbuf); diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h index ca1efa1..9cd65c4 100644 --- a/src/rendering/pipeline3d/render.h +++ b/src/rendering/pipeline3d/render.h @@ -4,6 +4,7 @@ #include #include "data.h" #include "../struct.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount); +#include "../camera.h" +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam); #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index 775d31b..98d9339 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -67,7 +67,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineVertexInputStateCreateInfo inputState = svlk_createPipelineVertexInputStateCI(bindes, 1, atdes, 2, 0); VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = svlk_createPipelineInputAssemblyStateCI(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineViewportStateCreateInfo viewportState = svlk_createPipelineViewportStateCI(1, 1, 0); - VkPipelineRasterizationStateCreateInfo rasterizerState = svlk_createPipelineRasterizationStateCI(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, 0); + VkPipelineRasterizationStateCreateInfo rasterizerState = svlk_createPipelineRasterizationStateCI(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0); VkPipelineMultisampleStateCreateInfo multisampleState = svlk_createPipelineMultisampleStateCI(VK_SAMPLE_COUNT_1_BIT, 0); VkPipelineDepthStencilStateCreateInfo depthState = svlk_createDepthStencilStateCI(VK_TRUE, VK_FALSE, VK_COMPARE_OP_GREATER_OR_EQUAL); VkPipelineColorBlendAttachmentState colorblendatt = svlk_createColorBlendAttachmentState(0xf, VK_FALSE); diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c index 95cc717..7cf6323 100644 --- a/src/rendering/rendering.c +++ b/src/rendering/rendering.c @@ -7,7 +7,7 @@ #include "init.h" #include "struct.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount){ +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam){ vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); uint32_t imageindex; @@ -31,7 +31,7 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount }; VkResult result = vkBeginCommandBuffer(s->graphicsBuffer, &begininfo); - recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount); + recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount, cam); vkEndCommandBuffer(s->graphicsBuffer); diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h index cbcae41..0a5bad8 100644 --- a/src/rendering/rendering.h +++ b/src/rendering/rendering.h @@ -2,8 +2,9 @@ #define __RENDERING_RENDERING_H__ #include "struct.h" #include "pipeline3d/data.h" +#include "camera.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount); +void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam); void recreateSwapchain(struct rendering_state *s); diff --git a/src/rendering/struct.h b/src/rendering/struct.h index dc30b1c..8210ad8 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -1,6 +1,7 @@ #ifndef __RENDERING_STRUCT_H__ #define __RENDERING_STRUCT_H__ +#include "camera.h" #include "pipeline3d/pipeline.h" #include #include @@ -92,6 +93,8 @@ struct rendering_state{ // filled in when framebuffer size changes int tmp_window_width; int tmp_window_height; + + void (*die)(); }; enum rendering_SwapchainRecreateFlags{ @@ -99,4 +102,9 @@ enum rendering_SwapchainRecreateFlags{ RNDR_RECREATE_SWAPCHAIN_USE_TMP = 2 // use tmp_window_width & height }; +struct rendering_windowUserPointer{ + struct camera *maincam; + struct rendering_state *s; +}; + #endif \ No newline at end of file From 4b18bd67968f6a87668d0654d3acf677d9a46969 Mon Sep 17 00:00:00 2001 From: miri Date: Wed, 12 Mar 2025 18:32:22 +0100 Subject: [PATCH 17/22] fixed debugging with renderdoc --- src/main.c | 7 +++-- src/rendering/init.c | 71 +++++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main.c b/src/main.c index 5831c9a..2e14308 100644 --- a/src/main.c +++ b/src/main.c @@ -50,9 +50,9 @@ void keyboardUpdate(GLFWwindow *window, struct camera *cam){ vec3 zaxis = {0.f,0.f,1.f}; if(glfwGetKey(window, GLFW_KEY_UP)) - glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); - if(glfwGetKey(window, GLFW_KEY_DOWN)) glm_vec3_rotate(cam->rotation, glm_rad(-1.f), cam->right); + if(glfwGetKey(window, GLFW_KEY_DOWN)) + glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); if(glfwGetKey(window, GLFW_KEY_LEFT)) glm_vec3_rotate(cam->rotation, glm_rad(-1.f), zaxis); if(glfwGetKey(window, GLFW_KEY_RIGHT)) @@ -93,6 +93,7 @@ int main(int argc, const char **argv){ }; s.die = ¨ + glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); rendering_initGLFW(&s); glfwSetWindowUserPointer(s.window, &wup); @@ -148,7 +149,7 @@ int main(int argc, const char **argv){ }; uint32_t triangleindices[] = {0,1,2, 1, 3, 2}; - renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[1]); + renderData_create(&s, rectverts, 4, triangleindices, 6, &rects[1]); rectverts[1].x = 0.f; rectverts[1].y = 1.f; renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[0]); diff --git a/src/rendering/init.c b/src/rendering/init.c index 0bb6ff4..973c09d 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -39,11 +39,21 @@ void rendering_initGLFW(struct rendering_state *s){ } s->extensions = glfwGetRequiredInstanceExtensions(&s->numextensions); + if(s->numextensions == 0){ + printf("extensions is null!\n"); + const char *err; + glfwGetError(&err); + printf("error: %s\n", err); + } } void rendering_vkinstance(struct rendering_state *s){ const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + printf("exts (num: %i):\n", s->numextensions); + for(int i = 0; i < s->numextensions; i++){ + printf(" ext:%s\n", s->extensions[i]); + } checkErrorVk("vkCreateInstance", svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance), s->die @@ -91,7 +101,6 @@ void rendering_vkswapchain(struct rendering_state *s){ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities), s->die ); - if(s->error != VK_SUCCESS) return; s->surfaceformat = svlk_selectSwapchainFormat(s->physdevice, s->surface, VK_FORMAT_B8G8R8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); @@ -109,21 +118,23 @@ void rendering_vkswapchain(struct rendering_state *s){ 1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, s->surfaceCapabilities.currentTransform - ); + ); unsigned int queueFamilies[] = {s->queues.graphicsQFI, s->queues.presentationQFI}; svlk_selectSharingMode(&swcreateinfo, s->queues.graphicsQFI != s->queues.presentationQFI, queueFamilies, 2); - s->error = vkCreateSwapchainKHR(s->device, &swcreateinfo, NULL, &s->swapchain); - if(s->error != VK_SUCCESS) return; + checkErrorVk("vkCreateSwapchainKHR", + vkCreateSwapchainKHR(s->device, &swcreateinfo, NULL, &s->swapchain), + s->die + ); s->swapchainImages = svlk_getSwapchainImages(s->device, s->swapchain, &s->numswapchainImages); - s->error = svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView); + checkErrorVk("vkCreateImageView", + svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView), + s->die + ); s->numswapchainImageView = s->numswapchainImages; - if(s->error != VK_SUCCESS){ - printf("nööööööö!\n"); - } } void rendering_createframebuffers(struct rendering_state *s){ @@ -146,7 +157,10 @@ void rendering_createframebuffers(struct rendering_state *s){ s->window_height, 1 }; - vkCreateFramebuffer(s->device, &fbCI, NULL, &s->framebuffers[i]); + checkErrorVk("vkCreateFramebuffer", + vkCreateFramebuffer(s->device, &fbCI, NULL, &s->framebuffers[i]), + s->die + ); } } @@ -163,22 +177,31 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ /* Here was a relic of the old wor-- I mean relic of dumbness */ void rendering_createCommandPools(struct rendering_state *s){ - VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); - if(result != VK_SUCCESS){ - printf("Creating the Graphics Pool Failed\n"); - } - result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer); - if(result != VK_SUCCESS){ - printf("Creating the Graphics Buffer Failed\n"); - } - result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->transferBuffer); - if(result != VK_SUCCESS){ - printf("Creating the Transfer Buffer Failed\n"); - } + checkErrorVk("vkCreateCommandPool", + svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool), + s->die + ); + checkErrorVk("vkAllocateCommandBuffer (graphicsBuffer)", + svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer), + s->die + ); + checkErrorVk("vkAllocateCommandBuffer (transferBuffer)", + svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->transferBuffer), + s->die + ); } void rendering_createSyncObjects(struct rendering_state *s){ - svlk_createFence(s->device, &s->sync.frameFinishFence, VK_FENCE_CREATE_SIGNALED_BIT); - svlk_createSemaphore(s->device, &s->sync.presentReadySemaphore); - svlk_createSemaphore(s->device, &s->sync.renderFinishSemaphore); + checkErrorVk("vkCreateFence", + svlk_createFence(s->device, &s->sync.frameFinishFence, VK_FENCE_CREATE_SIGNALED_BIT), + s->die + ); + checkErrorVk("vkCreateSemaphore", + svlk_createSemaphore(s->device, &s->sync.presentReadySemaphore), + s->die + ); + checkErrorVk("vkCreateSemaphore", + svlk_createSemaphore(s->device, &s->sync.renderFinishSemaphore), + s->die + ); } \ No newline at end of file From 65c7d87cd22b6b68e05d8f8cf6806826ec75186a Mon Sep 17 00:00:00 2001 From: miri Date: Wed, 12 Mar 2025 18:32:22 +0100 Subject: [PATCH 18/22] fixed debugging with renderdoc --- src/main.c | 7 +++-- src/rendering/init.c | 71 +++++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main.c b/src/main.c index 5831c9a..2e14308 100644 --- a/src/main.c +++ b/src/main.c @@ -50,9 +50,9 @@ void keyboardUpdate(GLFWwindow *window, struct camera *cam){ vec3 zaxis = {0.f,0.f,1.f}; if(glfwGetKey(window, GLFW_KEY_UP)) - glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); - if(glfwGetKey(window, GLFW_KEY_DOWN)) glm_vec3_rotate(cam->rotation, glm_rad(-1.f), cam->right); + if(glfwGetKey(window, GLFW_KEY_DOWN)) + glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); if(glfwGetKey(window, GLFW_KEY_LEFT)) glm_vec3_rotate(cam->rotation, glm_rad(-1.f), zaxis); if(glfwGetKey(window, GLFW_KEY_RIGHT)) @@ -93,6 +93,7 @@ int main(int argc, const char **argv){ }; s.die = ¨ + glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); rendering_initGLFW(&s); glfwSetWindowUserPointer(s.window, &wup); @@ -148,7 +149,7 @@ int main(int argc, const char **argv){ }; uint32_t triangleindices[] = {0,1,2, 1, 3, 2}; - renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[1]); + renderData_create(&s, rectverts, 4, triangleindices, 6, &rects[1]); rectverts[1].x = 0.f; rectverts[1].y = 1.f; renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[0]); diff --git a/src/rendering/init.c b/src/rendering/init.c index 0bb6ff4..973c09d 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -39,11 +39,21 @@ void rendering_initGLFW(struct rendering_state *s){ } s->extensions = glfwGetRequiredInstanceExtensions(&s->numextensions); + if(s->numextensions == 0){ + printf("extensions is null!\n"); + const char *err; + glfwGetError(&err); + printf("error: %s\n", err); + } } void rendering_vkinstance(struct rendering_state *s){ const char *devExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + printf("exts (num: %i):\n", s->numextensions); + for(int i = 0; i < s->numextensions; i++){ + printf(" ext:%s\n", s->extensions[i]); + } checkErrorVk("vkCreateInstance", svlk_createIinstance(layers, 1, s->extensions, s->numextensions, &s->instance), s->die @@ -91,7 +101,6 @@ void rendering_vkswapchain(struct rendering_state *s){ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s->physdevice, s->surface, &s->surfaceCapabilities), s->die ); - if(s->error != VK_SUCCESS) return; s->surfaceformat = svlk_selectSwapchainFormat(s->physdevice, s->surface, VK_FORMAT_B8G8R8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); @@ -109,21 +118,23 @@ void rendering_vkswapchain(struct rendering_state *s){ 1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, s->surfaceCapabilities.currentTransform - ); + ); unsigned int queueFamilies[] = {s->queues.graphicsQFI, s->queues.presentationQFI}; svlk_selectSharingMode(&swcreateinfo, s->queues.graphicsQFI != s->queues.presentationQFI, queueFamilies, 2); - s->error = vkCreateSwapchainKHR(s->device, &swcreateinfo, NULL, &s->swapchain); - if(s->error != VK_SUCCESS) return; + checkErrorVk("vkCreateSwapchainKHR", + vkCreateSwapchainKHR(s->device, &swcreateinfo, NULL, &s->swapchain), + s->die + ); s->swapchainImages = svlk_getSwapchainImages(s->device, s->swapchain, &s->numswapchainImages); - s->error = svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView); + checkErrorVk("vkCreateImageView", + svlk_createVkImageViews(s->device, s->swapchainImages, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, s->numswapchainImages,&s->swapchainImageView), + s->die + ); s->numswapchainImageView = s->numswapchainImages; - if(s->error != VK_SUCCESS){ - printf("nööööööö!\n"); - } } void rendering_createframebuffers(struct rendering_state *s){ @@ -146,7 +157,10 @@ void rendering_createframebuffers(struct rendering_state *s){ s->window_height, 1 }; - vkCreateFramebuffer(s->device, &fbCI, NULL, &s->framebuffers[i]); + checkErrorVk("vkCreateFramebuffer", + vkCreateFramebuffer(s->device, &fbCI, NULL, &s->framebuffers[i]), + s->die + ); } } @@ -163,22 +177,31 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ /* Here was a relic of the old wor-- I mean relic of dumbness */ void rendering_createCommandPools(struct rendering_state *s){ - VkResult result = svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool); - if(result != VK_SUCCESS){ - printf("Creating the Graphics Pool Failed\n"); - } - result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer); - if(result != VK_SUCCESS){ - printf("Creating the Graphics Buffer Failed\n"); - } - result = svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->transferBuffer); - if(result != VK_SUCCESS){ - printf("Creating the Transfer Buffer Failed\n"); - } + checkErrorVk("vkCreateCommandPool", + svlk_createCommandPool(s->device, s->queues.graphicsQFI, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, &s->graphicsPool), + s->die + ); + checkErrorVk("vkAllocateCommandBuffer (graphicsBuffer)", + svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->graphicsBuffer), + s->die + ); + checkErrorVk("vkAllocateCommandBuffer (transferBuffer)", + svlk_allocateCommandBuffer(s->device, s->graphicsPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &s->transferBuffer), + s->die + ); } void rendering_createSyncObjects(struct rendering_state *s){ - svlk_createFence(s->device, &s->sync.frameFinishFence, VK_FENCE_CREATE_SIGNALED_BIT); - svlk_createSemaphore(s->device, &s->sync.presentReadySemaphore); - svlk_createSemaphore(s->device, &s->sync.renderFinishSemaphore); + checkErrorVk("vkCreateFence", + svlk_createFence(s->device, &s->sync.frameFinishFence, VK_FENCE_CREATE_SIGNALED_BIT), + s->die + ); + checkErrorVk("vkCreateSemaphore", + svlk_createSemaphore(s->device, &s->sync.presentReadySemaphore), + s->die + ); + checkErrorVk("vkCreateSemaphore", + svlk_createSemaphore(s->device, &s->sync.renderFinishSemaphore), + s->die + ); } \ No newline at end of file From 2b15df2fe5e68f72b48e1d052d9ebd4a9628ab92 Mon Sep 17 00:00:00 2001 From: miri Date: Thu, 20 Mar 2025 16:46:55 +0100 Subject: [PATCH 19/22] textures --- .clangformat | 84 +++++++++++++ img.png | Bin 0 -> 5415 bytes requirements.txt | 5 + shd/triangle.frag | 6 +- shd/triangle.vert | 8 +- src/main.c | 41 +++++-- src/rendering/init.c | 4 +- src/rendering/pipeline3d/data.c | 36 ++++++ src/rendering/pipeline3d/data.h | 6 + src/rendering/pipeline3d/pipeline.h | 32 ++++- src/rendering/pipeline3d/render.c | 5 +- src/rendering/pipeline3d/setup.c | 118 ++++++++++++++---- src/rendering/pipeline3d/setup.h | 8 +- src/rendering/vulkan/commandpools.h | 2 + src/rendering/vulkan/gpumem.c | 182 +++++++++++++++++++++++++--- src/rendering/vulkan/gpumem.h | 7 ++ src/rendering/vulkan/gpumem_types.h | 12 ++ 17 files changed, 490 insertions(+), 66 deletions(-) create mode 100644 .clangformat create mode 100644 img.png create mode 100644 requirements.txt diff --git a/.clangformat b/.clangformat new file mode 100644 index 0000000..455321e --- /dev/null +++ b/.clangformat @@ -0,0 +1,84 @@ +# Vertically compact code without losing context for the human through newlines +# Language: C + +IndentWidth: 4 +TabWidth: 8 +ContinuationIndentWidth: 4 +UseTab: ForContinuationAndIndentation + +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignConsecutiveMacros: Consecutive +AlignEscapedNewlines: Left +AlignTrailingComments: + Kind: Always + OverEmptyLines: 1 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Always +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +# separate long string from code +AlwaysBreakBeforeMultilineStrings: true +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Before + +BraceWrapping: + AfterCaseLabel: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterStruct: false + AfterUnion: false + BeforeElse: false + BeforeWhile: false + +BreakAdjacentStringLiterals: true +BreakAfterReturnType: Automatic +BreakBeforeBinaryOperators: None +BreakBeforeInlineASMColon: Never +BreakBeforeTernaryOperators: true +# BreakBinaryOperations: Never +BreakStringLiterals: false +ColumnLimit: 120 +IncludeBlocks: Regroup +IndentCaseLabels: true +IndentGotoLabels: false +IndentWrappedFunctionNames: true +LineEnding: LF +MaxEmptyLinesToKeep: 2 +PointerAlignment: Right +QualifierAlignment: Leave +ReflowComments: false +RemoveParentheses: Leave +SortIncludes: CaseInsensitive +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: false + AfterFunctionDeclarationName: false + BeforeNonEmptyParentheses: false +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 2 +SpacesInParens: Never +SpacesInSquareBrackets: false + diff --git a/img.png b/img.png new file mode 100644 index 0000000000000000000000000000000000000000..db5e9ff686c5af53129d5fcedbddc52e19784e57 GIT binary patch literal 5415 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 Buf+fW literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a131f6c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +vulkan vulkan +cglm matrix / quaternion math library +math math +stb_image +vulkan-utility-libraries diff --git a/shd/triangle.frag b/shd/triangle.frag index 13009da..cd7f6a4 100644 --- a/shd/triangle.frag +++ b/shd/triangle.frag @@ -1,9 +1,11 @@ #version 450 layout(location = 0) in vec3 fragColor; - +layout(location = 1) in vec2 uv; layout(location = 0) out vec4 outColor; +layout(set=1, binding = 0) uniform sampler2D albedo; + void main() { - outColor = vec4(fragColor, 1.0); + outColor = vec4(texture(albedo, uv).rgb, 1.0); } \ No newline at end of file diff --git a/shd/triangle.vert b/shd/triangle.vert index dfe7f7c..14e3047 100644 --- a/shd/triangle.vert +++ b/shd/triangle.vert @@ -1,13 +1,14 @@ #version 450 -layout(binding = 0) uniform UniformBufferObject { +layout(set = 0, binding = 0) uniform UniformBufferObject { mat4 matrix; // do all the view matrix stuff on the cpu during queue submission } ubo; layout(location = 0) in vec3 pos; -layout(location = 1) in vec2 uv; +layout(location = 1) in vec2 uvi; layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 uv; /* vec2 positions[3] = vec2[]( @@ -25,5 +26,6 @@ vec3 colors[3] = vec3[]( void main() { gl_Position = ubo.matrix * vec4(pos, 1.0); - fragColor = vec3(uv, 1.0); + fragColor = vec3(uvi, 1.0); + uv = uvi; } diff --git a/src/main.c b/src/main.c index 2e14308..c4fd048 100644 --- a/src/main.c +++ b/src/main.c @@ -10,16 +10,17 @@ #include #include #include +#define STB_IMAGE_IMPLEMENTATION +#include + #include #include "rendering/camera.h" #include "rendering/init.h" -#include "rendering/management/elementHeap.h" #include "rendering/pipeline3d/data.h" #include "rendering/struct.h" #include "rendering/vulkan/gpumem.h" -#include "rendering/vulkan/renderingpipeline.h" +#include "rendering/vulkan/gpumem_types.h" #include "rendering/vulkan/svulc.h" -#include "helpers.h" #include "rendering/pipeline3d/setup.h" #include "rendering/rendering.h" @@ -31,9 +32,16 @@ void fbResizeCb(GLFWwindow *window, int width, int height){ } void keyCb(GLFWwindow *window, int key, int scancode, int action, int mods){ + struct rendering_windowUserPointer *wup = glfwGetWindowUserPointer(window); + struct camera *cam = wup->maincam; if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } + if(key == GLFW_KEY_R && action == GLFW_PRESS){ + vec4 quat = {0.f,1.f,0.f,0.f}; + glm_quat_copy(quat, cam->rotation); + glm_vec3_copy(GLM_VEC3_ZERO, cam->position); + } } void keyboardUpdate(GLFWwindow *window, struct camera *cam){ @@ -84,8 +92,8 @@ void die(){ int main(int argc, const char **argv){ struct rendering_state s = {0}; s.window_title = "Hi!"; - s.window_width = 100; - s.window_height = 100; + s.window_width = 600; + s.window_height = 600; struct rendering_windowUserPointer wup = { NULL, @@ -140,7 +148,7 @@ int main(int argc, const char **argv){ rendering_createSyncObjects(&s); // create a triangle renderdata - struct renderData rects[2] = {0}; + struct renderData rects[1] = {0}; struct vertex rectverts[] = { {-0.5f, 0.f, 0.5f, 0.f, 0.f}, {0.5f, 0.f, 0.5f, 1.f, 0.f}, @@ -149,10 +157,7 @@ int main(int argc, const char **argv){ }; uint32_t triangleindices[] = {0,1,2, 1, 3, 2}; - renderData_create(&s, rectverts, 4, triangleindices, 6, &rects[1]); - rectverts[1].x = 0.f; - rectverts[1].y = 1.f; - renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[0]); + renderData_create(&s, rectverts, 4, triangleindices, 6, &rects[0]); //triangles[1].position[2] = 2.f; // camera stuff uint32_t first = 1; @@ -168,13 +173,25 @@ int main(int argc, const char **argv){ camera_update(&cam); wup.maincam = &cam; + // image loading + int iw, ih, ic; + stbi_uc *pixels = stbi_load("img.png", &iw, &ih, &ic, STBI_rgb_alpha); + struct gpuimage img; + if(pixels){ + rendering_createImageForRender(iw, ih, pixels, iw * ih * ic, VK_FORMAT_R8G8B8A8_SRGB, &rects[0].albedo, &s); + renderData_setupTextures(&s, &rects[0], NULL); + printf("IMG: avail!\n"); + } else { + printf("IMG: not avail!\n"); + } + + while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); keyboardUpdate(s.window, &cam); - render(&s, rects, 2, &cam); + render(&s, rects, 1, &cam); if(first){ first = 0; - glm_mat4_print(rects[1].uniforms.ubo->matrix, stdout); } } diff --git a/src/rendering/init.c b/src/rendering/init.c index 973c09d..8c8070e 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -1,8 +1,6 @@ #include "vulkan/svulc.h" #include "vulkan/swapchain.h" -#include "vulkan/renderingpipeline.h" #include "vulkan/commandpools.h" -#include #include #include #include @@ -174,7 +172,7 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ vkDestroySwapchainKHR(s->device, s->swapchain, NULL); } -/* Here was a relic of the old wor-- I mean relic of dumbness */ +/* Here __was__ a relic of the old wor-- I mean relic of dumbness */ void rendering_createCommandPools(struct rendering_state *s){ checkErrorVk("vkCreateCommandPool", diff --git a/src/rendering/pipeline3d/data.c b/src/rendering/pipeline3d/data.c index f1b153a..db993ea 100644 --- a/src/rendering/pipeline3d/data.c +++ b/src/rendering/pipeline3d/data.c @@ -1,7 +1,10 @@ #include "data.h" +#include "setup.h" +#include "../vulkan/swapchain.h" #include #include #include +#include #include void uniformData_init(struct uniformData *ud, struct elementHeap *heap){ @@ -27,4 +30,37 @@ void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vec3 origin = {0.f,0.f,0.f}; glm_vec3_copy(origin, obj->position); +} + +const VkSamplerCreateInfo defaultSamplerInfo = { + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + NULL, + 0, + VK_FILTER_LINEAR, + VK_FILTER_LINEAR, + VK_SAMPLER_MIPMAP_MODE_NEAREST, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + 0.f, + VK_FALSE, + 0.f, + VK_FALSE, + VK_COMPARE_OP_ALWAYS, + 0.f, + 0.f, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, + VK_FALSE +}; + +const VkSamplerCreateInfo *getDefaultSamplerCi(){ + return &defaultSamplerInfo; +} + +void renderData_setupTextures(struct rendering_state *s, struct renderData *data, VkSamplerCreateInfo *samplerOverride){ + svlk_createVkImageView(s->device, data->albedo.image, VK_IMAGE_VIEW_TYPE_2D, data->albedo.fmt, &data->albedo.view); + vkCreateSampler(s->device, samplerOverride ? samplerOverride : getDefaultSamplerCi(), NULL, &data->albedo.sampler); + + createObjTextureDescriptorSet(s, data); + updateObjTextureDescriptorSet(s, data); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/data.h b/src/rendering/pipeline3d/data.h index 258a419..1fb3105 100644 --- a/src/rendering/pipeline3d/data.h +++ b/src/rendering/pipeline3d/data.h @@ -35,10 +35,16 @@ struct renderData { vec3 position; vec4 rotation; vec3 scale; + struct gpuimage albedo; + VkDescriptorSet textureDescSet; }; void uniformData_init(struct uniformData *ud, struct elementHeap *heap); void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vertslen, uint32_t *indicies, uint32_t indiceslen, struct renderData *obj); +void renderData_setupTextures(struct rendering_state *s, struct renderData *data, VkSamplerCreateInfo *samplerOverride); + +const VkSamplerCreateInfo *getDefaultSamplerCi(); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h index edb2163..4f9e141 100644 --- a/src/rendering/pipeline3d/pipeline.h +++ b/src/rendering/pipeline3d/pipeline.h @@ -5,10 +5,38 @@ #include "../management/elementHeap.h" #include "../vulkan/gpumem_types.h" + +/* + * Pipelone3d. + * this is a 3d pipeline designed for generic 3d game stuff with shadows and lighting (todo (shadows & lighting) ) + * triangles are indexed (with index buffer) matricies are calculated per object on the cpu + * depth pre pass with some defered rendering ish + * + * Textures: + * Albedo + * Normal TODO + * + * Textures (runtime): + * Out/Albedo + * Depth TODO + * Normal TODO + * Position TODO + * Lighting TODO + * + * + * Pipeline: + * Monolithic shader renders models and optionally lighting + * Post processing shaders do stuff with the runtime textures TODO + * Merger merges lighting runtime texture with output (optional) TODO + * Post postproccesser shaders do stuff with lit scene (optional optional) TODO + */ + struct pipeline3dSubpass { VkPipelineLayout layout; VkPipeline pipeline; - VkDescriptorSetLayout desclayout; + // next two must be in this order (used as an array, should change. too lazy) + VkDescriptorSetLayout uniformDesclayout; + VkDescriptorSetLayout textureDesclayout; }; struct pipeline3d { @@ -17,7 +45,7 @@ struct pipeline3d { VkDescriptorPool descriptorPool; // potentially wrap the uniform heap in a linked list struct elementHeap uniformHeap; - VkDescriptorSet descriptorSet; + VkDescriptorSet uniformDescriptorSet; struct gpubuffer uniformbuffer; }; diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index 186b798..dbd9043 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -1,6 +1,5 @@ #include "render.h" #include "pipeline.h" -#include "setup.h" #include #include #include @@ -55,7 +54,9 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, vtxoffsets); vkCmdBindIndexBuffer(cmdbuf, obj[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 0, - 1, &p->descriptorSet, 1, unioffsets); + 1, &p->uniformDescriptorSet, 1, unioffsets); + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 1, + 1, &obj[i].textureDescSet, 0, NULL); vkCmdDrawIndexed(cmdbuf, obj[i].indexCount, 1, 0, 0, 0); } diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index 98d9339..e328e8c 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -3,9 +3,7 @@ #include #include #include "../../helpers.h" -#include "../init.h" #include "../vulkan/renderingpipeline.h" -#include "../vulkan/svulc.h" #include "data.h" #include "pipeline.h" @@ -76,25 +74,48 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineDynamicStateCreateInfo dynamicState = svlk_createDynamicStateCI(dynstates, 2, 0); // Pipeline Layout - VkDescriptorSetLayoutBinding lb = { - 0, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - 1, - VK_SHADER_STAGE_VERTEX_BIT, - NULL + VkDescriptorSetLayoutBinding lbu[] = { + { + 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + 1, + VK_SHADER_STAGE_VERTEX_BIT, + NULL + }, }; - VkDescriptorSetLayoutCreateInfo dslCI = { - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - NULL, - 0, - 1, - &lb + VkDescriptorSetLayoutBinding lbt[] = { + { + 0, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, + VK_SHADER_STAGE_FRAGMENT_BIT, + NULL + } }; - vkCreateDescriptorSetLayout(s->device, &dslCI, NULL, &p->geometryPass.desclayout); + VkDescriptorSetLayoutCreateInfo dslCI[] = { + { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + NULL, + 0, + sizeof(lbu)/sizeof(lbu[0]), + lbu + }, + { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + NULL, + 0, + sizeof(lbt)/sizeof(lbt[0]), + lbt + } + }; - VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(&p->geometryPass.desclayout, 1); + vkCreateDescriptorSetLayout(s->device, &dslCI[0], NULL, &p->geometryPass.uniformDesclayout); + vkCreateDescriptorSetLayout(s->device, &dslCI[1], NULL, &p->geometryPass.textureDesclayout); + VkDescriptorSetLayout desclayouts[] = {p->geometryPass.uniformDesclayout, p->geometryPass.textureDesclayout}; + + VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(desclayouts, 2); vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); @@ -177,18 +198,24 @@ void createPipeline3d(struct rendering_state *s){ vkMapMemory(s->device, p->uniformbuffer.memory, 0, p->uniformbuffer.size, 0, &p->uniformHeap.buffer); // why descriptors, why. why museth you exist - VkDescriptorPoolSize poolsize = { - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // offsets are cool - 1 // no linked lists yet so 1 + VkDescriptorPoolSize poolsize[] = { + { + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // offsets are cool + 1 // no linked lists yet so 1 + }, + { + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1 * p->uniformHeap.len + } }; VkDescriptorPoolCreateInfo dpCI = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, NULL, 0, - 1, - 1, - &poolsize + poolsize[0].descriptorCount + poolsize[1].descriptorCount*1, + 2, + poolsize }; VkResult result = vkCreateDescriptorPool(s->device, &dpCI, NULL, &p->descriptorPool); @@ -201,9 +228,9 @@ void createPipeline3d(struct rendering_state *s){ NULL, p->descriptorPool, 1, - &p->geometryPass.desclayout + &p->geometryPass.uniformDesclayout }; - result = vkAllocateDescriptorSets(s->device, &allocInfo, &p->descriptorSet); + result = vkAllocateDescriptorSets(s->device, &allocInfo, &p->uniformDescriptorSet); if(result != VK_SUCCESS){ printf("Could not create descriptor set. result=%i\n", result); } @@ -221,7 +248,7 @@ void updateDescriptorSet(struct rendering_state *s){ VkWriteDescriptorSet descriptorWrite = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, - p->descriptorSet, + p->uniformDescriptorSet, 0, 0, 1, @@ -231,7 +258,46 @@ void updateDescriptorSet(struct rendering_state *s){ NULL }; - vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); // pain. + vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); +} +void createObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data){ + struct pipeline3d *p = &s->pipeline; + VkDescriptorSetAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + NULL, + p->descriptorPool, + 1, + &p->geometryPass.textureDesclayout + }; + checkErrorVk("vkAllocateDescriptorSets", + vkAllocateDescriptorSets(s->device, &allocInfo, &data->textureDescSet), + s->die + ); +} + +void updateObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data){ + struct pipeline3d *p = &s->pipeline; + VkDescriptorImageInfo imginfo[] = { + { + data->albedo.sampler, + data->albedo.view, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + } + }; + + VkWriteDescriptorSet descriptorWrite = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + NULL, + data->textureDescSet, + 0, + 0, + 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + imginfo, + NULL, + NULL + }; + vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.h b/src/rendering/pipeline3d/setup.h index 61b8126..49c8203 100644 --- a/src/rendering/pipeline3d/setup.h +++ b/src/rendering/pipeline3d/setup.h @@ -1,3 +1,5 @@ +#ifndef __PIPELINE3D_SETUP_H__ +#define __PIPELINE3D_SETUP_H__ #include #include #include @@ -5,4 +7,8 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum); void createPipeline3d(struct rendering_state *s); -void updateDescriptorSet(struct rendering_state *s); \ No newline at end of file +void updateDescriptorSet(struct rendering_state *s); + +void createObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data); +void updateObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data); +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.h b/src/rendering/vulkan/commandpools.h index 8ba6021..69dbde9 100644 --- a/src/rendering/vulkan/commandpools.h +++ b/src/rendering/vulkan/commandpools.h @@ -7,6 +7,8 @@ VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkC VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); +VkCommandBuffer svlk_createOneTimeCommandBuffer(); + VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore); VkResult svlk_createFence(VkDevice device, VkFence *fence, VkFenceCreateFlags flags); #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c index a632731..0200e0c 100644 --- a/src/rendering/vulkan/gpumem.c +++ b/src/rendering/vulkan/gpumem.c @@ -1,4 +1,7 @@ #include "gpumem.h" +#include "gpumem_types.h" +#include "../../helpers.h" +#include #include #include #include @@ -28,12 +31,15 @@ void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, rendering_findMemoryType(memreq.memoryTypeBits, properties, s) }; - VkResult result = vkAllocateMemory(s->device, &allocinfo, NULL, &buf->memory); - if(result != VK_SUCCESS){ - printf("Failed to allocate memory on the GPU. error: %i", result); - exit(EXIT_FAILURE); - } - vkBindBufferMemory(s->device, buf->buffer, buf->memory, 0); + + checkErrorVk("vkAllocateMemory", + vkAllocateMemory(s->device, &allocinfo, NULL, &buf->memory), + s->die + ); + checkErrorVk("vkBindBufferMemory", + vkBindBufferMemory(s->device, buf->buffer, buf->memory, 0), + s->die + ); } void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s){ @@ -65,6 +71,23 @@ uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags propertie exit(EXIT_FAILURE); } +void rendering_endSubmitWait(VkCommandBuffer cmdbuf, VkQueue queue){ + vkEndCommandBuffer(cmdbuf); + VkSubmitInfo submitinfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 0, + NULL, + 0, + 1, + &cmdbuf, + 0, + NULL + }; + vkQueueSubmit(queue, 1, &submitinfo, NULL); + vkQueueWaitIdle(queue); +} + void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct rendering_state *s){ VkCommandBufferBeginInfo begininfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -78,18 +101,147 @@ void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct size }; vkCmdCopyBuffer(s->transferBuffer, src, dst, 1, ©region); - vkEndCommandBuffer(s->transferBuffer); - VkSubmitInfo submitinfo = { - VK_STRUCTURE_TYPE_SUBMIT_INFO, - NULL, - 0, + rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); +} + +void rendering_createImage( + uint32_t w, uint32_t h, uint32_t d, VkImageType type, VkFormat fmt, uint32_t mipLevels, VkImageTiling tiling, + VkImageUsageFlags usageflags, VkMemoryPropertyFlags properties, struct gpuimage *img, struct rendering_state *s){ + img->w = w; + img->h = h; + img->d = d; + img->fmt = fmt; + + VkImageCreateInfo imgCI = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, + type, + fmt, + {w, h, d}, + mipLevels, 1, - &s->transferBuffer, + VK_SAMPLE_COUNT_1_BIT, + tiling, + usageflags, + VK_SHARING_MODE_EXCLUSIVE, + 0, + NULL, + VK_IMAGE_LAYOUT_UNDEFINED + }; + checkErrorVk("vkCreateImage", + vkCreateImage(s->device, &imgCI, NULL, &img->image), + s->die + ); + + VkMemoryRequirements memreq; + vkGetImageMemoryRequirements(s->device, img->image, &memreq); + + VkMemoryAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + NULL, + memreq.size, + rendering_findMemoryType(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, s) + }; + + checkErrorVk("vkAllocateMemory", + vkAllocateMemory(s->device, &allocinfo, NULL, &img->memory), + s->die + ); + + checkErrorVk("vkBindBufferMemory", + vkBindImageMemory(s->device, img->image, img->memory, 0), + s->die + ); +} + +void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uint32_t d, VkImageAspectFlags aspect, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, 0, NULL }; - vkQueueSubmit(s->queues.transferQueue, 1, &submitinfo, NULL); - vkQueueWaitIdle(s->queues.transferQueue); -} \ No newline at end of file + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + + VkBufferImageCopy region = { + 0, + 0, + 0, + {aspect, 0, 0, 1}, + {0, 0, 0}, + {w, h, d} + }; + vkCmdCopyBufferToImage(s->transferBuffer, src, img, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); +} + +void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + NULL, + 0, + 0, + old, + new, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + img, + { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + 1, + 0, + 1 + } + }; + + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + + if(old == VK_IMAGE_LAYOUT_UNDEFINED && new == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL){ + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if(old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL){ + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + + vkCmdPipelineBarrier(s->transferBuffer, srcStage, dstStage, 0, 0, NULL, 0, NULL, 1, &barrier); + + rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); +} + +void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t datalen, VkFormat fmt, struct gpuimage *img, struct rendering_state *s){ + struct gpubuffer stagebuf = {}; + stagebuf.size = datalen; + img->size = datalen; + // create and fill the staging buffer + rendering_createBuffer(&stagebuf, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); + rendering_putBuffer(&stagebuf, data, s); + + // create the image and transition the image layout + rendering_createImage(w, h, 1, VK_IMAGE_TYPE_2D, fmt, 1, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, img, s); + rendering_transitionImageLayout(img->image, fmt, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, s); + + // copy and transition to final stage + rendering_copyImageB(stagebuf.buffer, img->image, w, h, 1, VK_IMAGE_ASPECT_COLOR_BIT, s); + rendering_transitionImageLayout(img->image, fmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, s); + + rendering_destroyBuffer(&stagebuf, s); +}; \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h index 701aefc..290c8e4 100644 --- a/src/rendering/vulkan/gpumem.h +++ b/src/rendering/vulkan/gpumem.h @@ -19,4 +19,11 @@ static inline void rendering_destroyBuffer(struct gpubuffer *b, struct rendering vkFreeMemory(s->device, b->memory, NULL); } +void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uint32_t d, VkImageAspectFlags aspect, struct rendering_state *s); + +void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout newlayout, struct rendering_state *s); + +void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t datalen, VkFormat fmt, struct gpuimage *img, struct rendering_state *s); +//void rendering_createImage(); + #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem_types.h b/src/rendering/vulkan/gpumem_types.h index ff7a61d..f36b036 100644 --- a/src/rendering/vulkan/gpumem_types.h +++ b/src/rendering/vulkan/gpumem_types.h @@ -8,4 +8,16 @@ struct gpubuffer { uint64_t size; }; +struct gpuimage { + VkImage image; + VkDeviceMemory memory; + uint64_t size; + uint32_t w; + uint32_t h; + uint32_t d; + VkFormat fmt; + VkImageView view; + VkSampler sampler; +}; + #endif \ No newline at end of file From 2c2cf2cd8502caf1398ec22c397be9a4916e1188 Mon Sep 17 00:00:00 2001 From: miri Date: Thu, 20 Mar 2025 16:46:55 +0100 Subject: [PATCH 20/22] textures --- .clangformat | 84 +++++++++++++ img.png | Bin 0 -> 5415 bytes requirements.txt | 5 + shd/triangle.frag | 6 +- shd/triangle.vert | 8 +- src/main.c | 41 +++++-- src/rendering/init.c | 4 +- src/rendering/pipeline3d/data.c | 36 ++++++ src/rendering/pipeline3d/data.h | 6 + src/rendering/pipeline3d/pipeline.h | 32 ++++- src/rendering/pipeline3d/render.c | 5 +- src/rendering/pipeline3d/setup.c | 118 ++++++++++++++---- src/rendering/pipeline3d/setup.h | 8 +- src/rendering/vulkan/commandpools.h | 2 + src/rendering/vulkan/gpumem.c | 182 +++++++++++++++++++++++++--- src/rendering/vulkan/gpumem.h | 7 ++ src/rendering/vulkan/gpumem_types.h | 12 ++ 17 files changed, 490 insertions(+), 66 deletions(-) create mode 100644 .clangformat create mode 100644 img.png create mode 100644 requirements.txt diff --git a/.clangformat b/.clangformat new file mode 100644 index 0000000..455321e --- /dev/null +++ b/.clangformat @@ -0,0 +1,84 @@ +# Vertically compact code without losing context for the human through newlines +# Language: C + +IndentWidth: 4 +TabWidth: 8 +ContinuationIndentWidth: 4 +UseTab: ForContinuationAndIndentation + +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignConsecutiveMacros: Consecutive +AlignEscapedNewlines: Left +AlignTrailingComments: + Kind: Always + OverEmptyLines: 1 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Always +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +# separate long string from code +AlwaysBreakBeforeMultilineStrings: true +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Before + +BraceWrapping: + AfterCaseLabel: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterStruct: false + AfterUnion: false + BeforeElse: false + BeforeWhile: false + +BreakAdjacentStringLiterals: true +BreakAfterReturnType: Automatic +BreakBeforeBinaryOperators: None +BreakBeforeInlineASMColon: Never +BreakBeforeTernaryOperators: true +# BreakBinaryOperations: Never +BreakStringLiterals: false +ColumnLimit: 120 +IncludeBlocks: Regroup +IndentCaseLabels: true +IndentGotoLabels: false +IndentWrappedFunctionNames: true +LineEnding: LF +MaxEmptyLinesToKeep: 2 +PointerAlignment: Right +QualifierAlignment: Leave +ReflowComments: false +RemoveParentheses: Leave +SortIncludes: CaseInsensitive +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: false + AfterFunctionDeclarationName: false + BeforeNonEmptyParentheses: false +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 2 +SpacesInParens: Never +SpacesInSquareBrackets: false + diff --git a/img.png b/img.png new file mode 100644 index 0000000000000000000000000000000000000000..db5e9ff686c5af53129d5fcedbddc52e19784e57 GIT binary patch literal 5415 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 Buf+fW literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a131f6c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +vulkan vulkan +cglm matrix / quaternion math library +math math +stb_image +vulkan-utility-libraries diff --git a/shd/triangle.frag b/shd/triangle.frag index 13009da..cd7f6a4 100644 --- a/shd/triangle.frag +++ b/shd/triangle.frag @@ -1,9 +1,11 @@ #version 450 layout(location = 0) in vec3 fragColor; - +layout(location = 1) in vec2 uv; layout(location = 0) out vec4 outColor; +layout(set=1, binding = 0) uniform sampler2D albedo; + void main() { - outColor = vec4(fragColor, 1.0); + outColor = vec4(texture(albedo, uv).rgb, 1.0); } \ No newline at end of file diff --git a/shd/triangle.vert b/shd/triangle.vert index dfe7f7c..14e3047 100644 --- a/shd/triangle.vert +++ b/shd/triangle.vert @@ -1,13 +1,14 @@ #version 450 -layout(binding = 0) uniform UniformBufferObject { +layout(set = 0, binding = 0) uniform UniformBufferObject { mat4 matrix; // do all the view matrix stuff on the cpu during queue submission } ubo; layout(location = 0) in vec3 pos; -layout(location = 1) in vec2 uv; +layout(location = 1) in vec2 uvi; layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 uv; /* vec2 positions[3] = vec2[]( @@ -25,5 +26,6 @@ vec3 colors[3] = vec3[]( void main() { gl_Position = ubo.matrix * vec4(pos, 1.0); - fragColor = vec3(uv, 1.0); + fragColor = vec3(uvi, 1.0); + uv = uvi; } diff --git a/src/main.c b/src/main.c index 2e14308..c4fd048 100644 --- a/src/main.c +++ b/src/main.c @@ -10,16 +10,17 @@ #include #include #include +#define STB_IMAGE_IMPLEMENTATION +#include + #include #include "rendering/camera.h" #include "rendering/init.h" -#include "rendering/management/elementHeap.h" #include "rendering/pipeline3d/data.h" #include "rendering/struct.h" #include "rendering/vulkan/gpumem.h" -#include "rendering/vulkan/renderingpipeline.h" +#include "rendering/vulkan/gpumem_types.h" #include "rendering/vulkan/svulc.h" -#include "helpers.h" #include "rendering/pipeline3d/setup.h" #include "rendering/rendering.h" @@ -31,9 +32,16 @@ void fbResizeCb(GLFWwindow *window, int width, int height){ } void keyCb(GLFWwindow *window, int key, int scancode, int action, int mods){ + struct rendering_windowUserPointer *wup = glfwGetWindowUserPointer(window); + struct camera *cam = wup->maincam; if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } + if(key == GLFW_KEY_R && action == GLFW_PRESS){ + vec4 quat = {0.f,1.f,0.f,0.f}; + glm_quat_copy(quat, cam->rotation); + glm_vec3_copy(GLM_VEC3_ZERO, cam->position); + } } void keyboardUpdate(GLFWwindow *window, struct camera *cam){ @@ -84,8 +92,8 @@ void die(){ int main(int argc, const char **argv){ struct rendering_state s = {0}; s.window_title = "Hi!"; - s.window_width = 100; - s.window_height = 100; + s.window_width = 600; + s.window_height = 600; struct rendering_windowUserPointer wup = { NULL, @@ -140,7 +148,7 @@ int main(int argc, const char **argv){ rendering_createSyncObjects(&s); // create a triangle renderdata - struct renderData rects[2] = {0}; + struct renderData rects[1] = {0}; struct vertex rectverts[] = { {-0.5f, 0.f, 0.5f, 0.f, 0.f}, {0.5f, 0.f, 0.5f, 1.f, 0.f}, @@ -149,10 +157,7 @@ int main(int argc, const char **argv){ }; uint32_t triangleindices[] = {0,1,2, 1, 3, 2}; - renderData_create(&s, rectverts, 4, triangleindices, 6, &rects[1]); - rectverts[1].x = 0.f; - rectverts[1].y = 1.f; - renderData_create(&s, rectverts, 3, triangleindices, 6, &rects[0]); + renderData_create(&s, rectverts, 4, triangleindices, 6, &rects[0]); //triangles[1].position[2] = 2.f; // camera stuff uint32_t first = 1; @@ -168,13 +173,25 @@ int main(int argc, const char **argv){ camera_update(&cam); wup.maincam = &cam; + // image loading + int iw, ih, ic; + stbi_uc *pixels = stbi_load("img.png", &iw, &ih, &ic, STBI_rgb_alpha); + struct gpuimage img; + if(pixels){ + rendering_createImageForRender(iw, ih, pixels, iw * ih * ic, VK_FORMAT_R8G8B8A8_SRGB, &rects[0].albedo, &s); + renderData_setupTextures(&s, &rects[0], NULL); + printf("IMG: avail!\n"); + } else { + printf("IMG: not avail!\n"); + } + + while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); keyboardUpdate(s.window, &cam); - render(&s, rects, 2, &cam); + render(&s, rects, 1, &cam); if(first){ first = 0; - glm_mat4_print(rects[1].uniforms.ubo->matrix, stdout); } } diff --git a/src/rendering/init.c b/src/rendering/init.c index 973c09d..8c8070e 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -1,8 +1,6 @@ #include "vulkan/svulc.h" #include "vulkan/swapchain.h" -#include "vulkan/renderingpipeline.h" #include "vulkan/commandpools.h" -#include #include #include #include @@ -174,7 +172,7 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ vkDestroySwapchainKHR(s->device, s->swapchain, NULL); } -/* Here was a relic of the old wor-- I mean relic of dumbness */ +/* Here __was__ a relic of the old wor-- I mean relic of dumbness */ void rendering_createCommandPools(struct rendering_state *s){ checkErrorVk("vkCreateCommandPool", diff --git a/src/rendering/pipeline3d/data.c b/src/rendering/pipeline3d/data.c index f1b153a..db993ea 100644 --- a/src/rendering/pipeline3d/data.c +++ b/src/rendering/pipeline3d/data.c @@ -1,7 +1,10 @@ #include "data.h" +#include "setup.h" +#include "../vulkan/swapchain.h" #include #include #include +#include #include void uniformData_init(struct uniformData *ud, struct elementHeap *heap){ @@ -27,4 +30,37 @@ void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vec3 origin = {0.f,0.f,0.f}; glm_vec3_copy(origin, obj->position); +} + +const VkSamplerCreateInfo defaultSamplerInfo = { + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + NULL, + 0, + VK_FILTER_LINEAR, + VK_FILTER_LINEAR, + VK_SAMPLER_MIPMAP_MODE_NEAREST, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + 0.f, + VK_FALSE, + 0.f, + VK_FALSE, + VK_COMPARE_OP_ALWAYS, + 0.f, + 0.f, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, + VK_FALSE +}; + +const VkSamplerCreateInfo *getDefaultSamplerCi(){ + return &defaultSamplerInfo; +} + +void renderData_setupTextures(struct rendering_state *s, struct renderData *data, VkSamplerCreateInfo *samplerOverride){ + svlk_createVkImageView(s->device, data->albedo.image, VK_IMAGE_VIEW_TYPE_2D, data->albedo.fmt, &data->albedo.view); + vkCreateSampler(s->device, samplerOverride ? samplerOverride : getDefaultSamplerCi(), NULL, &data->albedo.sampler); + + createObjTextureDescriptorSet(s, data); + updateObjTextureDescriptorSet(s, data); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/data.h b/src/rendering/pipeline3d/data.h index 258a419..1fb3105 100644 --- a/src/rendering/pipeline3d/data.h +++ b/src/rendering/pipeline3d/data.h @@ -35,10 +35,16 @@ struct renderData { vec3 position; vec4 rotation; vec3 scale; + struct gpuimage albedo; + VkDescriptorSet textureDescSet; }; void uniformData_init(struct uniformData *ud, struct elementHeap *heap); void renderData_create(struct rendering_state *s, struct vertex *verts, uint32_t vertslen, uint32_t *indicies, uint32_t indiceslen, struct renderData *obj); +void renderData_setupTextures(struct rendering_state *s, struct renderData *data, VkSamplerCreateInfo *samplerOverride); + +const VkSamplerCreateInfo *getDefaultSamplerCi(); + #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h index edb2163..4f9e141 100644 --- a/src/rendering/pipeline3d/pipeline.h +++ b/src/rendering/pipeline3d/pipeline.h @@ -5,10 +5,38 @@ #include "../management/elementHeap.h" #include "../vulkan/gpumem_types.h" + +/* + * Pipelone3d. + * this is a 3d pipeline designed for generic 3d game stuff with shadows and lighting (todo (shadows & lighting) ) + * triangles are indexed (with index buffer) matricies are calculated per object on the cpu + * depth pre pass with some defered rendering ish + * + * Textures: + * Albedo + * Normal TODO + * + * Textures (runtime): + * Out/Albedo + * Depth TODO + * Normal TODO + * Position TODO + * Lighting TODO + * + * + * Pipeline: + * Monolithic shader renders models and optionally lighting + * Post processing shaders do stuff with the runtime textures TODO + * Merger merges lighting runtime texture with output (optional) TODO + * Post postproccesser shaders do stuff with lit scene (optional optional) TODO + */ + struct pipeline3dSubpass { VkPipelineLayout layout; VkPipeline pipeline; - VkDescriptorSetLayout desclayout; + // next two must be in this order (used as an array, should change. too lazy) + VkDescriptorSetLayout uniformDesclayout; + VkDescriptorSetLayout textureDesclayout; }; struct pipeline3d { @@ -17,7 +45,7 @@ struct pipeline3d { VkDescriptorPool descriptorPool; // potentially wrap the uniform heap in a linked list struct elementHeap uniformHeap; - VkDescriptorSet descriptorSet; + VkDescriptorSet uniformDescriptorSet; struct gpubuffer uniformbuffer; }; diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index 186b798..dbd9043 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -1,6 +1,5 @@ #include "render.h" #include "pipeline.h" -#include "setup.h" #include #include #include @@ -55,7 +54,9 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer vkCmdBindVertexBuffers(cmdbuf, 0, 1, &obj[i].vertexBuffer.buffer, vtxoffsets); vkCmdBindIndexBuffer(cmdbuf, obj[i].indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 0, - 1, &p->descriptorSet, 1, unioffsets); + 1, &p->uniformDescriptorSet, 1, unioffsets); + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.layout, 1, + 1, &obj[i].textureDescSet, 0, NULL); vkCmdDrawIndexed(cmdbuf, obj[i].indexCount, 1, 0, 0, 0); } diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index 98d9339..e328e8c 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -3,9 +3,7 @@ #include #include #include "../../helpers.h" -#include "../init.h" #include "../vulkan/renderingpipeline.h" -#include "../vulkan/svulc.h" #include "data.h" #include "pipeline.h" @@ -76,25 +74,48 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineDynamicStateCreateInfo dynamicState = svlk_createDynamicStateCI(dynstates, 2, 0); // Pipeline Layout - VkDescriptorSetLayoutBinding lb = { - 0, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - 1, - VK_SHADER_STAGE_VERTEX_BIT, - NULL + VkDescriptorSetLayoutBinding lbu[] = { + { + 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + 1, + VK_SHADER_STAGE_VERTEX_BIT, + NULL + }, }; - VkDescriptorSetLayoutCreateInfo dslCI = { - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - NULL, - 0, - 1, - &lb + VkDescriptorSetLayoutBinding lbt[] = { + { + 0, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, + VK_SHADER_STAGE_FRAGMENT_BIT, + NULL + } }; - vkCreateDescriptorSetLayout(s->device, &dslCI, NULL, &p->geometryPass.desclayout); + VkDescriptorSetLayoutCreateInfo dslCI[] = { + { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + NULL, + 0, + sizeof(lbu)/sizeof(lbu[0]), + lbu + }, + { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + NULL, + 0, + sizeof(lbt)/sizeof(lbt[0]), + lbt + } + }; - VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(&p->geometryPass.desclayout, 1); + vkCreateDescriptorSetLayout(s->device, &dslCI[0], NULL, &p->geometryPass.uniformDesclayout); + vkCreateDescriptorSetLayout(s->device, &dslCI[1], NULL, &p->geometryPass.textureDesclayout); + VkDescriptorSetLayout desclayouts[] = {p->geometryPass.uniformDesclayout, p->geometryPass.textureDesclayout}; + + VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(desclayouts, 2); vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); @@ -177,18 +198,24 @@ void createPipeline3d(struct rendering_state *s){ vkMapMemory(s->device, p->uniformbuffer.memory, 0, p->uniformbuffer.size, 0, &p->uniformHeap.buffer); // why descriptors, why. why museth you exist - VkDescriptorPoolSize poolsize = { - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // offsets are cool - 1 // no linked lists yet so 1 + VkDescriptorPoolSize poolsize[] = { + { + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // offsets are cool + 1 // no linked lists yet so 1 + }, + { + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1 * p->uniformHeap.len + } }; VkDescriptorPoolCreateInfo dpCI = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, NULL, 0, - 1, - 1, - &poolsize + poolsize[0].descriptorCount + poolsize[1].descriptorCount*1, + 2, + poolsize }; VkResult result = vkCreateDescriptorPool(s->device, &dpCI, NULL, &p->descriptorPool); @@ -201,9 +228,9 @@ void createPipeline3d(struct rendering_state *s){ NULL, p->descriptorPool, 1, - &p->geometryPass.desclayout + &p->geometryPass.uniformDesclayout }; - result = vkAllocateDescriptorSets(s->device, &allocInfo, &p->descriptorSet); + result = vkAllocateDescriptorSets(s->device, &allocInfo, &p->uniformDescriptorSet); if(result != VK_SUCCESS){ printf("Could not create descriptor set. result=%i\n", result); } @@ -221,7 +248,7 @@ void updateDescriptorSet(struct rendering_state *s){ VkWriteDescriptorSet descriptorWrite = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, - p->descriptorSet, + p->uniformDescriptorSet, 0, 0, 1, @@ -231,7 +258,46 @@ void updateDescriptorSet(struct rendering_state *s){ NULL }; - vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); // pain. + vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); +} +void createObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data){ + struct pipeline3d *p = &s->pipeline; + VkDescriptorSetAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + NULL, + p->descriptorPool, + 1, + &p->geometryPass.textureDesclayout + }; + checkErrorVk("vkAllocateDescriptorSets", + vkAllocateDescriptorSets(s->device, &allocInfo, &data->textureDescSet), + s->die + ); +} + +void updateObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data){ + struct pipeline3d *p = &s->pipeline; + VkDescriptorImageInfo imginfo[] = { + { + data->albedo.sampler, + data->albedo.view, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + } + }; + + VkWriteDescriptorSet descriptorWrite = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + NULL, + data->textureDescSet, + 0, + 0, + 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + imginfo, + NULL, + NULL + }; + vkUpdateDescriptorSets(s->device, 1, &descriptorWrite, 0, NULL); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.h b/src/rendering/pipeline3d/setup.h index 61b8126..49c8203 100644 --- a/src/rendering/pipeline3d/setup.h +++ b/src/rendering/pipeline3d/setup.h @@ -1,3 +1,5 @@ +#ifndef __PIPELINE3D_SETUP_H__ +#define __PIPELINE3D_SETUP_H__ #include #include #include @@ -5,4 +7,8 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_t subpassnum); void createPipeline3d(struct rendering_state *s); -void updateDescriptorSet(struct rendering_state *s); \ No newline at end of file +void updateDescriptorSet(struct rendering_state *s); + +void createObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data); +void updateObjTextureDescriptorSet(struct rendering_state *s, struct renderData *data); +#endif \ No newline at end of file diff --git a/src/rendering/vulkan/commandpools.h b/src/rendering/vulkan/commandpools.h index 8ba6021..69dbde9 100644 --- a/src/rendering/vulkan/commandpools.h +++ b/src/rendering/vulkan/commandpools.h @@ -7,6 +7,8 @@ VkResult svlk_createCommandPool(VkDevice device, int familyindex, int flags, VkC VkResult svlk_allocateCommandBuffer(VkDevice device, VkCommandPool cmdpool, int level, int count, VkCommandBuffer *cmdbuffers); VkResult svlk_recordCommandBuffer(VkCommandBuffer cmdbuf, VkImage image, uint32_t flags); +VkCommandBuffer svlk_createOneTimeCommandBuffer(); + VkResult svlk_createSemaphore(VkDevice device, VkSemaphore *semaphore); VkResult svlk_createFence(VkDevice device, VkFence *fence, VkFenceCreateFlags flags); #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c index a632731..0200e0c 100644 --- a/src/rendering/vulkan/gpumem.c +++ b/src/rendering/vulkan/gpumem.c @@ -1,4 +1,7 @@ #include "gpumem.h" +#include "gpumem_types.h" +#include "../../helpers.h" +#include #include #include #include @@ -28,12 +31,15 @@ void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, rendering_findMemoryType(memreq.memoryTypeBits, properties, s) }; - VkResult result = vkAllocateMemory(s->device, &allocinfo, NULL, &buf->memory); - if(result != VK_SUCCESS){ - printf("Failed to allocate memory on the GPU. error: %i", result); - exit(EXIT_FAILURE); - } - vkBindBufferMemory(s->device, buf->buffer, buf->memory, 0); + + checkErrorVk("vkAllocateMemory", + vkAllocateMemory(s->device, &allocinfo, NULL, &buf->memory), + s->die + ); + checkErrorVk("vkBindBufferMemory", + vkBindBufferMemory(s->device, buf->buffer, buf->memory, 0), + s->die + ); } void rendering_putBuffer(struct gpubuffer *buf, void *data, struct rendering_state *s){ @@ -65,6 +71,23 @@ uint32_t rendering_findMemoryType(uint32_t type, VkMemoryPropertyFlags propertie exit(EXIT_FAILURE); } +void rendering_endSubmitWait(VkCommandBuffer cmdbuf, VkQueue queue){ + vkEndCommandBuffer(cmdbuf); + VkSubmitInfo submitinfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + NULL, + 0, + NULL, + 0, + 1, + &cmdbuf, + 0, + NULL + }; + vkQueueSubmit(queue, 1, &submitinfo, NULL); + vkQueueWaitIdle(queue); +} + void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct rendering_state *s){ VkCommandBufferBeginInfo begininfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -78,18 +101,147 @@ void rendering_copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, struct size }; vkCmdCopyBuffer(s->transferBuffer, src, dst, 1, ©region); - vkEndCommandBuffer(s->transferBuffer); - VkSubmitInfo submitinfo = { - VK_STRUCTURE_TYPE_SUBMIT_INFO, - NULL, - 0, + rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); +} + +void rendering_createImage( + uint32_t w, uint32_t h, uint32_t d, VkImageType type, VkFormat fmt, uint32_t mipLevels, VkImageTiling tiling, + VkImageUsageFlags usageflags, VkMemoryPropertyFlags properties, struct gpuimage *img, struct rendering_state *s){ + img->w = w; + img->h = h; + img->d = d; + img->fmt = fmt; + + VkImageCreateInfo imgCI = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, + type, + fmt, + {w, h, d}, + mipLevels, 1, - &s->transferBuffer, + VK_SAMPLE_COUNT_1_BIT, + tiling, + usageflags, + VK_SHARING_MODE_EXCLUSIVE, + 0, + NULL, + VK_IMAGE_LAYOUT_UNDEFINED + }; + checkErrorVk("vkCreateImage", + vkCreateImage(s->device, &imgCI, NULL, &img->image), + s->die + ); + + VkMemoryRequirements memreq; + vkGetImageMemoryRequirements(s->device, img->image, &memreq); + + VkMemoryAllocateInfo allocinfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + NULL, + memreq.size, + rendering_findMemoryType(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, s) + }; + + checkErrorVk("vkAllocateMemory", + vkAllocateMemory(s->device, &allocinfo, NULL, &img->memory), + s->die + ); + + checkErrorVk("vkBindBufferMemory", + vkBindImageMemory(s->device, img->image, img->memory, 0), + s->die + ); +} + +void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uint32_t d, VkImageAspectFlags aspect, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, 0, NULL }; - vkQueueSubmit(s->queues.transferQueue, 1, &submitinfo, NULL); - vkQueueWaitIdle(s->queues.transferQueue); -} \ No newline at end of file + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + + VkBufferImageCopy region = { + 0, + 0, + 0, + {aspect, 0, 0, 1}, + {0, 0, 0}, + {w, h, d} + }; + vkCmdCopyBufferToImage(s->transferBuffer, src, img, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); +} + +void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + NULL, + 0, + 0, + old, + new, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + img, + { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + 1, + 0, + 1 + } + }; + + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + + if(old == VK_IMAGE_LAYOUT_UNDEFINED && new == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL){ + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if(old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL){ + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + + vkCmdPipelineBarrier(s->transferBuffer, srcStage, dstStage, 0, 0, NULL, 0, NULL, 1, &barrier); + + rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); +} + +void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t datalen, VkFormat fmt, struct gpuimage *img, struct rendering_state *s){ + struct gpubuffer stagebuf = {}; + stagebuf.size = datalen; + img->size = datalen; + // create and fill the staging buffer + rendering_createBuffer(&stagebuf, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); + rendering_putBuffer(&stagebuf, data, s); + + // create the image and transition the image layout + rendering_createImage(w, h, 1, VK_IMAGE_TYPE_2D, fmt, 1, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, img, s); + rendering_transitionImageLayout(img->image, fmt, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, s); + + // copy and transition to final stage + rendering_copyImageB(stagebuf.buffer, img->image, w, h, 1, VK_IMAGE_ASPECT_COLOR_BIT, s); + rendering_transitionImageLayout(img->image, fmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, s); + + rendering_destroyBuffer(&stagebuf, s); +}; \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h index 701aefc..290c8e4 100644 --- a/src/rendering/vulkan/gpumem.h +++ b/src/rendering/vulkan/gpumem.h @@ -19,4 +19,11 @@ static inline void rendering_destroyBuffer(struct gpubuffer *b, struct rendering vkFreeMemory(s->device, b->memory, NULL); } +void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uint32_t d, VkImageAspectFlags aspect, struct rendering_state *s); + +void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout newlayout, struct rendering_state *s); + +void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t datalen, VkFormat fmt, struct gpuimage *img, struct rendering_state *s); +//void rendering_createImage(); + #endif \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem_types.h b/src/rendering/vulkan/gpumem_types.h index ff7a61d..f36b036 100644 --- a/src/rendering/vulkan/gpumem_types.h +++ b/src/rendering/vulkan/gpumem_types.h @@ -8,4 +8,16 @@ struct gpubuffer { uint64_t size; }; +struct gpuimage { + VkImage image; + VkDeviceMemory memory; + uint64_t size; + uint32_t w; + uint32_t h; + uint32_t d; + VkFormat fmt; + VkImageView view; + VkSampler sampler; +}; + #endif \ No newline at end of file From 9aab707418c5c67a7ccaa89267ddfc51606c772f Mon Sep 17 00:00:00 2001 From: miri Date: Sat, 7 Jun 2025 17:16:32 +0200 Subject: [PATCH 21/22] getting ready for text rendering --- FuturaRenner-Regular.ttf | Bin 0 -> 137996 bytes src/main.c | 54 +++++++++++-- src/rendering/camera.h | 6 ++ src/rendering/init.c | 21 ++++- src/rendering/pipeline3d/data.h | 4 +- src/rendering/pipeline3d/pipeline.h | 2 +- src/rendering/pipeline3d/render.c | 42 +++++++--- src/rendering/pipeline3d/render.h | 2 +- src/rendering/pipeline3d/setup.c | 15 +++- src/rendering/pipelineText/data.h | 16 ++++ src/rendering/pipelineText/pipeline.h | 17 ++++ src/rendering/pipelineText/setup.c | 25 ++++++ src/rendering/pipelineText/setup.h | 6 ++ src/rendering/rendering.c | 112 +++++++++++++++++++++++--- src/rendering/rendering.h | 2 +- src/rendering/struct.h | 5 ++ src/rendering/vulkan/gpumem.c | 63 ++++++++++++--- src/rendering/vulkan/gpumem.h | 6 ++ src/rendering/vulkan/instance.c | 16 +++- src/rendering/vulkan/svulc.h | 1 + 20 files changed, 362 insertions(+), 53 deletions(-) create mode 100755 FuturaRenner-Regular.ttf create mode 100644 src/rendering/pipelineText/data.h create mode 100644 src/rendering/pipelineText/pipeline.h create mode 100644 src/rendering/pipelineText/setup.c create mode 100644 src/rendering/pipelineText/setup.h diff --git a/FuturaRenner-Regular.ttf b/FuturaRenner-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..09069b3d46df1047894d5617548dec399b06e171 GIT binary patch literal 137996 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 #include #include @@ -12,6 +15,8 @@ #include #define STB_IMAGE_IMPLEMENTATION #include +#define STB_TRUETYPE_IMPLEMENTATION +#include #include #include "rendering/camera.h" @@ -58,13 +63,13 @@ void keyboardUpdate(GLFWwindow *window, struct camera *cam){ vec3 zaxis = {0.f,0.f,1.f}; if(glfwGetKey(window, GLFW_KEY_UP)) - glm_vec3_rotate(cam->rotation, glm_rad(-1.f), cam->right); + camera_angleAxis(cam, glm_rad(1.f), cam->right); if(glfwGetKey(window, GLFW_KEY_DOWN)) - glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); + camera_angleAxis(cam, glm_rad(-1.f), cam->right); if(glfwGetKey(window, GLFW_KEY_LEFT)) - glm_vec3_rotate(cam->rotation, glm_rad(-1.f), zaxis); + camera_angleAxis(cam, glm_rad(1.f), cam->up); if(glfwGetKey(window, GLFW_KEY_RIGHT)) - glm_vec3_rotate(cam->rotation, glm_rad(1.f), zaxis); + camera_angleAxis(cam, glm_rad(-1.f), cam->up); camera_update(cam); } @@ -144,6 +149,7 @@ int main(int argc, const char **argv){ createPipeline3d(&s); rendering_createframebuffers(&s); + rendering_createCommandPools(&s); rendering_createSyncObjects(&s); @@ -176,23 +182,55 @@ int main(int argc, const char **argv){ // image loading int iw, ih, ic; stbi_uc *pixels = stbi_load("img.png", &iw, &ih, &ic, STBI_rgb_alpha); + + const unsigned char *fontdata = (const unsigned char*)readFileC("FuturaRenner-Regular.ttf"); + + //stbtt_fontinfo fontinfo; + //stbtt_InitFont(&fontinfo, fontdata, 0); + + void *fontpixels = malloc(512*512+1); + void *chardata = malloc(('z' + 1 - 'A') * sizeof(stbtt_bakedchar)); + + stbtt_pack_context packingctx = {0}; + stbtt_PackBegin(&packingctx, fontpixels, 512, 512, 0, 1, NULL); + + stbtt_pack_range packingranges[] = { + { + 2, + 'A', + NULL, + ('z' + 1 - 'A'), + chardata, + 0, + 0 + } + }; + + //stbtt_PackFontRanges(&packingctx, fontdata, 0, packingranges, 1); + + + stbtt_PackEnd(&packingctx); + + struct gpuimage img; - if(pixels){ - rendering_createImageForRender(iw, ih, pixels, iw * ih * ic, VK_FORMAT_R8G8B8A8_SRGB, &rects[0].albedo, &s); + if(fontpixels){ + rendering_createImageForRender(512, 512, fontpixels, 512 * 512 * 1, VK_FORMAT_R8_SRGB, &rects[0].albedo, &s); renderData_setupTextures(&s, &rects[0], NULL); printf("IMG: avail!\n"); } else { printf("IMG: not avail!\n"); } - while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); keyboardUpdate(s.window, &cam); - render(&s, rects, 1, &cam); + + render(&s, &s.renderTarget, rects, 1, &cam); if(first){ first = 0; } + glm_vec3_print(cam.right, stdout); + printf("\x1b[3A"); } } \ No newline at end of file diff --git a/src/rendering/camera.h b/src/rendering/camera.h index e9e08ca..daed151 100644 --- a/src/rendering/camera.h +++ b/src/rendering/camera.h @@ -43,4 +43,10 @@ static inline void camera_update(struct camera *cam){ glm_quat_rotatev(cam->rotation, right, cam->right); } +static inline void camera_angleAxis(struct camera *cam, float angle, vec3 axis){ + vec4 quat; + glm_quatv(quat, angle, axis); + glm_quat_mul(cam->rotation, quat, cam->rotation); +} + #endif \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index 8c8070e..437b7fd 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -1,3 +1,4 @@ +#include "vulkan/gpumem.h" #include "vulkan/svulc.h" #include "vulkan/swapchain.h" #include "vulkan/commandpools.h" @@ -78,13 +79,19 @@ void rendering_vkdevice(struct rendering_state *s){ svlk_createQueueInfo(s->queues.graphicsQFI, 2, &queuePriority) }; - VkPhysicalDeviceFeatures devFeatures = {0}; + VkPhysicalDeviceFeatures devFeatures = {}; + + VkPhysicalDeviceVulkan13Features dev13features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + NULL + }; + dev13features.dynamicRendering = VK_TRUE; checkErrorVk("vkCreateDevice", svlk_createLogicalDevice( s->physdevice, s->surface, deviceQueues,1, devFeatures, s->deviceextensions, - s->numdeviceextensions, &s->device + s->numdeviceextensions, &dev13features, &s->device ), s->die ); @@ -114,7 +121,7 @@ void rendering_vkswapchain(struct rendering_state *s){ VK_PRESENT_MODE_FIFO_KHR, s->swapchainExtent, 1, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, s->surfaceCapabilities.currentTransform ); @@ -133,6 +140,11 @@ void rendering_vkswapchain(struct rendering_state *s){ s->die ); s->numswapchainImageView = s->numswapchainImages; + + // rendertarget + rendering_createImage(s->window_width, s->window_height, 1, VK_IMAGE_TYPE_2D, s->surfaceformat.format, 1, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,& s->renderTarget, s); + svlk_createVkImageView(s->device, s->renderTarget.image, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, &s->renderTarget.view); } void rendering_createframebuffers(struct rendering_state *s){ @@ -161,7 +173,7 @@ void rendering_createframebuffers(struct rendering_state *s){ ); } } - +// IMPROTANT: also destroys rendertarget void rendering_cleanupSwapchain(struct rendering_state *s){ for(uint32_t i = 0; i < s->numframebuffers; i++){ vkDestroyFramebuffer(s->device, s->framebuffers[i], NULL); @@ -169,6 +181,7 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ for(uint32_t i = 0; i < s->numswapchainImageView; i++){ vkDestroyImageView(s->device, s->swapchainImageView[i], NULL); } + rendering_destroyImage(&s->renderTarget, s); vkDestroySwapchainKHR(s->device, s->swapchain, NULL); } diff --git a/src/rendering/pipeline3d/data.h b/src/rendering/pipeline3d/data.h index 1fb3105..2284446 100644 --- a/src/rendering/pipeline3d/data.h +++ b/src/rendering/pipeline3d/data.h @@ -16,14 +16,14 @@ struct vertex { float v; }; -struct UniformBufferObject{ +struct Pipeline3dUniformBufferObject{ mat4 matrix; }; struct uniformData { struct elementHeap *heap; uint32_t index; - struct UniformBufferObject *ubo; + struct Pipeline3dUniformBufferObject *ubo; }; // data for generic meshes (not things like heightmaps or other exotic things) diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h index 4f9e141..e28b9e9 100644 --- a/src/rendering/pipeline3d/pipeline.h +++ b/src/rendering/pipeline3d/pipeline.h @@ -7,7 +7,7 @@ /* - * Pipelone3d. + * Pipeline3d. * this is a 3d pipeline designed for generic 3d game stuff with shadows and lighting (todo (shadows & lighting) ) * triangles are indexed (with index buffer) matricies are calculated per object on the cpu * depth pre pass with some defered rendering ish diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index dbd9043..b52a77b 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -8,18 +8,38 @@ #include #include "../camera.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam){ +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkImageView target, VkExtent2D targetextent, VkImageLayout targetlayout, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam){ VkClearValue clearcolor = {0.4f,0.f,0.f,0.5f}; - VkRenderPassBeginInfo rpbi = { - VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - NULL, - rp, - framebuffer, - {{0,0}, {s->window_width, s->window_height}}, - 1, - &clearcolor + + VkRenderingAttachmentInfo attachments[1] = { + { + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + NULL, + target, + targetlayout, + VK_RESOLVE_MODE_NONE, + 0, + 0, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, + clearcolor + } }; - vkCmdBeginRenderPass(cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE); + + VkRenderingInfo renderinfo = { + VK_STRUCTURE_TYPE_RENDERING_INFO, + NULL, + 0, + {{0,0},targetextent}, + 1, + 0, + 1, + attachments, + NULL, + NULL + }; + + vkCmdBeginRendering(cmdbuf, &renderinfo); vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.pipeline); @@ -60,5 +80,5 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer vkCmdDrawIndexed(cmdbuf, obj[i].indexCount, 1, 0, 0, 0); } - vkCmdEndRenderPass(cmdbuf); + vkCmdEndRendering(cmdbuf); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h index 9cd65c4..3c2c8ea 100644 --- a/src/rendering/pipeline3d/render.h +++ b/src/rendering/pipeline3d/render.h @@ -5,6 +5,6 @@ #include "data.h" #include "../struct.h" #include "../camera.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam); +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkImageView target, VkExtent2D targetextent, VkImageLayout targetlayout, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam); #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index e328e8c..9606142 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -118,10 +118,19 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(desclayouts, 2); vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); + VkPipelineRenderingCreateInfo pipelineRenderingCI = { + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + NULL, + 0, + 1, + &s->surfaceformat.format, + 0, + 0 + }; VkGraphicsPipelineCreateInfo pipelineCI = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - NULL, + &pipelineRenderingCI, 0, 2, stages, @@ -135,7 +144,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ &colorblendState, &dynamicState, p->geometryPass.layout, - p->renderpass, + NULL, subpassnum, NULL, -1 @@ -242,7 +251,7 @@ void updateDescriptorSet(struct rendering_state *s){ VkDescriptorBufferInfo bufferInfo = { p->uniformbuffer.buffer, 0, - sizeof(struct UniformBufferObject) + sizeof(struct Pipeline3dUniformBufferObject) }; VkWriteDescriptorSet descriptorWrite = { diff --git a/src/rendering/pipelineText/data.h b/src/rendering/pipelineText/data.h new file mode 100644 index 0000000..3cce39a --- /dev/null +++ b/src/rendering/pipelineText/data.h @@ -0,0 +1,16 @@ +#ifndef __PIPELINE_TEXT_DATA_H__ +#define __PIPELINE_TEXT_DATA_H__ + +#include +// this is a push constant +struct Pipeline3dUniformBufferObject{ + vec2 p0; // TL + vec2 p1; // TR + vec2 p2; // BL + vec2 p3; // BR + + vec2 bp0; // position of the top left in the atlas + vec2 bp1; // position of bottom right in the atlas +}; + +#endif \ No newline at end of file diff --git a/src/rendering/pipelineText/pipeline.h b/src/rendering/pipelineText/pipeline.h new file mode 100644 index 0000000..33301a3 --- /dev/null +++ b/src/rendering/pipelineText/pipeline.h @@ -0,0 +1,17 @@ +#ifndef __PIPELINETEXT_PIPELINE_H__ +#define __PIPELINETEXT_PIPELINE_H__ + +#include +#include "../management/elementHeap.h" +#include "../vulkan/gpumem_types.h" + +struct pipelineText { + VkRenderPass renderpass; + VkDescriptorPool descriptorPool; + + struct elementHeap uniformHeap; + VkDescriptorSet uniformDescriptorSet; + struct gpubuffer uniformbuffer; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/pipelineText/setup.c b/src/rendering/pipelineText/setup.c new file mode 100644 index 0000000..96b30a8 --- /dev/null +++ b/src/rendering/pipelineText/setup.c @@ -0,0 +1,25 @@ +#include "setup.h" +#include + +void setupMainTextPipeline(struct rendering_state *s, struct pipelineText *p){ + +} + +void createPipelineText(struct rendering_state *s){ + struct pipelineText *p = &s->pipelinetext; + + VkAttachmentDescription attdescs[1] = {0}; + VkAttachmentDescription colordescs = { + 0, + s->surfaceformat.format, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL + }; attdescs[0] = colordescs; + + +} \ No newline at end of file diff --git a/src/rendering/pipelineText/setup.h b/src/rendering/pipelineText/setup.h new file mode 100644 index 0000000..1935aa6 --- /dev/null +++ b/src/rendering/pipelineText/setup.h @@ -0,0 +1,6 @@ +#include "pipeline.h" +#include "../struct.h" + + +void setupMainTextPipeline(struct rendering_state *s, struct pipelineText *p); +void createPipelineText(struct rendering_state *s); \ No newline at end of file diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c index 7cf6323..57a0f0a 100644 --- a/src/rendering/rendering.c +++ b/src/rendering/rendering.c @@ -6,23 +6,31 @@ #include #include "init.h" #include "struct.h" +#include "vulkan/gpumem.h" +#include "vulkan/gpumem_types.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam){ - vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); - +uint32_t render_getSwapchainPre(struct rendering_state *s, VkResult *res){ + // wait for next image uint32_t imageindex; - - VkResult nextImageResult = vkAcquireNextImageKHR(s->device, s->swapchain, UINT64_MAX, s->sync.presentReadySemaphore, NULL, &imageindex); + + if(res) *res = nextImageResult; + + // resize swapchain if surface size changed if(nextImageResult == VK_ERROR_OUT_OF_DATE_KHR){ recreateSwapchain(s); }else if(nextImageResult != VK_SUCCESS && nextImageResult != VK_SUBOPTIMAL_KHR){ printf("error getting next image!\n"); } - vkResetFences(s->device, 1, &s->sync.frameFinishFence); + return imageindex; +} + +void render_beginCmdBuf(struct rendering_state *s){ + // reset cmdbuf vkResetCommandBuffer(s->graphicsBuffer, 0); - + + // begin new cmdbuf VkCommandBufferBeginInfo begininfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, @@ -30,11 +38,59 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount NULL }; VkResult result = vkBeginCommandBuffer(s->graphicsBuffer, &begininfo); +} - recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount, cam); +void render_clearBg(struct rendering_state *s, struct gpuimage *target){ + // setup the rendertarget for the first clear + rendering_transitionImageLayoutCB(s->graphicsBuffer, target->image, s->surfaceformat.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // clear the rendertarget with clearcol + VkImageSubresourceRange isubr = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + 1, + 0, + 1 + }; + + VkClearColorValue clearcol = {0.1,0.5,0.2, 0.1f}; + + vkCmdClearColorImage(s->graphicsBuffer, target->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearcol, 1, &isubr); + + // transition the rendertarget to be a color attachment + rendering_transitionImageLayoutCB(s->graphicsBuffer, target->image, s->surfaceformat.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); +} + +void render_copyToSwap(struct rendering_state *s, struct gpuimage *target, VkImage swimg){ + // setup the rendertarget and current swapchain image for the blit + rendering_transitionImageLayoutCB(s->graphicsBuffer, target->image, s->surfaceformat.format, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + rendering_transitionImageLayoutCB(s->graphicsBuffer, swimg, s->surfaceformat.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // blit entire rendertarget to current swapchain image + VkImageBlit blit = { + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {{0,0,0}, {target->w, target->h, 1}}, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {{0,0,0}, {target->w, target->h, 1}}, + }; + + vkCmdBlitImage(s->graphicsBuffer, target->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swimg, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); + + // transition swapchain image back to be able to present + rendering_transitionImageLayoutCB(s->graphicsBuffer, swimg, s->surfaceformat.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); vkEndCommandBuffer(s->graphicsBuffer); +} +void render_waitForDevice(struct rendering_state *s){ + // wait until device has finished rendering the previous frame + vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); + // reset the fence to reuse it. + vkResetFences(s->device, 1, &s->sync.frameFinishFence); +} + +void render_submitToDevice(struct rendering_state *s){ + // submit the cmdbuf VkPipelineStageFlags waitstages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, @@ -47,8 +103,12 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount 1, &s->sync.renderFinishSemaphore }; - result = vkQueueSubmit(s->queues.graphicsQueue, 1, &submitInfo, s->sync.frameFinishFence); + VkResult result = vkQueueSubmit(s->queues.graphicsQueue, 1, &submitInfo, s->sync.frameFinishFence); +} + +void render_present(struct rendering_state *s, uint32_t imageindex, VkResult nextImageResult){ + // present the newly created image VkPresentInfoKHR presentI = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, NULL, @@ -59,6 +119,7 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount &imageindex, NULL }; + vkQueuePresentKHR(s->queues.presentationQueue, &presentI); if(nextImageResult == VK_SUBOPTIMAL_KHR || s->shouldRecreateswapchainFlags){ @@ -67,6 +128,39 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount } } +void render(struct rendering_state *s, struct gpuimage *target, struct renderData *obj, uint32_t objcount, struct camera *cam){ + VkResult nextImageResult = 0; + uint32_t imageindex = render_getSwapchainPre(s, &nextImageResult); + + render_beginCmdBuf(s); + + render_clearBg(s, target); + + VkExtent2D imageextent = {target->w, target->h}; + + // do the rendering + recordCommandBuffer( + s->graphicsBuffer, + s->pipeline.renderpass, + target->view, + imageextent, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + s, + &s->pipeline, + obj, + objcount, + cam); + + + render_copyToSwap(s, target, s->swapchainImages[imageindex]); + + render_waitForDevice(s); + + render_submitToDevice(s); + + render_present(s, imageindex, nextImageResult); +} + void recreateSwapchain(struct rendering_state *s){ if(s->shouldRecreateswapchainFlags & RNDR_RECREATE_SWAPCHAIN_USE_TMP){ s->window_width = s->tmp_window_width; diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h index 0a5bad8..e916272 100644 --- a/src/rendering/rendering.h +++ b/src/rendering/rendering.h @@ -4,7 +4,7 @@ #include "pipeline3d/data.h" #include "camera.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam); +void render(struct rendering_state *s, struct gpuimage *target, struct renderData *obj, uint32_t objcount, struct camera *cam); void recreateSwapchain(struct rendering_state *s); diff --git a/src/rendering/struct.h b/src/rendering/struct.h index 8210ad8..446e06d 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -3,6 +3,8 @@ #include "camera.h" #include "pipeline3d/pipeline.h" +#include "pipelineText/pipeline.h" +#include "vulkan/gpumem_types.h" #include #include #include @@ -82,11 +84,14 @@ struct rendering_state{ VkGraphicsPipelineCreateInfo *gptemplate; struct pipeline3d pipeline; + struct pipelineText pipelinetext; VkCommandPool graphicsPool; VkCommandBuffer graphicsBuffer; VkCommandBuffer transferBuffer; + struct gpuimage renderTarget; + struct rendering_sync sync; uint32_t shouldRecreateswapchainFlags; diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c index 0200e0c..dd8f4c2 100644 --- a/src/rendering/vulkan/gpumem.c +++ b/src/rendering/vulkan/gpumem.c @@ -19,6 +19,7 @@ void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, NULL }; + printf("haifisch %lu\n", buf->size); vkCreateBuffer(s->device, &bufferCI, NULL, &buf->buffer); VkMemoryRequirements memreq; @@ -177,15 +178,7 @@ void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uin rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); } -void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new, struct rendering_state *s){ - VkCommandBufferBeginInfo begininfo = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - NULL, - 0, - NULL - }; - vkBeginCommandBuffer(s->transferBuffer, &begininfo); - +void rendering_transitionImageLayoutCB(VkCommandBuffer buf, VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new){ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, @@ -220,9 +213,44 @@ void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout ol srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else if(old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR){ + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = 0; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } else if(old == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && new == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL){ + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if(old == VK_IMAGE_LAYOUT_UNDEFINED && new == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL){ + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } else if(old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL){ + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; } - vkCmdPipelineBarrier(s->transferBuffer, srcStage, dstStage, 0, 0, NULL, 0, NULL, 1, &barrier); + vkCmdPipelineBarrier(buf, srcStage, dstStage, 0, 0, NULL, 0, NULL, 1, &barrier); +} +void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + + rendering_transitionImageLayoutCB(s->transferBuffer, img, fmt, old, new); rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); } @@ -232,6 +260,7 @@ void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t stagebuf.size = datalen; img->size = datalen; // create and fill the staging buffer + printf("besonderer haifisch\n"); rendering_createBuffer(&stagebuf, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); rendering_putBuffer(&stagebuf, data, s); @@ -244,4 +273,16 @@ void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t rendering_transitionImageLayout(img->image, fmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, s); rendering_destroyBuffer(&stagebuf, s); -}; \ No newline at end of file +}; + +void rendering_destroyImage(struct gpuimage *img, struct rendering_state *s){ + if(img->sampler) vkDestroySampler(s->device, img->sampler, NULL); + if(img->view) vkDestroyImageView(s->device, img->view, NULL); + vkDestroyImage(s->device, img->image, NULL); + vkFreeMemory(s->device, img->memory, NULL); + img->size = 0; + img->w = 0; + img->h = 0; + img->d = 0; + img->fmt = 0; +} \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h index 290c8e4..98ecf2a 100644 --- a/src/rendering/vulkan/gpumem.h +++ b/src/rendering/vulkan/gpumem.h @@ -19,11 +19,17 @@ static inline void rendering_destroyBuffer(struct gpubuffer *b, struct rendering vkFreeMemory(s->device, b->memory, NULL); } +void rendering_createImage( + uint32_t w, uint32_t h, uint32_t d, VkImageType type, VkFormat fmt, uint32_t mipLevels, VkImageTiling tiling, + VkImageUsageFlags usageflags, VkMemoryPropertyFlags properties, struct gpuimage *img, struct rendering_state *s); + void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uint32_t d, VkImageAspectFlags aspect, struct rendering_state *s); +void rendering_transitionImageLayoutCB(VkCommandBuffer buf, VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout newlayout); void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout newlayout, struct rendering_state *s); void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t datalen, VkFormat fmt, struct gpuimage *img, struct rendering_state *s); +void rendering_destroyImage(struct gpuimage *img, struct rendering_state *s); //void rendering_createImage(); #endif \ No newline at end of file diff --git a/src/rendering/vulkan/instance.c b/src/rendering/vulkan/instance.c index 946b9b6..1b1c4e4 100644 --- a/src/rendering/vulkan/instance.c +++ b/src/rendering/vulkan/instance.c @@ -8,7 +8,18 @@ VkResult svlk_createIinstance(const char **layers, int layersNum, const char **e createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pNext = NULL; createInfo.flags = 0; - createInfo.pApplicationInfo = NULL; + + VkApplicationInfo appinfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, + NULL, + NULL, + 0, + NULL, + 0, + VK_API_VERSION_1_4 + }; + + createInfo.pApplicationInfo = &appinfo; createInfo.enabledLayerCount = layersNum; createInfo.ppEnabledLayerNames = layers; createInfo.enabledExtensionCount = extensionsNum; @@ -113,12 +124,13 @@ VkResult svlk_createLogicalDevice( VkPhysicalDeviceFeatures deviceFeatures, const char **deviceExtensions, int deviceExtensionsNum, + void *pNext, VkDevice *device ){ VkDeviceCreateInfo createInfo = {0}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pNext = NULL; + createInfo.pNext = pNext; createInfo.flags = 0; createInfo.queueCreateInfoCount = queueFamiliesNum; createInfo.pQueueCreateInfos = queueFamilies; diff --git a/src/rendering/vulkan/svulc.h b/src/rendering/vulkan/svulc.h index 8a61ea4..267c3ad 100644 --- a/src/rendering/vulkan/svulc.h +++ b/src/rendering/vulkan/svulc.h @@ -23,6 +23,7 @@ VkResult svlk_createLogicalDevice( VkPhysicalDeviceFeatures deviceFeatures, const char **deviceExtensions, int deviceExtensionsNum, + void *pNext, VkDevice *device); #endif \ No newline at end of file From 09ddec6e1ad01b1822521c73d52d40d473afd6a3 Mon Sep 17 00:00:00 2001 From: miri Date: Sat, 7 Jun 2025 17:16:32 +0200 Subject: [PATCH 22/22] getting ready for text rendering --- FuturaRenner-Regular.ttf | Bin 0 -> 137996 bytes src/main.c | 54 +++++++++++-- src/rendering/camera.h | 6 ++ src/rendering/init.c | 21 ++++- src/rendering/pipeline3d/data.h | 4 +- src/rendering/pipeline3d/pipeline.h | 2 +- src/rendering/pipeline3d/render.c | 42 +++++++--- src/rendering/pipeline3d/render.h | 2 +- src/rendering/pipeline3d/setup.c | 15 +++- src/rendering/pipelineText/data.h | 16 ++++ src/rendering/pipelineText/pipeline.h | 17 ++++ src/rendering/pipelineText/setup.c | 25 ++++++ src/rendering/pipelineText/setup.h | 6 ++ src/rendering/rendering.c | 112 +++++++++++++++++++++++--- src/rendering/rendering.h | 2 +- src/rendering/struct.h | 5 ++ src/rendering/vulkan/gpumem.c | 63 ++++++++++++--- src/rendering/vulkan/gpumem.h | 6 ++ src/rendering/vulkan/instance.c | 16 +++- src/rendering/vulkan/svulc.h | 1 + 20 files changed, 362 insertions(+), 53 deletions(-) create mode 100755 FuturaRenner-Regular.ttf create mode 100644 src/rendering/pipelineText/data.h create mode 100644 src/rendering/pipelineText/pipeline.h create mode 100644 src/rendering/pipelineText/setup.c create mode 100644 src/rendering/pipelineText/setup.h diff --git a/FuturaRenner-Regular.ttf b/FuturaRenner-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..09069b3d46df1047894d5617548dec399b06e171 GIT binary patch literal 137996 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 #include #include @@ -12,6 +15,8 @@ #include #define STB_IMAGE_IMPLEMENTATION #include +#define STB_TRUETYPE_IMPLEMENTATION +#include #include #include "rendering/camera.h" @@ -58,13 +63,13 @@ void keyboardUpdate(GLFWwindow *window, struct camera *cam){ vec3 zaxis = {0.f,0.f,1.f}; if(glfwGetKey(window, GLFW_KEY_UP)) - glm_vec3_rotate(cam->rotation, glm_rad(-1.f), cam->right); + camera_angleAxis(cam, glm_rad(1.f), cam->right); if(glfwGetKey(window, GLFW_KEY_DOWN)) - glm_vec3_rotate(cam->rotation, glm_rad(1.f), cam->right); + camera_angleAxis(cam, glm_rad(-1.f), cam->right); if(glfwGetKey(window, GLFW_KEY_LEFT)) - glm_vec3_rotate(cam->rotation, glm_rad(-1.f), zaxis); + camera_angleAxis(cam, glm_rad(1.f), cam->up); if(glfwGetKey(window, GLFW_KEY_RIGHT)) - glm_vec3_rotate(cam->rotation, glm_rad(1.f), zaxis); + camera_angleAxis(cam, glm_rad(-1.f), cam->up); camera_update(cam); } @@ -144,6 +149,7 @@ int main(int argc, const char **argv){ createPipeline3d(&s); rendering_createframebuffers(&s); + rendering_createCommandPools(&s); rendering_createSyncObjects(&s); @@ -176,23 +182,55 @@ int main(int argc, const char **argv){ // image loading int iw, ih, ic; stbi_uc *pixels = stbi_load("img.png", &iw, &ih, &ic, STBI_rgb_alpha); + + const unsigned char *fontdata = (const unsigned char*)readFileC("FuturaRenner-Regular.ttf"); + + //stbtt_fontinfo fontinfo; + //stbtt_InitFont(&fontinfo, fontdata, 0); + + void *fontpixels = malloc(512*512+1); + void *chardata = malloc(('z' + 1 - 'A') * sizeof(stbtt_bakedchar)); + + stbtt_pack_context packingctx = {0}; + stbtt_PackBegin(&packingctx, fontpixels, 512, 512, 0, 1, NULL); + + stbtt_pack_range packingranges[] = { + { + 2, + 'A', + NULL, + ('z' + 1 - 'A'), + chardata, + 0, + 0 + } + }; + + //stbtt_PackFontRanges(&packingctx, fontdata, 0, packingranges, 1); + + + stbtt_PackEnd(&packingctx); + + struct gpuimage img; - if(pixels){ - rendering_createImageForRender(iw, ih, pixels, iw * ih * ic, VK_FORMAT_R8G8B8A8_SRGB, &rects[0].albedo, &s); + if(fontpixels){ + rendering_createImageForRender(512, 512, fontpixels, 512 * 512 * 1, VK_FORMAT_R8_SRGB, &rects[0].albedo, &s); renderData_setupTextures(&s, &rects[0], NULL); printf("IMG: avail!\n"); } else { printf("IMG: not avail!\n"); } - while (!glfwWindowShouldClose(s.window)){ glfwPollEvents(); keyboardUpdate(s.window, &cam); - render(&s, rects, 1, &cam); + + render(&s, &s.renderTarget, rects, 1, &cam); if(first){ first = 0; } + glm_vec3_print(cam.right, stdout); + printf("\x1b[3A"); } } \ No newline at end of file diff --git a/src/rendering/camera.h b/src/rendering/camera.h index e9e08ca..daed151 100644 --- a/src/rendering/camera.h +++ b/src/rendering/camera.h @@ -43,4 +43,10 @@ static inline void camera_update(struct camera *cam){ glm_quat_rotatev(cam->rotation, right, cam->right); } +static inline void camera_angleAxis(struct camera *cam, float angle, vec3 axis){ + vec4 quat; + glm_quatv(quat, angle, axis); + glm_quat_mul(cam->rotation, quat, cam->rotation); +} + #endif \ No newline at end of file diff --git a/src/rendering/init.c b/src/rendering/init.c index 8c8070e..437b7fd 100644 --- a/src/rendering/init.c +++ b/src/rendering/init.c @@ -1,3 +1,4 @@ +#include "vulkan/gpumem.h" #include "vulkan/svulc.h" #include "vulkan/swapchain.h" #include "vulkan/commandpools.h" @@ -78,13 +79,19 @@ void rendering_vkdevice(struct rendering_state *s){ svlk_createQueueInfo(s->queues.graphicsQFI, 2, &queuePriority) }; - VkPhysicalDeviceFeatures devFeatures = {0}; + VkPhysicalDeviceFeatures devFeatures = {}; + + VkPhysicalDeviceVulkan13Features dev13features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + NULL + }; + dev13features.dynamicRendering = VK_TRUE; checkErrorVk("vkCreateDevice", svlk_createLogicalDevice( s->physdevice, s->surface, deviceQueues,1, devFeatures, s->deviceextensions, - s->numdeviceextensions, &s->device + s->numdeviceextensions, &dev13features, &s->device ), s->die ); @@ -114,7 +121,7 @@ void rendering_vkswapchain(struct rendering_state *s){ VK_PRESENT_MODE_FIFO_KHR, s->swapchainExtent, 1, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, s->surfaceCapabilities.currentTransform ); @@ -133,6 +140,11 @@ void rendering_vkswapchain(struct rendering_state *s){ s->die ); s->numswapchainImageView = s->numswapchainImages; + + // rendertarget + rendering_createImage(s->window_width, s->window_height, 1, VK_IMAGE_TYPE_2D, s->surfaceformat.format, 1, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,& s->renderTarget, s); + svlk_createVkImageView(s->device, s->renderTarget.image, VK_IMAGE_VIEW_TYPE_2D, s->surfaceformat.format, &s->renderTarget.view); } void rendering_createframebuffers(struct rendering_state *s){ @@ -161,7 +173,7 @@ void rendering_createframebuffers(struct rendering_state *s){ ); } } - +// IMPROTANT: also destroys rendertarget void rendering_cleanupSwapchain(struct rendering_state *s){ for(uint32_t i = 0; i < s->numframebuffers; i++){ vkDestroyFramebuffer(s->device, s->framebuffers[i], NULL); @@ -169,6 +181,7 @@ void rendering_cleanupSwapchain(struct rendering_state *s){ for(uint32_t i = 0; i < s->numswapchainImageView; i++){ vkDestroyImageView(s->device, s->swapchainImageView[i], NULL); } + rendering_destroyImage(&s->renderTarget, s); vkDestroySwapchainKHR(s->device, s->swapchain, NULL); } diff --git a/src/rendering/pipeline3d/data.h b/src/rendering/pipeline3d/data.h index 1fb3105..2284446 100644 --- a/src/rendering/pipeline3d/data.h +++ b/src/rendering/pipeline3d/data.h @@ -16,14 +16,14 @@ struct vertex { float v; }; -struct UniformBufferObject{ +struct Pipeline3dUniformBufferObject{ mat4 matrix; }; struct uniformData { struct elementHeap *heap; uint32_t index; - struct UniformBufferObject *ubo; + struct Pipeline3dUniformBufferObject *ubo; }; // data for generic meshes (not things like heightmaps or other exotic things) diff --git a/src/rendering/pipeline3d/pipeline.h b/src/rendering/pipeline3d/pipeline.h index 4f9e141..e28b9e9 100644 --- a/src/rendering/pipeline3d/pipeline.h +++ b/src/rendering/pipeline3d/pipeline.h @@ -7,7 +7,7 @@ /* - * Pipelone3d. + * Pipeline3d. * this is a 3d pipeline designed for generic 3d game stuff with shadows and lighting (todo (shadows & lighting) ) * triangles are indexed (with index buffer) matricies are calculated per object on the cpu * depth pre pass with some defered rendering ish diff --git a/src/rendering/pipeline3d/render.c b/src/rendering/pipeline3d/render.c index dbd9043..b52a77b 100644 --- a/src/rendering/pipeline3d/render.c +++ b/src/rendering/pipeline3d/render.c @@ -8,18 +8,38 @@ #include #include "../camera.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam){ +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkImageView target, VkExtent2D targetextent, VkImageLayout targetlayout, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam){ VkClearValue clearcolor = {0.4f,0.f,0.f,0.5f}; - VkRenderPassBeginInfo rpbi = { - VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - NULL, - rp, - framebuffer, - {{0,0}, {s->window_width, s->window_height}}, - 1, - &clearcolor + + VkRenderingAttachmentInfo attachments[1] = { + { + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + NULL, + target, + targetlayout, + VK_RESOLVE_MODE_NONE, + 0, + 0, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, + clearcolor + } }; - vkCmdBeginRenderPass(cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE); + + VkRenderingInfo renderinfo = { + VK_STRUCTURE_TYPE_RENDERING_INFO, + NULL, + 0, + {{0,0},targetextent}, + 1, + 0, + 1, + attachments, + NULL, + NULL + }; + + vkCmdBeginRendering(cmdbuf, &renderinfo); vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->geometryPass.pipeline); @@ -60,5 +80,5 @@ void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer vkCmdDrawIndexed(cmdbuf, obj[i].indexCount, 1, 0, 0, 0); } - vkCmdEndRenderPass(cmdbuf); + vkCmdEndRendering(cmdbuf); } \ No newline at end of file diff --git a/src/rendering/pipeline3d/render.h b/src/rendering/pipeline3d/render.h index 9cd65c4..3c2c8ea 100644 --- a/src/rendering/pipeline3d/render.h +++ b/src/rendering/pipeline3d/render.h @@ -5,6 +5,6 @@ #include "data.h" #include "../struct.h" #include "../camera.h" -void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkFramebuffer framebuffer, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam); +void recordCommandBuffer(VkCommandBuffer cmdbuf, VkRenderPass rp, VkImageView target, VkExtent2D targetextent, VkImageLayout targetlayout, struct rendering_state *s, struct pipeline3d *p, struct renderData *obj, uint32_t objcount, struct camera *cam); #endif \ No newline at end of file diff --git a/src/rendering/pipeline3d/setup.c b/src/rendering/pipeline3d/setup.c index e328e8c..9606142 100644 --- a/src/rendering/pipeline3d/setup.c +++ b/src/rendering/pipeline3d/setup.c @@ -118,10 +118,19 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ VkPipelineLayoutCreateInfo layoutCI = svlk_createPipelineLayoutCI(desclayouts, 2); vkCreatePipelineLayout(s->device, &layoutCI, NULL, &p->geometryPass.layout); + VkPipelineRenderingCreateInfo pipelineRenderingCI = { + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + NULL, + 0, + 1, + &s->surfaceformat.format, + 0, + 0 + }; VkGraphicsPipelineCreateInfo pipelineCI = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - NULL, + &pipelineRenderingCI, 0, 2, stages, @@ -135,7 +144,7 @@ void createGeometryPass(struct rendering_state *s, struct pipeline3d *p, uint32_ &colorblendState, &dynamicState, p->geometryPass.layout, - p->renderpass, + NULL, subpassnum, NULL, -1 @@ -242,7 +251,7 @@ void updateDescriptorSet(struct rendering_state *s){ VkDescriptorBufferInfo bufferInfo = { p->uniformbuffer.buffer, 0, - sizeof(struct UniformBufferObject) + sizeof(struct Pipeline3dUniformBufferObject) }; VkWriteDescriptorSet descriptorWrite = { diff --git a/src/rendering/pipelineText/data.h b/src/rendering/pipelineText/data.h new file mode 100644 index 0000000..3cce39a --- /dev/null +++ b/src/rendering/pipelineText/data.h @@ -0,0 +1,16 @@ +#ifndef __PIPELINE_TEXT_DATA_H__ +#define __PIPELINE_TEXT_DATA_H__ + +#include +// this is a push constant +struct Pipeline3dUniformBufferObject{ + vec2 p0; // TL + vec2 p1; // TR + vec2 p2; // BL + vec2 p3; // BR + + vec2 bp0; // position of the top left in the atlas + vec2 bp1; // position of bottom right in the atlas +}; + +#endif \ No newline at end of file diff --git a/src/rendering/pipelineText/pipeline.h b/src/rendering/pipelineText/pipeline.h new file mode 100644 index 0000000..33301a3 --- /dev/null +++ b/src/rendering/pipelineText/pipeline.h @@ -0,0 +1,17 @@ +#ifndef __PIPELINETEXT_PIPELINE_H__ +#define __PIPELINETEXT_PIPELINE_H__ + +#include +#include "../management/elementHeap.h" +#include "../vulkan/gpumem_types.h" + +struct pipelineText { + VkRenderPass renderpass; + VkDescriptorPool descriptorPool; + + struct elementHeap uniformHeap; + VkDescriptorSet uniformDescriptorSet; + struct gpubuffer uniformbuffer; +}; + +#endif \ No newline at end of file diff --git a/src/rendering/pipelineText/setup.c b/src/rendering/pipelineText/setup.c new file mode 100644 index 0000000..96b30a8 --- /dev/null +++ b/src/rendering/pipelineText/setup.c @@ -0,0 +1,25 @@ +#include "setup.h" +#include + +void setupMainTextPipeline(struct rendering_state *s, struct pipelineText *p){ + +} + +void createPipelineText(struct rendering_state *s){ + struct pipelineText *p = &s->pipelinetext; + + VkAttachmentDescription attdescs[1] = {0}; + VkAttachmentDescription colordescs = { + 0, + s->surfaceformat.format, + VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL + }; attdescs[0] = colordescs; + + +} \ No newline at end of file diff --git a/src/rendering/pipelineText/setup.h b/src/rendering/pipelineText/setup.h new file mode 100644 index 0000000..1935aa6 --- /dev/null +++ b/src/rendering/pipelineText/setup.h @@ -0,0 +1,6 @@ +#include "pipeline.h" +#include "../struct.h" + + +void setupMainTextPipeline(struct rendering_state *s, struct pipelineText *p); +void createPipelineText(struct rendering_state *s); \ No newline at end of file diff --git a/src/rendering/rendering.c b/src/rendering/rendering.c index 7cf6323..57a0f0a 100644 --- a/src/rendering/rendering.c +++ b/src/rendering/rendering.c @@ -6,23 +6,31 @@ #include #include "init.h" #include "struct.h" +#include "vulkan/gpumem.h" +#include "vulkan/gpumem_types.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam){ - vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); - +uint32_t render_getSwapchainPre(struct rendering_state *s, VkResult *res){ + // wait for next image uint32_t imageindex; - - VkResult nextImageResult = vkAcquireNextImageKHR(s->device, s->swapchain, UINT64_MAX, s->sync.presentReadySemaphore, NULL, &imageindex); + + if(res) *res = nextImageResult; + + // resize swapchain if surface size changed if(nextImageResult == VK_ERROR_OUT_OF_DATE_KHR){ recreateSwapchain(s); }else if(nextImageResult != VK_SUCCESS && nextImageResult != VK_SUBOPTIMAL_KHR){ printf("error getting next image!\n"); } - vkResetFences(s->device, 1, &s->sync.frameFinishFence); + return imageindex; +} + +void render_beginCmdBuf(struct rendering_state *s){ + // reset cmdbuf vkResetCommandBuffer(s->graphicsBuffer, 0); - + + // begin new cmdbuf VkCommandBufferBeginInfo begininfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, @@ -30,11 +38,59 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount NULL }; VkResult result = vkBeginCommandBuffer(s->graphicsBuffer, &begininfo); +} - recordCommandBuffer(s->graphicsBuffer, s->pipeline.renderpass, s->framebuffers[imageindex], s, &s->pipeline, obj, objcount, cam); +void render_clearBg(struct rendering_state *s, struct gpuimage *target){ + // setup the rendertarget for the first clear + rendering_transitionImageLayoutCB(s->graphicsBuffer, target->image, s->surfaceformat.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // clear the rendertarget with clearcol + VkImageSubresourceRange isubr = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + 1, + 0, + 1 + }; + + VkClearColorValue clearcol = {0.1,0.5,0.2, 0.1f}; + + vkCmdClearColorImage(s->graphicsBuffer, target->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearcol, 1, &isubr); + + // transition the rendertarget to be a color attachment + rendering_transitionImageLayoutCB(s->graphicsBuffer, target->image, s->surfaceformat.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); +} + +void render_copyToSwap(struct rendering_state *s, struct gpuimage *target, VkImage swimg){ + // setup the rendertarget and current swapchain image for the blit + rendering_transitionImageLayoutCB(s->graphicsBuffer, target->image, s->surfaceformat.format, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + rendering_transitionImageLayoutCB(s->graphicsBuffer, swimg, s->surfaceformat.format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // blit entire rendertarget to current swapchain image + VkImageBlit blit = { + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {{0,0,0}, {target->w, target->h, 1}}, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {{0,0,0}, {target->w, target->h, 1}}, + }; + + vkCmdBlitImage(s->graphicsBuffer, target->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swimg, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); + + // transition swapchain image back to be able to present + rendering_transitionImageLayoutCB(s->graphicsBuffer, swimg, s->surfaceformat.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); vkEndCommandBuffer(s->graphicsBuffer); +} +void render_waitForDevice(struct rendering_state *s){ + // wait until device has finished rendering the previous frame + vkWaitForFences(s->device, 1, &s->sync.frameFinishFence, 1, UINT32_MAX); + // reset the fence to reuse it. + vkResetFences(s->device, 1, &s->sync.frameFinishFence); +} + +void render_submitToDevice(struct rendering_state *s){ + // submit the cmdbuf VkPipelineStageFlags waitstages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, @@ -47,8 +103,12 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount 1, &s->sync.renderFinishSemaphore }; - result = vkQueueSubmit(s->queues.graphicsQueue, 1, &submitInfo, s->sync.frameFinishFence); + VkResult result = vkQueueSubmit(s->queues.graphicsQueue, 1, &submitInfo, s->sync.frameFinishFence); +} + +void render_present(struct rendering_state *s, uint32_t imageindex, VkResult nextImageResult){ + // present the newly created image VkPresentInfoKHR presentI = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, NULL, @@ -59,6 +119,7 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount &imageindex, NULL }; + vkQueuePresentKHR(s->queues.presentationQueue, &presentI); if(nextImageResult == VK_SUBOPTIMAL_KHR || s->shouldRecreateswapchainFlags){ @@ -67,6 +128,39 @@ void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount } } +void render(struct rendering_state *s, struct gpuimage *target, struct renderData *obj, uint32_t objcount, struct camera *cam){ + VkResult nextImageResult = 0; + uint32_t imageindex = render_getSwapchainPre(s, &nextImageResult); + + render_beginCmdBuf(s); + + render_clearBg(s, target); + + VkExtent2D imageextent = {target->w, target->h}; + + // do the rendering + recordCommandBuffer( + s->graphicsBuffer, + s->pipeline.renderpass, + target->view, + imageextent, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + s, + &s->pipeline, + obj, + objcount, + cam); + + + render_copyToSwap(s, target, s->swapchainImages[imageindex]); + + render_waitForDevice(s); + + render_submitToDevice(s); + + render_present(s, imageindex, nextImageResult); +} + void recreateSwapchain(struct rendering_state *s){ if(s->shouldRecreateswapchainFlags & RNDR_RECREATE_SWAPCHAIN_USE_TMP){ s->window_width = s->tmp_window_width; diff --git a/src/rendering/rendering.h b/src/rendering/rendering.h index 0a5bad8..e916272 100644 --- a/src/rendering/rendering.h +++ b/src/rendering/rendering.h @@ -4,7 +4,7 @@ #include "pipeline3d/data.h" #include "camera.h" -void render(struct rendering_state *s, struct renderData *obj, uint32_t objcount, struct camera *cam); +void render(struct rendering_state *s, struct gpuimage *target, struct renderData *obj, uint32_t objcount, struct camera *cam); void recreateSwapchain(struct rendering_state *s); diff --git a/src/rendering/struct.h b/src/rendering/struct.h index 8210ad8..446e06d 100644 --- a/src/rendering/struct.h +++ b/src/rendering/struct.h @@ -3,6 +3,8 @@ #include "camera.h" #include "pipeline3d/pipeline.h" +#include "pipelineText/pipeline.h" +#include "vulkan/gpumem_types.h" #include #include #include @@ -82,11 +84,14 @@ struct rendering_state{ VkGraphicsPipelineCreateInfo *gptemplate; struct pipeline3d pipeline; + struct pipelineText pipelinetext; VkCommandPool graphicsPool; VkCommandBuffer graphicsBuffer; VkCommandBuffer transferBuffer; + struct gpuimage renderTarget; + struct rendering_sync sync; uint32_t shouldRecreateswapchainFlags; diff --git a/src/rendering/vulkan/gpumem.c b/src/rendering/vulkan/gpumem.c index 0200e0c..dd8f4c2 100644 --- a/src/rendering/vulkan/gpumem.c +++ b/src/rendering/vulkan/gpumem.c @@ -19,6 +19,7 @@ void rendering_createBuffer(struct gpubuffer *buf, VkBufferUsageFlagBits usage, NULL }; + printf("haifisch %lu\n", buf->size); vkCreateBuffer(s->device, &bufferCI, NULL, &buf->buffer); VkMemoryRequirements memreq; @@ -177,15 +178,7 @@ void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uin rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); } -void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new, struct rendering_state *s){ - VkCommandBufferBeginInfo begininfo = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - NULL, - 0, - NULL - }; - vkBeginCommandBuffer(s->transferBuffer, &begininfo); - +void rendering_transitionImageLayoutCB(VkCommandBuffer buf, VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new){ VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, @@ -220,9 +213,44 @@ void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout ol srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else if(old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR){ + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = 0; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } else if(old == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && new == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL){ + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if(old == VK_IMAGE_LAYOUT_UNDEFINED && new == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL){ + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } else if(old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL){ + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; } - vkCmdPipelineBarrier(s->transferBuffer, srcStage, dstStage, 0, 0, NULL, 0, NULL, 1, &barrier); + vkCmdPipelineBarrier(buf, srcStage, dstStage, 0, 0, NULL, 0, NULL, 1, &barrier); +} +void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout new, struct rendering_state *s){ + VkCommandBufferBeginInfo begininfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + NULL, + 0, + NULL + }; + vkBeginCommandBuffer(s->transferBuffer, &begininfo); + + rendering_transitionImageLayoutCB(s->transferBuffer, img, fmt, old, new); rendering_endSubmitWait(s->transferBuffer, s->queues.transferQueue); } @@ -232,6 +260,7 @@ void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t stagebuf.size = datalen; img->size = datalen; // create and fill the staging buffer + printf("besonderer haifisch\n"); rendering_createBuffer(&stagebuf, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, s); rendering_putBuffer(&stagebuf, data, s); @@ -244,4 +273,16 @@ void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t rendering_transitionImageLayout(img->image, fmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, s); rendering_destroyBuffer(&stagebuf, s); -}; \ No newline at end of file +}; + +void rendering_destroyImage(struct gpuimage *img, struct rendering_state *s){ + if(img->sampler) vkDestroySampler(s->device, img->sampler, NULL); + if(img->view) vkDestroyImageView(s->device, img->view, NULL); + vkDestroyImage(s->device, img->image, NULL); + vkFreeMemory(s->device, img->memory, NULL); + img->size = 0; + img->w = 0; + img->h = 0; + img->d = 0; + img->fmt = 0; +} \ No newline at end of file diff --git a/src/rendering/vulkan/gpumem.h b/src/rendering/vulkan/gpumem.h index 290c8e4..98ecf2a 100644 --- a/src/rendering/vulkan/gpumem.h +++ b/src/rendering/vulkan/gpumem.h @@ -19,11 +19,17 @@ static inline void rendering_destroyBuffer(struct gpubuffer *b, struct rendering vkFreeMemory(s->device, b->memory, NULL); } +void rendering_createImage( + uint32_t w, uint32_t h, uint32_t d, VkImageType type, VkFormat fmt, uint32_t mipLevels, VkImageTiling tiling, + VkImageUsageFlags usageflags, VkMemoryPropertyFlags properties, struct gpuimage *img, struct rendering_state *s); + void rendering_copyImageB(VkBuffer src, VkImage img, uint32_t w, uint32_t h, uint32_t d, VkImageAspectFlags aspect, struct rendering_state *s); +void rendering_transitionImageLayoutCB(VkCommandBuffer buf, VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout newlayout); void rendering_transitionImageLayout(VkImage img, VkFormat fmt, VkImageLayout old, VkImageLayout newlayout, struct rendering_state *s); void rendering_createImageForRender(uint32_t w, uint32_t h, void *data, uint64_t datalen, VkFormat fmt, struct gpuimage *img, struct rendering_state *s); +void rendering_destroyImage(struct gpuimage *img, struct rendering_state *s); //void rendering_createImage(); #endif \ No newline at end of file diff --git a/src/rendering/vulkan/instance.c b/src/rendering/vulkan/instance.c index 946b9b6..1b1c4e4 100644 --- a/src/rendering/vulkan/instance.c +++ b/src/rendering/vulkan/instance.c @@ -8,7 +8,18 @@ VkResult svlk_createIinstance(const char **layers, int layersNum, const char **e createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pNext = NULL; createInfo.flags = 0; - createInfo.pApplicationInfo = NULL; + + VkApplicationInfo appinfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, + NULL, + NULL, + 0, + NULL, + 0, + VK_API_VERSION_1_4 + }; + + createInfo.pApplicationInfo = &appinfo; createInfo.enabledLayerCount = layersNum; createInfo.ppEnabledLayerNames = layers; createInfo.enabledExtensionCount = extensionsNum; @@ -113,12 +124,13 @@ VkResult svlk_createLogicalDevice( VkPhysicalDeviceFeatures deviceFeatures, const char **deviceExtensions, int deviceExtensionsNum, + void *pNext, VkDevice *device ){ VkDeviceCreateInfo createInfo = {0}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pNext = NULL; + createInfo.pNext = pNext; createInfo.flags = 0; createInfo.queueCreateInfoCount = queueFamiliesNum; createInfo.pQueueCreateInfos = queueFamilies; diff --git a/src/rendering/vulkan/svulc.h b/src/rendering/vulkan/svulc.h index 8a61ea4..267c3ad 100644 --- a/src/rendering/vulkan/svulc.h +++ b/src/rendering/vulkan/svulc.h @@ -23,6 +23,7 @@ VkResult svlk_createLogicalDevice( VkPhysicalDeviceFeatures deviceFeatures, const char **deviceExtensions, int deviceExtensionsNum, + void *pNext, VkDevice *device); #endif \ No newline at end of file