/** * 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 #include "../../platform/win32/Window.h" #elif __linux__ #include #endif #include #include #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(swapChainExtent.width); viewport.height = static_cast(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