From 9ab1361fbd05f685f978664c10e212059cdb93be Mon Sep 17 00:00:00 2001 From: Marvin Blum Date: Thu, 10 Jun 2021 23:47:41 +0200 Subject: [PATCH] Added swap chain. --- _go/01_Triangle/main.go | 346 ---------------------------------------- _go/go.mod | 8 - _go/go.sum | 4 - run.sh | 2 + src/main.c | 167 +++++++++++++++++-- src/util.c | 9 ++ src/util.h | 10 ++ 7 files changed, 175 insertions(+), 371 deletions(-) delete mode 100644 _go/01_Triangle/main.go delete mode 100644 _go/go.mod delete mode 100644 _go/go.sum create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/_go/01_Triangle/main.go b/_go/01_Triangle/main.go deleted file mode 100644 index 4f9c20e..0000000 --- a/_go/01_Triangle/main.go +++ /dev/null @@ -1,346 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "github.com/go-gl/glfw/v3.3/glfw" - vk "github.com/vulkan-go/vulkan" - "log" - "runtime" -) - -var ( - vkInstance vk.Instance - vkPhysicalDevice vk.PhysicalDevice - vkDevice vk.Device - vkGraphicsQueue vk.Queue - vkPresentQueue vk.Queue - vkSurface vk.Surface - vkSwapchain vk.Swapchain - vkSwapchainImages []vk.Image - vkSwapchainImageViews []vk.ImageView -) - -func init() { - runtime.LockOSThread() -} - -func createWindow(title string, width, height int) (*glfw.Window, error) { - if err := glfw.Init(); err != nil { - return nil, err - } - - if !glfw.VulkanSupported() { - return nil, errors.New("vulkan not support") - } - - glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI) - window, err := glfw.CreateWindow(width, height, title, nil, nil) - - if err != nil { - return nil, err - } - - window.SetSizeLimits(width, height, width, height) - return window, nil -} - -func destroyWindow(window *glfw.Window) { - window.Destroy() - glfw.Terminate() -} - -func initVk(window *glfw.Window, width, height uint32) error { - log.Println("Initializing vulkan...") - vk.SetGetInstanceProcAddr(glfw.GetVulkanGetInstanceProcAddress()) - - if err := vk.Init(); err != nil { - return err - } - - // create vulkan instance - extensions := window.GetRequiredInstanceExtensions() - - if result := vk.CreateInstance(&vk.InstanceCreateInfo{ - SType: vk.StructureTypeInstanceCreateInfo, - PApplicationInfo: &vk.ApplicationInfo{ - SType: vk.StructureTypeApplicationInfo, - PApplicationName: "01 Triangle", - ApiVersion: vk.ApiVersion11, - }, - EnabledExtensionCount: uint32(len(extensions)), - PpEnabledExtensionNames: extensions, - }, nil, &vkInstance); result != vk.Success { - return errors.New(fmt.Sprintf("vulkan: error creating instance: %d", result)) - } - - // create surface - surface, err := window.CreateWindowSurface(vkInstance, nil) - - if err != nil { - return errors.New(fmt.Sprintf("vulkan: error creating surface: %s", err)) - } - - vkSurface = vk.SurfaceFromPointer(surface) - - // select the first device with vulkan support - var deviceCount uint32 - - if result := vk.EnumeratePhysicalDevices(vkInstance, &deviceCount, nil); result != vk.Success { - return errors.New(fmt.Sprintf("vulkan: error counting devices: %d", result)) - } - - if deviceCount == 0 { - return errors.New("vulkan: no device with vulkan support found") - } - - log.Printf("vulkan: number of physical devices: %d", deviceCount) - devices := make([]vk.PhysicalDevice, deviceCount) - - if result := vk.EnumeratePhysicalDevices(vkInstance, &deviceCount, devices); result != vk.Success { - return errors.New(fmt.Sprintf("vulkan: error listing devices: %d", result)) - } - - for _, d := range devices { - vkPhysicalDevice = d - break // TODO check suitability - } - - if vkPhysicalDevice == nil { - return errors.New("vulkan: no suitable device found") - } - - // find a graphics and presentation queue - var queueCount uint32 - vk.GetPhysicalDeviceQueueFamilyProperties(vkPhysicalDevice, &queueCount, nil) - queueProperties := make([]vk.QueueFamilyProperties, queueCount) - vk.GetPhysicalDeviceQueueFamilyProperties(vkPhysicalDevice, &queueCount, queueProperties) - var presentQueueIndex, graphicsQueueIndex uint32 - foundPresentQueue, foundGraphicsQueue := false, false - - for i := range queueProperties { - queueProperties[i].Deref() - - if !foundPresentQueue { - var presentSupport vk.Bool32 - vk.GetPhysicalDeviceSurfaceSupport(vkPhysicalDevice, uint32(i), vkSurface, &presentSupport) - - if presentSupport == 1 { - presentQueueIndex = uint32(i) - foundPresentQueue = true - - if foundGraphicsQueue { - break - } - } - } - - if !foundGraphicsQueue && queueProperties[i].QueueFlags&vk.QueueFlags(vk.QueueGraphicsBit) != 0 { - graphicsQueueIndex = uint32(i) - foundGraphicsQueue = true - - if foundPresentQueue { - break - } - } - } - - if !foundPresentQueue { - return errors.New("vulkan: no present queue found") - } - - if !foundGraphicsQueue { - return errors.New("vulkan: no graphics queue found") - } - - // create a logical device and queues - queues := []vk.DeviceQueueCreateInfo{ - { - SType: vk.StructureTypeDeviceQueueCreateInfo, - QueueFamilyIndex: graphicsQueueIndex, - QueueCount: 1, - PQueuePriorities: []float32{1}, - }, - } - - if result := vk.CreateDevice(vkPhysicalDevice, &vk.DeviceCreateInfo{ - SType: vk.StructureTypeDeviceCreateInfo, - QueueCreateInfoCount: 1, - PQueueCreateInfos: queues, - }, nil, &vkDevice); result != vk.Success { - return errors.New(fmt.Sprintf("vulkan: error creating logical device: %d", result)) - } - - vk.GetDeviceQueue(vkDevice, graphicsQueueIndex, 0, &vkGraphicsQueue) - vk.GetDeviceQueue(vkDevice, presentQueueIndex, 0, &vkPresentQueue) - - // create swap chain - var surfaceCapabilities vk.SurfaceCapabilities - - if result := vk.GetPhysicalDeviceSurfaceCapabilities(vkPhysicalDevice, vkSurface, &surfaceCapabilities); result != vk.Success { - return errors.New(fmt.Sprintf("vulkan: error reading device surface capabilities: %d", result)) - } - - surfaceCapabilities.Deref() - var swapchainFormatCount uint32 - vk.GetPhysicalDeviceSurfaceFormats(vkPhysicalDevice, vkSurface, &swapchainFormatCount, nil) - - if swapchainFormatCount == 0 { - return errors.New("vulkan: no surface formats present") - } - - formats := make([]vk.SurfaceFormat, swapchainFormatCount) - vk.GetPhysicalDeviceSurfaceFormats(vkPhysicalDevice, vkSurface, &swapchainFormatCount, formats) - var swapchainFormat vk.SurfaceFormat - - for i := range formats { - formats[i].Deref() - swapchainFormat = formats[i] - break // TODO - } - - desiredSwapchainImages := surfaceCapabilities.MinImageCount + 1 - - if surfaceCapabilities.MaxImageCount > 0 && desiredSwapchainImages > surfaceCapabilities.MaxImageCount { - desiredSwapchainImages = surfaceCapabilities.MaxImageCount - } - - var swapchainPreTransform vk.SurfaceTransformFlagBits - requiredTransforms := vk.SurfaceTransformIdentityBit - supportedTransforms := surfaceCapabilities.SupportedTransforms - - if vk.SurfaceTransformFlagBits(supportedTransforms)&requiredTransforms != 0 { - swapchainPreTransform = requiredTransforms - } else { - swapchainPreTransform = surfaceCapabilities.CurrentTransform - } - - swapchainCompositeAlpha := vk.CompositeAlphaOpaqueBit - swapchainCompositeAlphaFlags := []vk.CompositeAlphaFlagBits{ - vk.CompositeAlphaOpaqueBit, - vk.CompositeAlphaPreMultipliedBit, - vk.CompositeAlphaPostMultipliedBit, - vk.CompositeAlphaInheritBit, - } - - for i := range swapchainCompositeAlphaFlags { - alphaFlags := vk.CompositeAlphaFlags(swapchainCompositeAlphaFlags[i]) - - if surfaceCapabilities.SupportedCompositeAlpha&alphaFlags != 0 { - swapchainCompositeAlpha = swapchainCompositeAlphaFlags[i] - break - } - } - - surfaceCapabilities.CurrentExtent.Deref() - var swapchainSize vk.Extent2D - - if surfaceCapabilities.CurrentExtent.Width == vk.MaxUint32 { - swapchainSize.Width = width - swapchainSize.Height = height - } else { - swapchainSize = surfaceCapabilities.CurrentExtent - } - - if result := vk.CreateSwapchain(vkDevice, &vk.SwapchainCreateInfo{ - SType: vk.StructureTypeSwapchainCreateInfo, - Surface: vkSurface, - MinImageCount: desiredSwapchainImages, - ImageFormat: swapchainFormat.Format, - ImageColorSpace: swapchainFormat.ColorSpace, - ImageExtent: vk.Extent2D{ - Width: swapchainSize.Width, - Height: swapchainSize.Height, - }, - ImageArrayLayers: 1, - ImageUsage: vk.ImageUsageFlags(vk.ImageUsageColorAttachmentBit), - ImageSharingMode: vk.SharingModeExclusive, - PreTransform: swapchainPreTransform, - CompositeAlpha: swapchainCompositeAlpha, - PresentMode: vk.PresentModeFifo, - Clipped: vk.True, - OldSwapchain: vk.NullSwapchain, - }, nil, &vkSwapchain); result != vk.Success { - return errors.New(fmt.Sprintf("error creating vulkan swap chain: %d", result)) - } - - /*var imgCount uint32 - vk.GetSwapchainImages(vkDevice, vkSwapchain, &imgCount, nil) - result = vk.GetSwapchainImages(vkDevice, vkSwapchain, &imgCount, vkSwapchainImages) - - if result != vk.Success { - return errors.New("error getting vulkan swap chain images") - } - - // create image views for the swap chain images - vkSwapchainImageViews = make([]vk.ImageView, 0, imgCount) - - for i, img := range vkSwapchainImages { - result = vk.CreateImageView(vkDevice, &vk.ImageViewCreateInfo{ - SType: vk.StructureTypeImageViewCreateInfo, - Image: img, - ViewType: vk.ImageViewType2d, - Format: vk.FormatB8g8r8a8Uint, - Components: vk.ComponentMapping{ - R: vk.ComponentSwizzleR, - G: vk.ComponentSwizzleG, - B: vk.ComponentSwizzleB, - A: vk.ComponentSwizzleA, - }, - SubresourceRange: vk.ImageSubresourceRange{ - AspectMask: vk.ImageAspectFlags(vk.ImageAspectColorBit), - BaseMipLevel: uint32(0), - LevelCount: uint32(1), - BaseArrayLayer: uint32(0), - LayerCount: uint32(1), - }, - }, nil, &vkSwapchainImageViews[i]) - - if result != vk.Success { - return errors.New("error creating swap chain image view") - } - }*/ - - log.Println("Done initializing vulkan...") - return nil -} - -func cleanupVk() { - /*for _, img := range vkSwapchainImageViews { - vk.DestroyImageView(vkDevice, img, nil) - } - - vk.DestroySwapchain(vkDevice, vkSwapchain, nil)*/ - vk.DestroyDevice(vkDevice, nil) - vk.DestroySurface(vkInstance, vkSurface, nil) - vk.DestroyInstance(vkInstance, nil) -} - -func load() { - -} - -func loop(window *glfw.Window) { - for !window.ShouldClose() { - // ... - glfw.PollEvents() - } -} - -func main() { - window, err := createWindow("01 Triangle", 640, 480) - - if err != nil { - log.Fatalf("error creating window: %s", err) - } - - defer destroyWindow(window) - - if err := initVk(window, 640, 480); err != nil { - log.Fatalf("error initializing vulkan: %s", err) - } - - defer cleanupVk() - load() - loop(window) -} diff --git a/_go/go.mod b/_go/go.mod deleted file mode 100644 index b8260b5..0000000 --- a/_go/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/Kugelschieber/vk-experiments - -go 1.16 - -require ( - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb // indirect - github.com/vulkan-go/vulkan v0.0.0-20210402152248-956e3850d8f9 // indirect -) diff --git a/_go/go.sum b/_go/go.sum deleted file mode 100644 index 6ec70b8..0000000 --- a/_go/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb h1:T6gaWBvRzJjuOrdCtg8fXXjKai2xSDqWTcKFUPuw8Tw= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/vulkan-go/vulkan v0.0.0-20210402152248-956e3850d8f9 h1:WFujQpkMAAd8dqccEm10n8dly4yQ/R5d2+Us7GutowA= -github.com/vulkan-go/vulkan v0.0.0-20210402152248-956e3850d8f9/go.mod h1:Y5Ti1uUBdKDsb0W8aPtIo9krs+29Y7p6Bc9yyy4AM6g= diff --git a/run.sh b/run.sh index 980e7ca..03e80a0 100755 --- a/run.sh +++ b/run.sh @@ -3,12 +3,14 @@ echo "Building..." rm -rf ./build mkdir -p ./build +gcc -c src/util.c -o build/util.o || exit gcc -c src/log.c -o build/log.o || exit gcc `pkg-config --static --libs glfw3` -c src/window.c -o build/window.o || exit gcc `pkg-config --static --libs glfw3` \ `pkg-config --static --libs vulkan` \ build/log.o \ build/window.o \ + build/util.o \ -o main src/main.c || exit echo "Done. Starting app..." ./main diff --git a/src/main.c b/src/main.c index 68ee68d..965f01b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include "util.h" #include "log.h" #include "window.h" @@ -10,18 +12,23 @@ typedef struct { VkInstance instance; VkPhysicalDevice physicalDevice; VkDevice device; - VkQueue graphicsQueue; VkSurfaceKHR surface; + VkQueue graphicsQueue; + VkQueue presentQueue; + VkSwapchainKHR swapChain; } VKEContext; typedef struct { const char* title; - int validationLayerCount; + uint32_t validationLayerCount; const char** validationLayers; + uint32_t deviceExtensionCount; + const char** deviceExtensions; } VKEConfig; typedef struct { uint32_t graphicsQueueFamily; + uint32_t presentQueueFamily; } VKEQueueFamilyIndices; bool vkeCheckValidationLayerSupport(const char** validationLayers, int n) { @@ -97,12 +104,21 @@ int vkeFindQueueFamilies(VKEContext* ctx, VKEQueueFamilyIndices* indices) { VkQueueFamilyProperties queueFamilies[queueFamilyCount]; vkGetPhysicalDeviceQueueFamilyProperties(ctx->physicalDevice, &queueFamilyCount, queueFamilies); bool foundGraphicsQueue = false; + bool foundPresentQueue = false; for(int i = 0; i < queueFamilyCount; i++) { if(queueFamilies[i].queueFlags&VK_QUEUE_GRAPHICS_BIT) { indices->graphicsQueueFamily = i; foundGraphicsQueue = true; } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(ctx->physicalDevice, i, ctx->surface, &presentSupport); + + if(presentSupport) { + indices->presentQueueFamily = i; + foundPresentQueue = true; + } } if(!foundGraphicsQueue) { @@ -110,24 +126,38 @@ int vkeFindQueueFamilies(VKEContext* ctx, VKEQueueFamilyIndices* indices) { return -1; } + if(!foundPresentQueue) { + vkeLogError("present queue family index not found"); + return -1; + } + return 0; } int vkeCreateLogicalDeviceAndQueues(VKEContext* ctx, VKEConfig* config, VKEQueueFamilyIndices* queueFamilies) { - VkDeviceQueueCreateInfo queueCreateInfo = { - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .queueFamilyIndex = queueFamilies->graphicsQueueFamily, - .queueCount = 1 - }; float queuePriority = 1.0f; - queueCreateInfo.pQueuePriorities = &queuePriority; + VkDeviceQueueCreateInfo queueCreateInfo[] = { + { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = queueFamilies->graphicsQueueFamily, + .queueCount = 1, + .pQueuePriorities = &queuePriority + }, + { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = queueFamilies->presentQueueFamily, + .queueCount = 1, + .pQueuePriorities = &queuePriority + } + }; //VkPhysicalDeviceFeatures deviceFeatures; TODO VkDeviceCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pQueueCreateInfos = &queueCreateInfo, - .queueCreateInfoCount = 1, + .pQueueCreateInfos = queueCreateInfo, + .queueCreateInfoCount = 2, .pEnabledFeatures = VK_NULL_HANDLE, - .enabledExtensionCount = 0 + .enabledExtensionCount = config->deviceExtensionCount, + .ppEnabledExtensionNames = config->deviceExtensions }; if(config->validationLayerCount > 0) { @@ -143,6 +173,107 @@ int vkeCreateLogicalDeviceAndQueues(VKEContext* ctx, VKEConfig* config, VKEQueue } vkGetDeviceQueue(ctx->device, queueFamilies->graphicsQueueFamily, 0, &ctx->graphicsQueue); + vkGetDeviceQueue(ctx->device, queueFamilies->presentQueueFamily, 0, &ctx->presentQueue); + return 0; +} + +int vkeCreateSwapChain(VKEContext* ctx, VKEQueueFamilyIndices* queueFamilies, GLFWwindow* window) { + // collect surface capabilities + VkSurfaceCapabilitiesKHR capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(ctx->physicalDevice, ctx->surface, &capabilities); + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->physicalDevice, ctx->surface, &formatCount, NULL); + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->physicalDevice, ctx->surface, &presentModeCount, NULL); + + if(formatCount == 0) { + vkeLogError("no color formats available"); + return -1; + } + + if(presentModeCount == 0) { + vkeLogError("no present modes available"); + return -2; + } + + VkSurfaceFormatKHR formats[formatCount]; + vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->physicalDevice, ctx->surface, &formatCount, formats); + VkPresentModeKHR presentModes[presentModeCount]; + vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->physicalDevice, ctx->surface, &presentModeCount, presentModes); + + // select the color format + VkSurfaceFormatKHR format = formats[0]; + + for(int i = 0; i < formatCount; i++) { + if(formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + format = formats[i]; + break; + } + } + + // select present mode + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + + for(int i = 0; i < presentModeCount; i++) { + if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + presentMode = presentModes[i]; + break; + } + } + + // choose extent + VkExtent2D extent; + + if(capabilities.currentExtent.width != UINT32_MAX) { + extent = capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + VkExtent2D actualExtent = {width, height}; + actualExtent.width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, actualExtent.height)); + extent = actualExtent; + } + + // select the image count + uint32_t imageCount = capabilities.minImageCount + 1; + + if(capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) { + imageCount = capabilities.maxImageCount; + } + + // create the swap chain + VkSwapchainCreateInfoKHR createInfo = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = ctx->surface, + .minImageCount = imageCount, + .imageFormat = format.format, + .imageColorSpace = format.colorSpace, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .preTransform = capabilities.currentTransform, + .presentMode = presentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE + }; + + if(queueFamilies->graphicsQueueFamily != queueFamilies->presentQueueFamily) { + const uint32_t indices[] = {queueFamilies->graphicsQueueFamily, queueFamilies->presentQueueFamily}; + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = indices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = NULL; + } + + if(vkCreateSwapchainKHR(ctx->device, &createInfo, NULL, &ctx->swapChain) != VK_SUCCESS) { + return -3; + } + return 0; } @@ -185,6 +316,10 @@ int vkeInit(VKEContext* ctx, VKEConfig* config, GLFWwindow* window) { return -3; } + if(vkeCreateWindowSurface(ctx->instance, window, &ctx->surface) != 0) { + return -6; + } + VKEQueueFamilyIndices queueFamilies; if(vkeFindQueueFamilies(ctx, &queueFamilies) != 0) { @@ -195,7 +330,7 @@ int vkeInit(VKEContext* ctx, VKEConfig* config, GLFWwindow* window) { return -5; } - if(vkeCreateWindowSurface(ctx->instance, window, &ctx->surface) != 0) { + if(vkeCreateSwapChain(ctx, &queueFamilies, window) != 0) { return -6; } @@ -203,6 +338,7 @@ int vkeInit(VKEContext* ctx, VKEConfig* config, GLFWwindow* window) { } void vkeDestroy(VKEContext* ctx) { + vkDestroySwapchainKHR(ctx->device, ctx->swapChain, NULL); vkDestroySurfaceKHR(ctx->instance, ctx->surface, NULL); vkDestroyDevice(ctx->device, NULL); vkDestroyInstance(ctx->instance, NULL); @@ -228,10 +364,15 @@ int main(int argc, const char *argv[]) { const char* validationLayers[] = { "VK_LAYER_KHRONOS_validation" }; + const char* deviceExtensions[] = { + "VK_KHR_swapchain" + }; VKEConfig config = { .title = title, .validationLayerCount = 1, - .validationLayers = validationLayers + .validationLayers = validationLayers, + .deviceExtensionCount = 1, + .deviceExtensions = deviceExtensions }; if(vkeInit(&ctx, &config, window) != 0) { diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..4c6f671 --- /dev/null +++ b/src/util.c @@ -0,0 +1,9 @@ +#include "util.h" + +int min(int a, int b) { + return a > b ? b : a; +} + +int max(int a, int b) { + return a > b ? a : b; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..5b99500 --- /dev/null +++ b/src/util.h @@ -0,0 +1,10 @@ +#ifndef VKE_UTIL_H +#define VKE_UTIL_H + +// Returns the smaller value of the parameters. +int min(int a, int b); + +// Returns the bigger value of the parameters. +int max(int a, int b); + +#endif