cOMS/gpuapi/vulkan/VulkanUtils.h

761 lines
31 KiB
C++

/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_GPUAPI_VULKAN_UTILS_H
#define TOS_GPUAPI_VULKAN_UTILS_H
#if _WIN32
#ifndef VK_USE_PLATFORM_WIN32_KHR
#define VK_USE_PLATFORM_WIN32_KHR 1
#endif
#include <vulkan/vulkan_win32.h>
#include "../../platform/win32/Window.h"
#elif __linux__
#include <vulkan/vulkan_xlib.h>
#endif
#include <vulkan/vulkan.h>
#include <vector>
#include "../../stdlib/Types.h"
#include "ShaderUtils.h"
#include "../../utils/TestUtils.h"
#include "../../log/Log.h"
#include "../../memory/RingMemory.h"
PACKED_STRUCT;
// WARNING: indices values start at one (are offset by +1) because 0 means no value in our implementation
// The reason for the packing is that sometimes we want to use it as an array
// I am only packing it on the off chance there is some funky behaviour.
struct VulkanQueueFamilyIndices {
uint32 graphics_family;
uint32 present_family;
};
UNPACKED_STRUCT;
struct VulkanSwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
uint32 format_size;
VkSurfaceFormatKHR* formats;
uint32 present_mode_size;
VkPresentModeKHR* present_modes;
};
inline
void change_viewport(Window* w, int32 offset_x = 0, int32 offset_y = 0)
{
// @todo implement
}
// @todo Implement logging of if() conditions
bool vulkan_check_validation_layer_support(const char** validation_layers, uint32 validation_layer_count, RingMemory* ring) {
uint32 layer_count;
vkEnumerateInstanceLayerProperties(&layer_count, NULL);
VkLayerProperties* available_layers = (VkLayerProperties *) ring_get_memory(ring, layer_count * sizeof(VkLayerProperties));
vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
for (uint32 i = 0; i < validation_layer_count; ++i) {
bool layerFound = false;
for (uint32 j = 0; j < layer_count; ++j) {
if (strcmp(validation_layers[i], available_layers[j].layerName) == 0) {
layerFound = true;
break;
}
}
if (!layerFound) {
LOG_FORMAT("Vulkan ValidationLayer: %s\n", LOG_DATA_CHAR_STR, &validation_layers[i], true, true);
return false;
}
}
return true;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT* debug_callback_data, void*
) {
LOG(debug_callback_data->pMessage, true, true);
ASSERT_SIMPLE(false);
return VK_FALSE;
}
void vulkan_populate_debug_messenger_create_info(VkDebugUtilsMessengerCreateInfoEXT* create_info)
{
create_info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info->messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
create_info->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;
create_info->pfnUserCallback = vulkan_debug_callback;
}
VkResult vulkan_debug_utils_messenger_create(
VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkAllocationCallbacks* allocator, VkDebugUtilsMessengerEXT* debug_messenger
) {
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (!func) {
ASSERT_SIMPLE(func);
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
return func(instance, create_info, allocator, debug_messenger);
}
void vulkan_debug_messenger_setup(VkInstance instance, VkDebugUtilsMessengerEXT* debug_messenger)
{
VkDebugUtilsMessengerCreateInfoEXT create_info = {};
vulkan_populate_debug_messenger_create_info(&create_info);
if (vulkan_debug_utils_messenger_create(instance, &create_info, NULL, debug_messenger) != VK_SUCCESS) {
ASSERT_SIMPLE(false);
}
}
void vulkan_instance_create(
VkInstance* instance, const char** extensions, uint32 extension_count,
const char** validation_layers = NULL, uint32 validation_layer_count = 0,
RingMemory* ring = NULL
) {
if (validation_layer_count
&& !vulkan_check_validation_layer_support(validation_layers, validation_layer_count, ring)
) {
ASSERT_SIMPLE(false);
}
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pApplicationName = "";
app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
app_info.pEngineName = "TalesOfSouls";
app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
app_info.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
create_info.enabledExtensionCount = extension_count;
create_info.ppEnabledExtensionNames = extensions;
VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {};
if (validation_layer_count) {
create_info.enabledLayerCount = validation_layer_count;
create_info.ppEnabledLayerNames = validation_layers;
vulkan_populate_debug_messenger_create_info(&debug_create_info);
create_info.pNext = (VkDebugUtilsMessengerCreateInfoEXT *) &debug_create_info;
}
VkResult result;
if ((result = vkCreateInstance(&create_info, NULL, instance)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateInstance: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
void vulkan_surface_create(VkInstance instance, VkSurfaceKHR* surface, Window* window)
{
#if _WIN32
VkWin32SurfaceCreateInfoKHR surface_create_info = {};
surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surface_create_info.hwnd = window->hwnd;
surface_create_info.hinstance = window->hInstance;
VkResult result;
if ((result = vkCreateWin32SurfaceKHR(instance, &surface_create_info, NULL, surface)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateWin32SurfaceKHR: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
return;
}
#elif __linux__
#endif
}
bool vulkan_device_supports_extensions(VkPhysicalDevice device, const char** device_extensions, uint32 device_extension_count, RingMemory* ring) {
uint32 extension_count;
vkEnumerateDeviceExtensionProperties(device, NULL, &extension_count, NULL);
VkExtensionProperties* available_extensions = (VkExtensionProperties *) ring_get_memory(ring, extension_count * sizeof(VkExtensionProperties));
vkEnumerateDeviceExtensionProperties(device, NULL, &extension_count, available_extensions);
for (int32 i = 0; i < device_extension_count; ++i) {
bool found = false;
for (int32 j = 0; j < extension_count; ++j) {
if (strcmp(device_extensions[i], available_extensions[j].extensionName) == 0) {
found = true;
break;
}
}
if (!found) {
LOG_FORMAT("Vulkan DeviceExtensions: %s\n", LOG_DATA_CHAR_STR, &device_extensions[i], true, true);
return false;
}
}
return true;
}
VulkanQueueFamilyIndices vulkan_find_queue_families(VkPhysicalDevice physical_device, VkSurfaceKHR surface, RingMemory* ring)
{
VulkanQueueFamilyIndices indices = {};
uint32 queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, NULL);
VkQueueFamilyProperties* queue_families = (VkQueueFamilyProperties *) ring_get_memory(ring, (queue_family_count + 1) * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families);
for (int32 i = 0; i < queue_family_count; ++i) {
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphics_family = i + 1;
}
VkBool32 present_support = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &present_support);
if (present_support) {
indices.present_family = i + 1;
}
if (indices.graphics_family && indices.present_family) {
break;
}
}
return indices;
}
// WARNING: Since we use a RingMemory for the data, we need to copy it if we want it to be persistent
VulkanSwapChainSupportDetails vulkan_query_swap_chain_support(VkPhysicalDevice physical_device, VkSurfaceKHR surface, RingMemory* ring)
{
VulkanSwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &details.capabilities);
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details.format_size, NULL);
if (details.format_size) {
details.formats = (VkSurfaceFormatKHR *) ring_get_memory(ring, (details.format_size + 1) * sizeof(VkSurfaceFormatKHR));
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details.format_size, details.formats);
}
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details.present_mode_size, NULL);
if (details.present_mode_size) {
details.present_modes = (VkPresentModeKHR *) ring_get_memory(ring, (details.present_mode_size + 1) * sizeof(VkPresentModeKHR));
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details.present_mode_size, details.present_modes);
}
return details;
}
bool vulkan_is_device_suitable(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const char** device_extensions, uint32 device_extension_count, RingMemory* ring)
{
VulkanQueueFamilyIndices indices = vulkan_find_queue_families(physical_device, surface, ring);
bool extensions_supported = vulkan_device_supports_extensions(physical_device, device_extensions, device_extension_count, ring);
bool swap_chain_adequate = false;
if (extensions_supported) {
VulkanSwapChainSupportDetails swap_chain_support = vulkan_query_swap_chain_support(physical_device, surface, ring);
swap_chain_adequate = swap_chain_support.format_size && swap_chain_support.present_modes;
}
return indices.graphics_family && indices.present_family
&& extensions_supported && swap_chain_adequate;
}
// @todo Do we want to implement something similar in opengl that does something vaguely different despite not really necessary? (see wglGetGPUIDs, wglCreateAssociatedContextAMD)
void gpuapi_pick_physical_device(
VkInstance instance, VkSurfaceKHR surface, VkPhysicalDevice* physical_device,
const char** device_extensions, uint32 device_extension_count, RingMemory* ring
) {
uint32 device_count;
vkEnumeratePhysicalDevices(instance, &device_count, NULL);
VkPhysicalDevice* devices = (VkPhysicalDevice *) ring_get_memory(ring, device_count * sizeof(VkPhysicalDevice));
vkEnumeratePhysicalDevices(instance, &device_count, devices);
for (int32 i = 0; i < device_count; ++i) {
if (vulkan_is_device_suitable(devices[i], surface, device_extensions, device_extension_count, ring)) {
// @question Do we really have to do a memcpy or could we just assign? Isn't VkPhysicalDevice just a pointer internally?
memcpy(physical_device, &devices[i], sizeof(VkPhysicalDevice));
return;
}
}
LOG("Vulkan failed to find physical device\n", true, true);
ASSERT_SIMPLE(false);
}
void gpuapi_create_logical_device(
VkInstance instance, VkSurfaceKHR surface, VkDevice* device, VkPhysicalDevice physical_device,
VkQueue* graphics_queue, VkQueue* present_queue,
const char** device_extensions, uint32 device_extension_count,
const char** validation_layers, uint32 validation_layer_count, RingMemory* ring
) {
VulkanQueueFamilyIndices indices = vulkan_find_queue_families(physical_device, surface, ring);
VkDeviceQueueCreateInfo* queue_create_infos = (VkDeviceQueueCreateInfo *) ring_get_memory(ring, 2 * sizeof(VkDeviceQueueCreateInfo), 4, true);
f32 queue_priority = 1.0f;
uint32 queue_create_info_count = 1;
queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_infos[0].queueFamilyIndex = indices.graphics_family;
queue_create_infos[0].queueCount = 1;
queue_create_infos[0].pQueuePriorities = &queue_priority;
if (indices.present_family != indices.graphics_family) {
queue_create_infos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_infos[1].queueFamilyIndex = indices.present_family;
queue_create_infos[1].queueCount = 1;
queue_create_infos[1].pQueuePriorities = &queue_priority;
++queue_create_info_count;
}
VkPhysicalDeviceFeatures device_features = {};
VkDeviceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = queue_create_info_count;
create_info.pQueueCreateInfos = queue_create_infos;
create_info.pEnabledFeatures = &device_features;
create_info.enabledExtensionCount = device_extension_count;
create_info.ppEnabledExtensionNames = device_extensions;
if (validation_layers) {
create_info.enabledLayerCount = validation_layer_count;
create_info.ppEnabledLayerNames = validation_layers;
} else {
create_info.enabledLayerCount = 0;
}
VkResult result;
if ((result = vkCreateDevice(physical_device, &create_info, NULL, device)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateDevice: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
vkGetDeviceQueue(*device, indices.graphics_family, 0, graphics_queue);
vkGetDeviceQueue(*device, indices.present_family, 0, present_queue);
}
// WARNING: swapchain_images needs to already have reserved enough memory
// @todo How can we ensure swapchain_images has enough but not too much space?
void vulkan_swap_chain_create(
VkDevice device, VkPhysicalDevice physical_device, VkSurfaceKHR surface,
VkSwapchainKHR* swapchain, VkImage* swapchain_images, uint32* swapchain_image_count,
VkFormat* swapchain_image_format, VkExtent2D* swapchain_extent,
Window* window, RingMemory* ring
) {
VulkanSwapChainSupportDetails swap_chain_support = vulkan_query_swap_chain_support(physical_device, surface, ring);
VkSurfaceFormatKHR* surface_format = &swap_chain_support.formats[0];
for (int32 i = 0; i < swap_chain_support.format_size; ++i) {
if (swap_chain_support.formats[i].format == VK_FORMAT_B8G8R8A8_SRGB
&& swap_chain_support.formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
) {
surface_format = &swap_chain_support.formats[i];
}
}
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
for (int32 i = 0; i < swap_chain_support.present_mode_size; ++i) {
if (swap_chain_support.present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
present_mode = swap_chain_support.present_modes[i];
}
}
if (swap_chain_support.capabilities.currentExtent.width != UINT32_MAX) {
*swapchain_extent = swap_chain_support.capabilities.currentExtent;
} else {
swapchain_extent->width = OMS_CLAMP(
window->width,
swap_chain_support.capabilities.maxImageExtent.width,
swap_chain_support.capabilities.minImageExtent.width
);
swapchain_extent->height = OMS_CLAMP(
window->height,
swap_chain_support.capabilities.maxImageExtent.height,
swap_chain_support.capabilities.minImageExtent.height
);
}
uint32 image_count = swap_chain_support.capabilities.minImageCount + 1;
if (swap_chain_support.capabilities.maxImageCount > 0
&& image_count > swap_chain_support.capabilities.maxImageCount
) {
image_count = swap_chain_support.capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR create_info = {};
create_info.minImageCount = image_count;
create_info.imageFormat = surface_format->format;
create_info.imageColorSpace = surface_format->colorSpace;
create_info.imageExtent = *swapchain_extent;
create_info.imageArrayLayers = 1;
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VulkanQueueFamilyIndices indices = vulkan_find_queue_families(physical_device, surface, ring);
if (indices.graphics_family != indices.present_family) {
create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
create_info.queueFamilyIndexCount = 2;
create_info.pQueueFamilyIndices = (uint32 *) &indices;
} else {
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
create_info.preTransform = swap_chain_support.capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = present_mode;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE;
VkResult result;
if ((result = vkCreateSwapchainKHR(device, &create_info, NULL, swapchain)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateSwapchainKHR: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
vkGetSwapchainImagesKHR(device, *swapchain, swapchain_image_count, NULL);
vkGetSwapchainImagesKHR(device, *swapchain, swapchain_image_count, swapchain_images);
memcpy(swapchain_image_format, &surface_format->format, sizeof(VkFormat));
}
void vulkan_image_views_create(
VkDevice device, VkImageView* swapchain_image_views,
VkImage* swapchain_images, uint32 swapchain_image_count, VkFormat swapchain_image_format
) {
VkResult result;
for (size_t i = 0; i < swapchain_image_count; ++i) {
VkImageViewCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.image = swapchain_images[i];
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = swapchain_image_format;
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
create_info.subresourceRange.baseMipLevel = 0;
create_info.subresourceRange.levelCount = 1;
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.layerCount = 1;
if ((result = vkCreateImageView(device, &create_info, NULL, &swapchain_image_views[i])) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateImageView: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
}
void create_render_pass(
VkDevice device, VkRenderPass* render_pass, VkFormat swapchain_image_format
) {
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = swapchain_image_format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
VkResult result;
if ((result = vkCreateRenderPass(device, &renderPassInfo, NULL, render_pass)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateRenderPass: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
// @todo This is very similar to program_make in opengl. Consider to rename opengl
void vulkan_pipeline_create(
VkDevice device,
VkShaderModule vertex_shader,
VkShaderModule fragment_shader,
VkShaderModule geometry_shader,
VkPipeline* pipeline,
VkPipelineLayout* pipeline_layout,
VkRenderPass render_pass,
const char* source,
int32 source_size
)
{
uint32 stage_count = 0;
VkPipelineShaderStageCreateInfo shaderStages[3];
VkPipelineShaderStageCreateInfo vs_stage_create = {};
vs_stage_create.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vs_stage_create.stage = VK_SHADER_STAGE_VERTEX_BIT;
vs_stage_create.module = vertex_shader;
vs_stage_create.pName = "main";
++stage_count;
VkPipelineShaderStageCreateInfo fs_stage_create = {};
fs_stage_create.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fs_stage_create.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fs_stage_create.module = fragment_shader;
fs_stage_create.pName = "main";
++stage_count;
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = ARRAY_COUNT(dynamicStates);
dynamicState.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pushConstantRangeCount = 0;
VkResult result;
if ((result = vkCreatePipelineLayout(device, &pipelineLayoutInfo, NULL, pipeline_layout)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreatePipelineLayout: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = stage_count;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = *pipeline_layout;
pipelineInfo.renderPass = render_pass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
if ((result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, pipeline)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateGraphicsPipelines: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
vkDestroyShaderModule(device, vertex_shader, NULL);
vkDestroyShaderModule(device, fragment_shader, NULL);
}
// @todo consider to rename to same name as opengl
// WARNING: swapchain_framebuffers needs to be initialized
void vulkan_framebuffer_create(
VkDevice device, VkFramebuffer* swapchain_framebuffers,
VkImageView* swapchain_image_views, uint32 swapchain_image_count, VkExtent2D swapchain_extent,
VkRenderPass render_pass
) {
VkResult result;
for (uint32 i = 0; i < swapchain_image_count; i++) {
VkImageView attachments[] = {
swapchain_image_views[i]
};
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = render_pass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = swapchain_extent.width;
framebufferInfo.height = swapchain_extent.height;
framebufferInfo.layers = 1;
if ((result = vkCreateFramebuffer(device, &framebufferInfo, NULL, &swapchain_framebuffers[i])) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateFramebuffer: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
}
void vulkan_command_pool_create(
VkDevice device, VkCommandPool* command_pool,
VkPhysicalDevice physical_device, VkSurfaceKHR surface, RingMemory* ring
) {
VulkanQueueFamilyIndices queue_family_indices = vulkan_find_queue_families(physical_device, surface, ring);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = queue_family_indices.graphics_family;
VkResult result;
if ((result = vkCreateCommandPool(device, &poolInfo, NULL, command_pool)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateCommandPool: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
void vulkan_command_buffer_create(VkDevice device, VkCommandBuffer* command_buffer, VkCommandPool command_pool)
{
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = command_pool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VkResult result;
if ((result = vkAllocateCommandBuffers(device, &allocInfo, command_buffer)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkAllocateCommandBuffers: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
void vulkan_sync_objects_create(VkDevice device, VkSemaphore* image_available_semaphore, VkSemaphore* render_finished_semaphore, VkFence* in_flight_fence)
{
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VkResult result;
if ((result = vkCreateSemaphore(device, &semaphoreInfo, NULL, image_available_semaphore)) != VK_SUCCESS
|| (result = vkCreateSemaphore(device, &semaphoreInfo, NULL, render_finished_semaphore)) != VK_SUCCESS
|| (result = vkCreateFence(device, &fenceInfo, NULL, in_flight_fence)) != VK_SUCCESS
) {
LOG_FORMAT("Vulkan vulkan_sync_objects_create: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
/*
void vulkan_command_buffer_record(
VkCommandBuffer command_buffer
) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer(command_buffer, &beginInfo) != VK_SUCCESS) {
ASSERT_SIMPLE(false);
}
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapChainExtent;
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapChainExtent.width);
viewport.height = static_cast<float>(swapChainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
vkCmdEndRenderPass(commandBuffer);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
ASSERT_SIMPLE(false);
}
}
*/
#endif