2025-10-03 20:04:40 +01:00
|
|
|
#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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 20:42:04 +01:00
|
|
|
#elif OS_LINUX
|
|
|
|
|
|
|
|
|
|
#define VK_PLATFORM_SURFACE_EXTENSION \
|
|
|
|
|
VK_KHR_XLIB_SURFACE_EXTENSION_NAME, \
|
|
|
|
|
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
|
|
|
|
|
|
|
|
|
|
static void *Vk_LoadLibrary() {
|
|
|
|
|
void *result = dlopen("libvulkan.so", RTLD_LAZY);
|
|
|
|
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) dlsym(result, "vkGetInstanceProcAddr");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void Vk_CreateSurface(SDL_Window *window) {
|
|
|
|
|
SDL_PropertiesID prop_id = SDL_GetWindowProperties(window);
|
|
|
|
|
|
|
|
|
|
struct wl_display *wl_display = SDL_GetPointerProperty(prop_id, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, 0);
|
|
|
|
|
if (wl_display == 0) {
|
|
|
|
|
// Assume we are running via X11 if there isn't a vaild Wayland display
|
|
|
|
|
//
|
|
|
|
|
VkXlibSurfaceCreateInfoKHR create_info = { 0 };
|
|
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
|
|
|
|
|
create_info.dpy = SDL_GetPointerProperty(prop_id, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, 0);
|
|
|
|
|
create_info.window = SDL_GetNumberProperty(prop_id, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
|
|
|
|
|
|
|
|
|
vk.err = vk.CreateXlibSurfaceKHR(vk.instance, &create_info, 0, &vk.surface);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
VkWaylandSurfaceCreateInfoKHR create_info = { 0 };
|
|
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
|
|
|
|
|
create_info.display = wl_display;
|
|
|
|
|
create_info.surface = SDL_GetPointerProperty(prop_id, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, 0);
|
|
|
|
|
|
|
|
|
|
vk.err = vk.CreateWaylandSurfaceKHR(vk.instance, &create_info, 0, &vk.surface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
#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);
|
|
|
|
|
|
2025-10-04 00:46:26 +01:00
|
|
|
vk.in_flight = n_images + 1;
|
|
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
if (n_images != vk.swapchain.n_images) {
|
|
|
|
|
printf("[Warn] :: Swapchain image count mismatch\n");
|
|
|
|
|
vk.swapchain.n_images = n_images;
|
|
|
|
|
}
|
2025-10-04 00:46:26 +01:00
|
|
|
else if (n_images >= VK_MAX_FRAMES_IN_FLIGHT) {
|
|
|
|
|
printf("[Warn] :: Min image count too high: %d\n", n_images);
|
|
|
|
|
vk.in_flight = VK_MAX_FRAMES_IN_FLIGHT;
|
|
|
|
|
}
|
2025-10-03 20:04:40 +01:00
|
|
|
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 00:46:26 +01:00
|
|
|
for (U32 it = 0; it < vk.in_flight && vk.err == VK_SUCCESS; ++it) {
|
2025-10-03 20:04:40 +01:00
|
|
|
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);
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
// Base command buffer
|
2025-10-03 20:04:40 +01:00
|
|
|
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);
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
// Scratch command buffers
|
|
|
|
|
VkCommandBuffer scratch[VK_NUM_SCRATCH];
|
|
|
|
|
alloc.commandBufferCount = VK_NUM_SCRATCH;
|
2025-10-03 20:04:40 +01:00
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
vk.AllocateCommandBuffers(vk.device, &alloc, scratch);
|
2025-10-03 20:04:40 +01:00
|
|
|
|
|
|
|
|
VkFenceCreateInfo fence = { 0 };
|
|
|
|
|
fence.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
|
fence.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
for (U32 s = 0; s < VK_NUM_SCRATCH; ++s) {
|
|
|
|
|
frame->scratch[s].handle = scratch[s];
|
|
|
|
|
err = Min(vk.CreateFence(vk.device, &fence, 0, &frame->scratch[s].fence), err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2025-10-03 20:04:40 +01:00
|
|
|
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
|
2025-10-04 00:46:26 +01:00
|
|
|
Vk_Frame *frame = &vk.frames[vk.n_frames % vk.in_flight];
|
2025-10-03 20:04:40 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2025-10-04 11:52:39 +01:00
|
|
|
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(frame->cmd, &colour_barrier);
|
|
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Vk_FrameEnd() {
|
2025-10-04 00:46:26 +01:00
|
|
|
Vk_Frame *frame = &vk.frames[vk.n_frames % vk.in_flight];
|
2025-10-03 20:04:40 +01:00
|
|
|
|
2025-10-04 11:52:39 +01:00
|
|
|
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(frame->cmd, &to_present);
|
|
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2025-10-04 17:24:30 +01:00
|
|
|
|
|
|
|
|
Vk_CommandBuffer *Vk_CommandBufferPush() {
|
|
|
|
|
Vk_Frame *frame = &vk.frames[vk.n_frames % vk.in_flight];
|
|
|
|
|
|
|
|
|
|
// If this scratch buffer is still in use wait for it to finish, this is _bad_ but we
|
|
|
|
|
// shouldn't hit this
|
|
|
|
|
U32 idx = frame->next_scratch & (VK_NUM_SCRATCH - 1);
|
|
|
|
|
|
|
|
|
|
Vk_CommandBuffer *result = &frame->scratch[idx];
|
|
|
|
|
|
|
|
|
|
vk.WaitForFences(vk.device, 1, &result->fence, VK_TRUE, U64_MAX);
|
|
|
|
|
vk.ResetFences(vk.device, 1, &result->fence);
|
|
|
|
|
|
|
|
|
|
VkCommandBufferBeginInfo begin_info = { 0 };
|
|
|
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
|
|
|
|
|
|
vk.BeginCommandBuffer(result->handle, &begin_info);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Vk_CommandBufferSubmit(Vk_CommandBuffer *cmds, B32 wait) {
|
|
|
|
|
vk.EndCommandBuffer(cmds->handle);
|
|
|
|
|
|
|
|
|
|
VkSubmitInfo submit_info = { 0 };
|
|
|
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
|
submit_info.commandBufferCount = 1;
|
|
|
|
|
submit_info.pCommandBuffers = &cmds->handle;
|
|
|
|
|
|
|
|
|
|
vk.QueueSubmit(vk.queue.handle, 1, &submit_info, cmds->fence);
|
|
|
|
|
|
|
|
|
|
if (wait) {
|
|
|
|
|
vk.DeviceWaitIdle(vk.device);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define VK_HOST_VISIBLE_FLAGS (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
|
|
|
|
|
|
|
|
|
|
internal VkDeviceMemory Vk_Allocate(VkMemoryRequirements *mreq, VkMemoryPropertyFlags usage) {
|
|
|
|
|
VkDeviceMemory result = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceMemoryProperties2 _props = { 0 };
|
|
|
|
|
_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
|
|
|
|
|
|
|
|
|
|
vk.GetPhysicalDeviceMemoryProperties2(vk.gpu, &_props);
|
|
|
|
|
|
|
|
|
|
// ?????
|
|
|
|
|
VkPhysicalDeviceMemoryProperties *props = &_props.memoryProperties;
|
|
|
|
|
|
|
|
|
|
U32 type_index = U32_MAX;
|
|
|
|
|
|
|
|
|
|
for (U32 it = 0; it < props->memoryTypeCount; ++it) {
|
|
|
|
|
VkMemoryType *type = &props->memoryTypes[it];
|
|
|
|
|
if ((type->propertyFlags & usage) == usage) {
|
|
|
|
|
type_index = it;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type_index != -1) {
|
|
|
|
|
VkMemoryAllocateInfo alloc_info = { 0 };
|
|
|
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
|
|
|
alloc_info.allocationSize = mreq->size;
|
|
|
|
|
alloc_info.memoryTypeIndex = type_index;
|
|
|
|
|
|
|
|
|
|
vk.AllocateMemory(vk.device, &alloc_info, 0, &result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vk_Buffer Vk_BufferCreate(U64 size, B32 host_visible) {
|
|
|
|
|
Vk_Buffer result = { 0 };
|
|
|
|
|
|
|
|
|
|
VkBufferCreateInfo create_info = { 0 };
|
|
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
|
|
|
create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
|
|
|
create_info.size = size;
|
|
|
|
|
|
|
|
|
|
vk.CreateBuffer(vk.device, &create_info, 0, &result.handle);
|
|
|
|
|
|
|
|
|
|
VkMemoryRequirements req;
|
|
|
|
|
vk.GetBufferMemoryRequirements(vk.device, result.handle, &req);
|
|
|
|
|
|
|
|
|
|
VkMemoryPropertyFlags usage = host_visible ? VK_HOST_VISIBLE_FLAGS : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
|
|
|
|
|
|
|
|
result.size = req.size;
|
|
|
|
|
result.memory = Vk_Allocate(&req, usage);
|
|
|
|
|
|
|
|
|
|
vk.BindBufferMemory(vk.device, result.handle, result.memory, 0);
|
|
|
|
|
|
|
|
|
|
if (host_visible) {
|
|
|
|
|
vk.MapMemory(vk.device, result.memory, 0, result.size, 0, &result.data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Vk_ImageCreate(Vk_Image *image) {
|
|
|
|
|
VkImageCreateInfo create_info = { 0 };
|
|
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
|
|
|
create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
|
|
|
create_info.format = image->format;
|
|
|
|
|
create_info.extent.width = image->width;
|
|
|
|
|
create_info.extent.height = image->height;
|
|
|
|
|
create_info.extent.depth = 1;
|
|
|
|
|
create_info.mipLevels = 1;
|
|
|
|
|
create_info.arrayLayers = 1;
|
|
|
|
|
create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | image->usage;
|
|
|
|
|
create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
|
create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
|
|
|
|
|
|
vk.CreateImage(vk.device, &create_info, 0, &image->handle);
|
|
|
|
|
|
|
|
|
|
VkMemoryRequirements req;
|
|
|
|
|
vk.GetImageMemoryRequirements(vk.device, image->handle, &req);
|
|
|
|
|
|
|
|
|
|
image->size = req.size;
|
|
|
|
|
image->memory = Vk_Allocate(&req, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
|
|
|
|
|
|
|
|
vk.BindImageMemory(vk.device, image->handle, image->memory, 0);
|
|
|
|
|
|
|
|
|
|
VkImageViewCreateInfo view_info = { 0 };
|
|
|
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
|
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
|
view_info.format = image->format;
|
|
|
|
|
view_info.image = image->handle;
|
|
|
|
|
|
|
|
|
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
view_info.subresourceRange.levelCount = 1;
|
|
|
|
|
view_info.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
vk.CreateImageView(vk.device, &view_info, 0, &image->view);
|
|
|
|
|
}
|