Files
UwU-Engine/Core/Core/RenderEngine/RenderEngine.cpp
T
2025-10-18 16:39:43 +07:00

1010 lines
42 KiB
C++

#include "RenderEngine.hpp"
#include <queue>
#include <set>
#include <stdexcept>
#include <fstream>
#include "Core/CoreInstance.hpp"
#include "Log/Log.hpp"
#include "Game/GameInstance.hpp"
#include "Game/World/World.hpp"
#include "Game/Actors/Mesh/Mesh.hpp"
#include "GLFW/glfw3.h"
const std::vector<const char*> RenderEngine::deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
#ifdef _DEBUG
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
std::string msg = "validation layer: ";
msg += pCallbackData->pMessage;
Loging::Log(msg);
return VK_FALSE;
}
VkResult RenderEngine::enable_layer_validation(const VkDebugUtilsMessengerCreateInfoEXT* create_info)
{
PFN_vkCreateDebugUtilsMessengerEXT debug_creator = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(vkGetInstanceProcAddr(instance_, "vkCreateDebugUtilsMessengerEXT"));
return debug_creator(instance_, create_info, nullptr, &debug_messenger_);
}
#endif
RenderEngine::SwapchainSupportDetails RenderEngine::query_swapchain_details() const
{
SwapchainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device_, surface_, &details.capabilities);
uint32_t surface_format_cnt = 0;
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &surface_format_cnt, nullptr));
details.formats.resize(surface_format_cnt);
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &surface_format_cnt, details.formats.data()));
uint32_t present_modes_cnt = 0;
VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device_, surface_, &present_modes_cnt, nullptr));
details.present_modes.resize(surface_format_cnt);
VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device_, surface_, &present_modes_cnt, details.present_modes.data()));
return details;
}
std::vector<const char*> RenderEngine::get_required_extensions()
{
uint32_t extensions_count = 0;
const char** glfw_extension = glfwGetRequiredInstanceExtensions(&extensions_count);
std::vector<const char*> extensions(glfw_extension, glfw_extension + extensions_count);
#ifdef _DEBUG
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
return extensions;
}
VkVertexInputBindingDescription RenderEngine::Vertex::get_binding_description()
{
VkVertexInputBindingDescription description;
description.binding = 0;
description.stride = sizeof(Vertex);
description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return description;
}
std::array<VkVertexInputAttributeDescription, 2> RenderEngine::Vertex::get_vertex_attribute_descriptions()
{
std::array<VkVertexInputAttributeDescription, 2> descriptions;
// Bind vertex loc
descriptions[0].binding = 0;
descriptions[0].location = 0;
descriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
descriptions[0].offset = offsetof(Vertex, pos);
// Bind color
descriptions[1].binding = 0;
descriptions[1].location = 1;
descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
descriptions[1].offset = offsetof(Vertex, color);
return descriptions;
}
bool RenderEngine::is_device_suitable(VkPhysicalDevice device, VkSurfaceKHR surface)
{
VkPhysicalDeviceProperties device_properties;
VkPhysicalDeviceFeatures device_features;
vkGetPhysicalDeviceProperties(device, &device_properties);
vkGetPhysicalDeviceFeatures(device, &device_features);
try {
// Ловить исключения имеет смысыл только здесь
QueueFamilyIndices indices = find_queue_family_indices(device, surface);
} catch (...) {
return false;
}
return check_device_extensions_support(device);
}
bool RenderEngine::check_device_extensions_support(VkPhysicalDevice device)
{
uint32_t extension_count;
VK_CHECK(vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, nullptr));
std::vector<VkExtensionProperties> available_extensions(extension_count);
VK_CHECK(vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, available_extensions.data()));
for (const auto& extension : deviceExtensions)
{
bool isFind = false;
for (const auto& i : available_extensions)
{
if (std::strcmp(i.extensionName, extension) == 0)
{
isFind = true;
break;
}
}
if (!isFind)
return false;
}
return true;
}
RenderEngine::QueueFamilyIndices RenderEngine::find_queue_family_indices(VkPhysicalDevice physical_device, VkSurfaceKHR surface)
{
QueueFamilyIndices queue_family_indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> family_properties(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queueFamilyCount, family_properties.data());
// Find graphics
for(uint32_t i = 0; i < family_properties.size(); ++i)
{
if (family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
queue_family_indices.graphics_family = i;
break;
}
}
if (queue_family_indices.graphics_family.has_value() == false)
throw std::runtime_error("Failed to find a graphics queue family");
// Find present
VkBool32 present_support = false;
VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_indices.graphics_family.value(), surface, &present_support));
// Если графическая очередь поддерживет презентацию кадров, то используем её
if (present_support == VK_TRUE)
queue_family_indices.present_family = queue_family_indices.graphics_family;
// иначе ищем другую подходящую очередь
else
{
for(uint32_t i = 0; i < family_properties.size(); ++i)
{
present_support = false;
VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &present_support));
if (present_support == VK_TRUE)
{
queue_family_indices.present_family = i;
break;
}
}
}
if (queue_family_indices.present_family.has_value() == false)
throw std::runtime_error("Failed to find a present queue family");
// С начала пытаемся найти очередь специалезированную
// для копирования отличную от графической
for(uint32_t i = 0; i < family_properties.size(); ++i)
{
if (i == queue_family_indices.graphics_family.value())
continue;
if (family_properties[i].queueFlags & VK_QUEUE_TRANSFER_BIT)
queue_family_indices.transfer_family = i;
}
// Если не находим, то используем графическую
if (queue_family_indices.transfer_family.has_value() == false)
queue_family_indices.transfer_family = queue_family_indices.graphics_family;
return queue_family_indices;
}
VkSurfaceFormatKHR RenderEngine::choose_surface_format(const std::vector<VkSurfaceFormatKHR>& surface_formats)
{
for (const auto& i : surface_formats)
{
if (i.format == VK_FORMAT_B8G8R8A8_SRGB && i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
return i;
}
return surface_formats[0];
}
VkPresentModeKHR RenderEngine::choose_present_mode(const std::vector<VkPresentModeKHR>& present_modes, VkPresentModeKHR desired_present_mode)
{
for (const auto& i : present_modes)
{
if (i == desired_present_mode)
return i;
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D RenderEngine::choose_extent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window)
{
// Здесь вычисляется допустимое разрешение рендера
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
return capabilities.currentExtent;
else
{
int width, height;
glfwGetFramebufferSize(window, &width, &height);
VkExtent2D actualExtent = {
static_cast<uint32_t>(width),
static_cast<uint32_t>(height)
};
actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));
return actualExtent;
}
}
VkShaderModule RenderEngine::create_shader_module(const std::string& code) const
{
VkShaderModuleCreateInfo shader_module_info{};
shader_module_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_module_info.codeSize = code.size();
shader_module_info.pCode = reinterpret_cast<const uint32_t*>(code.c_str());
VkShaderModule shader_module;
VK_CHECK(vkCreateShaderModule(device_, &shader_module_info, nullptr, &shader_module));
return shader_module;
}
void RenderEngine::create_buffer(VkDeviceSize size, VkBufferUsageFlags usage,
VkBuffer* buffer, const std::vector<uint32_t>& queue_families)
{
VkBufferCreateInfo buffer_info{};
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = size;
buffer_info.usage = usage;
if (queue_families.size() > 1)
{
buffer_info.sharingMode = VK_SHARING_MODE_CONCURRENT;
buffer_info.queueFamilyIndexCount = static_cast<uint32_t>(queue_families.size());
buffer_info.pQueueFamilyIndices = queue_families.data();
}
else
{
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.queueFamilyIndexCount = 0;
buffer_info.pQueueFamilyIndices = nullptr;
}
VK_CHECK(vkCreateBuffer(device_, &buffer_info, nullptr, buffer));
}
void RenderEngine::allocate_memory(VkBuffer buffer, VkMemoryPropertyFlags property, VkDeviceMemory* device_memory)
{
VkMemoryRequirements requirements{};
vkGetBufferMemoryRequirements(device_, buffer, &requirements);
VkMemoryAllocateInfo allocate_info{};
allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocate_info.allocationSize = requirements.size;
allocate_info.memoryTypeIndex = find_memory_type(requirements.memoryTypeBits, property);
VK_CHECK(vkAllocateMemory(device_, &allocate_info, nullptr, device_memory));
}
void RenderEngine::create_instance()
{
std::vector<const char*> extensions = get_required_extensions();
VkApplicationInfo app_info{};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.apiVersion = VK_API_VERSION_1_0;
app_info.applicationVersion = VK_MAKE_VERSION(0, 0, 0);
app_info.pApplicationName = core_.getGameName().c_str();
app_info.pEngineName = "UwU Engine";
app_info.engineVersion = VK_MAKE_VERSION(0, 0, 0);
VkInstanceCreateInfo instance_create_info{};
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_create_info.pApplicationInfo = &app_info;
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
instance_create_info.ppEnabledExtensionNames = extensions.data();
#ifdef _DEBUG
const std::vector<const char*> layers_validation = {
"VK_LAYER_KHRONOS_validation"
};
instance_create_info.enabledLayerCount = static_cast<uint32_t>(layers_validation.size());
instance_create_info.ppEnabledLayerNames = layers_validation.data();
VkDebugUtilsMessengerCreateInfoEXT debug_messenger_create_info{};
debug_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
debug_messenger_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;
debug_messenger_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;
debug_messenger_create_info.pfnUserCallback = &debugCallback;
instance_create_info.pNext = &debug_messenger_create_info;
#endif
VK_CHECK(vkCreateInstance(&instance_create_info, nullptr, &instance_));
#ifdef _DEBUG
VK_CHECK(enable_layer_validation(&debug_messenger_create_info));
#endif
}
void RenderEngine::pick_physical_device()
{
uint32_t device_count = 0;
std::vector<VkPhysicalDevice> devices;
VK_CHECK(vkEnumeratePhysicalDevices(instance_, &device_count, nullptr));
devices.resize(device_count);
VK_CHECK(vkEnumeratePhysicalDevices(instance_, &device_count, devices.data()));
for (const auto& device : devices)
{
if (is_device_suitable(device, surface_))
{
physical_device_ = device;
VkPhysicalDeviceProperties device_properties;
vkGetPhysicalDeviceProperties(physical_device_, &device_properties);
Loging::Log("Using device: " + std::string(device_properties.deviceName));
break;
}
}
if (VK_NULL_HANDLE == physical_device_)
throw std::runtime_error("No suitable device found");
}
void RenderEngine::create_logical_device()
{
QueueFamilyIndices indices = find_queue_family_indices(physical_device_, surface_);
VkPhysicalDeviceFeatures device_features{};
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
std::set<uint32_t> unique_queue_families = {indices.graphics_family.value(),
indices.present_family.value(),
indices.transfer_family.value()};
float queue_priority = 1.0f;
VkDeviceQueueCreateInfo device_queue_create_info{};
device_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
device_queue_create_info.pQueuePriorities = &queue_priority;
for (auto queue_family : unique_queue_families)
{
device_queue_create_info.queueFamilyIndex = queue_family;
device_queue_create_info.queueCount = 1;
queue_create_infos.push_back(device_queue_create_info);
}
VkDeviceCreateInfo device_create_info{};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.queueCreateInfoCount = static_cast<uint32_t>(unique_queue_families.size());
device_create_info.pQueueCreateInfos = queue_create_infos.data();
device_create_info.pEnabledFeatures = &device_features;
device_create_info.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
device_create_info.ppEnabledExtensionNames = deviceExtensions.data();
#ifdef _DEBUG
const std::vector<const char*> layers_validation = {
"VK_LAYER_KHRONOS_validation"
};
device_create_info.enabledLayerCount = static_cast<uint32_t>(layers_validation.size());
device_create_info.ppEnabledLayerNames = layers_validation.data();
#else
device_create_info.enabledLayerCount = 0;
device_create_info.ppEnabledLayerNames = nullptr;
#endif
VK_CHECK(vkCreateDevice(physical_device_, &device_create_info, nullptr, &device_));
vkGetDeviceQueue(device_, indices.graphics_family.value(), 0, &graphics_queue_);
vkGetDeviceQueue(device_, indices.present_family.value(), 0, &present_queue_);
vkGetDeviceQueue(device_, indices.transfer_family.value(), 0, &transfer_queue_);
}
void RenderEngine::create_swapchain(VkPresentModeKHR desired_present_mode)
{
SwapchainSupportDetails swapchain_support = query_swapchain_details();
VkSurfaceFormatKHR surface_format = choose_surface_format(swapchain_support.formats);
VkPresentModeKHR present_mode = choose_present_mode(swapchain_support.present_modes, desired_present_mode);
VkExtent2D extent = choose_extent(swapchain_support.capabilities, window_);
uint32_t image_count = swapchain_support.capabilities.minImageCount + 1;
if (swapchain_support.capabilities.maxImageCount > 0 && image_count > swapchain_support.capabilities.maxImageCount)
image_count = swapchain_support.capabilities.maxImageCount;
VkSwapchainCreateInfoKHR swapchain_create_info{};
swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_create_info.surface = surface_;
swapchain_create_info.minImageCount = image_count;
swapchain_create_info.imageFormat = surface_format.format;
swapchain_create_info.imageColorSpace = surface_format.colorSpace;
swapchain_create_info.imageExtent = extent;
swapchain_create_info.imageArrayLayers = 1;
swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_create_info.preTransform = swapchain_support.capabilities.currentTransform;
swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchain_create_info.presentMode = present_mode;
swapchain_create_info.clipped = VK_TRUE;
swapchain_create_info.oldSwapchain = VK_NULL_HANDLE;
QueueFamilyIndices family_indices = find_queue_family_indices(physical_device_, surface_);
const std::set<uint32_t> queue_family_indices = { family_indices.graphics_family.value(),
family_indices.present_family.value(),
family_indices.transfer_family.value() };
std::vector<uint32_t> arr_families;
if (queue_family_indices.size() > 1)
{
arr_families.reserve(queue_family_indices.size());
for(auto& i : queue_family_indices)
arr_families.push_back(i);
swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchain_create_info.queueFamilyIndexCount = static_cast<uint32_t>(arr_families.size());
swapchain_create_info.pQueueFamilyIndices = arr_families.data();
}
else
{
swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain_create_info.queueFamilyIndexCount = 0;
swapchain_create_info.pQueueFamilyIndices = nullptr;
}
VK_CHECK(vkCreateSwapchainKHR(device_, &swapchain_create_info, nullptr, &swapchain_));
vkGetSwapchainImagesKHR(device_, swapchain_, &image_count, nullptr);
swapchain_images_.resize(image_count);
vkGetSwapchainImagesKHR(device_, swapchain_, &image_count, swapchain_images_.data());
swapchain_image_format_ = surface_format.format;
swapchain_extent_ = extent;
}
void RenderEngine::create_image_views()
{
VkImageViewCreateInfo image_view_info{};
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
image_view_info.format = swapchain_image_format_;
image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_view_info.subresourceRange.baseMipLevel = 0;
image_view_info.subresourceRange.levelCount = 1;
image_view_info.subresourceRange.baseArrayLayer = 0;
image_view_info.subresourceRange.layerCount = 1;
swapchain_image_views_.resize(swapchain_images_.size());
for (uint32_t i = 0; i < swapchain_images_.size(); ++i)
{
image_view_info.image = swapchain_images_[i];
VK_CHECK(vkCreateImageView(device_, &image_view_info, nullptr, &swapchain_image_views_[i]));
}
}
void RenderEngine::create_render_pass()
{
VkAttachmentDescription attachment_description{};
attachment_description.format = swapchain_image_format_;
attachment_description.samples = VK_SAMPLE_COUNT_1_BIT;
attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_description.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference attachment_reference;
attachment_reference.attachment = 0;
attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass_description{};
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_description.colorAttachmentCount = 1;
subpass_description.pColorAttachments = &attachment_reference;
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 render_pass_info{};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &attachment_description;
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass_description;
render_pass_info.dependencyCount = 1;
render_pass_info.pDependencies = &dependency;
VK_CHECK(vkCreateRenderPass(device_, &render_pass_info, nullptr, &render_pass_));
}
void RenderEngine::create_graphics_pipeline()
{
std::ifstream vertex_shader_file("Shaders/vertex.spv", std::ios::binary);
if (!vertex_shader_file.is_open())
throw std::runtime_error("Vertex shader not found");
std::ifstream fragment_shader_file("Shaders/fragment.spv", std::ios::binary);
if (!fragment_shader_file.is_open()) {
vertex_shader_file.close();
throw std::runtime_error("Fragment shader not found");
}
std::string vertex_shader_code((std::istreambuf_iterator<char>(vertex_shader_file)), std::istreambuf_iterator<char>());
std::string fragment_shader_code((std::istreambuf_iterator<char>(fragment_shader_file)), std::istreambuf_iterator<char>());
vertex_shader_file.close();
fragment_shader_file.close();
VkShaderModule vertex_shader = create_shader_module(vertex_shader_code);
VkShaderModule fragment_shader = create_shader_module(fragment_shader_code);
VkPipelineShaderStageCreateInfo vertex_shader_stage_info{};
vertex_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_shader_stage_info.module = vertex_shader;
vertex_shader_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo fragment_shader_stage_info{};
fragment_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_shader_stage_info.module = fragment_shader;
fragment_shader_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo shader_stages[] = {vertex_shader_stage_info, fragment_shader_stage_info};
const std::vector<VkDynamicState> dynamic_states = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamic_state_info{};
dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_info.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
dynamic_state_info.pDynamicStates = dynamic_states.data();
VkVertexInputBindingDescription binding_description = Vertex::get_binding_description();
std::array<VkVertexInputAttributeDescription, 2> attribute_descriptions = Vertex::get_vertex_attribute_descriptions();
VkPipelineVertexInputStateCreateInfo vertex_input_info{};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 1;
vertex_input_info.pVertexBindingDescriptions = &binding_description;
vertex_input_info.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_descriptions.size());
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly_info{};
input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_info.primitiveRestartEnable = VK_FALSE;
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapchain_extent_.width);
viewport.height = static_cast<float>(swapchain_extent_.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapchain_extent_;
VkPipelineViewportStateCreateInfo viewport_info{};
viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_info.viewportCount = 1;
viewport_info.pViewports = &viewport;
viewport_info.scissorCount = 1;
viewport_info.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterization_info{};
rasterization_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_info.depthClampEnable = VK_FALSE;
rasterization_info.rasterizerDiscardEnable = VK_FALSE;
rasterization_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_info.lineWidth = 1.0f;
rasterization_info.cullMode = VK_CULL_MODE_BACK_BIT;
rasterization_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterization_info.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisample_info{};
multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_info.sampleShadingEnable = VK_FALSE;
multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState color_blend_attachment_state{};
color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
color_blend_attachment_state.blendEnable = VK_TRUE;
color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo color_blend_info{};
color_blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blend_info.logicOpEnable = VK_FALSE;
color_blend_info.attachmentCount = 1;
color_blend_info.pAttachments = &color_blend_attachment_state;
color_blend_info.blendConstants[0] = 0.0f;
color_blend_info.blendConstants[1] = 0.0f;
color_blend_info.blendConstants[2] = 0.0f;
color_blend_info.blendConstants[3] = 0.0f;
VkPipelineLayoutCreateInfo pipeline_layout_info{};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
VK_CHECK(vkCreatePipelineLayout(device_, &pipeline_layout_info, nullptr, &pipeline_layout_));
VkGraphicsPipelineCreateInfo graphics_pipeline_info{};
graphics_pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
graphics_pipeline_info.stageCount = 2;
graphics_pipeline_info.pStages = shader_stages;
graphics_pipeline_info.layout = pipeline_layout_;
graphics_pipeline_info.pColorBlendState = &color_blend_info;
graphics_pipeline_info.pDynamicState = &dynamic_state_info;
graphics_pipeline_info.pInputAssemblyState = &input_assembly_info;
graphics_pipeline_info.pMultisampleState = &multisample_info;
graphics_pipeline_info.pRasterizationState = &rasterization_info;
graphics_pipeline_info.pViewportState = &viewport_info;
graphics_pipeline_info.pVertexInputState = &vertex_input_info;
graphics_pipeline_info.pDepthStencilState = nullptr;
graphics_pipeline_info.renderPass = render_pass_;
graphics_pipeline_info.subpass = 0;
graphics_pipeline_info.basePipelineHandle = nullptr;
graphics_pipeline_info.basePipelineIndex = -1;
VK_CHECK(vkCreateGraphicsPipelines(device_, nullptr, 1, &graphics_pipeline_info, nullptr, &graphics_pipeline_));
vkDestroyShaderModule(device_, vertex_shader, nullptr);
vkDestroyShaderModule(device_, fragment_shader, nullptr);
}
void RenderEngine::create_framebuffers()
{
framebuffers_.resize(swapchain_images_.size());
VkFramebufferCreateInfo framebuffer_info{};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = render_pass_;
framebuffer_info.attachmentCount = 1;
framebuffer_info.width = swapchain_extent_.width;
framebuffer_info.height = swapchain_extent_.height;
framebuffer_info.layers = 1;
for (size_t i = 0; i < framebuffers_.size(); ++i)
{
framebuffer_info.pAttachments = &swapchain_image_views_[i];
VK_CHECK(vkCreateFramebuffer(device_, &framebuffer_info, nullptr, &framebuffers_[i]));
}
}
void RenderEngine::create_command_pool()
{
QueueFamilyIndices queue_family_indices = find_queue_family_indices(physical_device_, surface_);
VkCommandPoolCreateInfo command_pool_info{};
command_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
command_pool_info.queueFamilyIndex = queue_family_indices.graphics_family.value();
command_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VK_CHECK(vkCreateCommandPool(device_, &command_pool_info, nullptr, &command_pool_));
}
uint32_t RenderEngine::find_memory_type(uint32_t typeFilter, VkMemoryPropertyFlags properties) const
{
VkPhysicalDeviceMemoryProperties memory_properties{};
vkGetPhysicalDeviceMemoryProperties(physical_device_, &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i)
{
if (typeFilter & (1 << i) && (memory_properties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
throw std::runtime_error("failed to find suitable memory type!");
}
void RenderEngine::allocate_vertex_buffer()
{
QueueFamilyIndices indices = find_queue_family_indices(physical_device_, surface_);
std::set<uint32_t> set_indices = {indices.graphics_family.value(),
indices.present_family.value(),
indices.transfer_family.value()};
std::vector<uint32_t> arr_indices;
std::vector<uint32_t> transfer_index = {indices.transfer_family.value()};
arr_indices.reserve(set_indices.size());
for (auto& i : set_indices)
arr_indices.push_back(i);
VkBuffer staging_buffer = VK_NULL_HANDLE;
VkDeviceMemory staging_buffer_memory = VK_NULL_HANDLE;
VkDeviceSize size_buffer = sizeof(vertices[0]) * vertices.size();
create_buffer(size_buffer, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, &staging_buffer, transfer_index);
allocate_memory(staging_buffer, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &staging_buffer_memory);
create_buffer(size_buffer, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &vertex_buffer_, arr_indices);
allocate_memory(vertex_buffer_, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertex_buffer_memory_);
VK_CHECK(vkBindBufferMemory(device_, staging_buffer, staging_buffer_memory, 0));
VK_CHECK(vkBindBufferMemory(device_, vertex_buffer_, vertex_buffer_memory_, 0));
void* data;
VK_CHECK(vkMapMemory(device_, staging_buffer_memory, 0, size_buffer, 0, &data));
memcpy(data, vertices.data(), size_buffer);
vkUnmapMemory(device_, staging_buffer_memory);
copy_memory(staging_buffer, vertex_buffer_, size_buffer);
vkFreeMemory(device_, staging_buffer_memory, nullptr);
vkDestroyBuffer(device_, staging_buffer, nullptr);
}
void RenderEngine::copy_memory(VkBuffer src, VkBuffer dst, VkDeviceSize size)
{
QueueFamilyIndices indices = find_queue_family_indices(physical_device_, surface_);
VkCommandPool copy_pool;
VkCommandPoolCreateInfo copy_pool_info{};
copy_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
copy_pool_info.queueFamilyIndex = indices.transfer_family.value();
copy_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VK_CHECK(vkCreateCommandPool(device_, &copy_pool_info, nullptr, &copy_pool));
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_allocate_info.commandPool = copy_pool;
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
command_buffer_allocate_info.commandBufferCount = 1;
VkCommandBuffer copy_buffer;
VK_CHECK(vkAllocateCommandBuffers(device_, &command_buffer_allocate_info, &copy_buffer));
VkCommandBufferBeginInfo command_buffer_begin_info{};
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VK_CHECK(vkBeginCommandBuffer(copy_buffer, &command_buffer_begin_info));
VkBufferCopy buffer_copy{};
buffer_copy.srcOffset = 0;
buffer_copy.dstOffset = 0;
buffer_copy.size = size;
vkCmdCopyBuffer(copy_buffer, src, dst, 1, &buffer_copy);
vkEndCommandBuffer(copy_buffer);
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &copy_buffer;
vkQueueSubmit(transfer_queue_, 1, &submit_info, VK_NULL_HANDLE);
vkQueueWaitIdle(transfer_queue_);
vkFreeCommandBuffers(device_, copy_pool, 1, &copy_buffer);
vkDestroyCommandPool(device_, copy_pool, nullptr);
}
void RenderEngine::allocate_command_buffers()
{
command_buffers_.resize(swapchain_images_.size());
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_allocate_info.commandPool = command_pool_;
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
command_buffer_allocate_info.commandBufferCount = static_cast<uint32_t>(command_buffers_.size());
VK_CHECK(vkAllocateCommandBuffers(device_, &command_buffer_allocate_info, command_buffers_.data()));
}
void RenderEngine::create_sync_objects()
{
image_available_semaphores_.resize(swapchain_images_.size());
render_finished_semaphores_.resize(swapchain_images_.size());
in_flight_fences_.resize(swapchain_images_.size());
VkSemaphoreCreateInfo semaphore_info{};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fence_info{};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < swapchain_images_.size(); ++i)
{
VK_CHECK(vkCreateSemaphore(device_, &semaphore_info, nullptr, &image_available_semaphores_[i]));
VK_CHECK(vkCreateSemaphore(device_, &semaphore_info, nullptr, &render_finished_semaphores_[i]));
VK_CHECK(vkCreateFence(device_, &fence_info, nullptr, &in_flight_fences_[i]));
}
}
void RenderEngine::record_command_buffer(VkCommandBuffer command_buffer, uint32_t image_index) const
{
VkCommandBufferBeginInfo command_buffer_begin_info{};
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
VK_CHECK(vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info));
constexpr VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo render_pass_begin_info{};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.renderPass = render_pass_;
render_pass_begin_info.framebuffer = framebuffers_[image_index];
render_pass_begin_info.renderArea.offset = {0, 0};
render_pass_begin_info.renderArea.extent = swapchain_extent_;
render_pass_begin_info.clearValueCount = 1;
render_pass_begin_info.pClearValues = &clear_color;
vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_);
VkBuffer vertex_buffers[] = {vertex_buffer_};
VkDeviceSize offset[] = {0};
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offset);
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapchain_extent_.width);
viewport.height = static_cast<float>(swapchain_extent_.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
VkRect2D scissor;
scissor.offset = {0, 0};
scissor.extent = swapchain_extent_;
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
vkCmdDraw(command_buffer, static_cast<uint32_t>(vertices.size()), 1, 0, 0);
vkCmdEndRenderPass(command_buffer);
VK_CHECK(vkEndCommandBuffer(command_buffer));
}
void RenderEngine::draw_frame()
{
vkWaitForFences(device_, 1, &in_flight_fences_[current_frame_], VK_TRUE, UINT64_MAX);
vkResetFences(device_, 1, &in_flight_fences_[current_frame_]);
uint32_t image_index;
vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX, image_available_semaphores_[current_frame_], VK_NULL_HANDLE, &image_index);
vkResetCommandBuffer(command_buffers_[current_frame_], 0);
record_command_buffer(command_buffers_[current_frame_], image_index);
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffers_[current_frame_];
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &image_available_semaphores_[current_frame_];
submit_info.pWaitDstStageMask = &wait_stage;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &render_finished_semaphores_[current_frame_];
VK_CHECK(vkQueueSubmit(graphics_queue_, 1, &submit_info, in_flight_fences_[current_frame_]));
VkPresentInfoKHR present_info{};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &render_finished_semaphores_[current_frame_];
present_info.swapchainCount = 1;
present_info.pSwapchains = &swapchain_;
present_info.pImageIndices = &image_index;
present_info.pResults = nullptr;
vkQueuePresentKHR(present_queue_, &present_info);
current_frame_ = (current_frame_ + 1) % static_cast<int>(swapchain_images_.size());
}
void RenderEngine::create_surface()
{
VK_CHECK(glfwCreateWindowSurface(instance_, window_, nullptr, &surface_));
}
RenderEngine::RenderEngine(CoreInstance& core, GLFWwindow* window, VkPresentModeKHR desired_present_mode):
core_(core),
window_(window)
#ifdef _DEBUG
,debug_messenger_(nullptr)
#endif
{
create_instance();
create_surface();
pick_physical_device();
create_logical_device();
create_swapchain(desired_present_mode);
create_image_views();
create_render_pass();
create_graphics_pipeline();
create_framebuffers();
create_command_pool();
allocate_vertex_buffer();
allocate_command_buffers();
create_sync_objects();
}
RenderEngine::~RenderEngine()
{
vkDeviceWaitIdle(device_);
if (vertex_buffer_memory_ != VK_NULL_HANDLE)
vkFreeMemory(device_, vertex_buffer_memory_, nullptr);
if (vertex_buffer_ != VK_NULL_HANDLE)
vkDestroyBuffer(device_, vertex_buffer_, nullptr);
for (auto& i : image_available_semaphores_)
vkDestroySemaphore(device_, i, nullptr);
for (auto& i : render_finished_semaphores_)
vkDestroySemaphore(device_, i, nullptr);
for (auto& i : in_flight_fences_)
vkDestroyFence(device_, i, nullptr);
if (command_buffers_.empty() == false)
vkFreeCommandBuffers(device_, command_pool_, static_cast<uint32_t>(command_buffers_.size()), command_buffers_.data());
if (command_pool_ != VK_NULL_HANDLE)
vkDestroyCommandPool(device_, command_pool_, nullptr);
for (auto& i : framebuffers_)
vkDestroyFramebuffer(device_, i, nullptr);
if (graphics_pipeline_ != VK_NULL_HANDLE)
vkDestroyPipeline(device_, graphics_pipeline_, nullptr);
if (pipeline_layout_ != VK_NULL_HANDLE)
vkDestroyPipelineLayout(device_, pipeline_layout_, nullptr);
if (render_pass_ != VK_NULL_HANDLE)
vkDestroyRenderPass(device_, render_pass_, nullptr);
for (auto image_view : swapchain_image_views_)
vkDestroyImageView(device_, image_view, nullptr);
swapchain_image_views_.clear();
if (swapchain_ != VK_NULL_HANDLE)
vkDestroySwapchainKHR(device_, swapchain_, nullptr);
if (device_ != VK_NULL_HANDLE)
vkDestroyDevice(device_, nullptr);
if (surface_ != VK_NULL_HANDLE)
vkDestroySurfaceKHR(instance_, surface_, nullptr);
#ifdef _DEBUG
if (debug_messenger_ != nullptr && instance_ != nullptr) {
PFN_vkDestroyDebugUtilsMessengerEXT destroyer_messenger = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(vkGetInstanceProcAddr(instance_, "vkDestroyDebugUtilsMessengerEXT"));
destroyer_messenger(instance_, debug_messenger_, nullptr);
}
#endif
if (instance_ != VK_NULL_HANDLE)
vkDestroyInstance(instance_, nullptr);
}
void RenderEngine::start()
{
World* world = core_.GetGameInstance()->GetWorld();
std::vector<UType::object_ptr<Mesh>> meshes = world->GetActorsByClass<Mesh>();
for (auto& i : meshes)
i->load_model(i->GetModelName());
while (!glfwWindowShouldClose(window_))
{
glfwPollEvents();
draw_frame();
}
}
void RenderEngine::stop_render() const
{
if (window_ != nullptr && !glfwWindowShouldClose(window_))
glfwSetWindowShouldClose(window_, true);
}