2503 lines
90 KiB
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, ©.source);
|
|
let(destination_buffer, ©.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;
|
|
}
|
|
}
|