From 655964852cc17691f0e30cf9cac8bc5322d92f16 Mon Sep 17 00:00:00 2001 From: James Bulman Date: Fri, 3 Oct 2025 20:04:40 +0100 Subject: [PATCH] Red screen? Initalised vulkan for rendering bar some validation errors Added core types, platform detection and util macros --- code/core/macros.h | 15 ++ code/core/platform.h | 73 ++++++++ code/core/types.h | 32 ++++ code/first.c | 88 ++++++++++ code/vulkan/core.c | 364 ++++++++++++++++++++++++++++++++++++++++ code/vulkan/core.h | 80 +++++++++ code/vulkan/functions.h | 38 +++++ windows.bat | 11 +- 8 files changed, 698 insertions(+), 3 deletions(-) create mode 100644 code/core/macros.h create mode 100644 code/core/platform.h create mode 100644 code/core/types.h create mode 100644 code/vulkan/core.c create mode 100644 code/vulkan/core.h create mode 100644 code/vulkan/functions.h diff --git a/code/core/macros.h b/code/core/macros.h new file mode 100644 index 0000000..f1487db --- /dev/null +++ b/code/core/macros.h @@ -0,0 +1,15 @@ +#if !defined(LD_CORE_MACROS_H_) +#define LD_CORE_MACROS_H_ + +#define ArraySize(x) (sizeof(x) / sizeof((x)[0])) +#define Min(a, b) ((a) < (b) ? (a) : (b)) +#define Max(a, b) ((a) > (b) ? (a) : (b)) + +#define _Glue(a, b) a##b +#define _Stringify(x) #x + +#define Glue(a, b) _Glue(a, b) +#define Stringify(x) _Stringify(x) + + +#endif // LD_CORE_MACROS_H_ diff --git a/code/core/platform.h b/code/core/platform.h new file mode 100644 index 0000000..fb8d4ce --- /dev/null +++ b/code/core/platform.h @@ -0,0 +1,73 @@ +#if !defined(LD_CORE_PLATFORM_H_) +#define LD_CORE_PLATFORM_H_ + +#if !defined(LD_RELEASE) + #define LD_RELEASE 0 +#endif + +// -- Operating system + +#define OS_WINDOWS 0 +#define OS_LINUX 0 + +#if defined(_WIN32) + #undef OS_WINDOWS + #define OS_WINDOWS 1 +#elif defined(__linux__) + #undef OS_LINUX + #define OS_LINUX 1 +#else + #error "unsupported operating system" +#endif + +// -- Architecture + +#define ARCH_AMD64 0 + +#if defined(__amd64__) || defined(_M_AMD64) + #undef ARCH_AMD64 + #define ARCH_AMD64 1 +#else + #error "unsupported architecture" +#endif + +// -- Compiler + +#define COMPILER_CLANG 0 +#define COMPILER_CL 0 +#define COMPILER_GCC 0 + +#if defined(__clang__) + #undef COMPILER_CLANG + #define COMPILER_CLANG 1 +#elif defined(_MSC_VER) + #undef COMPILER_CL + #define COMPILER_CL 1 +#elif defined(__GNUC__) + #undef COMPILER_GCC + #define COMPILER_GCC 1 +#else + #error "unsupported compiler" +#endif + +// -- Language + +#define LANG_C 0 +#define LANG_CPP 0 + +#if defined(__OBJC__) + #error "unuspported language" +#elif defined(__cplusplus) + #undef LANG_CPP + #define LANG_CPP 1 +#else + #undef LANG_C + #define LANG_C 1 +#endif + +#if OS_WINDOWS + #define WIN32_LEAN_AND_MEAN 1 + #include +#endif + +#endif // LD_CORE_PLATFORM_H_ diff --git a/code/core/types.h b/code/core/types.h new file mode 100644 index 0000000..08436db --- /dev/null +++ b/code/core/types.h @@ -0,0 +1,32 @@ +#if !defined(LD_CORE_TYPES_H_) +#define LD_CORE_TYPES_H_ + +#include +#include +#include + +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; + +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; + +typedef int8_t B8; +typedef int16_t B16; +typedef int32_t B32; +typedef int64_t B64; + +typedef float F32; +typedef double F64; + +typedef struct Str8 Str8; +struct Str8 { + S64 count; + U8 *data; +}; + +#endif // LD_CORE_TYPES_H_ diff --git a/code/first.c b/code/first.c index 6a6e98b..08fe9c4 100644 --- a/code/first.c +++ b/code/first.c @@ -2,7 +2,16 @@ #include #include +#include "core/types.h" +#include "core/platform.h" +#include "core/macros.h" + +#include "vulkan/core.h" + int main(int argc, char **argv) { + (void) argc; + (void) argv; + if (!SDL_Init(SDL_INIT_VIDEO)) { printf("[Error] :: Failed to initialise SDL3 (%s)\n", SDL_GetError()); return 1; @@ -14,12 +23,89 @@ int main(int argc, char **argv) { return 1; } + Vk_Setup(window); + bool running = true; while (running) { SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_EVENT_QUIT) { running = false; } } + + int w, h; + SDL_GetWindowSizeInPixels(window, &w, &h); + + Vk_Frame *frame = Vk_FrameBegin(window); + VkCommandBuffer cmd = frame->cmd; + + VkImageMemoryBarrier2 colour_optimal = { 0 }; + colour_optimal.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + colour_optimal.srcStageMask = VK_PIPELINE_STAGE_2_NONE; + colour_optimal.srcAccessMask = VK_ACCESS_2_NONE; + colour_optimal.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colour_optimal.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + colour_optimal.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colour_optimal.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colour_optimal.image = vk.swapchain.images[frame->image]; + + colour_optimal.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colour_optimal.subresourceRange.layerCount = 1; + colour_optimal.subresourceRange.levelCount = 1; + + VkDependencyInfo colour_barrier = { 0 }; + colour_barrier.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + colour_barrier.imageMemoryBarrierCount = 1; + colour_barrier.pImageMemoryBarriers = &colour_optimal; + + vk.CmdPipelineBarrier2(cmd, &colour_barrier); + + VkClearValue clear_colour; + clear_colour.color.float32[0] = 1.0f; + clear_colour.color.float32[1] = 0.0f; + clear_colour.color.float32[2] = 0.0f; + clear_colour.color.float32[3] = 1.0f; + + VkRenderingAttachmentInfo colour_attachment = { 0 }; + colour_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + colour_attachment.imageView = vk.swapchain.views[frame->image]; + colour_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colour_attachment.clearValue = clear_colour; + + VkRenderingInfo rendering_info = { 0 }; + rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + rendering_info.renderArea = (VkRect2D) { 0, 0, w, h }; + rendering_info.layerCount = 1; + rendering_info.colorAttachmentCount = 1; + rendering_info.pColorAttachments = &colour_attachment; + + vk.CmdBeginRendering(cmd, &rendering_info); + + vk.CmdEndRendering(cmd); + + VkImageMemoryBarrier2 present_src = { 0 }; + present_src.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + present_src.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + present_src.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + present_src.dstStageMask = VK_PIPELINE_STAGE_2_NONE; + present_src.dstAccessMask = VK_ACCESS_2_NONE; + present_src.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + present_src.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + present_src.image = vk.swapchain.images[frame->image]; + + present_src.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + present_src.subresourceRange.layerCount = 1; + present_src.subresourceRange.levelCount = 1; + + VkDependencyInfo to_present = { 0 }; + to_present.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + to_present.imageMemoryBarrierCount = 1; + to_present.pImageMemoryBarriers = &present_src; + + vk.CmdPipelineBarrier2(cmd, &to_present); + + Vk_FrameEnd(); } SDL_DestroyWindow(window); @@ -27,3 +113,5 @@ int main(int argc, char **argv) { return 0; } + +#include "vulkan/core.c" diff --git a/code/vulkan/core.c b/code/vulkan/core.c new file mode 100644 index 0000000..742c8b6 --- /dev/null +++ b/code/vulkan/core.c @@ -0,0 +1,364 @@ +#if OS_WINDOWS + +#define VK_PLATFORM_SURFACE_EXTENSION VK_KHR_WIN32_SURFACE_EXTENSION_NAME + +static void *Vk_LoadLibrary() { + void *result = LoadLibraryA("vulkan-1.dll"); + + if (result != 0) { + vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(result, "vkGetInstanceProcAddr"); + } + + return result; +} + +static void Vk_CreateSurface(SDL_Window *window) { + SDL_PropertiesID prop_id = SDL_GetWindowProperties(window); + + VkWin32SurfaceCreateInfoKHR create_info = { 0 }; + create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create_info.hinstance = SDL_GetPointerProperty(prop_id, SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, 0); + create_info.hwnd = SDL_GetPointerProperty(prop_id, SDL_PROP_WINDOW_WIN32_HWND_POINTER, 0); + + vk.err = vk.CreateWin32SurfaceKHR(vk.instance, &create_info, 0, &vk.surface); +} + +#endif + +Vk_Context vk; + +bool Vk_Setup(SDL_Window *window) { + bool result = false; + + vk.lib = Vk_LoadLibrary(); + + // Create instance + // + if (vk.err == VK_SUCCESS) { + vk.CreateInstance = (PFN_vkCreateInstance) vk.GetInstanceProcAddr(0, "vkCreateInstance"); + + const char *extensions[] = { + VK_KHR_SURFACE_EXTENSION_NAME, + VK_PLATFORM_SURFACE_EXTENSION, + + #if !LD_RELEASE + VK_EXT_DEBUG_UTILS_EXTENSION_NAME + #endif + }; + + const char *layers[] = { + "VK_LAYER_KHRONOS_validation" + }; + + U32 n_layers = LD_RELEASE ? 0 : ArraySize(layers); + + VkApplicationInfo app = { 0 }; + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.apiVersion = VK_API_VERSION_1_3; // I know 1.4 is out + + VkInstanceCreateInfo create_info = { 0 }; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app; + create_info.ppEnabledExtensionNames = extensions; + create_info.enabledExtensionCount = ArraySize(extensions); + create_info.ppEnabledLayerNames = layers; + create_info.enabledLayerCount = n_layers; + + vk.err = vk.CreateInstance(&create_info, 0, &vk.instance); + } + + // Load instance level functions and create window surface + // + if (vk.err == VK_SUCCESS) { + #define VK_INSTANCE_FUNCTIONS + #define VK_FUNC(x) vk.x = (PFN_vk##x) vk.GetInstanceProcAddr(vk.instance, Stringify(Glue(vk, x))) + #include "functions.h" + #undef VK_FUNC + + Vk_CreateSurface(window); + } + + // Select GPU + // + if (vk.err == VK_SUCCESS) { + VkPhysicalDevice devices[8] = { 0 }; + U32 n_devices; + + vk.EnumeratePhysicalDevices(vk.instance, &n_devices, 0); + vk.EnumeratePhysicalDevices(vk.instance, &n_devices, devices); + + vk.gpu = devices[0]; + + for (U32 it = 0; it < n_devices; ++it) { + VkPhysicalDeviceProperties props; + vk.GetPhysicalDeviceProperties(devices[it], &props); + + if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + vk.gpu = devices[it]; + break; + } + } + + if (vk.gpu != VK_NULL_HANDLE) { + VkQueueFamilyProperties qprops[32] = { 0 }; + U32 n_qprops = 0; + + vk.GetPhysicalDeviceQueueFamilyProperties(vk.gpu, &n_qprops, 0); + vk.GetPhysicalDeviceQueueFamilyProperties(vk.gpu, &n_qprops, qprops); + + for (U32 it = 0; it < n_qprops; ++it) { + if (qprops[it].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + // Assumed combined graphics-present queue, this is the case on all desktop class + // gpus + vk.queue.family = it; + break; + } + } + } + } + else { + printf("[Error] :: Failed to create instance\n"); + } + + // Create logical device + // + if (vk.gpu != VK_NULL_HANDLE) { + const char *extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + VkPhysicalDeviceVulkan11Features features11 = { 0 }; + features11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + features11.storageBuffer16BitAccess = VK_TRUE; + features11.uniformAndStorageBuffer16BitAccess = VK_TRUE; + + VkPhysicalDeviceVulkan12Features features12 = { 0 }; + features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + features12.pNext = &features11; + features12.shaderFloat16 = VK_TRUE; + features12.shaderInt8 = VK_TRUE; + features12.storageBuffer8BitAccess = VK_TRUE; + features12.uniformAndStorageBuffer8BitAccess = VK_TRUE; + features12.descriptorIndexing = VK_TRUE; + + // @Todo: we will probably need to enable some of the 'nonuniform' indexing features + + VkPhysicalDeviceVulkan13Features features13 = { 0 }; + features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + features13.pNext = &features12; + features13.synchronization2 = VK_TRUE; + features13.dynamicRendering = VK_TRUE; + + F32 priority = 1.0f; + + VkDeviceQueueCreateInfo queue_info = { 0 }; + queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info.queueFamilyIndex = vk.queue.family; + queue_info.queueCount = 1; + queue_info.pQueuePriorities = &priority; + + VkDeviceCreateInfo create_info = { 0 }; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.pNext = &features13; + create_info.queueCreateInfoCount = 1; + create_info.pQueueCreateInfos = &queue_info; + create_info.enabledExtensionCount = ArraySize(extensions); + create_info.ppEnabledExtensionNames = extensions; + create_info.pEnabledFeatures = 0; + + vk.err = vk.CreateDevice(vk.gpu, &create_info, 0, &vk.device); + } + else { + printf("[Error] :: Failed to find suitable GPU\n"); + vk.err = VK_ERROR_DEVICE_LOST; + } + + if (vk.err == VK_SUCCESS) { + // Load device level functions and acquire the device queue + #define VK_DEVICE_FUNCTIONS + #define VK_FUNC(x) vk.x = (PFN_vk##x) vk.GetDeviceProcAddr(vk.device, Stringify(Glue(vk, x))) + #include "functions.h" + #undef VK_FUNC + + vk.GetDeviceQueue(vk.device, vk.queue.family, 0, &vk.queue.handle); + + // Create swapchain + U32 n_images = 2; + { + VkSurfaceCapabilitiesKHR caps; + vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(vk.gpu, vk.surface, &caps); + + if (caps.currentExtent.width == -1) { + int w, h; + SDL_GetWindowSizeInPixels(window, &w, &h); + + vk.swapchain.width = w; + vk.swapchain.height = h; + } + else { + vk.swapchain.width = caps.currentExtent.width; + vk.swapchain.height = caps.currentExtent.height; + } + + n_images = Max(n_images, caps.minImageCount); + } + + { + VkSurfaceFormatKHR formats[32]; + U32 n_formats = 0; + + vk.GetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &n_formats, 0); + vk.GetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &n_formats, formats); + + vk.swapchain.format = formats[0]; + + for (U32 it = 0; it < n_formats; ++it) { + VkSurfaceFormatKHR *fmt = &formats[it]; + + if ((fmt->format == VK_FORMAT_B8G8R8A8_SRGB) || + (fmt->format == VK_FORMAT_R8G8B8A8_SRGB)) + { + if (fmt->colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + vk.swapchain.format = formats[it]; + break; + } + } + } + } + + VkSwapchainCreateInfoKHR create_info = { 0 }; + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + create_info.surface = vk.surface; + create_info.minImageCount = n_images; + create_info.imageFormat = vk.swapchain.format.format; + create_info.imageColorSpace = vk.swapchain.format.colorSpace; + create_info.imageExtent = (VkExtent2D) { vk.swapchain.width, vk.swapchain.height }; + create_info.imageArrayLayers = 1; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + create_info.clipped = VK_TRUE; + + vk.err = vk.CreateSwapchainKHR(vk.device, &create_info, 0, &vk.swapchain.handle); + + vk.swapchain.n_images = n_images; + } + else { + printf("[Error] :: Failed to create device\n"); + } + + if (vk.err == VK_SUCCESS) { + // Get the swapchain images and create image views using them + U32 n_images = 0; + vk.GetSwapchainImagesKHR(vk.device, vk.swapchain.handle, &n_images, 0); + vk.GetSwapchainImagesKHR(vk.device, vk.swapchain.handle, &n_images, vk.swapchain.images); + + if (n_images != vk.swapchain.n_images) { + printf("[Warn] :: Swapchain image count mismatch\n"); + vk.swapchain.n_images = n_images; + } + + VkImageViewCreateInfo create_info = { 0 }; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + create_info.format = vk.swapchain.format.format; + + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + create_info.subresourceRange.levelCount = 1; + create_info.subresourceRange.layerCount = 1; + + for (U32 it = 0; it < n_images && vk.err == VK_SUCCESS; ++it) { + create_info.image = vk.swapchain.images[it]; + vk.err = vk.CreateImageView(vk.device, &create_info, 0, &vk.swapchain.views[it]); + } + } + + for (U32 it = 0; it < VK_FRAMES_IN_FLIGHT && vk.err == VK_SUCCESS; ++it) { + Vk_Frame *frame = &vk.frames[it]; + + VkCommandPoolCreateInfo pool = { 0 }; + pool.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + pool.queueFamilyIndex = vk.queue.family; + + VkResult err = vk.CreateCommandPool(vk.device, &pool, 0, &frame->pool); + + VkCommandBufferAllocateInfo alloc = { 0 }; + alloc.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc.commandBufferCount = 1; + alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc.commandPool = frame->pool; + + vk.AllocateCommandBuffers(vk.device, &alloc, &frame->cmd); + + VkSemaphoreCreateInfo semaphore = { 0 }; + semaphore.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + err = Min(vk.CreateSemaphore(vk.device, &semaphore, 0, &frame->acquire), err); + err = Min(vk.CreateSemaphore(vk.device, &semaphore, 0, &frame->complete), err); + + VkFenceCreateInfo fence = { 0 }; + fence.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + err = Min(vk.CreateFence(vk.device, &fence, 0, &frame->fence), err); + + vk.err = err; + } + + result = (vk.err == VK_SUCCESS); + return result; +} + +Vk_Frame *Vk_FrameBegin(SDL_Window *window) { + (void) window; // might need this for the resize later + Vk_Frame *frame = &vk.frames[vk.n_frames & 1]; + + vk.WaitForFences(vk.device, 1, &frame->fence, VK_TRUE, UINT64_MAX); + + vk.ResetFences(vk.device, 1, &frame->fence); + vk.ResetCommandPool(vk.device, frame->pool, 0); + + VkResult res = vk.AcquireNextImageKHR(vk.device, vk.swapchain.handle, UINT64_MAX, frame->acquire, 0, &frame->image); + (void) res; // @Todo: check res to see if swapchain is out of date and rebuild + + VkCommandBufferBeginInfo begin_info = { 0 }; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + vk.BeginCommandBuffer(frame->cmd, &begin_info); + + return frame; +} + +void Vk_FrameEnd() { + Vk_Frame *frame = &vk.frames[vk.n_frames & 1]; + + vk.EndCommandBuffer(frame->cmd); + + VkPipelineStageFlags stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + VkSubmitInfo submit = { 0 }; + submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit.waitSemaphoreCount = 1; + submit.pWaitSemaphores = &frame->acquire; + submit.pWaitDstStageMask = &stage; + submit.commandBufferCount = 1; + submit.pCommandBuffers = &frame->cmd; + submit.signalSemaphoreCount = 1; + submit.pSignalSemaphores = &frame->complete; + + vk.QueueSubmit(vk.queue.handle, 1, &submit, frame->fence); + + VkPresentInfoKHR present = { 0 }; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.waitSemaphoreCount = 1; + present.pWaitSemaphores = &frame->complete; + present.swapchainCount = 1; + present.pSwapchains = &vk.swapchain.handle; + present.pImageIndices = &frame->image; + + vk.QueuePresentKHR(vk.queue.handle, &present); + + vk.n_frames += 1; +} diff --git a/code/vulkan/core.h b/code/vulkan/core.h new file mode 100644 index 0000000..d4aee9a --- /dev/null +++ b/code/vulkan/core.h @@ -0,0 +1,80 @@ +#if !defined(LD_VULKAN_CORE_H_) +#define LD_VULKAN_CORE_H_ + +#if OS_WINDOWS + #define VK_USE_PLATFORM_WIN32_KHR 1 + + #if defined(CreateSemaphore) + // Sigh + #undef CreateSemaphore + #endif +#elif OS_LINUX + #define VK_USE_PLATFORM_XLIB_KHR 1 + #define VK_USE_PLATFORM_WAYLAND_KHR 1 +#endif + +#define VK_NO_PROTOTYPES 1 +#include + +#define VK_FRAMES_IN_FLIGHT 2 + +typedef struct Vk_Frame Vk_Frame; +struct Vk_Frame { + VkCommandPool pool; + VkCommandBuffer cmd; + + VkSemaphore acquire, complete; + VkFence fence; + + U32 image; // swapchain image index +}; + +typedef struct Vk_Context Vk_Context; +struct Vk_Context { + void *lib; + PFN_vkGetInstanceProcAddr GetInstanceProcAddr; + PFN_vkCreateInstance CreateInstance; + + #define VK_INSTANCE_FUNCTIONS + #define VK_DEVICE_FUNCTIONS + #define VK_FUNC(x) PFN_vk##x x + #include "functions.h" + #undef VK_FUNC + + VkResult err; + VkInstance instance; + + VkSurfaceKHR surface; + + VkPhysicalDevice gpu; + VkDevice device; + + U32 n_frames; // monotonically increasing + Vk_Frame frames[VK_FRAMES_IN_FLIGHT]; + + struct { + U32 family; + VkQueue handle; + } queue; + + struct { + VkSwapchainKHR handle; + + U32 n_images; + VkImage images[8]; + VkImageView views[8]; + + VkSurfaceFormatKHR format; + + U32 width; + U32 height; + } swapchain; +}; + +extern Vk_Context vk; + +static bool Vk_Setup(SDL_Window *window); +static Vk_Frame *Vk_FrameBegin(SDL_Window *window); +static void Vk_FrameEnd(); + +#endif // LD_VULKAN_CORE_H_ diff --git a/code/vulkan/functions.h b/code/vulkan/functions.h new file mode 100644 index 0000000..1a59d2a --- /dev/null +++ b/code/vulkan/functions.h @@ -0,0 +1,38 @@ +#if defined(VK_INSTANCE_FUNCTIONS) + VK_FUNC(EnumeratePhysicalDevices); + VK_FUNC(GetPhysicalDeviceQueueFamilyProperties); + VK_FUNC(GetPhysicalDeviceProperties); + VK_FUNC(CreateDevice); + VK_FUNC(GetDeviceProcAddr); + VK_FUNC(GetPhysicalDeviceSurfaceCapabilitiesKHR); + VK_FUNC(GetPhysicalDeviceSurfaceFormatsKHR); + +#if defined(VK_USE_PLATFORM_WIN32_KHR) + VK_FUNC(CreateWin32SurfaceKHR); +#endif + #undef VK_INSTANCE_FUNCTIONS +#endif + +#if defined(VK_DEVICE_FUNCTIONS) + VK_FUNC(GetDeviceQueue); + VK_FUNC(CreateSwapchainKHR); + VK_FUNC(GetSwapchainImagesKHR); + VK_FUNC(CreateImageView); + VK_FUNC(CreateCommandPool); + VK_FUNC(CreateSemaphore); + VK_FUNC(CreateFence); + VK_FUNC(AllocateCommandBuffers); + VK_FUNC(WaitForFences); + VK_FUNC(ResetFences); + VK_FUNC(ResetCommandPool); + VK_FUNC(AcquireNextImageKHR); + VK_FUNC(QueueSubmit); + VK_FUNC(QueuePresentKHR); + VK_FUNC(BeginCommandBuffer); + VK_FUNC(EndCommandBuffer); + + VK_FUNC(CmdPipelineBarrier2); + VK_FUNC(CmdBeginRendering); + VK_FUNC(CmdEndRendering); + #undef VK_DEVICE_FUNCTIONS +#endif diff --git a/windows.bat b/windows.bat index 143d28e..3539c84 100644 --- a/windows.bat +++ b/windows.bat @@ -14,6 +14,11 @@ FOR %%A in (%*) DO ( SET %%A=1 ) +IF NOT EXIST "%VULKAN_SDK%" ( + ECHO [Error: Vulkan SDK required to build] + EXIT /b 1 +) + IF NOT EXIST "deps\SDL3" ( SET deps=1 ) @@ -48,15 +53,15 @@ IF %deps% equ 1 ( ECHO [Building source] -SET COMPILER_OPTS=-nologo -W4 -I"deps\SDL3\include" -I"deps\stb" +SET COMPILER_OPTS=-nologo -W4 -I"deps\SDL3\include" -I"deps\stb" -I"%VULKAN_SDK%\Include" SET LINKER_OPTS=-LIBPATH:"deps\SDL3\lib" SDL3.lib IF %release% equ 1 ( ECHO [Release build] - cl -O2 -WX %COMPILER_OPTS% "..\code\first.c" -link %LINKER_OPTS% + cl -O2 -WX %COMPILER_OPTS% -DLD_RELEASE=1 "%~dp0code\first.c" -link %LINKER_OPTS% ) ELSE ( ECHO [Debug build] - cl -Od -Zi %COMPILER_OPTS% "..\code\first.c" -link %LINKER_OPTS% + cl -Od -Zi %COMPILER_OPTS% "%~dp0code\first.c" -link %LINKER_OPTS% ) POPD