#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); } #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); } } #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; features12.runtimeDescriptorArray = 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); vk.in_flight = n_images + 1; if (n_images != vk.swapchain.n_images) { printf("[Warn] :: Swapchain image count mismatch\n"); vk.swapchain.n_images = n_images; } 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; } 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.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); // Base command buffer 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); // Scratch command buffers VkCommandBuffer scratch[VK_NUM_SCRATCH]; alloc.commandBufferCount = VK_NUM_SCRATCH; vk.AllocateCommandBuffers(vk.device, &alloc, scratch); VkFenceCreateInfo fence = { 0 }; fence.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence.flags = VK_FENCE_CREATE_SIGNALED_BIT; 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); } // Descriptor pool VkDescriptorPoolSize pool_sizes[2] = { 0 }; pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_sizes[0].descriptorCount = 2048; pool_sizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; pool_sizes[1].descriptorCount = 2048; VkDescriptorPoolCreateInfo descriptor_pool = { 0 }; descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool.maxSets = 1024; descriptor_pool.poolSizeCount = 2; descriptor_pool.pPoolSizes = pool_sizes; vk.CreateDescriptorPool(vk.device, &descriptor_pool, 0, &frame->descriptors); 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); err = Min(vk.CreateFence(vk.device, &fence, 0, &frame->fence), err); vk.err = err; } { VkSamplerCreateInfo create_info = { 0 }; create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; create_info.magFilter = VK_FILTER_NEAREST; create_info.minFilter = VK_FILTER_NEAREST; create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; vk.CreateSampler(vk.device, &create_info, 0, &vk.sampler); } 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 % vk.in_flight]; 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); vk.ResetDescriptorPool(vk.device, frame->descriptors, 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); 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); return frame; } void Vk_FrameEnd() { Vk_Frame *frame = &vk.frames[vk.n_frames % vk.in_flight]; 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); 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; } 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; } void Vk_BufferCreate(Vk_Buffer *buffer) { VkBufferCreateInfo create_info = { 0 }; create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; create_info.usage = buffer->usage; create_info.size = buffer->size; vk.CreateBuffer(vk.device, &create_info, 0, &buffer->handle); VkMemoryRequirements req; vk.GetBufferMemoryRequirements(vk.device, buffer->handle, &req); VkMemoryPropertyFlags usage = buffer->host_visible ? VK_HOST_VISIBLE_FLAGS : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; buffer->size = req.size; buffer->memory = Vk_Allocate(&req, usage); vk.BindBufferMemory(vk.device, buffer->handle, buffer->memory, 0); if (buffer->host_visible) { vk.MapMemory(vk.device, buffer->memory, 0, buffer->size, 0, &buffer->data); } } 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); } void Vk_PipelineCreate(Vk_Pipeline *pipeline) { VkPipelineInputAssemblyStateCreateInfo ia = { 0 }; ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPipelineShaderStageCreateInfo shaders[8] = { 0 }; for (U32 it = 0; it < pipeline->shaders.count; ++it) { shaders[it].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaders[it].stage = pipeline->shaders.items[it].stage; shaders[it].module = pipeline->shaders.items[it].handle; shaders[it].pName = "main"; } VkPipelineVertexInputStateCreateInfo vi = { 0 }; vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; // This will be updated later using dynamic commands but we still need to supply one here for // some reason VkViewport viewport = { 0, 0, 10, 10, 0, 1 }; VkRect2D scissor = { 0, 0, 10, 10 }; VkPipelineViewportStateCreateInfo vp = { 0 }; vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; vp.viewportCount = 1; vp.pViewports = &viewport; vp.scissorCount = 1; vp.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rs = { 0 }; rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rs.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo ms = { 0 }; ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineDepthStencilStateCreateInfo ds = { 0 }; ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; VkPipelineColorBlendAttachmentState blend = { 0 }; blend.blendEnable = VK_TRUE; blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; blend.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; blend.colorBlendOp = VK_BLEND_OP_ADD; blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; blend.alphaBlendOp = VK_BLEND_OP_ADD; blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo om = { 0 }; om.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; om.attachmentCount = 1; om.pAttachments = &blend; VkDynamicState dynamic_state[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dyn = { 0 }; dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dyn.dynamicStateCount = ArraySize(dynamic_state); dyn.pDynamicStates = dynamic_state; VkFormat formats[8]; for (U32 it = 0; it < pipeline->targets.count; ++it) { formats[it] = pipeline->targets.items[it]; } VkPipelineRenderingCreateInfo rendering_info = { 0 }; rendering_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; rendering_info.colorAttachmentCount = pipeline->targets.count; rendering_info.pColorAttachmentFormats = formats; VkGraphicsPipelineCreateInfo create_info = { 0 }; create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; create_info.pNext = &rendering_info; create_info.stageCount = pipeline->shaders.count; create_info.pStages = shaders; create_info.pVertexInputState = &vi; create_info.pInputAssemblyState = &ia; create_info.pViewportState = &vp; create_info.pRasterizationState = &rs; create_info.pMultisampleState = &ms; create_info.pDepthStencilState = &ds; create_info.pColorBlendState = &om; create_info.pDynamicState = &dyn; create_info.layout = pipeline->layout.pipeline; vk.CreateGraphicsPipelines(vk.device, 0, 1, &create_info, 0, &pipeline->handle); }