1311 lines
56 KiB
C
1311 lines
56 KiB
C
// vim:fileencoding=utf-8:foldmethod=marker
|
|
|
|
#include "volk/volk.h"
|
|
#include "vulkan/vulkan_core.h"
|
|
#include "wapp/wapp.h"
|
|
#include <stddef.h>
|
|
#define CGLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
#include "cglm/cglm.h"
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_events.h>
|
|
#include <SDL3/SDL_init.h>
|
|
#include <SDL3/SDL_keycode.h>
|
|
#include <SDL3/SDL_timer.h>
|
|
#include <SDL3/SDL_video.h>
|
|
#include <SDL3/SDL_vulkan.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define WINDOW_WIDTH 1600
|
|
#define WINDOW_HEIGHT 1200
|
|
#define FRAMES_IN_FLIGHT 3
|
|
#define FRAME_TIME 1.0 / 60.0 * 1e+9
|
|
#define SHADER_FILE "09_shader_base.spv"
|
|
#define SHADER_COUNT 2
|
|
#define MIN_DEPTH 0.0f
|
|
#define MAX_DEPTH 1.0f
|
|
|
|
typedef struct {
|
|
vec3 position;
|
|
vec3 color;
|
|
} Vertex;
|
|
|
|
WAPP_DEF_ARRAY_TYPE(VkFence, VkFenceArray);
|
|
WAPP_DEF_ARRAY_TYPE(VkSemaphore, VkSemaphoreArray);
|
|
WAPP_DEF_ARRAY_TYPE(VkImage, VkImageArray);
|
|
WAPP_DEF_ARRAY_TYPE(VkImageView, VkImageViewArray);
|
|
WAPP_DEF_ARRAY_TYPE(Vertex, VertexArray);
|
|
|
|
enum ReturnCodes {
|
|
RETURN_SUCCESS,
|
|
RETURN_FAILED_TO_GET_INSTANCE_EXTENSIONS,
|
|
RETURN_FAILED_TO_ALLOCATE_EXTENSION_NAMES,
|
|
RETURN_FAILED_TO_GET_VK_GET_INSTANCE_PROC_ADDR,
|
|
RETURN_FAILED_TO_INITIALISE_VOLK,
|
|
RETURN_FAILED_TO_QUERY_API_VERSION,
|
|
RETURN_FAILED_TO_CREATE_INSTANCE,
|
|
RETURN_FAILED_TO_CREATE_SURFACE,
|
|
RETURN_FAILED_TO_QUERY_PHYSICAL_DEVICE_COUNT,
|
|
RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_PHYSICAL_DEVICES,
|
|
RETURN_FAILED_TO_RETRIEVE_PHYSICAL_DEVICES,
|
|
RETURN_FAILED_TO_FIND_SUITABLE_PHYSICAL_DEVICE,
|
|
RETURN_NO_QUEUE_FAMILY_PROPERTIES_FOUND,
|
|
RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_QUEUE_FAMILY_PROPERTIES,
|
|
RETURN_SELECTED_DEVICE_DOES_NOT_SUPPORT_GRAPHICS,
|
|
RETURN_FAILED_TO_ENUMERATE_PHYSICAL_DEVICE_EXTENSIONS,
|
|
RETURN_FAILED_TO_QUERY_PHYSICAL_DEVICE_EXTENSIONS,
|
|
RETURN_FAILED_TO_CREATE_LOGICAL_DEVICE,
|
|
RETURN_FAILED_TO_CREATE_COMMAND_POOL,
|
|
RETURN_FAILED_TO_ALLOCATE_COMMAND_BUFFER,
|
|
RETURN_FAILED_TO_QUERY_SURFACE_CAPABILITIES,
|
|
RETURN_FAILED_TO_ENUMERATE_SUPPORTED_SURFACE_FORMATS,
|
|
RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_SURFACE_FORMATS,
|
|
RETURN_FAILED_TO_QUERY_SURPPORTED_SURFACE_FORMATS,
|
|
RETURN_FAILED_TO_ENUMERATE_SUPPORTED_SURFACE_PRESENT_MODES,
|
|
RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_SURFACE_PRESENT_MODES,
|
|
RETURN_FAILED_TO_QUERY_SURPPORTED_SURFACE_PRESENT_MODES,
|
|
RETURN_FAILED_TO_CREATE_SWAPCHAIN,
|
|
RETURN_FAILED_TO_GET_SWAPCHAIN_IMAGE_COUNT,
|
|
RETURN_FAILED_TO_ALLOCATE_SWAPCHAIN_IMAGES_MEMORY,
|
|
RETURN_FAILED_TO_GET_SWAPCHAIN_IMAGES,
|
|
RETURN_FAILED_TO_CREATE_IMAGE_VIEWS,
|
|
RETURN_FAILED_TO_CREATE_SYNC_OBJECTS,
|
|
RETURN_FAILED_TO_CREATE_SHADER_MODULE,
|
|
RETURN_FAILED_TO_CREATE_DEPTH_IMAGE,
|
|
RETURN_FAILED_TO_FIND_SUITABLE_MEMORY_TYPE_FOR_DEPTH_IMAGE,
|
|
RETURN_FAILED_TO_ALLOCATE_DEPTH_IMAGE_MEMORY,
|
|
RETURN_FAILED_TO_BIND_DEPTH_IMAGE_MEMORY,
|
|
RETURN_FAILED_TO_CREATE_DEPTH_IMAGE_VIEW,
|
|
RETURN_FAILED_TO_CREATE_COPY_COMMAND_BUFFER,
|
|
RETURN_FAILED_TO_CREATE_VERTEX_BUFFER,
|
|
RETURN_FAILED_TO_CREATE_INDEX_BUFFER,
|
|
RETURN_FAILED_TO_COPY_VERTEX_AND_INDEX_DATA,
|
|
RETURN_FAILED_TO_CREATE_PIPELINE_LAYOUT,
|
|
RETURN_FAILED_TO_CREATE_GRAPHICS_PIPELINE,
|
|
};
|
|
|
|
VkInstance instance = VK_NULL_HANDLE;
|
|
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
|
|
u32 queue_family_index = VK_QUEUE_FAMILY_IGNORED;
|
|
VkDevice device = VK_NULL_HANDLE;
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
VkCommandPool command_pool = VK_NULL_HANDLE;
|
|
VkCommandBuffer command_buffers[FRAMES_IN_FLIGHT] = {0};
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
|
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
|
VkSurfaceFormatKHR swapchain_format;
|
|
VkPresentModeKHR swapchain_present_mode;
|
|
VkImageArray *swapchain_images;
|
|
VkImageViewArray *swapchain_image_views;
|
|
VkExtent2D swapchain_extent;
|
|
u32 image_index;
|
|
VkImage depth_image = VK_NULL_HANDLE;
|
|
VkDeviceMemory depth_image_memory = VK_NULL_HANDLE;
|
|
VkImageView depth_image_view = VK_NULL_HANDLE;
|
|
VkFormat depth_image_format;
|
|
VkBuffer vertex_buffer = VK_NULL_HANDLE;
|
|
VkDeviceMemory vertex_buffer_memory = VK_NULL_HANDLE;
|
|
VkBuffer index_buffer = VK_NULL_HANDLE;
|
|
VkDeviceMemory index_buffer_memory = VK_NULL_HANDLE;
|
|
VkFenceArray fences = wapp_array_with_capacity(VkFence,
|
|
VkFenceArray,
|
|
FRAMES_IN_FLIGHT,
|
|
true);
|
|
VkSemaphoreArray present_complete_semaphores = wapp_array_with_capacity(VkSemaphore,
|
|
VkSemaphoreArray,
|
|
FRAMES_IN_FLIGHT,
|
|
true);
|
|
VkSemaphoreArray render_finished_semaphores = wapp_array_with_capacity(VkSemaphore,
|
|
VkSemaphoreArray,
|
|
FRAMES_IN_FLIGHT,
|
|
true);
|
|
VkShaderModule shader_module = VK_NULL_HANDLE;
|
|
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
|
|
VkPipeline graphics_pipeline = VK_NULL_HANDLE;
|
|
u32 physical_device_count;
|
|
u64 frame_index = 0;
|
|
VertexArray vertices = wapp_array(Vertex, VertexArray,
|
|
{{-0.5f, 0.5f, 0.5f}, {0.42f, 0.05f, 0.14f}},
|
|
{{-0.5f, -0.5f, 0.5f}, {0.42f, 0.05f, 0.14f}},
|
|
{{-0.5f, 0.5f, -0.5f}, { 0.0f, 0.1f, 0.21f}},
|
|
{{-0.5f, -0.5f, -0.5f}, { 0.0f, 0.1f, 0.21f}},
|
|
{{ 0.5f, 0.5f, 0.5f}, {0.42f, 0.05f, 0.14f}},
|
|
{{ 0.5f, -0.5f, 0.5f}, {0.42f, 0.05f, 0.14f}},
|
|
{{ 0.5f, 0.5f, -0.5f}, { 0.0f, 0.1f, 0.21f}},
|
|
{{ 0.5f, -0.5f, -0.5f}, { 0.0f, 0.1f, 0.21f}});
|
|
U32Array indices = wapp_array(u32, U32Array, 0, 2, 3,
|
|
3, 1, 0,
|
|
6, 4, 5,
|
|
5, 7, 6,
|
|
4, 0, 1,
|
|
1, 5, 4,
|
|
2, 6, 7,
|
|
7, 3, 2,
|
|
0, 4, 6,
|
|
6, 2, 0,
|
|
3, 7, 5,
|
|
5, 1, 3);
|
|
|
|
wapp_intern inline void draw_frame(void);
|
|
wapp_intern inline void record_command_buffer(void);
|
|
wapp_intern inline b8 create_buffer(VkPhysicalDeviceMemoryProperties *properties, VkDevice device,
|
|
VkBufferCreateFlags flags, VkDeviceSize size, VkBufferUsageFlags usage,
|
|
VkMemoryPropertyFlags memory_properties, VkBuffer *buffer,
|
|
VkDeviceMemory *buffer_memory);
|
|
wapp_intern inline void transition_image_layout(
|
|
VkCommandBuffer command_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout,
|
|
VkPipelineStageFlags2 src_stage, VkPipelineStageFlags2 dst_stage,
|
|
VkAccessFlags2 src_access, VkAccessFlags2 dst_access, VkImageAspectFlags aspect_mask
|
|
);
|
|
wapp_intern inline i32 get_memory_type(const VkPhysicalDeviceMemoryProperties *properties,
|
|
u32 memory_type_bits, VkMemoryPropertyFlags flags);
|
|
wapp_intern inline u32 set_error(u32 code, const char *msg);
|
|
|
|
int main(void) {
|
|
// Initialisation {{{
|
|
Allocator arena = wapp_mem_arena_allocator_init(MB(256));
|
|
u32 status = RETURN_SUCCESS;
|
|
|
|
SDL_Init(SDL_INIT_VIDEO);
|
|
|
|
SDL_Window *window = SDL_CreateWindow("Vulkan", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_VULKAN);
|
|
|
|
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
|
|
if (!vkGetInstanceProcAddr) {
|
|
status = set_error(RETURN_FAILED_TO_GET_VK_GET_INSTANCE_PROC_ADDR,
|
|
"Failed to get address of vkGetInstanceProcAddr");
|
|
goto SDL_CLEANUP;
|
|
}
|
|
|
|
if (volkInitialize() != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_INITIALISE_VOLK, "Failed to initialise volk");
|
|
goto SDL_CLEANUP;
|
|
}
|
|
// }}}
|
|
|
|
// Instance creation {{{
|
|
u32 sdl_extensions_count;
|
|
const char *const *sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extensions_count);
|
|
if (!sdl_extensions) {
|
|
status = set_error(RETURN_FAILED_TO_GET_INSTANCE_EXTENSIONS,
|
|
"Failed to get instance extensions");
|
|
goto SDL_CLEANUP;
|
|
}
|
|
|
|
const char *extra_extensions[] = {
|
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
|
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME
|
|
};
|
|
u32 extra_extensions_count = sizeof(extra_extensions) / sizeof(const char *);
|
|
|
|
u32 all_extensions_count = sdl_extensions_count + extra_extensions_count;
|
|
const char **all_instance_extensions = wapp_mem_allocator_alloc(&arena, all_extensions_count * sizeof(const char *));
|
|
if (!all_instance_extensions) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_EXTENSION_NAMES,
|
|
"Failed to allocate memory for instance extensions names");
|
|
goto SDL_CLEANUP;
|
|
}
|
|
|
|
memcpy(all_instance_extensions, sdl_extensions, sdl_extensions_count * sizeof(const char *));
|
|
memcpy(&(all_instance_extensions[sdl_extensions_count]), extra_extensions, extra_extensions_count * sizeof(const char *));
|
|
|
|
u32 api_version;
|
|
if (vkEnumerateInstanceVersion(&api_version) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_QUERY_API_VERSION,
|
|
"Failed to query API version");
|
|
goto SDL_CLEANUP;
|
|
}
|
|
|
|
fprintf(stdout, "Vulkan API version: %u.%u.%u.%u\n", VK_API_VERSION_MAJOR(api_version),
|
|
VK_API_VERSION_MINOR(api_version),
|
|
VK_API_VERSION_PATCH(api_version),
|
|
VK_API_VERSION_VARIANT(api_version));
|
|
|
|
VkApplicationInfo app_info = {
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
.pNext = NULL,
|
|
.pApplicationName = "Vulkan spec test",
|
|
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
|
.pEngineName = "My vulkan engine",
|
|
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
|
.apiVersion = api_version,
|
|
};
|
|
|
|
VkInstanceCreateInfo instance_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pApplicationInfo = &app_info,
|
|
.enabledExtensionCount = all_extensions_count,
|
|
.ppEnabledExtensionNames = all_instance_extensions,
|
|
};
|
|
|
|
if (vkCreateInstance(&instance_create_info, NULL, &instance) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_INSTANCE,
|
|
"Failed to create vulkan instance");
|
|
goto SDL_CLEANUP;
|
|
}
|
|
|
|
volkLoadInstanceOnly(instance);
|
|
// }}}
|
|
|
|
// Surface creation {{{
|
|
if (!SDL_Vulkan_CreateSurface(window, instance, NULL, &surface)) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_SURFACE,
|
|
"Failed to create surface");
|
|
goto DESTROY_VULKAN_INSTANCE;
|
|
}
|
|
// }}}
|
|
|
|
// Find suitable physical device {{{
|
|
if (vkEnumeratePhysicalDevices(instance, &physical_device_count, NULL) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_QUERY_PHYSICAL_DEVICE_COUNT,
|
|
"Failed to query physical device count");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
VkPhysicalDevice *physical_devices = wapp_mem_allocator_alloc(&arena, physical_device_count * sizeof(VkPhysicalDevice));
|
|
if (!physical_devices) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_PHYSICAL_DEVICES,
|
|
"Failed to allocate memory for physical devices");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
if (vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_RETRIEVE_PHYSICAL_DEVICES,
|
|
"Failed to retrieve physical devices");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
int32_t index = -1;
|
|
u32 api = 0;
|
|
for (int32_t i = 0; i < physical_device_count; ++i) {
|
|
VkPhysicalDeviceProperties2 base_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
|
|
vkGetPhysicalDeviceProperties2(physical_devices[i], &base_properties);
|
|
switch (base_properties.properties.deviceType) {
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
|
|
if (base_properties.properties.apiVersion > api) {
|
|
api = base_properties.properties.apiVersion;
|
|
index = i;
|
|
}
|
|
} break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
|
|
if (base_properties.properties.apiVersion > api) {
|
|
api = base_properties.properties.apiVersion;
|
|
index = i;
|
|
}
|
|
} break;
|
|
default: continue;
|
|
}
|
|
}
|
|
|
|
if (index == -1 || api == 0) {
|
|
status = set_error(RETURN_FAILED_TO_FIND_SUITABLE_PHYSICAL_DEVICE,
|
|
"Couldn't find suitable physical device");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
physical_device = physical_devices[index];
|
|
|
|
VkPhysicalDeviceProperties2 physical_device_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
|
|
VkPhysicalDeviceVulkan11Properties vulkan_11_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES };
|
|
VkPhysicalDeviceVulkan12Properties vulkan_12_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES };
|
|
VkPhysicalDeviceVulkan13Properties vulkan_13_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES };
|
|
VkPhysicalDeviceVulkan14Properties vulkan_14_properties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_PROPERTIES };
|
|
|
|
void *last = &physical_device_properties;
|
|
if (api >= VK_API_VERSION_1_1) {
|
|
last = ((VkPhysicalDeviceProperties2 *)last)->pNext = &vulkan_11_properties;
|
|
}
|
|
if (api >= VK_API_VERSION_1_2) {
|
|
last = ((VkPhysicalDeviceVulkan11Properties *)last)->pNext = &vulkan_12_properties;
|
|
}
|
|
if (api >= VK_API_VERSION_1_3) {
|
|
last = ((VkPhysicalDeviceVulkan12Properties *)last)->pNext = &vulkan_13_properties;
|
|
}
|
|
if (api >= VK_API_VERSION_1_4) {
|
|
last = ((VkPhysicalDeviceVulkan13Properties *)last)->pNext = &vulkan_14_properties;
|
|
}
|
|
|
|
vkGetPhysicalDeviceProperties2(physical_device, &physical_device_properties);
|
|
|
|
VkPhysicalDeviceMemoryProperties2 memory_properties = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2,
|
|
};
|
|
vkGetPhysicalDeviceMemoryProperties2(physical_device, &memory_properties);
|
|
// }}}
|
|
|
|
// Find suitable queue family {{{
|
|
u32 queue_family_property_count = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_property_count, NULL);
|
|
if (queue_family_property_count == 0) {
|
|
status = set_error(RETURN_NO_QUEUE_FAMILY_PROPERTIES_FOUND, "No queue family properties found");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
u32 alloc_size = queue_family_property_count * sizeof(VkQueueFamilyProperties2);
|
|
VkQueueFamilyProperties2 *queue_family_properties = wapp_mem_allocator_alloc(&arena, alloc_size);
|
|
if (!queue_family_properties) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_QUEUE_FAMILY_PROPERTIES,
|
|
"Failed to allocate memory for queue family properties");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
memset(queue_family_properties, 0, alloc_size);
|
|
for (u32 i = 0; i < queue_family_property_count; ++i) {
|
|
queue_family_properties[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
|
|
}
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_property_count, queue_family_properties);
|
|
b8 supports_graphics = false;
|
|
for (u32 i = 0; i < queue_family_property_count; ++i) {
|
|
supports_graphics = supports_graphics ||
|
|
(queue_family_properties[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) == VK_QUEUE_GRAPHICS_BIT;
|
|
if (supports_graphics) {
|
|
queue_family_index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!supports_graphics) {
|
|
status = set_error(RETURN_SELECTED_DEVICE_DOES_NOT_SUPPORT_GRAPHICS,
|
|
"Selected physical device doesn't support graphics operations");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
// }}}
|
|
|
|
// Logical device creation {{{
|
|
VkPhysicalDeviceShaderObjectFeaturesEXT shader_object_feature = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT,
|
|
.shaderObject = VK_TRUE,
|
|
};
|
|
VkPhysicalDevicePresentModeFifoLatestReadyFeaturesKHR fifo_latest_ready_feature = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_MODE_FIFO_LATEST_READY_FEATURES_KHR,
|
|
.pNext = &shader_object_feature,
|
|
.presentModeFifoLatestReady = VK_TRUE,
|
|
};
|
|
VkPhysicalDeviceVulkan14Features vulkan_14_features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES,
|
|
.pNext = &fifo_latest_ready_feature,
|
|
.bresenhamLines = VK_TRUE,
|
|
.smoothLines = VK_TRUE,
|
|
.pushDescriptor = VK_TRUE,
|
|
};
|
|
VkPhysicalDeviceVulkan13Features vulkan_13_features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
|
.pNext = &vulkan_14_features,
|
|
.dynamicRendering = VK_TRUE,
|
|
.synchronization2 = VK_TRUE,
|
|
};
|
|
VkPhysicalDeviceVulkan12Features vulkan_12_features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
|
.pNext = &vulkan_13_features,
|
|
.timelineSemaphore = VK_TRUE,
|
|
};
|
|
VkPhysicalDeviceVulkan11Features vulkan_11_features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
|
.pNext = &vulkan_12_features,
|
|
.shaderDrawParameters = VK_TRUE,
|
|
};
|
|
VkPhysicalDeviceFeatures core_features = {
|
|
.logicOp = VK_TRUE,
|
|
.fillModeNonSolid = VK_TRUE,
|
|
.samplerAnisotropy = VK_TRUE,
|
|
};
|
|
VkPhysicalDeviceFeatures2 device_features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
|
.pNext = &vulkan_11_features,
|
|
.features = core_features,
|
|
};
|
|
f32 priorities[] = {0.5};
|
|
VkDeviceQueueCreateInfo queue_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = queue_family_index,
|
|
.queueCount = 1,
|
|
.pQueuePriorities = priorities,
|
|
};
|
|
const char *device_extensions[] = {
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
VK_EXT_SHADER_OBJECT_EXTENSION_NAME,
|
|
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
|
|
VK_EXT_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME,
|
|
};
|
|
u32 device_extensions_count = sizeof(device_extensions) / sizeof(const char *);
|
|
VkDeviceCreateInfo device_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
.pNext = &device_features,
|
|
.queueCreateInfoCount = 1,
|
|
.pQueueCreateInfos = &queue_create_info,
|
|
.enabledExtensionCount = device_extensions_count,
|
|
.ppEnabledExtensionNames = device_extensions,
|
|
};
|
|
|
|
u32 dev_ext_count;
|
|
if (vkEnumerateDeviceExtensionProperties(physical_device, NULL,
|
|
&dev_ext_count, NULL) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_ENUMERATE_PHYSICAL_DEVICE_EXTENSIONS,
|
|
"Failed to enumerate physical device extensions");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
VkExtensionProperties *extension_properties = wapp_mem_allocator_alloc(&arena, dev_ext_count * sizeof(VkExtensionProperties));
|
|
if (vkEnumerateDeviceExtensionProperties(physical_device, NULL,
|
|
&dev_ext_count, extension_properties) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_QUERY_PHYSICAL_DEVICE_EXTENSIONS,
|
|
"Failed to query physical device extensions");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
for (u32 i = 0; i < dev_ext_count; ++i) {
|
|
if (memcmp(extension_properties[i].extensionName, VK_KHR_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME,
|
|
sizeof(VK_KHR_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME)) == 0) {
|
|
device_extensions[device_extensions_count - 1] = VK_KHR_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vkCreateDevice(physical_device, &device_create_info, NULL, &device) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_LOGICAL_DEVICE,
|
|
"Failed to create device");
|
|
goto DESTROY_VULKAN_SURFACE;
|
|
}
|
|
|
|
volkLoadDevice(device);
|
|
/// }}}
|
|
|
|
// Get device queue {{{
|
|
vkGetDeviceQueue(device, queue_family_index, 0, &queue);
|
|
// }}}
|
|
|
|
// Create command pool {{{
|
|
VkCommandPoolCreateInfo pool_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
.queueFamilyIndex = queue_family_index,
|
|
};
|
|
if (vkCreateCommandPool(device, &pool_create_info, NULL, &command_pool) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_COMMAND_POOL, "Failed to create command pool");
|
|
goto DESTROY_VULKAN_DEVICE;
|
|
}
|
|
// }}}
|
|
|
|
// Allocate command buffers {{{
|
|
VkCommandBufferAllocateInfo cmd_buf_alloc_info = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
.commandBufferCount = 1,
|
|
.commandPool = command_pool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
};
|
|
for (u32 i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
|
if (vkAllocateCommandBuffers(device, &cmd_buf_alloc_info, &command_buffers[i]) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_COMMAND_BUFFER, "Failed to allocate command buffer");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
}
|
|
// }}}
|
|
|
|
// Create swapchain {{{
|
|
VkSurfaceCapabilitiesKHR capabilities = {0};
|
|
if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_QUERY_SURFACE_CAPABILITIES, "Failed to query surface capabilities");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
|
|
swapchain_extent = capabilities.currentExtent;
|
|
|
|
u32 format_count;
|
|
if (vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, NULL) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_ENUMERATE_SUPPORTED_SURFACE_FORMATS,
|
|
"Failed to enumerate supported surface formats");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
VkSurfaceFormatKHR *formats = wapp_mem_allocator_alloc(&arena, format_count * sizeof(VkSurfaceFormatKHR));
|
|
if (!formats) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_SURFACE_FORMATS,
|
|
"Failed to allocate memory for surface formats");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
if (vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, formats) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_QUERY_SURPPORTED_SURFACE_FORMATS,
|
|
"Failed to query supported surface formats");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
|
|
VkSurfaceFormatKHR *format = NULL;
|
|
for (u32 i = 0; i < format_count; ++i) {
|
|
if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && formats[i].colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
|
|
format = &(formats[i]);
|
|
break;
|
|
}
|
|
}
|
|
if (!format) { format = &formats[0]; }
|
|
swapchain_format = *format;
|
|
|
|
u32 present_mode_count;
|
|
if (vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, NULL) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_ENUMERATE_SUPPORTED_SURFACE_PRESENT_MODES,
|
|
"Failed to enumerate supported surface present modes");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
VkPresentModeKHR *present_modes = wapp_mem_allocator_alloc(&arena, present_mode_count * sizeof(VkPresentModeKHR));
|
|
if (!present_modes) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_MEMORY_FOR_SURFACE_PRESENT_MODES,
|
|
"Failed to allocate memory for surface present modes");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
|
for (u32 i = 0; i < present_mode_count; ++i) {
|
|
if (present_modes[i] == VK_PRESENT_MODE_FIFO_LATEST_READY_KHR) {
|
|
swapchain_present_mode = present_modes[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR swapchain_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
|
.surface = surface,
|
|
.minImageCount = capabilities.minImageCount,
|
|
.imageFormat = swapchain_format.format,
|
|
.imageColorSpace = swapchain_format.colorSpace,
|
|
.imageExtent = capabilities.currentExtent,
|
|
.imageArrayLayers = 1,
|
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
.queueFamilyIndexCount = 1,
|
|
.pQueueFamilyIndices = &queue_family_index,
|
|
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
|
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
.presentMode = swapchain_present_mode,
|
|
};
|
|
|
|
if (vkCreateSwapchainKHR(device, &swapchain_info, NULL, &swapchain) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_SWAPCHAIN, "Failed to create swapchain");
|
|
goto FREE_COMMAND_BUFFER;
|
|
}
|
|
// }}}
|
|
|
|
// Get swapchain images {{{
|
|
u32 image_count;
|
|
if (vkGetSwapchainImagesKHR(device, swapchain, &image_count, NULL) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_GET_SWAPCHAIN_IMAGE_COUNT, "Failed to get swapchain images count");
|
|
goto DESTROY_SWAPCHAIN;
|
|
}
|
|
swapchain_images = wapp_array_alloc_capacity(VkImage, VkImageArray, &arena, image_count, true);
|
|
swapchain_image_views = wapp_array_alloc_capacity(VkImageView, VkImageViewArray, &arena, image_count, false);
|
|
if (!swapchain_images || !swapchain_image_views) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_SWAPCHAIN_IMAGES_MEMORY,
|
|
"Failed to allocate swapchain images memory");
|
|
goto DESTROY_SWAPCHAIN;
|
|
}
|
|
if (vkGetSwapchainImagesKHR(device, swapchain, (u32 *)&(swapchain_images->count), swapchain_images->items) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_GET_SWAPCHAIN_IMAGES, "Failed to get swapchain images");
|
|
goto DESTROY_SWAPCHAIN;
|
|
}
|
|
// }}}
|
|
|
|
// Create swapchain image views {{{
|
|
for (u32 i = 0; i < swapchain_images->count; ++i) {
|
|
VkImageViewCreateInfo view_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = *wapp_array_get(VkImage, swapchain_images, i),
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = swapchain_format.format,
|
|
.subresourceRange = {
|
|
.levelCount = 1,
|
|
.baseMipLevel = 0,
|
|
.layerCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
},
|
|
};
|
|
VkImageView view;
|
|
if (vkCreateImageView(device, &view_info, NULL, &view) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_IMAGE_VIEWS, "Failed to create image views");
|
|
goto DESTROY_IMAGE_VIEWS;
|
|
}
|
|
wapp_array_append_capped(VkImageView, swapchain_image_views, &view);
|
|
}
|
|
// }}}
|
|
|
|
// Create synchronization objects {{{
|
|
VkFenceCreateInfo fence_info = {
|
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
|
};
|
|
VkSemaphoreCreateInfo semaphore_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
|
};
|
|
for (u32 i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
|
if (vkCreateFence(device, &fence_info, NULL, wapp_array_get(VkFence, &fences, i)) != VK_SUCCESS ||
|
|
vkCreateSemaphore(device, &semaphore_info, NULL, wapp_array_get(VkSemaphore, &present_complete_semaphores, i)) != VK_SUCCESS ||
|
|
vkCreateSemaphore(device, &semaphore_info, NULL, wapp_array_get(VkSemaphore, &render_finished_semaphores, i)) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_SYNC_OBJECTS, "Failed to create sync objects");
|
|
goto DESTROY_SYNC_OBJECTS;
|
|
}
|
|
}
|
|
// }}}
|
|
|
|
// Create shader module {{{
|
|
FILE *shader_file = fopen(SHADER_FILE, "rb");
|
|
fseek(shader_file, 0, SEEK_END);
|
|
u64 length = ftell(shader_file);
|
|
fseek(shader_file, 0, SEEK_SET);
|
|
|
|
u8 *code = wapp_mem_allocator_alloc(&arena, length * sizeof(u8));
|
|
fread(code, sizeof(u8), length, shader_file);
|
|
|
|
VkShaderModuleCreateInfo module_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
.codeSize = length,
|
|
.pCode = (u32 *)code,
|
|
};
|
|
if (vkCreateShaderModule(device, &module_info, NULL, &shader_module) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_SHADER_MODULE, "Failed to create shader module");
|
|
goto DESTROY_SYNC_OBJECTS;
|
|
}
|
|
|
|
fclose(shader_file);
|
|
// }}}
|
|
|
|
// Create depth resources {{{
|
|
depth_image_format = VK_FORMAT_D32_SFLOAT_S8_UINT;
|
|
VkExtent3D depth_image_extent = {
|
|
.width = swapchain_extent.width,
|
|
.height = swapchain_extent.height,
|
|
.depth = 1
|
|
};
|
|
VkImageCreateInfo depth_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
.imageType = VK_IMAGE_TYPE_2D,
|
|
.format = depth_image_format,
|
|
.extent = depth_image_extent,
|
|
.mipLevels = 1,
|
|
.arrayLayers = 1,
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
|
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
};
|
|
|
|
if (vkCreateImage(device, &depth_info, NULL, &depth_image) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_DEPTH_IMAGE, "Failed to create depth image");
|
|
goto DESTROY_DEPTH_RESOURCES;
|
|
}
|
|
|
|
VkMemoryRequirements depth_memory_requirements = {0};
|
|
vkGetImageMemoryRequirements(device, depth_image, &depth_memory_requirements);
|
|
|
|
i32 memory_type_index = get_memory_type(&memory_properties.memoryProperties,
|
|
depth_memory_requirements.memoryTypeBits,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
if (memory_type_index == -1) {
|
|
status = set_error(RETURN_FAILED_TO_FIND_SUITABLE_MEMORY_TYPE_FOR_DEPTH_IMAGE,
|
|
"Failed to find suitable memory type for depth image");
|
|
goto DESTROY_DEPTH_RESOURCES;
|
|
}
|
|
|
|
VkMemoryPriorityAllocateInfoEXT allocation_priority = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT,
|
|
.priority = 1.0f,
|
|
};
|
|
VkMemoryAllocateInfo depth_memory_alloc_info = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
.pNext = &allocation_priority,
|
|
.allocationSize = depth_memory_requirements.size,
|
|
.memoryTypeIndex = (u32)memory_type_index,
|
|
};
|
|
if (vkAllocateMemory(device, &depth_memory_alloc_info, NULL, &depth_image_memory) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_ALLOCATE_DEPTH_IMAGE_MEMORY, "Failed to allocate depth image memory");
|
|
goto DESTROY_DEPTH_RESOURCES;
|
|
}
|
|
|
|
if (vkBindImageMemory(device, depth_image, depth_image_memory, 0) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_BIND_DEPTH_IMAGE_MEMORY, "Failed to bind depth image memory");
|
|
goto DESTROY_DEPTH_RESOURCES;
|
|
}
|
|
|
|
VkImageViewCreateInfo depth_view_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = depth_image,
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = depth_image_format,
|
|
.subresourceRange = (VkImageSubresourceRange){
|
|
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1,
|
|
},
|
|
};
|
|
if (vkCreateImageView(device, &depth_view_info, NULL, &depth_image_view) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_DEPTH_IMAGE_VIEW, "Failed to create depth image view");
|
|
goto DESTROY_DEPTH_RESOURCES;
|
|
}
|
|
// }}}
|
|
|
|
// Create copying command buffer and fence {{{
|
|
VkFence copy_fence = VK_NULL_HANDLE;
|
|
VkFenceCreateInfo copy_fence_info = {
|
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
};
|
|
if (vkCreateFence(device, ©_fence_info, NULL, ©_fence) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_COPY_COMMAND_BUFFER, "Failed to create copy command buffer");
|
|
goto FREE_COPY_BUFFER;
|
|
}
|
|
|
|
VkCommandBuffer copy_buffer = VK_NULL_HANDLE;
|
|
VkCommandBufferAllocateInfo copy_buffer_info = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
.commandPool = command_pool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
.commandBufferCount = 1,
|
|
};
|
|
if (vkAllocateCommandBuffers(device, ©_buffer_info, ©_buffer) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_COPY_COMMAND_BUFFER, "Failed to create copy command buffer");
|
|
goto FREE_COPY_BUFFER;
|
|
}
|
|
// }}}
|
|
|
|
VkCommandBufferBeginInfo begin_info = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
};
|
|
vkBeginCommandBuffer(copy_buffer, &begin_info);
|
|
|
|
// Create vertex buffer {{{
|
|
u64 vertex_buffer_size = sizeof(Vertex) * vertices.count;
|
|
VkBuffer vertex_staging = VK_NULL_HANDLE;
|
|
VkDeviceMemory vertex_staging_memory = VK_NULL_HANDLE;
|
|
if (!create_buffer(&memory_properties.memoryProperties, device, 0, vertex_buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&vertex_staging, &vertex_staging_memory)) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_VERTEX_BUFFER, "Failed to create vertex buffer");
|
|
goto DESTROY_VERTEX_BUFFER;
|
|
}
|
|
|
|
void *vertex_staging_mapped = NULL;
|
|
if (vkMapMemory(device, vertex_staging_memory, 0, vertex_buffer_size, 0, &vertex_staging_mapped) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_VERTEX_BUFFER, "Failed to create vertex buffer");
|
|
goto DESTROY_VERTEX_BUFFER;
|
|
}
|
|
memcpy(vertex_staging_mapped, vertices.items, vertex_buffer_size);
|
|
vkUnmapMemory(device, vertex_staging_memory);
|
|
|
|
if (!create_buffer(&memory_properties.memoryProperties, device, 0, vertex_buffer_size,
|
|
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertex_buffer, &vertex_buffer_memory)) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_VERTEX_BUFFER, "Failed to create vertex buffer");
|
|
goto DESTROY_VERTEX_BUFFER;
|
|
}
|
|
|
|
VkBufferCopy vertex_buffer_copy = {
|
|
.srcOffset = 0,
|
|
.dstOffset = 0,
|
|
.size = vertex_buffer_size,
|
|
};
|
|
vkCmdCopyBuffer(copy_buffer, vertex_staging, vertex_buffer, 1, &vertex_buffer_copy);
|
|
// }}}
|
|
|
|
// Create index buffer {{{
|
|
u64 index_buffer_size = sizeof(u32) * indices.count;
|
|
VkBuffer index_staging = VK_NULL_HANDLE;
|
|
VkDeviceMemory index_staging_memory = VK_NULL_HANDLE;
|
|
if (!create_buffer(&memory_properties.memoryProperties, device, 0, index_buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
&index_staging, &index_staging_memory)) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_VERTEX_BUFFER, "Failed to create index buffer");
|
|
goto DESTROY_INDEX_BUFFER;
|
|
}
|
|
|
|
void *index_staging_mapped = NULL;
|
|
if (vkMapMemory(device, index_staging_memory, 0, index_buffer_size, 0, &index_staging_mapped) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_VERTEX_BUFFER, "Failed to create index buffer");
|
|
goto DESTROY_INDEX_BUFFER;
|
|
}
|
|
memcpy(index_staging_mapped, indices.items, index_buffer_size);
|
|
vkUnmapMemory(device, index_staging_memory);
|
|
|
|
if (!create_buffer(&memory_properties.memoryProperties, device, 0, index_buffer_size,
|
|
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &index_buffer, &index_buffer_memory)) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_VERTEX_BUFFER, "Failed to create index buffer");
|
|
goto DESTROY_INDEX_BUFFER;
|
|
}
|
|
|
|
VkBufferCopy index_buffer_copy = {
|
|
.srcOffset = 0,
|
|
.dstOffset = 0,
|
|
.size = index_buffer_size,
|
|
};
|
|
vkCmdCopyBuffer(copy_buffer, index_staging, index_buffer, 1, &index_buffer_copy);
|
|
// }}}
|
|
|
|
vkEndCommandBuffer(copy_buffer);
|
|
|
|
// Submit copying vertex and index buffers {{{
|
|
VkSubmitInfo copy_submit_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = ©_buffer,
|
|
};
|
|
if (vkQueueSubmit(queue, 1, ©_submit_info, copy_fence) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_COPY_VERTEX_AND_INDEX_DATA, "Failed to copy vertex and index data");
|
|
goto DESTROY_INDEX_BUFFER;
|
|
}
|
|
while (vkWaitForFences(device, 1, ©_fence, VK_TRUE, UINT64_MAX) == VK_TIMEOUT) {}
|
|
// }}}
|
|
|
|
// Create graphics pipeline {{{
|
|
VkPipelineShaderStageCreateInfo shader_stages[SHADER_COUNT] = {
|
|
(VkPipelineShaderStageCreateInfo){
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
.module = shader_module,
|
|
.pName = "vertMain",
|
|
},
|
|
(VkPipelineShaderStageCreateInfo){
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.module = shader_module,
|
|
.pName = "fragMain",
|
|
},
|
|
};
|
|
VkVertexInputBindingDescription vertex_binding = {
|
|
.binding = 0,
|
|
.stride = sizeof(Vertex),
|
|
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
|
};
|
|
VkVertexInputAttributeDescription vertex_attributes[] = {
|
|
(VkVertexInputAttributeDescription){
|
|
.location = 0,
|
|
.binding = 0,
|
|
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
|
.offset = offsetof(Vertex, position),
|
|
},
|
|
(VkVertexInputAttributeDescription){
|
|
.location = 1,
|
|
.binding = 0,
|
|
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
|
.offset = offsetof(Vertex, color),
|
|
},
|
|
};
|
|
u64 attributes_count = sizeof(vertex_attributes) / sizeof(vertex_attributes[0]);
|
|
VkPipelineVertexInputStateCreateInfo vertex_input = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
.vertexBindingDescriptionCount = 1,
|
|
.pVertexBindingDescriptions = &vertex_binding,
|
|
.vertexAttributeDescriptionCount = attributes_count,
|
|
.pVertexAttributeDescriptions = vertex_attributes,
|
|
};
|
|
VkPipelineInputAssemblyStateCreateInfo input_assembly = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
};
|
|
VkPipelineRasterizationStateCreateInfo rasterisation = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
.polygonMode = VK_POLYGON_MODE_FILL,
|
|
.cullMode = VK_CULL_MODE_BACK_BIT,
|
|
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
|
.lineWidth = 1.0f,
|
|
};
|
|
VkPipelineMultisampleStateCreateInfo multisampling = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
|
};
|
|
VkPipelineDepthStencilStateCreateInfo depth_stencil = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
|
.depthTestEnable = VK_TRUE,
|
|
.depthWriteEnable = VK_TRUE,
|
|
.depthCompareOp = VK_COMPARE_OP_LESS,
|
|
};
|
|
VkPipelineColorBlendAttachmentState blend_state = {
|
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
|
|
VK_COLOR_COMPONENT_G_BIT |
|
|
VK_COLOR_COMPONENT_B_BIT |
|
|
VK_COLOR_COMPONENT_A_BIT,
|
|
};
|
|
VkPipelineColorBlendStateCreateInfo blending = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
.logicOpEnable = VK_TRUE,
|
|
.logicOp = VK_LOGIC_OP_COPY,
|
|
.attachmentCount = 1,
|
|
.pAttachments = &blend_state,
|
|
};
|
|
VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT};
|
|
VkPipelineDynamicStateCreateInfo dynamic_state = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
.dynamicStateCount = 2,
|
|
.pDynamicStates = dynamic_states,
|
|
};
|
|
VkPipelineLayoutCreateInfo layout_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
};
|
|
if (vkCreatePipelineLayout(device, &layout_info, NULL, &pipeline_layout) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_PIPELINE_LAYOUT, "Failed to create pipeline layout");
|
|
goto DESTROY_DEPTH_RESOURCES;
|
|
}
|
|
VkFormat color_attachment_format = swapchain_format.format;
|
|
VkPipelineRenderingCreateInfo rendering_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
|
|
.colorAttachmentCount = 1,
|
|
.pColorAttachmentFormats = &color_attachment_format,
|
|
.depthAttachmentFormat = depth_image_format,
|
|
.stencilAttachmentFormat = depth_image_format,
|
|
};
|
|
|
|
VkGraphicsPipelineCreateInfo pipeline_info = {
|
|
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
.pNext = &rendering_info,
|
|
.stageCount = SHADER_COUNT,
|
|
.pStages = shader_stages,
|
|
.pVertexInputState = &vertex_input,
|
|
.pInputAssemblyState = &input_assembly,
|
|
.pRasterizationState = &rasterisation,
|
|
.pMultisampleState = &multisampling,
|
|
.pDepthStencilState = &depth_stencil,
|
|
.pColorBlendState = &blending,
|
|
.pDynamicState = &dynamic_state,
|
|
.layout = pipeline_layout,
|
|
};
|
|
if (vkCreateGraphicsPipelines(device, NULL, 1, &pipeline_info, NULL, &graphics_pipeline) != VK_SUCCESS) {
|
|
status = set_error(RETURN_FAILED_TO_CREATE_GRAPHICS_PIPELINE, "Failed to create graphics pipeline");
|
|
goto DESTROY_PIPELINE_LAYOUT;
|
|
}
|
|
// }}}
|
|
|
|
// Main loop {{{
|
|
u64 frame_start, frame_time;
|
|
b8 running = true;
|
|
SDL_Event event = {0};
|
|
while (running) {
|
|
frame_start = SDL_GetTicksNS();
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
switch (event.type) {
|
|
case SDL_EVENT_QUIT: {
|
|
running = false;
|
|
} break;
|
|
case SDL_EVENT_KEY_DOWN: {
|
|
if (event.key.key == SDLK_ESCAPE) {
|
|
running = false;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
draw_frame();
|
|
|
|
frame_time = SDL_GetTicksNS() - frame_start;
|
|
if (frame_time < FRAME_TIME) { SDL_DelayNS(FRAME_TIME - frame_time); }
|
|
}
|
|
// }}}
|
|
|
|
// Cleanup {{{
|
|
vkDeviceWaitIdle(device);
|
|
|
|
DESTROY_GRAPHICS_PIPELINE:
|
|
vkDestroyPipeline(device, graphics_pipeline, NULL);
|
|
DESTROY_PIPELINE_LAYOUT:
|
|
vkDestroyPipelineLayout(device, pipeline_layout, NULL);
|
|
DESTROY_INDEX_BUFFER:
|
|
if (index_staging != VK_NULL_HANDLE) { vkDestroyBuffer(device, index_staging, NULL); }
|
|
if (index_staging_memory != VK_NULL_HANDLE) { vkFreeMemory(device, index_staging_memory, NULL); }
|
|
if (index_buffer != VK_NULL_HANDLE) { vkDestroyBuffer(device, index_buffer, NULL); }
|
|
if (index_buffer_memory != VK_NULL_HANDLE) { vkFreeMemory(device, index_buffer_memory, NULL); }
|
|
DESTROY_VERTEX_BUFFER:
|
|
if (vertex_staging != VK_NULL_HANDLE) { vkDestroyBuffer(device, vertex_staging, NULL); }
|
|
if (vertex_staging_memory != VK_NULL_HANDLE) { vkFreeMemory(device, vertex_staging_memory, NULL); }
|
|
if (vertex_buffer != VK_NULL_HANDLE) { vkDestroyBuffer(device, vertex_buffer, NULL); }
|
|
if (vertex_buffer_memory != VK_NULL_HANDLE) { vkFreeMemory(device, vertex_buffer_memory, NULL); }
|
|
FREE_COPY_BUFFER:
|
|
if (copy_fence != VK_NULL_HANDLE) { vkDestroyFence(device, copy_fence, NULL); }
|
|
if (copy_buffer != VK_NULL_HANDLE) { vkFreeCommandBuffers(device, command_pool, 1, ©_buffer); }
|
|
DESTROY_DEPTH_RESOURCES:
|
|
if (depth_image_view != VK_NULL_HANDLE) { vkDestroyImageView(device, depth_image_view, NULL); }
|
|
if (depth_image_memory != VK_NULL_HANDLE) { vkFreeMemory(device, depth_image_memory, NULL); }
|
|
if (depth_image != VK_NULL_HANDLE) { vkDestroyImage(device, depth_image, NULL); }
|
|
DESTROY_SHADER_MODULE:
|
|
if (shader_module != VK_NULL_HANDLE) { vkDestroyShaderModule(device, shader_module, NULL); }
|
|
DESTROY_SYNC_OBJECTS:
|
|
for (u32 i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
|
if (wapp_array_get(VkFence, &fences, i) != VK_NULL_HANDLE) {
|
|
vkDestroyFence(device, *wapp_array_get(VkFence, &fences, i), NULL);
|
|
}
|
|
if (wapp_array_get(VkSemaphore, &present_complete_semaphores, i) != VK_NULL_HANDLE) {
|
|
vkDestroySemaphore(device, *wapp_array_get(VkSemaphore, &present_complete_semaphores, i), NULL);
|
|
}
|
|
if (wapp_array_get(VkSemaphore, &render_finished_semaphores, i) != VK_NULL_HANDLE) {
|
|
vkDestroySemaphore(device, *wapp_array_get(VkSemaphore, &render_finished_semaphores, i), NULL);
|
|
}
|
|
}
|
|
DESTROY_IMAGE_VIEWS:
|
|
for (u32 i = 0; i < swapchain_images->count; ++i) {
|
|
VkImageView view = *wapp_array_get(VkImageView, swapchain_image_views, i);
|
|
if (view != VK_NULL_HANDLE) { vkDestroyImageView(device, view, NULL); }
|
|
}
|
|
DESTROY_SWAPCHAIN:
|
|
vkDestroySwapchainKHR(device, swapchain, NULL);
|
|
FREE_COMMAND_BUFFER:
|
|
for (u32 i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
|
if (command_buffers[i] != VK_NULL_HANDLE) { vkFreeCommandBuffers(device, command_pool, 1, &command_buffers[i]); }
|
|
}
|
|
DESTROY_COMMAND_POOL:
|
|
vkDestroyCommandPool(device, command_pool, NULL);
|
|
DESTROY_VULKAN_DEVICE:
|
|
vkDestroyDevice(device, NULL);
|
|
DESTROY_VULKAN_SURFACE:
|
|
SDL_Vulkan_DestroySurface(instance, surface, NULL);
|
|
DESTROY_VULKAN_INSTANCE:
|
|
vkDestroyInstance(instance, NULL);
|
|
SDL_CLEANUP:
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
|
|
wapp_mem_arena_allocator_destroy(&arena);
|
|
// }}}
|
|
|
|
return status;
|
|
}
|
|
|
|
static inline void draw_frame(void) {
|
|
// Ensure queue isn't still rendering using objects for the current frame_index {{{
|
|
while (vkWaitForFences(device, 1, wapp_array_get(VkFence, &fences, frame_index), VK_TRUE, UINT64_MAX) == VK_TIMEOUT) {}
|
|
vkResetFences(device, 1, wapp_array_get(VkFence, &fences, frame_index));
|
|
// }}}
|
|
|
|
vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, *wapp_array_get(VkSemaphore, &present_complete_semaphores, frame_index),
|
|
VK_NULL_HANDLE, &image_index);
|
|
|
|
record_command_buffer();
|
|
|
|
// Submit command buffer to queue {{{
|
|
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
VkSubmitInfo submit_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
.waitSemaphoreCount = 1,
|
|
.pWaitSemaphores = wapp_array_get(VkSemaphore, &present_complete_semaphores, frame_index),
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = &command_buffers[frame_index],
|
|
.pWaitDstStageMask = &wait_stage,
|
|
.signalSemaphoreCount = 1,
|
|
.pSignalSemaphores = wapp_array_get(VkSemaphore, &render_finished_semaphores, frame_index),
|
|
};
|
|
vkQueueSubmit(queue, 1, &submit_info, *wapp_array_get(VkFence, &fences, frame_index));
|
|
// }}}
|
|
|
|
// Present the swapchain image when rendering is done {{{
|
|
VkPresentInfoKHR present_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
|
.waitSemaphoreCount = 1,
|
|
.pWaitSemaphores = wapp_array_get(VkSemaphore, &render_finished_semaphores, frame_index),
|
|
.swapchainCount = 1,
|
|
.pSwapchains = &(swapchain),
|
|
.pImageIndices = &image_index,
|
|
};
|
|
vkQueuePresentKHR(queue, &present_info);
|
|
// }}}
|
|
|
|
frame_index = (frame_index + 1) % FRAMES_IN_FLIGHT;
|
|
}
|
|
|
|
static inline void record_command_buffer() {
|
|
VkCommandBufferBeginInfo begin_info = {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
};
|
|
vkBeginCommandBuffer(command_buffers[frame_index], &begin_info);
|
|
|
|
// Transition image layout for rendering {{{
|
|
transition_image_layout(
|
|
command_buffers[frame_index],
|
|
*wapp_array_get(VkImage, swapchain_images, image_index),
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_ACCESS_NONE,
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_IMAGE_ASPECT_COLOR_BIT
|
|
);
|
|
transition_image_layout(
|
|
command_buffers[frame_index],
|
|
depth_image,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
|
|
);
|
|
// }}}
|
|
|
|
// Dynamic rendering {{{
|
|
vkCmdBindPipeline(command_buffers[frame_index], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
|
|
|
VkDeviceSize vertex_buffer_offset = 0;
|
|
vkCmdBindVertexBuffers(command_buffers[frame_index], 0, 1, &vertex_buffer, &vertex_buffer_offset);
|
|
vkCmdBindIndexBuffer(command_buffers[frame_index], index_buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
|
|
VkViewport viewport = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = swapchain_extent.width,
|
|
.height = swapchain_extent.height,
|
|
.minDepth = MIN_DEPTH,
|
|
.maxDepth = MAX_DEPTH,
|
|
};
|
|
vkCmdSetViewportWithCount(command_buffers[frame_index], 1, &viewport);
|
|
|
|
VkRect2D scissor = {
|
|
.offset = (VkOffset2D){ .x = 0, .y = 0 },
|
|
.extent = swapchain_extent,
|
|
};
|
|
vkCmdSetScissorWithCount(command_buffers[frame_index], 1, &scissor);
|
|
|
|
VkClearValue color_clear = {
|
|
.color = (VkClearColorValue){ .float32 = {0.0f, 0.0f, 0.0f, 0.0f} }
|
|
};
|
|
VkRenderingAttachmentInfo color_attachment = {
|
|
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
|
.imageView = *wapp_array_get(VkImageView, swapchain_image_views, image_index),
|
|
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
.clearValue = color_clear
|
|
};
|
|
VkClearValue depth_clear = {
|
|
.depthStencil = (VkClearDepthStencilValue){ .depth = MAX_DEPTH }
|
|
};
|
|
VkRenderingAttachmentInfo depth_attachment = {
|
|
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
|
.imageView = depth_image_view,
|
|
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
.clearValue = depth_clear
|
|
};
|
|
VkRect2D render_area = {
|
|
.offset = (VkOffset2D){.x = 0, .y = 0},
|
|
.extent = swapchain_extent,
|
|
};
|
|
VkRenderingInfo rendering_info = {
|
|
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
|
.renderArea = render_area,
|
|
.layerCount = 1,
|
|
.colorAttachmentCount = 1,
|
|
.pColorAttachments = &color_attachment,
|
|
.pDepthAttachment = &depth_attachment
|
|
};
|
|
vkCmdBeginRendering(command_buffers[frame_index], &rendering_info);
|
|
vkCmdDrawIndexed(command_buffers[frame_index], indices.count, 1, 0, 0, 0);
|
|
vkCmdEndRendering(command_buffers[frame_index]);
|
|
// }}}
|
|
|
|
// Transition image layout for presentation {{{
|
|
transition_image_layout(
|
|
command_buffers[frame_index],
|
|
*wapp_array_get(VkImage, swapchain_images, image_index),
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
|
VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_ACCESS_2_NONE,
|
|
VK_IMAGE_ASPECT_COLOR_BIT
|
|
);
|
|
// }}}
|
|
|
|
vkEndCommandBuffer(command_buffers[frame_index]);
|
|
}
|
|
|
|
wapp_intern inline b8 create_buffer(VkPhysicalDeviceMemoryProperties *properties, VkDevice device,
|
|
VkBufferCreateFlags flags, VkDeviceSize size, VkBufferUsageFlags usage,
|
|
VkMemoryPropertyFlags memory_properties, VkBuffer *buffer,
|
|
VkDeviceMemory *buffer_memory) {
|
|
VkBufferCreateInfo buffer_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.flags = flags,
|
|
.size = size,
|
|
.usage = usage,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
};
|
|
if (vkCreateBuffer(device, &buffer_info, NULL, buffer) != VK_SUCCESS) { return false; }
|
|
|
|
VkBufferMemoryRequirementsInfo2 requirements_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,
|
|
.buffer = *buffer,
|
|
};
|
|
VkMemoryRequirements2 requirements = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
|
|
};
|
|
vkGetBufferMemoryRequirements2(device, &requirements_info, &requirements);
|
|
|
|
i32 memory_type = get_memory_type(properties, requirements.memoryRequirements.memoryTypeBits, memory_properties);
|
|
if (memory_type < 0) { return false; }
|
|
|
|
VkMemoryPriorityAllocateInfoEXT priority = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT,
|
|
.priority = 0.5f,
|
|
};
|
|
VkMemoryAllocateInfo alloc_info = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
.pNext = &priority,
|
|
.memoryTypeIndex = memory_type,
|
|
.allocationSize = requirements.memoryRequirements.size,
|
|
};
|
|
if (vkAllocateMemory(device, &alloc_info, NULL, buffer_memory) != VK_SUCCESS) { return false; }
|
|
|
|
if (vkBindBufferMemory(device, *buffer, *buffer_memory, 0) != VK_SUCCESS) { return false; }
|
|
|
|
return true;
|
|
}
|
|
|
|
wapp_intern inline void transition_image_layout(
|
|
VkCommandBuffer command_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout,
|
|
VkPipelineStageFlags2 src_stage, VkPipelineStageFlags2 dst_stage,
|
|
VkAccessFlags2 src_access, VkAccessFlags2 dst_access, VkImageAspectFlags aspect_mask
|
|
) {
|
|
VkImageMemoryBarrier2 barrier = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
.image = image,
|
|
.oldLayout = old_layout,
|
|
.newLayout = new_layout,
|
|
.srcStageMask = src_stage,
|
|
.dstStageMask = dst_stage,
|
|
.srcAccessMask = src_access,
|
|
.dstAccessMask = dst_access,
|
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
.subresourceRange = {
|
|
.aspectMask = aspect_mask,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1,
|
|
},
|
|
};
|
|
VkDependencyInfo dependency = {
|
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
.imageMemoryBarrierCount = 1,
|
|
.pImageMemoryBarriers = &barrier,
|
|
};
|
|
vkCmdPipelineBarrier2(command_buffer, &dependency);
|
|
}
|
|
|
|
wapp_intern inline i32 get_memory_type(const VkPhysicalDeviceMemoryProperties *properties,
|
|
u32 memory_type_bits, VkMemoryPropertyFlags flags) {
|
|
for (u32 i = 0; i < properties->memoryTypeCount; ++i) {
|
|
u32 type = (u32)1 << i;
|
|
b8 is_type = (type & memory_type_bits) == type;
|
|
if (!is_type) { continue; }
|
|
|
|
b8 has_properties = (flags & properties->memoryTypes[i].propertyFlags) == flags;
|
|
if (has_properties) { return (i32)i; }
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
wapp_intern inline u32 set_error(u32 code, const char *msg) {
|
|
fprintf(stderr, "%s\n", msg);
|
|
return code;
|
|
}
|