Files
fs2open.github.com/code/graphics/vulkan/VulkanRenderer.cpp
T
2020-03-02 01:07:17 -05:00

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