From 027a512c762e0ac5e46908f533b94127010de565 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Fri, 25 Oct 2019 16:53:00 -0500 Subject: Initial commit --- bk.c | 754 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 754 insertions(+) create mode 100644 bk.c (limited to 'bk.c') diff --git a/bk.c b/bk.c new file mode 100644 index 0000000..6b669f4 --- /dev/null +++ b/bk.c @@ -0,0 +1,754 @@ +#include +#include + +#include +#include +#include + +#define ERRFQ(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + exit(1); \ + } while(0) + +#define ERRQ(str) \ + do { \ + fputs((str), stderr); \ + fputc('\n', stderr); \ + exit(1); \ + } while(0) + +uint8_t *read_file(const char *path, size_t size) +{ + FILE *file = fopen(path, "rb"); + if (!file) { + return NULL; + } + fseek(file, 0, SEEK_END); + size_t len = ftell(file); + rewind(file); + uint8_t *buf = malloc(len); + fread(buf, len, 1, file); + fclose(file); + *size = len; + return buf; +} + +/* + * Modifications for Vulkan: + * Row-column flipped + * Negative y + * Expect coordinates on range [0, 1] + */ +void perspective_init(float matrix[4][4], + const float fov, + const float aspect, + const float znear, + const float zfar) +{ + memset(matrix, 0, 16*sizeof(float)); + const float f = 1.0f / tanf(0.5*fov); + matrix[0][0] = f / aspect; + matrix[1][1] = -f; + matrix[2][2] = -znear/(zfar - znear) - 1.0f; + matrix[3][2] = -zfar*znear/(zfar - znear); + matrix[2][3] = -1.0f; +} + +void identity_init(float matrix[4][4]) +{ + memset(matrix, 0, 16*sizeof(float)); + matrix[0][0] = 1.0f; + matrix[1][1] = 1.0f; + matrix[2][2] = 1.0f; + matrix[3][3] = 1.0f; +} + +struct Uniform { + float model[4][4]; + float view[4][4]; + float proj[4][4]; +}; + +static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objType, + uint64_t obj, + size_t location, + int32_t code, + const char *layerPrefix, + const char *msg, + void *userData + ) +{ + fprintf(stderr, "Validation layer: %s\n", msg); + return VK_FALSE; +} + +VkResult CreateDebugReportCallbackEXT( + VkInstance instance, + const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkDebugReportCallbackEXT *pCallback + ) +{ + PFN_vkCreateDebugReportCallbackEXT func = (void*)vkGetInstanceProcAddr(instance, + "vkCreateDebugReportCallbackEXT"); + if (func) { + return func(instance, pCreateInfo, pAllocator, pCallback); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, + const VkAllocationCallbacks *pAllocator) +{ + PFN_vkDestroyDebugReportCallbackEXT func = (void*)vkGetInstanceProcAddr(instance, + "vkDestroyDebugReportCallbackEXT"); + if (func) { + func(instance, callback, pAllocator); + } +} + +SDL_Window *win; +VkInstance instance; +VkDebugReportCallbackEXT callback; +VkSurfaceKHR surface; +VkPhysicalDevice physicalDevice; +uint32_t qIndex; +uint32_t nImages; +VkDevice device; +VkSurfaceFormatKHR format; +VkCommandPool cmdPool; +VkCommandBuffer cmdBuffers; +VkSwapchainKHR swapchain; +VkImage *scImages; +VkImageView *scImageViews; +VkImage depthImage; +VkDeviceMemory depthMem; +VkImageView depthView; +VkExtent2D extent; +VkQueue graphicsQueue; +VkQueue presentQueue; + +static void createInstance() +{ + VkApplicationInfo appInfo = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "VulkanGame", + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "VulkanGameEngine", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_0 + }; + + unsigned i, nRequired; + if (!SDL_Vulkan_GetInstanceExtensions(win, &nRequired, NULL)) { + ERRFQ("Could not get number of instance extensions\n"); + } + static const char *const additional[] = { + VK_EXT_DEBUG_REPORT_EXTENSION_NAME + }; + const unsigned nAdditional = sizeof(additional) / sizeof(additional[0]); + const uint32_t nExtensions = nRequired + nAdditional; + const char **extensions = malloc(sizeof(const char*) * nExtensions); + if (!SDL_Vulkan_GetInstanceExtensions(win, &nRequired, extensions)) { + /* OS will handle memory freeing */ + ERRQ(SDL_GetError()); + } + for (i = 0; i < nAdditional; ++i) { + extensions[nRequired + i] = additional[i]; + } + + /* Just printing verbosely */ + for (i = 0; i < nExtensions; ++i) { + printf("%s\n", extensions[i]); + } + printf("\n"); + +#if 1 + const uint32_t nLayers = 1; + static const char *const layers[] = { + "VK_LAYER_LUNARG_standard_validation" + }; +#endif + VkInstanceCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &appInfo, + .enabledExtensionCount = nExtensions, + .ppEnabledExtensionNames = extensions, +#if 1 + .enabledLayerCount = nLayers, + .ppEnabledLayerNames = layers +#endif + }; + VkResult result; + if ((result = vkCreateInstance(&createInfo, NULL, &instance)) != VK_SUCCESS) { + /* OS will handle memory freeing */ + ERRFQ("Could not create Vulkan instance: %d\n", result); + } + free(extensions); +} + +static void createDebugReporter() +{ + VkDebugReportCallbackCreateInfoEXT createInfo = { + .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + .flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, + .pfnCallback = debugCallback + }; + if (CreateDebugReportCallbackEXT(instance, &createInfo, NULL, &callback) != VK_SUCCESS) { + ERRQ("Could not create debug reporter"); + } +} + +static void selectPhysicalDevice(const char *hint) +{ + uint32_t nDevices; + vkEnumeratePhysicalDevices(instance, &nDevices, NULL); + if (!nDevices) { + ERRQ("No physical devices found"); + } + VkPhysicalDevice *devices = malloc(nDevices * sizeof(*devices)); + vkEnumeratePhysicalDevices(instance, &nDevices, devices); + + /* Just printing verbosely */ + for (uint32_t i = 0; i < nDevices; ++i) { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(devices[i], &props); + printf("%s\n", props.deviceName); + } + + /* FIXME: Blindly choosing the first device */ + physicalDevice = devices[0]; + + uint32_t nQueueFamilies = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &nQueueFamilies, NULL); + VkQueueFamilyProperties *qprops = malloc(nQueueFamilies * sizeof(*qprops)); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &nQueueFamilies, qprops); + + for (qIndex = 0; qIndex < nQueueFamilies; ++qIndex) { + if (qprops[qIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + break; + } + } + if (qIndex >= nQueueFamilies || nQueueFamilies <= 0) { + ERRQ("No queue families capable of graphics"); + } + + VkBool32 canSurface; + vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, qIndex, surface, &canSurface); + if (!canSurface) { + /* Strange devices may be accommodated for but not for now */ + ERRQ("Graphics device cannot output a surface"); + } + + VkBool32 canSwapchain = 0; + uint32_t nExtensions; + vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &nExtensions, NULL); + VkExtensionProperties *availableExtensions = malloc(nExtensions * sizeof(*availableExtensions)); + vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &nExtensions, availableExtensions); + + for (uint32_t i = 0; i < nExtensions; ++i) { + if (!strcmp(availableExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { + canSwapchain = 1; + break; + } + } + if (!canSwapchain) { + ERRQ("Graphics device not swapchain capable"); + } + free(availableExtensions); + free(qprops); + free(devices); +} + +static void createLogicalDevice() +{ + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(physicalDevice, &features); + float qPriority = 1.0f; + VkDeviceQueueCreateInfo qCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = qIndex, + .queueCount = 1, + .pQueuePriorities = &qPriority + }; + + const char *const swapchainName = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + VkDeviceCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pQueueCreateInfos = &qCreateInfo, + .queueCreateInfoCount = 1, + .pEnabledFeatures = &features, + .enabledExtensionCount = 1, + .ppEnabledExtensionNames = &swapchainName + }; + + if (vkCreateDevice(physicalDevice, &createInfo, NULL, &device)) { + ERRQ("Could not create logical device"); + } +} + +static void destroyLogicalDevice() +{ + vkDestroyDevice(device, NULL); +} + +static void createCommandPool() +{ + VkCommandPoolCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .queueFamilyIndex = qIndex + }; + VkResult result; + if ((result = vkCreateCommandPool(device, &createInfo, NULL, &cmdPool)) != VK_SUCCESS) { + ERRFQ("Could not create command pool: %d\n", result); + } +} + +static void destroyCommandPool() +{ + vkDestroyCommandPool(device, cmdPool, NULL); +} + +static void createCommandBuffers() +{ + VkCommandBufferAllocateInfo allocInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = cmdPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + VkResult result; + if ((result = vkAllocateCommandBuffers(device, &allocInfo, &cmdBuffers)) != VK_SUCCESS) { + ERRFQ("Could not create command buffers: %d\n", result); + } +} + +static void destroyCommandBuffers() +{ + vkFreeCommandBuffers(device, cmdPool, 1, &cmdBuffers); +} + +static void createSwapchain() +{ + struct { + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR *formats; + VkPresentModeKHR *presentModes; + } sc = {0}; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &sc.capabilities); + uint32_t nFormats; + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &nFormats, NULL); + if (!nFormats) { + ERRQ("No surface formats available"); + } + sc.formats = malloc(nFormats * sizeof(*sc.formats)); + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &nFormats, sc.formats); + + uint32_t nPresentModes; + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &nPresentModes, NULL); + if (!nPresentModes) { + ERRQ("No surface present modes available"); + } + sc.presentModes = malloc(nPresentModes * sizeof(*sc.presentModes)); + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &nPresentModes, sc.presentModes); + + for (uint32_t i = 0; i < nFormats; ++i) { + VkSurfaceFormatKHR want = { + VK_FORMAT_B8G8R8A8_UNORM, // BGRA + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + }; + if (sc.formats[i].format == VK_FORMAT_UNDEFINED) { + format = want; + fprintf(stderr, "Obtained wanted surface format on first try\n"); + break; + } + if (sc.formats[i].format == want.format && sc.formats[i].colorSpace == want.colorSpace) { + format = want; + fprintf(stderr, "Obtained wanted format on try %d/%d\n", i+1, nFormats); + break; + } + } + + /* FIXME: Just assuming this is available */ + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + + int width, height; + SDL_Vulkan_GetDrawableSize(win, &width, &height); + extent.width = width; + extent.height = height; + + nImages = sc.capabilities.minImageCount + 1; + if (sc.capabilities.maxImageCount > 0 && nImages > sc.capabilities.maxImageCount) { + /* Clamp to max image count if too many requested */ + nImages = sc.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = surface, + .minImageCount = nImages, + .imageFormat = format.format, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, // assuming same family + .preTransform = sc.capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = presentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE + }; + + VkResult result; + if ((result = vkCreateSwapchainKHR(device, &createInfo, NULL, &swapchain)) != VK_SUCCESS) { + ERRFQ("Could not create swapchain: %d\n", result); + } + free(sc.presentModes); + free(sc.formats); +} + +static void destroySwapchain() +{ + if (swapchain) { + vkDestroySwapchainKHR(device, swapchain, NULL); + } + swapchain = NULL; +} + +/* Allocates and reallocates memory */ +static void createSwapchainImages() +{ + vkGetSwapchainImagesKHR(device, swapchain, &nImages, NULL); + scImages = realloc(scImages, nImages * sizeof(*scImages)); + vkGetSwapchainImagesKHR(device, swapchain, &nImages, scImages); + scImageViews = realloc(scImageViews, nImages * sizeof(*scImageViews)); + + for (uint32_t i = 0; i < nImages; ++i) { + VkImageViewCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = scImages[i], + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format.format, + .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.baseMipLevel = 0, + .subresourceRange.levelCount = 1, + .subresourceRange.baseArrayLayer = 0, + .subresourceRange.layerCount = 1 + + }; + if (vkCreateImageView(device, &createInfo, NULL, &scImageViews[i]) != VK_SUCCESS) { + ERRQ("Could not create swapchain image views"); + } + } +} + +static void destroySwapchainImages() +{ + for (uint32_t i = 0; i < nImages; ++i) { + vkDestroyImageView(device, scImageViews[i], NULL); + } + free(scImageViews); + free(scImages); + scImageViews = NULL; + scImages = NULL; +} + +static void createDepthBuffer() +{ + VkImageCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_D32_SFLOAT, + .extent.width = extent.width, + .extent.height = extent.height, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + VkResult result; + if ((result = vkCreateImage(device, &createInfo, NULL, &depthImage)) != VK_SUCCESS) { + ERRFQ("Could not create depth image: %d\n", result); + } + VkPhysicalDeviceMemoryProperties memProps; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); + VkMemoryRequirements memReq; + vkGetImageMemoryRequirements(device, depthImage, &memReq); + uint32_t typeFilter = memReq.memoryTypeBits; + uint32_t memType; + uint32_t props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + for (memType = 0; memType < memProps.memoryTypeCount; ++memType) { + if (typeFilter & (1<