bloat-buster/bootstrap/std/vulkan_rendering.c

2503 lines
90 KiB
C

#pragma once
#define vulkan_load_function_generic(fn, load_fn, context) fn = (PFN_ ## fn) load_fn(context, #fn)
#define vulkan_load_instance_function(instance, fn) vulkan_load_function_generic(fn, vkGetInstanceProcAddr, instance)
#define vulkan_load_device_function(device, fn) vulkan_load_function_generic(fn, vkGetDeviceProcAddr, device)
global_variable OSLibrary vulkan_library;
#define MAX_SWAPCHAIN_IMAGE_COUNT (16)
#define MAX_FRAME_COUNT (2)
#define MAX_DESCRIPTOR_SET_COUNT (16)
#define MAX_PUSH_CONSTANT_RANGE_COUNT (16)
#define MAX_SHADER_MODULE_COUNT_PER_PIPELINE (16)
#define MAX_DESCRIPTOR_SET_LAYOUT_COUNT (16)
#define MAX_DESCRIPTOR_SET_LAYOUT_BINDING_COUNT (16)
#define MAX_TEXTURE_COUNT (16)
#define MAX_TEXTURE_UPDATE_COUNT (32)
#define MAX_DESCRIPTOR_SET_UPDATE_COUNT (16)
#define MAX_LOCAL_BUFFER_COPY_COUNT (16)
#define vkok(call) do {\
VkResult _r_e_s_u_l_t_ = call; \
if (unlikely(_r_e_s_u_l_t_ != VK_SUCCESS)) wrong_vulkan_result(_r_e_s_u_l_t_, strlit(#call), strlit(__FILE__), __LINE__); \
} while(0)
STRUCT(VulkanImageCreate)
{
u32 width;
u32 height;
u32 mip_levels;
VkFormat format;
VkImageUsageFlags usage;
};
STRUCT(GPUMemory)
{
VkDeviceMemory handle;
u64 size;
};
STRUCT(VulkanImage)
{
VkImage handle;
VkImageView view;
GPUMemory memory;
VkFormat format;
};
STRUCT(GPUDrawPushConstants)
{
u64 vertex_buffer;
f32 width;
f32 height;
};
STRUCT(DescriptorSetLayoutBindings)
{
VkDescriptorSetLayoutBinding buffer[MAX_DESCRIPTOR_SET_LAYOUT_BINDING_COUNT];
u32 count;
};
STRUCT(Pipeline)
{
VkPipeline handle;
VkPipelineLayout layout;
u32 descriptor_set_count;
u32 push_constant_range_count;
DescriptorSetLayoutBindings descriptor_set_layout_bindings[MAX_DESCRIPTOR_SET_COUNT];
VkDescriptorSetLayout descriptor_set_layouts[MAX_DESCRIPTOR_SET_COUNT];
VkPushConstantRange push_constant_ranges[MAX_PUSH_CONSTANT_RANGE_COUNT];
};
STRUCT(RectPipeline)
{
Pipeline pipeline;
};
STRUCT(ImmediateContext)
{
VkDevice device;
VkFence fence;
VkCommandBuffer command_buffer;
VkCommandPool command_pool;
VkQueue queue;
};
STRUCT(VulkanBuffer)
{
VkBuffer handle;
GPUMemory memory;
u64 address;
VkDeviceSize size;
BufferType type;
};
STRUCT(LocalBufferCopy)
{
VulkanBuffer destination;
VulkanBuffer source;
Slice(LocalBufferCopyRegion) regions;
};
declare_slice(LocalBufferCopy);
STRUCT(VertexBuffer)
{
VulkanBuffer gpu;
VirtualBuffer(u8) cpu;
u32 count;
};
STRUCT(IndexBuffer)
{
VulkanBuffer gpu;
VirtualBuffer(u32) cpu;
};
STRUCT(Renderer)
{
VkAllocationCallbacks* allocator;
VkPhysicalDevice physical_device;
VkDevice device;
VkQueue graphics_queue;
u32 graphics_queue_family_index;
VkSampler sampler;
ImmediateContext immediate;
VkPhysicalDeviceMemoryProperties memory_properties;
Pipeline pipelines[BB_PIPELINE_COUNT];
TextureAtlas fonts[RENDER_FONT_TYPE_COUNT];
};
STRUCT(PipelineInstantiation)
{
VkWriteDescriptorSet descriptor_set_update;
VkDescriptorSet descriptor_sets[MAX_DESCRIPTOR_SET_COUNT];
VkDescriptorImageInfo texture_descriptors[MAX_TEXTURE_UPDATE_COUNT];
};
STRUCT(FramePipelineInstantiation)
{
VertexBuffer vertex_buffer;
IndexBuffer index_buffer;
VulkanBuffer transient_buffer;
};
STRUCT(WindowFrame)
{
VkCommandPool command_pool;
VkCommandBuffer command_buffer;
VkSemaphore swapchain_semaphore;
VkSemaphore render_semaphore;
VkFence render_fence;
BBPipeline bound_pipeline;
VkBuffer index_buffer;
GPUDrawPushConstants push_constants;
FramePipelineInstantiation pipeline_instantiations[BB_PIPELINE_COUNT];
};
STRUCT(RenderWindow)
{
VkSwapchainKHR swapchain;
VkSurfaceKHR surface;
VkFormat swapchain_image_format;
u32 width;
u32 height;
u32 last_width;
u32 last_height;
u32 frame_index;
u32 swapchain_image_index;
u32 swapchain_image_count;
VulkanImage render_image;
VkImage swapchain_images[MAX_SWAPCHAIN_IMAGE_COUNT];
VkImageView swapchain_image_views[MAX_SWAPCHAIN_IMAGE_COUNT];
WindowFrame frames[MAX_FRAME_COUNT];
PipelineInstantiation pipeline_instantiations[BB_PIPELINE_COUNT];
};
STRUCT(VulkanTexture)
{
VulkanImage image;
VkSampler sampler;
};
STRUCT(VulkanCopyImage)
{
VkImage handle;
VkExtent2D extent;
};
STRUCT(VulkanCopyImageArgs)
{
VulkanCopyImage source;
VulkanCopyImage destination;
};
global_variable Renderer renderer_memory;
global_variable RenderWindow renderer_window_memory;
global_variable VulkanTexture textures[MAX_TEXTURE_COUNT];
global_variable u32 texture_count;
global_variable VkInstance instance;
fn String vulkan_result_to_string(VkResult result)
{
switch (result)
{
case_to_name(VK_, SUCCESS);
case_to_name(VK_, NOT_READY);
case_to_name(VK_, TIMEOUT);
case_to_name(VK_, EVENT_SET);
case_to_name(VK_, EVENT_RESET);
case_to_name(VK_, INCOMPLETE);
case_to_name(VK_, ERROR_OUT_OF_HOST_MEMORY);
case_to_name(VK_, ERROR_OUT_OF_DEVICE_MEMORY);
case_to_name(VK_, ERROR_INITIALIZATION_FAILED);
case_to_name(VK_, ERROR_DEVICE_LOST);
case_to_name(VK_, ERROR_MEMORY_MAP_FAILED);
case_to_name(VK_, ERROR_LAYER_NOT_PRESENT);
case_to_name(VK_, ERROR_EXTENSION_NOT_PRESENT);
case_to_name(VK_, ERROR_FEATURE_NOT_PRESENT);
case_to_name(VK_, ERROR_INCOMPATIBLE_DRIVER);
case_to_name(VK_, ERROR_TOO_MANY_OBJECTS);
case_to_name(VK_, ERROR_FORMAT_NOT_SUPPORTED);
case_to_name(VK_, ERROR_FRAGMENTED_POOL);
case_to_name(VK_, ERROR_UNKNOWN);
case_to_name(VK_, ERROR_OUT_OF_POOL_MEMORY);
case_to_name(VK_, ERROR_INVALID_EXTERNAL_HANDLE);
case_to_name(VK_, ERROR_FRAGMENTATION);
case_to_name(VK_, ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS);
case_to_name(VK_, PIPELINE_COMPILE_REQUIRED);
case_to_name(VK_, ERROR_SURFACE_LOST_KHR);
case_to_name(VK_, ERROR_NATIVE_WINDOW_IN_USE_KHR);
case_to_name(VK_, SUBOPTIMAL_KHR);
case_to_name(VK_, ERROR_OUT_OF_DATE_KHR);
case_to_name(VK_, ERROR_INCOMPATIBLE_DISPLAY_KHR);
case_to_name(VK_, ERROR_VALIDATION_FAILED_EXT);
case_to_name(VK_, ERROR_INVALID_SHADER_NV);
case_to_name(VK_, ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR);
case_to_name(VK_, ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR);
case_to_name(VK_, ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR);
case_to_name(VK_, ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR);
case_to_name(VK_, ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR);
case_to_name(VK_, ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR);
case_to_name(VK_, ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
case_to_name(VK_, ERROR_NOT_PERMITTED_KHR);
case_to_name(VK_, ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT);
case_to_name(VK_, THREAD_IDLE_KHR);
case_to_name(VK_, THREAD_DONE_KHR);
case_to_name(VK_, OPERATION_DEFERRED_KHR);
case_to_name(VK_, OPERATION_NOT_DEFERRED_KHR);
case_to_name(VK_, ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR);
case_to_name(VK_, ERROR_COMPRESSION_EXHAUSTED_EXT);
//case_to_name(VK_, INCOMPATIBLE_SHADER_BINARY_EXT);
//case_to_name(VK_, PIPELINE_BINARY_MISSING_KHR);
//case_to_name(VK_, ERROR_NOT_ENOUGH_SPACE_KHR);
default: unreachable();
}
}
BB_NORETURN BB_COLD fn void wrong_vulkan_result(VkResult result, String call_string, String file, int line)
{
unused(result);
unused(call_string);
unused(file);
unused(line);
String result_name = vulkan_result_to_string(result);
panic("Wrong Vulkan result {s} at \"{s}\" {s}:{u32}\n", result_name, call_string, file, line);
}
fn void buffer_copy_to_local_command(VkCommandBuffer command_buffer, Slice(LocalBufferCopy) copies)
{
for (u64 i = 0; i < copies.length; i += 1)
{
let(copy, copies.pointer[i]);
let(source_buffer, &copy.source);
let(destination_buffer, &copy.destination);
VkBufferCopy2 buffer_copies[MAX_LOCAL_BUFFER_COPY_COUNT];
for (u64 i = 0; i < copy.regions.length; i += 1)
{
let(copy_region, copy.regions.pointer[i]);
buffer_copies[i] = (VkBufferCopy2) {
.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2,
.pNext = 0,
.srcOffset = copy_region.source_offset,
.dstOffset = copy_region.destination_offset,
.size = copy_region.size,
};
}
VkCopyBufferInfo2 info = {
.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2,
.pNext = 0,
.srcBuffer = source_buffer->handle,
.dstBuffer = destination_buffer->handle,
.regionCount = copy.regions.length,
.pRegions = buffer_copies,
};
vkCmdCopyBuffer2(command_buffer, &info);
}
}
fn void buffer_copy_to_host(VulkanBuffer buffer, Slice(HostBufferCopy) regions)
{
assert(buffer.type == BUFFER_TYPE_STAGING);
let(buffer_pointer, (u8*)buffer.address);
for (u64 i = 0; i < regions.length; i += 1)
{
let(region, regions.pointer[i]);
let(destination, buffer_pointer + region.destination_offset);
assert(destination + region.source.length <= (u8*)buffer.address + buffer.size);
#if USE_MEMCPY
memcpy(destination, region.source.pointer, region.source.length);
#else
for (u64 i = 0; i < region.source.length; i += 1)
{
destination[i] = region.source.pointer[i];
}
#endif
}
}
fn VkShaderStageFlags vulkan_shader_stage_from_path(String shader_binary_path)
{
VkShaderStageFlags shader_stage;
if (string_ends_with(shader_binary_path, strlit(".vert.spv")))
{
shader_stage = VK_SHADER_STAGE_VERTEX_BIT;
}
else if (string_ends_with(shader_binary_path, strlit(".frag.spv")))
{
shader_stage = VK_SHADER_STAGE_FRAGMENT_BIT;
}
else
{
failed_execution();
}
return shader_stage;
}
fn VkShaderStageFlags vulkan_shader_stage(ShaderStage shader_stage)
{
VkShaderStageFlags result;
switch (shader_stage)
{
case SHADER_STAGE_VERTEX:
result = VK_SHADER_STAGE_VERTEX_BIT;
break;
case SHADER_STAGE_FRAGMENT:
result = VK_SHADER_STAGE_FRAGMENT_BIT;
break;
}
return result;
}
fn VkDescriptorType vulkan_descriptor_type(DescriptorType type)
{
VkDescriptorType result;
switch (type)
{
case DESCRIPTOR_TYPE_IMAGE_PLUS_SAMPLER:
result = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
break;
case DESCRIPTOR_TYPE_COUNT:
unreachable();
}
return result;
}
fn DescriptorType descriptor_type_from_vulkan(VkDescriptorType descriptor_type)
{
DescriptorType result;
switch (descriptor_type)
{
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
result = DESCRIPTOR_TYPE_IMAGE_PLUS_SAMPLER;
break;
default: unreachable();
}
return result;
}
fn GPUMemory vk_allocate_memory(VkDevice device, const VkAllocationCallbacks* allocation_callbacks, VkPhysicalDeviceMemoryProperties memory_properties, VkMemoryRequirements memory_requirements, VkMemoryPropertyFlags flags, u8 use_device_address_bit)
{
u32 memory_type_index;
for (memory_type_index = 0; memory_type_index < memory_properties.memoryTypeCount; memory_type_index += 1)
{
let(memory_type, memory_properties.memoryTypes[memory_type_index]);
if ((memory_requirements.memoryTypeBits & (1 << memory_type_index)) != 0 && (memory_type.propertyFlags & flags) == flags)
{
break;
}
}
if (memory_type_index == memory_properties.memoryTypeCount)
{
failed_execution();
}
VkMemoryAllocateInfo allocate_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = 0,
.allocationSize = memory_requirements.size,
.memoryTypeIndex = memory_type_index,
};
VkMemoryAllocateFlagsInfo flags_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
.pNext = 0,
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT,
.deviceMask = 1,
};
if (use_device_address_bit)
{
allocate_info.pNext = &flags_info;
}
VkDeviceMemory memory = 0;
vkok(vkAllocateMemory(device, &allocate_info, allocation_callbacks, &memory));
return (GPUMemory) { .handle = memory, .size = allocate_info.allocationSize };
}
fn VulkanBuffer vk_buffer_create(VkDevice device, const VkAllocationCallbacks* allocation_callbacks, VkPhysicalDeviceMemoryProperties physical_device_memory_properties, VkDeviceSize buffer_size, VkBufferUsageFlags usage_flags, VkMemoryPropertyFlags memory_flags)
{
VulkanBuffer result = {
.size = buffer_size,
};
VkBufferCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = 0,
.flags = 0,
.size = buffer_size,
.usage = usage_flags,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = 0,
};
vkok(vkCreateBuffer(device, &create_info, allocation_callbacks, &result.handle));
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(device, result.handle, &memory_requirements);
u8 use_device_address_bit = !!(create_info.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
result.memory = vk_allocate_memory(device, allocation_callbacks, physical_device_memory_properties, memory_requirements, memory_flags, use_device_address_bit);
VkDeviceSize memory_offset = 0;
vkok(vkBindBufferMemory(device, result.handle, result.memory.handle, memory_offset));
u8 map_memory = !!(memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
assert(((map_memory | use_device_address_bit) == 0) | (map_memory == !use_device_address_bit));
if (map_memory)
{
void* data = 0;
VkDeviceSize offset = 0;
VkMemoryMapFlags map_flags = 0;
vkok(vkMapMemory(device, result.memory.handle, offset, memory_requirements.size, map_flags, &data));
result.address = (u64)data;
}
if (use_device_address_bit)
{
VkBufferDeviceAddressInfo device_address_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.pNext = 0,
.buffer = result.handle,
};
result.address = vkGetBufferDeviceAddress(device, &device_address_info);
}
return result;
}
fn VulkanBuffer buffer_create(Renderer* renderer, u64 size, BufferType type)
{
u8 is_dst = (type == BUFFER_TYPE_VERTEX) | (type == BUFFER_TYPE_INDEX);
u8 is_src = type == BUFFER_TYPE_STAGING;
VkBufferUsageFlags usage =
(VK_BUFFER_USAGE_TRANSFER_DST_BIT * is_dst) |
(VK_BUFFER_USAGE_TRANSFER_SRC_BIT * is_src) |
((VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) * (type == BUFFER_TYPE_VERTEX)) |
(VK_BUFFER_USAGE_INDEX_BUFFER_BIT * (type == BUFFER_TYPE_INDEX));
VkMemoryPropertyFlags memory_flags =
(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT * is_dst) |
((VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) * is_src);
let(result, vk_buffer_create(renderer->device, renderer->allocator, renderer->memory_properties, size, usage, memory_flags));
result.type = type;
return result;
}
fn u8 vk_layer_is_supported(String layer_name)
{
assert(layer_name.pointer[layer_name.length] == 0);
VkLayerProperties layers[256];
u32 layer_count;
vkok(vkEnumerateInstanceLayerProperties(&layer_count, 0));
if (layer_count > array_length(layers))
{
failed_execution();
}
vkok(vkEnumerateInstanceLayerProperties(&layer_count, layers));
u8 supported = 0;
for (u32 i = 0; i < layer_count; i += 1)
{
VkLayerProperties* properties = &layers[i];
let(candidate_layer_name, cstr(properties->layerName));
if (s_equal(candidate_layer_name, layer_name))
{
supported = 1;
break;
}
}
return supported;
}
#if BB_DEBUG
fn String message_severity_to_string(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
{
switch (message_severity)
{
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
return strlit("VERBOSE");
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
return strlit("INFO");
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
return strlit("WARNING");
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
return strlit("ERROR");
default:
unreachable();
}
}
fn String message_type_to_string(VkDebugUtilsMessageTypeFlagBitsEXT message_type)
{
switch (message_type)
{
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
return strlit("GENERAL");
case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
return strlit("VALIDATION");
case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
return strlit("PERFORMANCE");
case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT:
return strlit("DEVICE_ADDRESS_BINDING");
default:
unreachable();
}
}
fn VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
{
unused(user_data);
if (callback_data->pMessage)
{
print("Validation message ({s}) ({s}) ({cstr}): {cstr}\n", message_severity_to_string(message_severity), message_type_to_string(message_type), callback_data->pMessageIdName ? callback_data->pMessageIdName : "ID_NONE", callback_data->pMessage);
}
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
{
failed_execution();
}
return VK_FALSE;
}
#endif
fn void immediate_start(ImmediateContext context)
{
VkFence fences[] = { context.fence };
VkCommandBufferResetFlags reset_flags = 0;
vkok(vkResetFences(context.device, array_length(fences), fences));
vkok(vkResetCommandBuffer(context.command_buffer, reset_flags));
VkCommandBufferBeginInfo command_buffer_begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkok(vkBeginCommandBuffer(context.command_buffer, &command_buffer_begin_info));
}
fn void immediate_end(ImmediateContext context)
{
VkFence fences[] = { context.fence };
vkok(vkEndCommandBuffer(context.command_buffer));
VkCommandBufferSubmitInfo command_buffer_submit_infos[] = {
{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
.pNext = 0,
.deviceMask = 0,
.commandBuffer = context.command_buffer,
}
};
VkSubmitFlags submit_flags = 0;
VkSubmitInfo2 submit_info[] = {
{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
.pNext = 0,
.flags = submit_flags,
.waitSemaphoreInfoCount = 0,
.pWaitSemaphoreInfos = 0,
.commandBufferInfoCount = array_length(command_buffer_submit_infos),
.pCommandBufferInfos = command_buffer_submit_infos,
.signalSemaphoreInfoCount = 0,
.pSignalSemaphoreInfos = 0,
}
};
vkok(vkQueueSubmit2(context.queue, array_length(submit_info), submit_info, context.fence));
VkBool32 wait_all = 1;
let(timeout, ~(u64)0);
vkok(vkWaitForFences(context.device, array_length(fences), fences, wait_all, timeout));
}
fn VulkanImage vk_image_create(VkDevice device, const VkAllocationCallbacks* allocation_callbacks, VkPhysicalDeviceMemoryProperties memory_properties, VulkanImageCreate create)
{
assert(create.width);
assert(create.height);
VulkanImage result = {};
result.format = create.format;
VkImageCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = create.format,
.extent = {
.width = create.width,
.height = create.height,
.depth = 1,
},
.mipLevels = create.mip_levels,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = create.usage,
.sharingMode = 0,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = 0,
.initialLayout = 0,
};
vkok(vkCreateImage(device, &create_info, allocation_callbacks, &result.handle));
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(device, result.handle, &memory_requirements);
VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
u8 use_device_address_bit = 0;
result.memory = vk_allocate_memory(device, allocation_callbacks, memory_properties, memory_requirements, flags, use_device_address_bit);
VkDeviceSize memory_offset = 0;
vkok(vkBindImageMemory(device, result.handle, result.memory.handle, memory_offset));
VkImageViewCreateInfo view_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = result.handle,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = create_info.format,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = create.mip_levels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vkok(vkCreateImageView(device, &view_create_info, allocation_callbacks, &result.view));
return result;
}
fn void vk_image_transition(VkCommandBuffer command_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout)
{
VkImageMemoryBarrier2 image_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.oldLayout = old_layout,
.newLayout = new_layout,
.image = image,
.subresourceRange = {
.aspectMask = (new_layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
VkDependencyInfo dependency_info = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &image_barrier,
};
vkCmdPipelineBarrier2(command_buffer, &dependency_info);
}
fn void vk_image_copy(VkCommandBuffer command_buffer, VulkanCopyImageArgs args)
{
VkImageSubresourceLayers subresource_layers = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkImageBlit2 blit_regions[] = {
{
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.pNext = 0,
.srcSubresource = subresource_layers,
.srcOffsets = {
[1] = {
.x = args.source.extent.width,
.y = args.source.extent.height,
.z = 1,
},
},
.dstSubresource = subresource_layers,
.dstOffsets = {
[1] = {
.x = args.destination.extent.width,
.y = args.destination.extent.height,
.z = 1,
},
},
},
};
VkBlitImageInfo2 blit_info = {
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.pNext = 0,
.srcImage = args.source.handle,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = args.destination.handle,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = array_length(blit_regions),
.pRegions = blit_regions,
.filter = VK_FILTER_LINEAR,
};
vkCmdBlitImage2(command_buffer, &blit_info);
}
fn Renderer* rendering_initialize(Arena* arena)
{
Renderer* renderer = &renderer_memory;
#ifdef __linux__
vulkan_library = os_library_load("libvulkan.so.1");
#elif _WIN32
vulkan_library = os_library_load("vulkan-1.dll");
#elif __APPLE__
vulkan_library = os_library_load("libvulkan.dylib");
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("libvulkan.1.dylib");
}
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("libMoltenVK.dylib");
}
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("vulkan.framework/vulkan");
}
if (!os_library_is_valid(vulkan_library))
{
vulkan_library = os_library_load("MoltenVK.framework/MoltenVK");
}
#endif
if (!os_library_is_valid(vulkan_library))
{
failed_execution();
}
vkGetInstanceProcAddr = os_symbol_load(vulkan_library, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddr)
{
failed_execution();
}
vulkan_load_instance_function(0, vkEnumerateInstanceVersion);
vulkan_load_instance_function(0, vkCreateInstance);
u32 api_version = 0;
if (vkEnumerateInstanceVersion)
{
vkok(vkEnumerateInstanceVersion(&api_version));
}
if (!api_version)
{
if (vkCreateInstance)
{
api_version = VK_API_VERSION_1_0;
}
}
if (!api_version)
{
failed_execution();
}
if (api_version < VK_API_VERSION_1_3)
{
failed_execution();
}
// Proceed to load Vulkan instance-level functions
vulkan_load_instance_function(0, vkEnumerateInstanceLayerProperties);
{
#if BB_DEBUG
let(debug_layer, strlit("VK_LAYER_KHRONOS_validation"));
if (!vk_layer_is_supported(debug_layer))
{
failed_execution();
}
const char* layers[] =
{
string_to_c(debug_layer),
};
let(layer_count, array_length(layers));
#else
const char** layers = 0;
u32 layer_count = 0;
#endif
const char* extensions[] = {
#if BB_DEBUG
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
#endif
VK_KHR_SURFACE_EXTENSION_NAME,
#ifdef VK_USE_PLATFORM_WIN32_KHR
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
#endif
#ifdef VK_USE_PLATFORM_XCB_KHR
VK_KHR_XCB_SURFACE_EXTENSION_NAME,
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
#endif
#ifdef VK_USE_PLATFORM_METAL_EXT
VK_EXT_METAL_SURFACE_EXTENSION_NAME,
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
#endif
};
#if BB_DEBUG
VkValidationFeatureEnableEXT enabled_validation_features[] =
{
VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT,
};
VkDebugUtilsMessengerCreateInfoEXT msg_ci = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT,
.pfnUserCallback = debug_callback,
.pUserData = 0,
};
u8 enable_shader_debug_printf = BB_DEBUG;
VkValidationFeaturesEXT validation_features = {
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
.enabledValidationFeatureCount = array_length(enabled_validation_features),
.pEnabledValidationFeatures = enabled_validation_features,
.pNext = &msg_ci,
};
#endif
#if BB_DEBUG
let(pNext, enable_shader_debug_printf ? (void*)&validation_features : (void*)&msg_ci);
#else
void* pNext = 0;
#endif
VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.apiVersion = api_version,
};
VkInstanceCreateInfo ci = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = pNext,
#if __APPLE__
.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR,
#else
.flags = 0,
#endif
.pApplicationInfo = &app_info,
.ppEnabledLayerNames = layers,
.enabledLayerCount = layer_count,
.ppEnabledExtensionNames = extensions,
.enabledExtensionCount = array_length(extensions),
};
vkok(vkCreateInstance(&ci, renderer->allocator, &instance));
#if BB_DEBUG
vulkan_load_instance_function(instance, vkCreateDebugUtilsMessengerEXT);
VkDebugUtilsMessengerEXT messenger;
vkok(vkCreateDebugUtilsMessengerEXT(instance, &msg_ci, renderer->allocator, &messenger));
#endif
}
vulkan_load_instance_function(instance, vkGetDeviceProcAddr);
vulkan_load_instance_function(instance, vkEnumeratePhysicalDevices);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceMemoryProperties);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceProperties);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceQueueFamilyProperties);
vulkan_load_instance_function(instance, vkCreateDevice);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
vulkan_load_instance_function(instance, vkGetPhysicalDeviceSurfacePresentModesKHR);
#ifdef VK_USE_PLATFORM_XCB_KHR
vulkan_load_instance_function(instance, vkCreateXcbSurfaceKHR);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
vulkan_load_instance_function(instance, vkCreateWin32SurfaceKHR);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
vulkan_load_instance_function(instance, vkCreateWin32SurfaceKHR);
#endif
#ifdef VK_USE_PLATFORM_METAL_EXT
vulkan_load_instance_function(instance, vkCreateMetalSurfaceEXT);
#endif
{
u32 physical_device_count;
VkPhysicalDevice physical_devices[256];
vkok(vkEnumeratePhysicalDevices(instance, &physical_device_count, 0));
if (physical_device_count == 0)
{
failed_execution();
}
if (physical_device_count > array_length(physical_devices))
{
failed_execution();
}
vkok(vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices));
renderer->physical_device = physical_devices[0];
}
vkGetPhysicalDeviceMemoryProperties(renderer->physical_device, &renderer->memory_properties);
u32 graphics_queue_family_index;
{
u32 present_queue_family_index;
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(renderer->physical_device, &properties);
u32 queue_count;
vkGetPhysicalDeviceQueueFamilyProperties(renderer->physical_device, &queue_count, 0);
VkQueueFamilyProperties queue_properties[64];
if (queue_count > array_length(queue_properties))
{
failed_execution();
}
vkGetPhysicalDeviceQueueFamilyProperties(renderer->physical_device, &queue_count, queue_properties);
for (graphics_queue_family_index = 0; graphics_queue_family_index < queue_count; graphics_queue_family_index += 1)
{
VkQueueFamilyProperties* properties = &queue_properties[graphics_queue_family_index];
if (properties->queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
break;
}
}
if (graphics_queue_family_index == queue_count)
{
failed_execution();
}
present_queue_family_index = 0;
// for (present_queue_family_index = 0; present_queue_family_index < queue_count; present_queue_family_index += 1)
// {
// VkBool32 support;
// vkok(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, present_queue_family_index, surface, &support));
// if (support)
// {
// break;
// }
// }
if (present_queue_family_index == queue_count)
{
failed_execution();
}
if (present_queue_family_index != graphics_queue_family_index)
{
failed_execution();
}
f32 queue_priorities[] = { 1.0f };
VkDeviceQueueCreateInfo queue_create_infos[] = {
{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = graphics_queue_family_index,
.queueCount = array_length(queue_priorities),
.pQueuePriorities = queue_priorities,
},
};
const char* extensions[] =
{
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
#ifdef __APPLE__
"VK_KHR_portability_subset",
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
#endif
};
#ifdef __APPLE__
VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
.pNext = 0,
.dynamicRendering = VK_TRUE,
};
#else
VkPhysicalDeviceVulkan13Features features13 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.dynamicRendering = 1,
.synchronization2 = 1,
};
#endif
VkPhysicalDeviceVulkan12Features features12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.bufferDeviceAddress = 1,
.descriptorIndexing = 1,
.runtimeDescriptorArray = 1,
.shaderSampledImageArrayNonUniformIndexing = 1,
#ifdef __APPLE__
.pNext = &dynamic_rendering_features,
#else
.pNext = &features13,
#endif
};
VkPhysicalDeviceFeatures2 features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.features = {
},
.pNext = &features12,
};
VkDeviceCreateInfo ci = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.ppEnabledExtensionNames = extensions,
.enabledExtensionCount = array_length(extensions),
.pQueueCreateInfos = queue_create_infos,
.queueCreateInfoCount = array_length(queue_create_infos),
.pNext = &features,
};
vkok(vkCreateDevice(renderer->physical_device, &ci, renderer->allocator, &renderer->device));
}
// Load device table
vulkan_load_device_function(renderer->device, vkCreateSwapchainKHR);
vulkan_load_device_function(renderer->device, vkDestroySwapchainKHR);
vulkan_load_device_function(renderer->device, vkGetSwapchainImagesKHR);
vulkan_load_device_function(renderer->device, vkGetImageMemoryRequirements);
vulkan_load_device_function(renderer->device, vkGetBufferMemoryRequirements);
vulkan_load_device_function(renderer->device, vkMapMemory);
vulkan_load_device_function(renderer->device, vkUnmapMemory);
vulkan_load_device_function(renderer->device, vkAllocateMemory);
vulkan_load_device_function(renderer->device, vkBindImageMemory);
vulkan_load_device_function(renderer->device, vkBindBufferMemory);
vulkan_load_device_function(renderer->device, vkBindBufferMemory);
vulkan_load_device_function(renderer->device, vkGetDeviceQueue);
vulkan_load_device_function(renderer->device, vkCreateCommandPool);
vulkan_load_device_function(renderer->device, vkAllocateCommandBuffers);
vulkan_load_device_function(renderer->device, vkCreateFence);
vulkan_load_device_function(renderer->device, vkCreateSemaphore);
vulkan_load_device_function(renderer->device, vkCreateSampler);
vulkan_load_device_function(renderer->device, vkCreateShaderModule);
vulkan_load_device_function(renderer->device, vkCreateDescriptorSetLayout);
vulkan_load_device_function(renderer->device, vkCreatePipelineLayout);
vulkan_load_device_function(renderer->device, vkCreateGraphicsPipelines);
vulkan_load_device_function(renderer->device, vkCreateImage);
vulkan_load_device_function(renderer->device, vkCreateImageView);
vulkan_load_device_function(renderer->device, vkCreateBuffer);
vulkan_load_device_function(renderer->device, vkCreateDescriptorPool);
vulkan_load_device_function(renderer->device, vkAllocateDescriptorSets);
vulkan_load_device_function(renderer->device, vkResetFences);
vulkan_load_device_function(renderer->device, vkResetCommandBuffer);
vulkan_load_device_function(renderer->device, vkBeginCommandBuffer);
vulkan_load_device_function(renderer->device, vkCmdPipelineBarrier2);
vulkan_load_device_function(renderer->device, vkCmdCopyBufferToImage);
vulkan_load_device_function(renderer->device, vkEndCommandBuffer);
vulkan_load_device_function(renderer->device, vkQueueSubmit2);
vulkan_load_device_function(renderer->device, vkWaitForFences);
vulkan_load_device_function(renderer->device, vkUpdateDescriptorSets);
vulkan_load_device_function(renderer->device, vkAcquireNextImageKHR);
vulkan_load_device_function(renderer->device, vkGetBufferDeviceAddress);
vulkan_load_device_function(renderer->device, vkCmdCopyBuffer2);
vulkan_load_device_function(renderer->device, vkCmdSetViewport);
vulkan_load_device_function(renderer->device, vkCmdSetScissor);
vulkan_load_device_function(renderer->device, vkCmdBeginRendering);
vulkan_load_device_function(renderer->device, vkCmdEndRendering);
vulkan_load_device_function(renderer->device, vkCmdBindPipeline);
vulkan_load_device_function(renderer->device, vkCmdBindDescriptorSets);
vulkan_load_device_function(renderer->device, vkCmdBindIndexBuffer);
vulkan_load_device_function(renderer->device, vkCmdPushConstants);
vulkan_load_device_function(renderer->device, vkCmdDrawIndexed);
vulkan_load_device_function(renderer->device, vkCmdBlitImage2);
vulkan_load_device_function(renderer->device, vkQueuePresentKHR);
vulkan_load_device_function(renderer->device, vkDeviceWaitIdle);
vulkan_load_device_function(renderer->device, vkDestroyImageView);
vulkan_load_device_function(renderer->device, vkDestroyImage);
vulkan_load_device_function(renderer->device, vkFreeMemory);
vkGetDeviceQueue(renderer->device, graphics_queue_family_index, 0, &renderer->graphics_queue);
renderer->immediate.device = renderer->device;
renderer->immediate.queue = renderer->graphics_queue;
VkCommandPoolCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = graphics_queue_family_index,
};
vkok(vkCreateCommandPool(renderer->device, &create_info, renderer->allocator, &renderer->immediate.command_pool));
VkCommandBufferAllocateInfo command_buffer_allocate_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = renderer->immediate.command_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
vkok(vkAllocateCommandBuffers(renderer->device, &command_buffer_allocate_info, &renderer->immediate.command_buffer));
VkFenceCreateInfo fence_create_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
vkok(vkCreateFence(renderer->device, &fence_create_info, renderer->allocator, &renderer->immediate.fence));
{
VkFilter sampler_filter = VK_FILTER_LINEAR;
VkSamplerCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = 0,
.flags = 0,
.magFilter = sampler_filter,
.minFilter = sampler_filter,
.mipmapMode = 0,
.addressModeU = 0,
.addressModeV = 0,
.addressModeW = 0,
.mipLodBias = 0,
.anisotropyEnable = 0,
.maxAnisotropy = 0,
.compareEnable = 0,
.compareOp = 0,
.minLod = 0,
.maxLod = 0,
.borderColor = 0,
.unnormalizedCoordinates = 0,
};
vkok(vkCreateSampler(renderer->device, &create_info, renderer->allocator, &renderer->sampler));
}
String shader_binaries[] = {
strlit(BUILD_DIR "/" "rect.vert.spv"),
strlit(BUILD_DIR "/" "rect.frag.spv"),
};
PipelineLayoutCreate pipeline_layouts[] = {
(PipelineLayoutCreate) {
.push_constant_ranges = array_to_slice(((PushConstantRange[]){
(PushConstantRange) {
.offset = 0,
.size = sizeof(GPUDrawPushConstants),
.stage = SHADER_STAGE_VERTEX,
},
})),
.descriptor_set_layouts = array_to_slice(((DescriptorSetLayoutCreate[]){
(DescriptorSetLayoutCreate) {
.bindings = array_to_slice(((DescriptorSetLayoutBinding[]) {
{
.binding = 0,
.type = DESCRIPTOR_TYPE_IMAGE_PLUS_SAMPLER,
.stage = SHADER_STAGE_FRAGMENT,
.count = RECT_TEXTURE_SLOT_COUNT,
},
})),
},
})),
},
};
PipelineCreate pipeline_create[] = {
(PipelineCreate) {
.shader_source_indices = array_to_slice(((u16[]){0, 1})),
.layout_index = 0,
},
};
GraphicsPipelinesCreate create_data = {
.layouts = array_to_slice(pipeline_layouts),
.pipelines = array_to_slice(pipeline_create),
.shader_binaries = array_to_slice(shader_binaries),
};
let(graphics_pipeline_count, create_data.pipelines.length);
assert(graphics_pipeline_count);
let(pipeline_layout_count, create_data.layouts.length);
assert(pipeline_layout_count);
assert(pipeline_layout_count <= graphics_pipeline_count);
let(shader_count, create_data.shader_binaries.length);
VkPipeline pipeline_handles[BB_PIPELINE_COUNT];
VkPipelineShaderStageCreateInfo shader_create_infos[MAX_SHADER_MODULE_COUNT_PER_PIPELINE];
VkGraphicsPipelineCreateInfo graphics_pipeline_create_infos[BB_PIPELINE_COUNT];
VkPipelineVertexInputStateCreateInfo vertex_input_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.vertexBindingDescriptionCount = 0,
.pVertexBindingDescriptions = 0,
.vertexAttributeDescriptionCount = 0,
.pVertexAttributeDescriptions = 0,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
VkPipelineViewportStateCreateInfo viewport_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = 0,
.viewportCount = 1,
.scissorCount = 1,
};
VkPipelineRasterizationStateCreateInfo rasterization_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.depthClampEnable = 0,
.rasterizerDiscardEnable = 0,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = 0,
.depthBiasConstantFactor = 0,
.depthBiasClamp = 0,
.depthBiasSlopeFactor = 0,
.lineWidth = 1.0f,
};
VkPipelineMultisampleStateCreateInfo multisample_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = 0,
.minSampleShading = 1.0f,
.pSampleMask = 0,
.alphaToCoverageEnable = 0,
.alphaToOneEnable = 0,
};
VkPipelineDepthStencilStateCreateInfo depth_stencil_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.depthTestEnable = 0,
.depthWriteEnable = 0,
.depthCompareOp = VK_COMPARE_OP_NEVER,
.depthBoundsTestEnable = 0,
.stencilTestEnable = 0,
.front = {},
.back = {},
.minDepthBounds = 0.0f,
.maxDepthBounds = 1.0f,
};
VkPipelineColorBlendAttachmentState attachments[] = {
{
.blendEnable = 1,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
},
};
VkPipelineColorBlendStateCreateInfo color_blend_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = array_length(attachments),
.pAttachments = attachments,
.blendConstants = {},
};
VkDynamicState states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamic_state_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.dynamicStateCount = array_length(states),
.pDynamicStates = states,
};
// TODO: abstract away
VkFormat common_image_format = VK_FORMAT_B8G8R8A8_UNORM;
VkFormat color_attachment_formats[] = {
common_image_format,
};
VkPipelineRenderingCreateInfo rendering_create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.pNext = 0,
.viewMask = 0,
.colorAttachmentCount = array_length(color_attachment_formats),
.pColorAttachmentFormats = color_attachment_formats,
.depthAttachmentFormat = 0,
.stencilAttachmentFormat = 0,
};
let(shader_modules, arena_allocate(arena, VkShaderModule, shader_count));
for (u64 i = 0; i < shader_count; i += 1)
{
String shader_binary_path = create_data.shader_binaries.pointer[i];
let(binary, file_read(arena, shader_binary_path));
VkShaderModuleCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = binary.length,
.pCode = (u32*)binary.pointer,
};
vkok(vkCreateShaderModule(renderer->device, &create_info, renderer->allocator, &shader_modules[i]));
}
for (u64 pipeline_index = 0; pipeline_index < pipeline_layout_count; pipeline_index += 1)
{
let(create, create_data.layouts.pointer[pipeline_index]);
let(descriptor_set_layout_count, create.descriptor_set_layouts.length);
let(push_constant_range_count, create.push_constant_ranges.length);
let(pipeline, &renderer->pipelines[pipeline_index]);
pipeline->descriptor_set_count = descriptor_set_layout_count;
pipeline->push_constant_range_count = push_constant_range_count;
if (descriptor_set_layout_count > MAX_DESCRIPTOR_SET_LAYOUT_COUNT)
{
failed_execution();
}
// u16 descriptor_type_counter[DESCRIPTOR_TYPE_COUNT] = {};
for (u64 descriptor_set_layout_index = 0; descriptor_set_layout_index < descriptor_set_layout_count; descriptor_set_layout_index += 1)
{
let(set_layout_create, create.descriptor_set_layouts.pointer[descriptor_set_layout_index]);
let(binding_count, set_layout_create.bindings.length);
let(descriptor_set_layout_bindings, &pipeline->descriptor_set_layout_bindings[descriptor_set_layout_index]);
descriptor_set_layout_bindings->count = binding_count;
for (u64 binding_index = 0; binding_index < binding_count; binding_index += 1)
{
let(binding_descriptor, set_layout_create.bindings.pointer[binding_index]);
VkDescriptorType descriptor_type = vulkan_descriptor_type(binding_descriptor.type);
VkShaderStageFlags shader_stage = vulkan_shader_stage(binding_descriptor.stage);
descriptor_set_layout_bindings->buffer[binding_index] = (VkDescriptorSetLayoutBinding) {
.binding = binding_descriptor.binding,
.descriptorType = descriptor_type,
.descriptorCount = binding_descriptor.count,
.stageFlags = shader_stage,
.pImmutableSamplers = 0,
};
}
VkDescriptorSetLayoutCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = 0,
.flags = 0,
.bindingCount = binding_count,
.pBindings = descriptor_set_layout_bindings->buffer,
};
vkok(vkCreateDescriptorSetLayout(renderer->device, &create_info, renderer->allocator, &pipeline->descriptor_set_layouts[descriptor_set_layout_index]));
}
if (push_constant_range_count > MAX_PUSH_CONSTANT_RANGE_COUNT)
{
failed_execution();
}
for (u64 push_constant_index = 0; push_constant_index < push_constant_range_count; push_constant_index += 1)
{
let(push_constant_descriptor, create.push_constant_ranges.pointer[push_constant_index]);
pipeline->push_constant_ranges[push_constant_index] = (VkPushConstantRange) {
.stageFlags = vulkan_shader_stage(push_constant_descriptor.stage),
.offset = push_constant_descriptor.offset,
.size = push_constant_descriptor.size,
};
}
VkPipelineLayoutCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = 0,
.flags = 0,
.setLayoutCount = descriptor_set_layout_count,
.pSetLayouts = pipeline->descriptor_set_layouts,
.pushConstantRangeCount = push_constant_range_count,
.pPushConstantRanges = pipeline->push_constant_ranges,
};
vkok(vkCreatePipelineLayout(renderer->device, &create_info, renderer->allocator, &pipeline->layout));
}
for (u64 i = 0; i < graphics_pipeline_count; i += 1)
{
let(create, create_data.pipelines.pointer[i]);
let(pipeline_shader_count, create.shader_source_indices.length);
if (pipeline_shader_count > MAX_SHADER_MODULE_COUNT_PER_PIPELINE)
{
failed_execution();
}
for (u64 i = 0; i < pipeline_shader_count; i += 1)
{
let(shader_index, create.shader_source_indices.pointer[i]);
let(shader_source_path, create_data.shader_binaries.pointer[shader_index]);
shader_create_infos[i] = (VkPipelineShaderStageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = 0,
.flags = 0,
.stage = vulkan_shader_stage_from_path(shader_source_path),
.module = shader_modules[i],
.pName = "main",
.pSpecializationInfo = 0,
};
}
graphics_pipeline_create_infos[i] = (VkGraphicsPipelineCreateInfo)
{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &rendering_create_info,
.flags = 0,
.stageCount = shader_count,
.pStages = shader_create_infos,
.pVertexInputState = &vertex_input_state_create_info,
.pInputAssemblyState = &input_assembly_state_create_info,
.pTessellationState = 0,
.pViewportState = &viewport_state_create_info,
.pRasterizationState = &rasterization_state_create_info,
.pMultisampleState = &multisample_state_create_info,
.pDepthStencilState = &depth_stencil_state_create_info,
.pColorBlendState = &color_blend_state_create_info,
.pDynamicState = &dynamic_state_create_info,
.layout = renderer->pipelines[i].layout,
.renderPass = 0,
.subpass = 0,
.basePipelineHandle = 0,
.basePipelineIndex = 0,
};
}
VkPipelineCache pipeline_cache = 0;
vkok(vkCreateGraphicsPipelines(renderer->device, pipeline_cache, graphics_pipeline_count, graphics_pipeline_create_infos, renderer->allocator, pipeline_handles));
for (u32 i = 0; i < graphics_pipeline_count; i += 1)
{
renderer->pipelines[i].handle = pipeline_handles[i];
}
return renderer;
}
fn void destroy_image(Renderer* renderer, VulkanImage image)
{
vkDestroyImageView(renderer->device, image.view, renderer->allocator);
vkDestroyImage(renderer->device, image.handle, renderer->allocator);
vkFreeMemory(renderer->device, image.memory.handle, renderer->allocator);
}
fn void swapchain_recreate(Renderer* renderer, RenderWindow* window)
{
VkSurfaceCapabilitiesKHR surface_capabilities;
vkok(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(renderer->physical_device, window->surface, &surface_capabilities));
VkSwapchainKHR old_swapchain = window->swapchain;
VkImageView old_swapchain_image_views[MAX_SWAPCHAIN_IMAGE_COUNT];
if (old_swapchain)
{
vkok(vkDeviceWaitIdle(renderer->device));
for (u32 i = 0; i < window->swapchain_image_count; i += 1)
{
old_swapchain_image_views[i] = window->swapchain_image_views[i];
}
}
u32 queue_family_indices[] = { renderer->graphics_queue_family_index };
VkImageUsageFlags swapchain_image_usage_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
window->swapchain_image_format = VK_FORMAT_B8G8R8A8_UNORM;
window->last_width = window->width;
window->last_height = window->height;
window->width = surface_capabilities.currentExtent.width;
window->height = surface_capabilities.currentExtent.height;
VkPresentModeKHR preferred_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
VkPresentModeKHR present_modes[16];
u32 present_mode_count = array_length(present_modes);
vkok(vkGetPhysicalDeviceSurfacePresentModesKHR(renderer->physical_device, window->surface, &present_mode_count, present_modes));
for (u32 i = 0; i < present_mode_count; i += 1)
{
if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
{
preferred_present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
}
}
VkSwapchainCreateInfoKHR swapchain_create_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = 0,
.flags = 0,
.surface = window->surface,
.minImageCount = surface_capabilities.minImageCount,
.imageFormat = window->swapchain_image_format,
.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
.imageExtent = surface_capabilities.currentExtent,
.imageArrayLayers = 1,
.imageUsage = swapchain_image_usage_flags,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = array_length(queue_family_indices),
.pQueueFamilyIndices = queue_family_indices,
.preTransform = surface_capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = preferred_present_mode,
.clipped = 0,
.oldSwapchain = window->swapchain,
};
vkok(vkCreateSwapchainKHR(renderer->device, &swapchain_create_info, renderer->allocator, &window->swapchain));
assert(window->swapchain != old_swapchain);
if (old_swapchain)
{
for (u32 i = 0; i < window->swapchain_image_count; i += 1)
{
vkDestroyImageView(renderer->device, old_swapchain_image_views[i], renderer->allocator);
}
vkDestroySwapchainKHR(renderer->device, old_swapchain, renderer->allocator);
destroy_image(renderer, window->render_image);
}
{
vkok(vkGetSwapchainImagesKHR(renderer->device, window->swapchain, &window->swapchain_image_count, 0));
if (window->swapchain_image_count == 0)
{
failed_execution();
}
if (window->swapchain_image_count > array_length(window->swapchain_images))
{
failed_execution();
}
vkok(vkGetSwapchainImagesKHR(renderer->device, window->swapchain, &window->swapchain_image_count, window->swapchain_images));
// VkImageViewUsageCreateInfo image_view_usage_create_info = {
// .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
// .pNext = 0,
// .usage = swapchain_create_info.imageUsage,
// };
for (u32 i = 0; i < window->swapchain_image_count; i += 1)
{
VkImageViewCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
// .pNext = &image_view_usage_create_info,
.flags = 0,
.image = window->swapchain_images[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = swapchain_create_info.imageFormat,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vkok(vkCreateImageView(renderer->device, &create_info, renderer->allocator, &window->swapchain_image_views[i]));
}
}
window->render_image = vk_image_create(renderer->device, renderer->allocator, renderer->memory_properties, (VulkanImageCreate) {
.width = surface_capabilities.currentExtent.width,
.height = surface_capabilities.currentExtent.height,
.mip_levels = 1,
.format = window->swapchain_image_format,
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
});
}
fn RenderWindow* rendering_initialize_window(Renderer* renderer, WindowingInstance* window)
{
// TODO: truly allocate
RenderWindow* render_window = &renderer_window_memory;
#if BB_WINDOWING_BACKEND_X11
VkXcbSurfaceCreateInfoKHR create_info = {
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
.pNext = 0,
.flags = 0,
.connection = xcb_connection_get(),
.window = xcb_window_from_windowing_instance(window),
};
vkok(vkCreateXcbSurfaceKHR(instance, &create_info, renderer->allocator, &render_window->surface));
#endif
#if BB_WINDOWING_BACKEND_WIN32
VkWin32SurfaceCreateInfoKHR create_info = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.pNext = 0,
.flags = 0,
.hinstance = windowing_connection.instance,
.hwnd = window->handle,
};
vkok(vkCreateWin32SurfaceKHR(instance, &create_info, renderer->allocator, &render_window->surface));
#endif
#if BB_WINDOWING_BACKEND_COCOA
CAMetalLayer* layer = [CAMetalLayer layer];
layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
layer.framebufferOnly = true;
layer.frame = window.frame;
window.contentView.layer = layer;
window.opaque = true;
window.backgroundColor = nil;
VkMetalSurfaceCreateInfoEXT create_info = {
.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
.pNext = 0,
.flags = 0,
.pLayer = [CAMetalLayer layer],
};
vkCreateMetalSurfaceEXT(instance, &create_info, renderer->allocator, &render_window->surface);
#endif
WindowingSize window_size = windowing_get_instance_framebuffer_size(window);
swapchain_recreate(renderer, render_window);
for (u64 frame_index = 0; frame_index < MAX_FRAME_COUNT; frame_index += 1)
{
for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1)
{
render_window->frames[frame_index].pipeline_instantiations[pipeline_index].vertex_buffer.gpu.type = BUFFER_TYPE_VERTEX;
render_window->frames[frame_index].pipeline_instantiations[pipeline_index].index_buffer.gpu.type = BUFFER_TYPE_INDEX;
render_window->frames[frame_index].pipeline_instantiations[pipeline_index].transient_buffer.type = BUFFER_TYPE_STAGING;
}
}
for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1)
{
let(pipeline_descriptor, &renderer->pipelines[pipeline_index]);
let(pipeline_instantiation, &render_window->pipeline_instantiations[pipeline_index]);
u16 descriptor_type_counter[DESCRIPTOR_TYPE_COUNT] = {};
for (u64 descriptor_index = 0; descriptor_index < pipeline_descriptor->descriptor_set_count; descriptor_index += 1)
{
let(descriptor_set_layout_bindings, &pipeline_descriptor->descriptor_set_layout_bindings[descriptor_index]);
for (u64 binding_index = 0; binding_index < descriptor_set_layout_bindings->count; binding_index += 1)
{
let(binding_descriptor, &descriptor_set_layout_bindings->buffer[binding_index]);
let(descriptor_type, descriptor_type_from_vulkan(binding_descriptor->descriptorType));
let(counter_ptr, &descriptor_type_counter[descriptor_type]);
let(old_counter, *counter_ptr);
*counter_ptr = old_counter + binding_descriptor->descriptorCount;
}
}
VkDescriptorPoolSize pool_sizes[DESCRIPTOR_TYPE_COUNT];
u32 pool_size_count = 0;
for (DescriptorType i = 0; i < DESCRIPTOR_TYPE_COUNT; i += 1)
{
let(count, descriptor_type_counter[i]);
if (count)
{
let(pool_size, &pool_sizes[pool_size_count]);
pool_size_count += 1;
*pool_size = (VkDescriptorPoolSize) {
.type = vulkan_descriptor_type(i),
.descriptorCount = count,
};
}
}
VkDescriptorPoolCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = 0,
.flags = 0,
.maxSets = pipeline_descriptor->descriptor_set_count,
.poolSizeCount = pool_size_count,
.pPoolSizes = pool_sizes,
};
VkDescriptorPool descriptor_pool;
vkok(vkCreateDescriptorPool(renderer->device, &create_info, renderer->allocator, &descriptor_pool));
VkDescriptorSetAllocateInfo allocate_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = 0,
.descriptorPool = descriptor_pool,
.descriptorSetCount = pipeline_descriptor->descriptor_set_count,
.pSetLayouts = pipeline_descriptor->descriptor_set_layouts,
};
vkok(vkAllocateDescriptorSets(renderer->device, &allocate_info, pipeline_instantiation->descriptor_sets));
}
for (u32 i = 0; i < MAX_FRAME_COUNT; i += 1)
{
VkCommandPoolCreateInfo command_pool_create_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = renderer->graphics_queue_family_index,
};
VkFenceCreateInfo fence_create_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
VkSemaphoreCreateInfo semaphore_create_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.flags = 0,
};
WindowFrame* frame = &render_window->frames[i];
vkok(vkCreateCommandPool(renderer->device, &command_pool_create_info, renderer->allocator, &frame->command_pool));
VkCommandBufferAllocateInfo command_buffer_allocate_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = frame->command_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
vkok(vkAllocateCommandBuffers(renderer->device, &command_buffer_allocate_info, &frame->command_buffer));
vkok(vkCreateFence(renderer->device, &fence_create_info, renderer->allocator, &frame->render_fence));
vkok(vkCreateSemaphore(renderer->device, &semaphore_create_info, renderer->allocator, &frame->render_semaphore));
vkok(vkCreateSemaphore(renderer->device, &semaphore_create_info, renderer->allocator, &frame->swapchain_semaphore));
frame->bound_pipeline = BB_PIPELINE_COUNT;
}
return render_window;
}
fn WindowFrame* window_frame(RenderWindow* window)
{
return &window->frames[window->frame_index % MAX_FRAME_COUNT];
}
fn void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window)
{
let(frame, window_frame(window));
let(timeout, ~(u64)0);
u32 fence_count = 1;
VkBool32 wait_all = 1;
vkok(vkWaitForFences(renderer->device, fence_count, &frame->render_fence, wait_all, timeout));
VkFence image_fence = 0;
VkResult next_image_result = vkAcquireNextImageKHR(renderer->device, window->swapchain, timeout, frame->swapchain_semaphore, image_fence, &window->swapchain_image_index);
if (next_image_result == VK_ERROR_OUT_OF_DATE_KHR)
{
swapchain_recreate(renderer, window);
}
else if (next_image_result != VK_SUCCESS && next_image_result != VK_SUBOPTIMAL_KHR)
{
vkok(next_image_result);
}
vkok(vkResetFences(renderer->device, fence_count, &frame->render_fence));
VkCommandBufferResetFlags reset_flags = 0;
vkok(vkResetCommandBuffer(frame->command_buffer, reset_flags));
// Reset frame data
for (u32 i = 0; i < array_length(window->pipeline_instantiations); i += 1)
{
let(pipeline_instantiation, &frame->pipeline_instantiations[i]);
pipeline_instantiation->vertex_buffer.cpu.length = 0;
pipeline_instantiation->vertex_buffer.count = 0;
pipeline_instantiation->index_buffer.cpu.length = 0;
}
}
fn void buffer_destroy(Renderer* renderer, VulkanBuffer buffer)
{
if (buffer.handle)
{
vkDestroyBuffer(renderer->device, buffer.handle, renderer->allocator);
}
if (buffer.memory.handle)
{
if (buffer.type == BUFFER_TYPE_STAGING)
{
vkUnmapMemory(renderer->device, buffer.memory.handle);
}
vkFreeMemory(renderer->device, buffer.memory.handle, renderer->allocator);
}
}
fn void buffer_ensure_capacity(Renderer* renderer, VulkanBuffer* buffer, u64 needed_size)
{
if (unlikely(needed_size > buffer->memory.size))
{
buffer_destroy(renderer, *buffer);
*buffer = buffer_create(renderer, needed_size, buffer->type);
}
}
fn void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
{
let(frame, window_frame(window));
VkCommandBufferBeginInfo command_buffer_begin_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkok(vkBeginCommandBuffer(frame->command_buffer, &command_buffer_begin_info));
for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1)
{
let(frame_pipeline_instantiation, &frame->pipeline_instantiations[i]);
if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length))
{
let(new_vertex_buffer_size, frame_pipeline_instantiation->vertex_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->vertex_buffer.cpu.pointer));
let(new_index_buffer_size, frame_pipeline_instantiation->index_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->index_buffer.cpu.pointer));
let(new_transient_buffer_size, new_vertex_buffer_size + new_index_buffer_size);
buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->transient_buffer, new_transient_buffer_size);
buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->vertex_buffer.gpu, new_vertex_buffer_size);
buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->index_buffer.gpu, new_index_buffer_size);
buffer_copy_to_host(frame_pipeline_instantiation->transient_buffer, (Slice(HostBufferCopy)) array_to_slice(((HostBufferCopy[]) {
(HostBufferCopy) {
.source = (String) {
.pointer = (u8*)frame_pipeline_instantiation->vertex_buffer.cpu.pointer,
.length = new_vertex_buffer_size,
},
.destination_offset = 0,
},
(HostBufferCopy) {
.source = (String) {
.pointer = (u8*)frame_pipeline_instantiation->index_buffer.cpu.pointer,
.length = new_index_buffer_size,
},
.destination_offset = new_vertex_buffer_size,
},
})));
buffer_copy_to_local_command(frame->command_buffer, (Slice(LocalBufferCopy)) array_to_slice(((LocalBufferCopy[]) {
{
.destination = frame_pipeline_instantiation->vertex_buffer.gpu,
.source = frame_pipeline_instantiation->transient_buffer,
.regions = array_to_slice(((LocalBufferCopyRegion[]) {
{
.source_offset = 0,
.destination_offset = 0,
.size = new_vertex_buffer_size,
},
})),
},
{
.destination = frame_pipeline_instantiation->index_buffer.gpu,
.source = frame_pipeline_instantiation->transient_buffer,
.regions = array_to_slice(((LocalBufferCopyRegion[]) {
{
.source_offset = new_vertex_buffer_size,
.destination_offset = 0,
.size = new_index_buffer_size,
},
})),
},
})));
}
}
vk_image_transition(frame->command_buffer, window->render_image.handle, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkViewport viewports[] = {
{
.x = 0,
.y = 0,
.width = window->width,
.height = window->height,
.minDepth = 0.0f,
.maxDepth = 1.0f,
}
};
u32 first_viewport = 0;
vkCmdSetViewport(frame->command_buffer, first_viewport, array_length(viewports), viewports);
VkRect2D scissors[] = {
{
.offset = {
.x = 0,
.y = 0,
},
.extent = {
.width = window->width,
.height = window->height,
},
}
};
u32 first_scissor = 0;
vkCmdSetScissor(frame->command_buffer, first_scissor, array_length(scissors), scissors);
VkRenderingAttachmentInfo color_attachments[] = {
{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = window->render_image.view,
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = { .color = { .float32 = { 255.0f, 0.0f, 255.0f, 1.0f } } },
},
};
VkRenderingInfo rendering_info = {
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea = {
.extent = {
.width = window->width,
.height = window->height,
},
},
.layerCount = 1,
.colorAttachmentCount = array_length(color_attachments),
.pColorAttachments = color_attachments,
};
vkCmdBeginRendering(frame->command_buffer, &rendering_info);
for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1)
{
let(pipeline, &renderer->pipelines[i]);
let(pipeline_instantiation, &window->pipeline_instantiations[i]);
let(frame_pipeline_instantiation, &frame->pipeline_instantiations[i]);
if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length))
{
// Bind pipeline and descriptor sets
{
vkCmdBindPipeline(frame->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle);
// print("Binding pipeline: 0x{u64}\n", pipeline->handle);
u32 dynamic_offset_count = 0;
u32* dynamic_offsets = 0;
u32 first_set = 0;
vkCmdBindDescriptorSets(frame->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, first_set, pipeline->descriptor_set_count, pipeline_instantiation->descriptor_sets, dynamic_offset_count, dynamic_offsets);
// print("Binding descriptor sets: 0x{u64}\n", pipeline_instantiation->descriptor_sets);
frame->bound_pipeline = i;
}
// Bind index buffer
{
vkCmdBindIndexBuffer(frame->command_buffer, frame_pipeline_instantiation->index_buffer.gpu.handle, 0, VK_INDEX_TYPE_UINT32);
frame->index_buffer = frame_pipeline_instantiation->index_buffer.gpu.handle;
// print("Binding descriptor sets: 0x{u64}\n", frame->index_buffer);
}
// Send vertex buffer and screen dimensions to the shader
GPUDrawPushConstants push_constants = {
.vertex_buffer = frame_pipeline_instantiation->vertex_buffer.gpu.address,
.width = window->width,
.height = window->height,
};
{
let(push_constant_range, pipeline->push_constant_ranges[0]);
vkCmdPushConstants(frame->command_buffer, pipeline->layout, push_constant_range.stageFlags, push_constant_range.offset, push_constant_range.size, &push_constants);
frame->push_constants = push_constants;
}
vkCmdDrawIndexed(frame->command_buffer, frame_pipeline_instantiation->index_buffer.cpu.length, 1, 0, 0, 0);
}
}
vkCmdEndRendering(frame->command_buffer);
vk_image_transition(frame->command_buffer, window->render_image.handle, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VkImage swapchain_image = window->swapchain_images[window->swapchain_image_index];
vk_image_transition(frame->command_buffer, swapchain_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vk_image_copy(frame->command_buffer, (VulkanCopyImageArgs) {
.source = {
.handle = window->render_image.handle,
.extent = {
.width = window->width,
.height = window->height,
},
},
.destination = {
.handle = swapchain_image,
.extent = {
.width = window->width,
.height = window->height,
},
},
});
vk_image_transition(frame->command_buffer, swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
vkok(vkEndCommandBuffer(frame->command_buffer));
VkCommandBufferSubmitInfo command_buffer_submit_info[] = {
{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
.commandBuffer = frame->command_buffer,
.deviceMask = 0,
},
};
VkSemaphoreSubmitInfo wait_semaphore_submit_info[] = {
{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
.semaphore = frame->swapchain_semaphore,
.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.deviceIndex = 0,
.value = 1,
},
};
VkSemaphoreSubmitInfo signal_semaphore_submit_info[] = {
{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
.semaphore = frame->render_semaphore,
.stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT,
.deviceIndex = 0,
.value = 1,
},
};
VkSubmitInfo2 submit_info[] = {
{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
.flags = 0,
.waitSemaphoreInfoCount = array_length(wait_semaphore_submit_info),
.pWaitSemaphoreInfos = wait_semaphore_submit_info,
.signalSemaphoreInfoCount = array_length(signal_semaphore_submit_info),
.pSignalSemaphoreInfos = signal_semaphore_submit_info,
.commandBufferInfoCount = array_length(command_buffer_submit_info),
.pCommandBufferInfos = command_buffer_submit_info,
},
};
vkok(vkQueueSubmit2(renderer->graphics_queue, array_length(submit_info), submit_info, frame->render_fence));
const VkSwapchainKHR swapchains[] = { window->swapchain };
const u32 swapchain_image_indices[] = { window->swapchain_image_index };
const VkSemaphore wait_semaphores[] = { frame->render_semaphore };
VkResult results[array_length(swapchains)];
VkPresentInfoKHR present_info = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = array_length(wait_semaphores),
.pWaitSemaphores = wait_semaphores,
.swapchainCount = array_length(swapchains),
.pSwapchains = swapchains,
.pImageIndices = swapchain_image_indices,
.pResults = results,
};
VkResult present_result = vkQueuePresentKHR(renderer->graphics_queue, &present_info);
if (present_result == VK_SUCCESS)
{
for (u32 i = 0; i < array_length(results); i += 1)
{
vkok(results[i]);
}
}
else if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR)
{
swapchain_recreate(renderer, window);
}
else
{
vkok(present_result);
}
window->frame_index += 1;
}
fn VkFormat vk_texture_format(TextureFormat format)
{
VkFormat result;
switch (format)
{
case TEXTURE_FORMAT_R8_UNORM:
result = VK_FORMAT_R8_UNORM;
break;
case TEXTURE_FORMAT_R8G8B8A8_SRGB:
result = VK_FORMAT_R8G8B8A8_SRGB;
break;
}
return result;
}
fn u32 format_channel_count(TextureFormat format)
{
switch (format)
{
case TEXTURE_FORMAT_R8_UNORM:
return 1;
case TEXTURE_FORMAT_R8G8B8A8_SRGB:
return 4;
}
unreachable();
}
fn TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory)
{
assert(texture_memory.depth == 1);
let(texture_index, texture_count);
texture_count += 1;
let(texture, &textures[texture_index]);
texture->image = vk_image_create(renderer->device, renderer->allocator, renderer->memory_properties, (VulkanImageCreate) {
.width = texture_memory.width,
.height = texture_memory.height,
.mip_levels = 1,
.format = vk_texture_format(texture_memory.format),
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
});
texture->sampler = renderer->sampler;
let(image_size, (u64)texture_memory.depth * texture_memory.width * texture_memory.height * format_channel_count(texture_memory.format));
VkBufferUsageFlags buffer_usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VkMemoryPropertyFlags buffer_memory_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
let(transfer_buffer, vk_buffer_create(renderer->device, renderer->allocator, renderer->memory_properties, image_size, buffer_usage_flags, buffer_memory_flags));
memcpy((void*)transfer_buffer.address, texture_memory.pointer, image_size);
immediate_start(renderer->immediate);
vk_image_transition(renderer->immediate.command_buffer, texture->image.handle, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy copy_regions[] = {
{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent = {
.width = texture_memory.width,
.height = texture_memory.height,
.depth = texture_memory.depth,
},
}
};
vkCmdCopyBufferToImage(renderer->immediate.command_buffer, transfer_buffer.handle, texture->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, array_length(copy_regions), copy_regions);
vk_image_transition(renderer->immediate.command_buffer, texture->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
immediate_end(renderer->immediate);
return (TextureIndex) { .value = texture_index };
}
fn void window_draw_indexed(RenderWindow* window, u32 index_count, u32 instance_count, u32 first_index, s32 vertex_offset, u32 first_instance)
{
let(frame, window_frame(window));
vkCmdDrawIndexed(frame->command_buffer, index_count, instance_count, first_index, vertex_offset, first_instance);
}
fn void window_texture_update_begin(RenderWindow* window, BBPipeline pipeline_index, u32 descriptor_count)
{
let(pipeline_instantiation, &window->pipeline_instantiations[pipeline_index]);
assert(descriptor_count <= array_length(pipeline_instantiation->texture_descriptors));
pipeline_instantiation->descriptor_set_update = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = 0,
.dstSet = pipeline_instantiation->descriptor_sets[0],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = descriptor_count,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = pipeline_instantiation->texture_descriptors,
.pBufferInfo = 0,
.pTexelBufferView = 0,
};
}
fn void window_rect_texture_update_begin(RenderWindow* window)
{
window_texture_update_begin(window, BB_PIPELINE_RECT, RECT_TEXTURE_SLOT_COUNT);
}
fn void window_queue_pipeline_texture_update(RenderWindow* window, BBPipeline pipeline_index, u32 resource_slot, TextureIndex texture_index)
{
let(pipeline_instantiation, &window->pipeline_instantiations[pipeline_index]);
VkDescriptorImageInfo* descriptor_image = &pipeline_instantiation->texture_descriptors[resource_slot];
VulkanTexture* texture = &textures[texture_index.value];
*descriptor_image = (VkDescriptorImageInfo) {
.sampler = texture->sampler,
.imageView = texture->image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // TODO: specify
};
}
fn void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index)
{
window_queue_pipeline_texture_update(window, BB_PIPELINE_RECT, slot, texture_index);
}
fn void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas)
{
static_assert(RECT_TEXTURE_SLOT_MONOSPACE_FONT < RECT_TEXTURE_SLOT_PROPORTIONAL_FONT);
let(slot, RECT_TEXTURE_SLOT_MONOSPACE_FONT + type);
window_queue_rect_texture_update(window, slot, atlas.texture);
renderer->fonts[type] = atlas;
}
fn void window_texture_update_end(Renderer* renderer, RenderWindow* window, BBPipeline pipeline_index)
{
let(pipeline_instantiation, &window->pipeline_instantiations[pipeline_index]);
u32 descriptor_copy_count = 0;
VkCopyDescriptorSet* descriptor_copies = 0;
VkWriteDescriptorSet descriptor_set_writes[] = {
pipeline_instantiation->descriptor_set_update,
};
vkUpdateDescriptorSets(renderer->device, array_length(descriptor_set_writes), descriptor_set_writes, descriptor_copy_count, descriptor_copies);
}
fn void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window)
{
window_texture_update_end(renderer, window, BB_PIPELINE_RECT);
}
fn u32 window_pipeline_add_vertices(RenderWindow* window, BBPipeline pipeline_index, String vertex_memory, u32 vertex_count)
{
let(frame, window_frame(window));
let(vertex_buffer, &frame->pipeline_instantiations[pipeline_index].vertex_buffer);
vb_copy_string(&vertex_buffer->cpu, vertex_memory);
let(vertex_offset, vertex_buffer->count);
vertex_buffer->count = vertex_offset + vertex_count;
return vertex_offset;
}
fn void window_pipeline_add_indices(RenderWindow* window, BBPipeline pipeline_index, Slice(u32) indices)
{
let(frame, window_frame(window));
let(index_pointer, vb_add(&frame->pipeline_instantiations[pipeline_index].index_buffer.cpu, indices.length));
memcpy(index_pointer, indices.pointer, indices.length * sizeof(*indices.pointer));
}
fn void window_render_rect(RenderWindow* window, RectDraw draw)
{
let(p0, draw.vertex.p0);
let(uv0, draw.texture.p0);
if (draw.texture.p1.x != 0)
{
assert(draw.texture.p1.x - draw.texture.p0.x == draw.vertex.p1.x - draw.vertex.p0.x);
assert(draw.texture.p1.y - draw.texture.p0.y == draw.vertex.p1.y - draw.vertex.p0.y);
}
let(corner_radius, 5.0f);
let(extent, float2_sub(draw.vertex.p1, p0));
RectVertex vertices[] = {
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = draw.texture_index,
.colors = { draw.colors[0], draw.colors[1], draw.colors[2], draw.colors[3] },
.softness = 1.0,
.corner_radius = corner_radius,
},
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = draw.texture_index,
.colors = { draw.colors[0], draw.colors[1], draw.colors[2], draw.colors[3] },
.softness = 1.0,
.corner_radius = corner_radius,
},
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = draw.texture_index,
.colors = { draw.colors[0], draw.colors[1], draw.colors[2], draw.colors[3] },
.softness = 1.0,
.corner_radius = corner_radius,
},
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = draw.texture_index,
.colors = { draw.colors[0], draw.colors[1], draw.colors[2], draw.colors[3] },
.softness = 1.0,
.corner_radius = corner_radius,
},
};
let(vertex_offset, window_pipeline_add_vertices(window, BB_PIPELINE_RECT, (String)array_to_bytes(vertices), array_length(vertices)));
u32 indices[] = {
vertex_offset + 0,
vertex_offset + 1,
vertex_offset + 2,
vertex_offset + 1,
vertex_offset + 3,
vertex_offset + 2,
};
window_pipeline_add_indices(window, BB_PIPELINE_RECT, (Slice(u32))array_to_slice(indices));
}
// TODO: support gradient
fn void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset)
{
let(texture_atlas, &renderer->fonts[font_type]);
let(height, texture_atlas->ascent - texture_atlas->descent);
let(texture_index, texture_atlas->texture.value);
for (u64 i = 0; i < string.length; i += 1)
{
let(ch, string.pointer[i]);
let(character, &texture_atlas->characters[ch]);
let(uv_x, character->x);
let(uv_y, character->y);
let(char_width, character->width);
let(char_height, character->height);
let(pos_x, x_offset);
let(pos_y, y_offset + character->y_offset + height + texture_atlas->descent); // Offset of the height to render the character from the bottom (y + height) up (y)
vec2 p0 = { pos_x, pos_y };
vec2 uv0 = { uv_x, uv_y };
vec2 extent = { char_width, char_height };
// print("P0: ({u32}, {u32}). P1: ({u32}, {u32})\n", (u32)p0.x, (u32)p0.y, (u32)p1.x, (u32)p1.y);
RectVertex vertices[] = {
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = texture_index,
.colors = { color, color, color, color },
.softness = 1.0,
},
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = texture_index,
.colors = { color, color, color, color },
.softness = 1.0,
},
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.texture_index = texture_index,
.colors = { color, color, color, color },
.softness = 1.0,
},
(RectVertex) {
.p0 = p0,
.uv0 = uv0,
.extent = extent,
.colors = { color, color, color, color },
.texture_index = texture_index,
.softness = 1.0,
},
};
let(vertex_offset, window_pipeline_add_vertices(window, BB_PIPELINE_RECT, (String)array_to_bytes(vertices), array_length(vertices)));
u32 indices[] = {
vertex_offset + 0,
vertex_offset + 1,
vertex_offset + 2,
vertex_offset + 1,
vertex_offset + 3,
vertex_offset + 2,
};
window_pipeline_add_indices(window, BB_PIPELINE_RECT, (Slice(u32))array_to_slice(indices));
u32 kerning = (texture_atlas->kerning_tables + ch * 256)[string.pointer[i + 1]];
x_offset += character->advance + kerning;
}
}