mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-11 04:31:35 -10:00
231 lines
7.1 KiB
C++
231 lines
7.1 KiB
C++
|
|
#include "VulkanRenderer.h"
|
|
|
|
#include "graphics/2d.h"
|
|
#include "libs/renderdoc/renderdoc.h"
|
|
#include "mod_table/mod_table.h"
|
|
#include "globalincs/version.h"
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
|
#include <SDL_vulkan.h>
|
|
#endif
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
|
|
|
namespace graphics {
|
|
namespace vulkan {
|
|
|
|
namespace {
|
|
#if SDL_SUPPORTS_VULKAN
|
|
const char* EngineName = "FreeSpaceOpen";
|
|
|
|
const gameversion::version MinVulkanVersion(1, 1, 0, 0);
|
|
|
|
VkBool32 VKAPI_PTR debugReportCallback(VkDebugReportFlagsEXT /*flags*/, VkDebugReportObjectTypeEXT /*objectType*/,
|
|
uint64_t /*object*/, size_t /*location*/, int32_t /*messageCode*/,
|
|
const char* pLayerPrefix, const char* pMessage, void* /*pUserData*/)
|
|
{
|
|
mprintf(("Vulkan message: [%s]: %s\n", pLayerPrefix, pMessage));
|
|
return VK_FALSE;
|
|
}
|
|
#endif
|
|
} // namespace
|
|
|
|
VulkanRenderer::VulkanRenderer(std::unique_ptr<os::GraphicsOperations> graphicsOps)
|
|
: m_graphicsOps(std::move(graphicsOps))
|
|
{
|
|
}
|
|
bool VulkanRenderer::initialize()
|
|
{
|
|
mprintf(("Initializing Vulkan graphics device at %ix%i with %i-bit color...\n", gr_screen.max_w, gr_screen.max_h,
|
|
gr_screen.bits_per_pixel));
|
|
|
|
// Load the RenderDoc API if available before doing anything with OpenGL
|
|
renderdoc::loadApi();
|
|
|
|
if (!initDisplayDevice()) {
|
|
return false;
|
|
}
|
|
|
|
if (!initializeInstance()) {
|
|
mprintf(("Failed to create Vulkan instance!\n"));
|
|
return false;
|
|
}
|
|
|
|
if (!initializeSurface()) {
|
|
mprintf(("Failed to create Vulkan surface!\n"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VulkanRenderer::initDisplayDevice()
|
|
{
|
|
os::ViewPortProperties attrs;
|
|
attrs.enable_opengl = false;
|
|
attrs.enable_vulkan = true;
|
|
|
|
attrs.display = os_config_read_uint("Video", "Display", 0);
|
|
attrs.width = (uint32_t)gr_screen.max_w;
|
|
attrs.height = (uint32_t)gr_screen.max_h;
|
|
|
|
attrs.title = Osreg_title;
|
|
if (!Window_title.empty()) {
|
|
attrs.title = Window_title;
|
|
}
|
|
|
|
if (Using_in_game_options) {
|
|
switch (Gr_configured_window_state) {
|
|
case os::ViewportState::Windowed:
|
|
// That's the default
|
|
break;
|
|
case os::ViewportState::Borderless:
|
|
attrs.flags.set(os::ViewPortFlags::Borderless);
|
|
break;
|
|
case os::ViewportState::Fullscreen:
|
|
attrs.flags.set(os::ViewPortFlags::Fullscreen);
|
|
break;
|
|
}
|
|
} else {
|
|
if (!Cmdline_window && !Cmdline_fullscreen_window) {
|
|
attrs.flags.set(os::ViewPortFlags::Fullscreen);
|
|
} else if (Cmdline_fullscreen_window) {
|
|
attrs.flags.set(os::ViewPortFlags::Borderless);
|
|
}
|
|
}
|
|
|
|
m_viewPort = m_graphicsOps->createViewport(attrs);
|
|
if (!m_viewPort) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool VulkanRenderer::initializeInstance()
|
|
{
|
|
#if SDL_SUPPORTS_VULKAN
|
|
const auto vkGetInstanceProcAddr =
|
|
reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
|
|
|
const auto window = m_viewPort->toSDLWindow();
|
|
|
|
unsigned int count;
|
|
if (!SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr)) {
|
|
mprintf(("Error in first SDL_Vulkan_GetInstanceExtensions: %s\n", SDL_GetError()));
|
|
return false;
|
|
}
|
|
|
|
std::vector<const char*> extensions;
|
|
extensions.resize(count);
|
|
|
|
if (!SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data())) {
|
|
mprintf(("Error in second SDL_Vulkan_GetInstanceExtensions: %s\n", SDL_GetError()));
|
|
return false;
|
|
}
|
|
|
|
const auto instanceVersion = vk::enumerateInstanceVersion();
|
|
gameversion::version vulkanVersion(VK_VERSION_MAJOR(instanceVersion), VK_VERSION_MINOR(instanceVersion),
|
|
VK_VERSION_PATCH(instanceVersion), 0);
|
|
mprintf(("Vulkan instance version %s\n", gameversion::format_version(vulkanVersion).c_str()));
|
|
|
|
if (vulkanVersion < MinVulkanVersion) {
|
|
mprintf(("Vulkan version is less than the minimum which is %s.\n",
|
|
gameversion::format_version(MinVulkanVersion).c_str()));
|
|
return false;
|
|
}
|
|
|
|
const auto supportedExtensions = vk::enumerateInstanceExtensionProperties();
|
|
mprintf(("Instance extensions:\n"));
|
|
for (const auto& ext : supportedExtensions) {
|
|
mprintf((" Found support for %s version %" PRIu32 "\n", ext.extensionName, ext.specVersion));
|
|
if (FSO_DEBUG || Cmdline_graphics_debug_output) {
|
|
if (!stricmp(ext.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
|
|
extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
|
m_debugReportEnabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<const char*> layers;
|
|
const auto supportedLayers = vk::enumerateInstanceLayerProperties();
|
|
mprintf(("Instance layers:\n"));
|
|
for (const auto& layer : supportedLayers) {
|
|
mprintf((" Found layer %s(%s). Spec version %" PRIu32 " and implementation %" PRIu32 "\n", layer.layerName,
|
|
layer.description, layer.specVersion, layer.implementationVersion));
|
|
if (FSO_DEBUG || Cmdline_graphics_debug_output) {
|
|
if (!stricmp(layer.layerName, "VK_LAYER_LUNARG_core_validation")) {
|
|
layers.push_back("VK_LAYER_LUNARG_core_validation");
|
|
}
|
|
}
|
|
}
|
|
|
|
vk::ApplicationInfo appInfo(Window_title.c_str(), 1, EngineName, 1, VK_API_VERSION_1_1);
|
|
|
|
// Now we can make the Vulkan instance
|
|
vk::InstanceCreateInfo createInfo(vk::Flags<vk::InstanceCreateFlagBits>(), &appInfo);
|
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
|
createInfo.ppEnabledExtensionNames = extensions.data();
|
|
createInfo.enabledLayerCount = static_cast<uint32_t>(layers.size());
|
|
createInfo.ppEnabledLayerNames = layers.data();
|
|
|
|
vk::DebugReportCallbackCreateInfoEXT createInstanceReportInfo(vk::DebugReportFlagBitsEXT::eError |
|
|
vk::DebugReportFlagBitsEXT::eWarning |
|
|
vk::DebugReportFlagBitsEXT::ePerformanceWarning);
|
|
createInstanceReportInfo.pfnCallback = debugReportCallback;
|
|
|
|
vk::StructureChain<vk::InstanceCreateInfo, vk::DebugReportCallbackCreateInfoEXT> createInstanceChain(
|
|
createInfo, createInstanceReportInfo);
|
|
|
|
if (!m_debugReportEnabled) {
|
|
createInstanceChain.unlink<vk::DebugReportCallbackCreateInfoEXT>();
|
|
}
|
|
|
|
vk::UniqueInstance instance = vk::createInstanceUnique(createInstanceChain.get<vk::InstanceCreateInfo>(), nullptr);
|
|
if (!instance) {
|
|
return false;
|
|
}
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance.get());
|
|
|
|
if (m_debugReportEnabled) {
|
|
vk::DebugReportCallbackCreateInfoEXT reportCreateInfo(vk::DebugReportFlagBitsEXT::eError |
|
|
vk::DebugReportFlagBitsEXT::eWarning |
|
|
vk::DebugReportFlagBitsEXT::ePerformanceWarning);
|
|
reportCreateInfo.pfnCallback = debugReportCallback;
|
|
|
|
m_debugReport = instance->createDebugReportCallbackEXTUnique(reportCreateInfo);
|
|
}
|
|
|
|
m_vkInstance = std::move(instance);
|
|
return true;
|
|
#else
|
|
mprintf(("SDL does not support Vulkan in its current version.\n"));
|
|
return false;
|
|
#endif
|
|
}
|
|
bool VulkanRenderer::initializeSurface()
|
|
{
|
|
#if SDL_SUPPORTS_VULKAN
|
|
const auto window = m_viewPort->toSDLWindow();
|
|
|
|
VkSurfaceKHR surface;
|
|
if (!SDL_Vulkan_CreateSurface(window, static_cast<VkInstance>(*m_vkInstance), &surface)) {
|
|
mprintf(("Failed to create vulkan surface: %s\n", SDL_GetError()));
|
|
return false;
|
|
}
|
|
|
|
vk::ObjectDestroy<vk::Instance, VULKAN_HPP_DEFAULT_DISPATCHER_TYPE> deleter(*m_vkInstance, nullptr,
|
|
VULKAN_HPP_DEFAULT_DISPATCHER);
|
|
m_vkSurface = vk::UniqueSurfaceKHR(vk::SurfaceKHR(surface), deleter);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
} // namespace vulkan
|
|
} // namespace graphics
|