mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-04 05:04:33 +00:00
VulkanDevice: Persist instance throughout launches
Testing with AMD on Windows and LLVMPipe on Linux, creating and destroying a Vulkan instance appears to leak around 20-30MB of memory. Just keep the thing around for the whole time. Reduces startup time too, so everyone wins. Unless you're switching renderers all the time, then you lose a bit of memory.
This commit is contained in:
@@ -1660,15 +1660,29 @@ void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry, SettingsPa
|
||||
|
||||
void FullscreenUI::PopulateGraphicsAdapterList()
|
||||
{
|
||||
GPURenderer renderer;
|
||||
const auto lock = Core::GetSettingsLock();
|
||||
const GPURenderer renderer =
|
||||
Settings::ParseRendererName(GetEffectiveTinyStringSetting(GetEditingSettingsInterface(false), "GPU", "Renderer",
|
||||
Settings::GetRendererName(Settings::DEFAULT_GPU_RENDERER))
|
||||
.c_str())
|
||||
.value_or(Settings::DEFAULT_GPU_RENDERER);
|
||||
{
|
||||
renderer = Settings::ParseRendererName(
|
||||
GetEffectiveTinyStringSetting(GetEditingSettingsInterface(false), "GPU", "Renderer").c_str())
|
||||
.value_or(Settings::DEFAULT_GPU_RENDERER);
|
||||
}
|
||||
|
||||
s_settings_locals.graphics_adapter_list_cache =
|
||||
GPUDevice::GetAdapterListForAPI(Settings::GetRenderAPIForRenderer(renderer));
|
||||
Error error;
|
||||
std::optional<GPUDevice::AdapterInfoList> adapter_list = GPUDevice::GetAdapterListForAPI(
|
||||
Settings::GetRenderAPIForRenderer(renderer),
|
||||
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetWindowInfo().type :
|
||||
WindowInfoType::Surfaceless,
|
||||
&error);
|
||||
if (adapter_list.has_value())
|
||||
{
|
||||
s_settings_locals.graphics_adapter_list_cache = std::move(adapter_list.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG("Failed to enumerate graphics adapters: {}", error.GetDescription());
|
||||
s_settings_locals.graphics_adapter_list_cache = {};
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::PopulateGameListDirectoryCache(const SettingsInterface& si)
|
||||
|
||||
@@ -18,6 +18,7 @@ class ThreadHandle;
|
||||
|
||||
enum class RenderAPI : u8;
|
||||
enum class GPUVSyncMode : u8;
|
||||
enum class WindowInfoType : u8;
|
||||
|
||||
enum class GPURenderer : u8;
|
||||
enum class GPUBackendCommandType : u8;
|
||||
@@ -117,6 +118,9 @@ namespace Host {
|
||||
std::optional<WindowInfo> AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
|
||||
Error* error);
|
||||
|
||||
/// Returns the window type for the host.
|
||||
WindowInfoType GetRenderWindowInfoType();
|
||||
|
||||
/// Called when the core is finished with a render window.
|
||||
void ReleaseRenderWindow();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "achievements.h"
|
||||
#include "controller.h"
|
||||
#include "core.h"
|
||||
#include "gpu_thread.h"
|
||||
#include "gte_types.h"
|
||||
#include "imgui_overlays.h"
|
||||
#include "system.h"
|
||||
@@ -1585,7 +1586,7 @@ RenderAPI Settings::GetRenderAPIForRenderer(GPURenderer renderer)
|
||||
case GPURenderer::Software:
|
||||
case GPURenderer::Automatic:
|
||||
default:
|
||||
return GPUDevice::GetPreferredAPI();
|
||||
return GPUDevice::GetPreferredAPI(Host::GetRenderWindowInfoType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1622,11 +1623,6 @@ GPURenderer Settings::GetRendererForRenderAPI(RenderAPI api)
|
||||
}
|
||||
}
|
||||
|
||||
GPURenderer Settings::GetAutomaticRenderer()
|
||||
{
|
||||
return GetRendererForRenderAPI(GPUDevice::GetPreferredAPI());
|
||||
}
|
||||
|
||||
static constexpr const std::array s_texture_filter_names = {
|
||||
"Nearest", "Bilinear", "BilinearBinAlpha", "JINC2", "JINC2BinAlpha", "xBR",
|
||||
"xBRBinAlpha", "Scale2x", "Scale3x", "MMPX", "MMPXEnhanced",
|
||||
|
||||
@@ -22,6 +22,7 @@ enum class Level : u32;
|
||||
}
|
||||
|
||||
enum class RenderAPI : u8;
|
||||
enum class WindowInfoType : u8;
|
||||
enum class MediaCaptureBackend : u8;
|
||||
enum class OSDMessageType : u8;
|
||||
|
||||
@@ -476,7 +477,6 @@ struct Settings : public GPUSettings
|
||||
static const char* GetRendererDisplayName(GPURenderer renderer);
|
||||
static RenderAPI GetRenderAPIForRenderer(GPURenderer renderer);
|
||||
static GPURenderer GetRendererForRenderAPI(RenderAPI api);
|
||||
static GPURenderer GetAutomaticRenderer();
|
||||
|
||||
static std::optional<GPUTextureFilter> ParseTextureFilterName(const char* str);
|
||||
static const char* GetTextureFilterName(GPUTextureFilter filter);
|
||||
|
||||
@@ -650,6 +650,12 @@ std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool f
|
||||
return wi;
|
||||
}
|
||||
|
||||
WindowInfoType Host::GetRenderWindowInfoType()
|
||||
{
|
||||
// Assume SDL for GL/Vulkan.
|
||||
return WindowInfoType::SDL;
|
||||
}
|
||||
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
using namespace MiniHost;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "graphicssettingswidget.h"
|
||||
#include "qthost.h"
|
||||
#include "qtutils.h"
|
||||
#include "qtwindowinfo.h"
|
||||
#include "settingswindow.h"
|
||||
#include "settingwidgetbinder.h"
|
||||
#include "ui_texturereplacementsettingsdialog.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
#include "util/media_capture.h"
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/log.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtWidgets/QDialog>
|
||||
@@ -28,6 +30,8 @@
|
||||
|
||||
#include "moc_graphicssettingswidget.cpp"
|
||||
|
||||
LOG_CHANNEL(Host);
|
||||
|
||||
static QVariant GetMSAAModeValue(uint multisamples, bool ssaa)
|
||||
{
|
||||
const uint userdata = (multisamples & 0x7FFFFFFFu) | (static_cast<uint>(ssaa) << 31);
|
||||
@@ -836,8 +840,17 @@ void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_
|
||||
m_adapters_render_api = render_api;
|
||||
|
||||
QtAsyncTask::create(this, [this, render_api]() {
|
||||
GPUDevice::AdapterInfoList adapters = GPUDevice::GetAdapterListForAPI(render_api);
|
||||
return [this, adapters = std::move(adapters), render_api]() mutable {
|
||||
Error error;
|
||||
std::optional<GPUDevice::AdapterInfoList> adapters =
|
||||
GPUDevice::GetAdapterListForAPI(render_api, QtUtils::GetWindowInfoType(), &error);
|
||||
if (!adapters.has_value())
|
||||
{
|
||||
ERROR_LOG("Failed to get adapter list for {} API: {}", GPUDevice::RenderAPIToString(render_api),
|
||||
error.GetDescription());
|
||||
adapters.emplace();
|
||||
}
|
||||
|
||||
return [this, adapters = std::move(adapters.value()), render_api]() mutable {
|
||||
if (m_adapters_render_api != render_api)
|
||||
return;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "mainwindow.h"
|
||||
#include "qtprogresscallback.h"
|
||||
#include "qtutils.h"
|
||||
#include "qtwindowinfo.h"
|
||||
#include "settingswindow.h"
|
||||
#include "setupwizarddialog.h"
|
||||
|
||||
@@ -2872,6 +2873,11 @@ std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool f
|
||||
return g_core_thread->acquireRenderWindow(render_api, fullscreen, exclusive_fullscreen, error);
|
||||
}
|
||||
|
||||
WindowInfoType Host::GetRenderWindowInfoType()
|
||||
{
|
||||
return QtUtils::GetWindowInfoType();
|
||||
}
|
||||
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
g_core_thread->releaseRenderWindow();
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "qtwindowinfo.h"
|
||||
#include "qtutils.h"
|
||||
|
||||
#include "core/core.h"
|
||||
|
||||
#include "util/gpu_device.h"
|
||||
|
||||
#include "common/error.h"
|
||||
@@ -61,6 +63,32 @@ std::pair<QSize, qreal> QtUtils::GetPixelSizeForWidget(const QWidget* widget)
|
||||
return std::make_pair(ApplyDevicePixelRatioToSize(widget->size(), device_pixel_ratio), device_pixel_ratio);
|
||||
}
|
||||
|
||||
WindowInfoType QtUtils::GetWindowInfoType()
|
||||
{
|
||||
// Windows and Apple are easy here since there's no display connection.
|
||||
#if defined(_WIN32)
|
||||
return WindowInfoType::Win32;
|
||||
#elif defined(__APPLE__)
|
||||
return WindowInfoType::MacOS;
|
||||
#else
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("xcb"))
|
||||
{
|
||||
// This is only used for determining the automatic Vulkan renderer, therefore XCB/XLib doesn't matter here.
|
||||
// See the comment below for information about this bullshit.
|
||||
return WindowInfoType::XCB;
|
||||
}
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
{
|
||||
return WindowInfoType::Wayland;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WindowInfoType::Surfaceless;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, RenderAPI render_api, Error* error)
|
||||
{
|
||||
WindowInfo wi;
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace QtUtils {
|
||||
/// Also returns the "real" DPR scale for the widget, ignoring any operating-system level downsampling.
|
||||
std::pair<QSize, qreal> GetPixelSizeForWidget(const QWidget* widget);
|
||||
|
||||
/// Returns the window type for the current Qt platform.
|
||||
WindowInfoType GetWindowInfoType();
|
||||
|
||||
/// Returns the common window info structure for a Qt widget.
|
||||
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget, RenderAPI render_api, Error* error = nullptr);
|
||||
|
||||
|
||||
@@ -437,6 +437,11 @@ std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool f
|
||||
return WindowInfo();
|
||||
}
|
||||
|
||||
WindowInfoType Host::GetRenderWindowInfoType()
|
||||
{
|
||||
return WindowInfoType::Surfaceless;
|
||||
}
|
||||
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
//
|
||||
|
||||
@@ -173,8 +173,8 @@ if(ENABLE_VULKAN)
|
||||
vulkan_builders.h
|
||||
vulkan_device.cpp
|
||||
vulkan_device.h
|
||||
vulkan_entry_points.h
|
||||
vulkan_entry_points.inl
|
||||
vulkan_headers.h
|
||||
vulkan_loader.cpp
|
||||
vulkan_loader.h
|
||||
vulkan_pipeline.cpp
|
||||
|
||||
@@ -301,13 +301,13 @@ static std::string FixupDuplicateAdapterNames(const GPUDevice::AdapterInfoList&
|
||||
return adapter_name;
|
||||
}
|
||||
|
||||
GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
||||
std::optional<GPUDevice::AdapterInfoList> D3DCommon::GetAdapterInfoList(Error* error)
|
||||
{
|
||||
GPUDevice::AdapterInfoList adapters;
|
||||
std::optional<GPUDevice::AdapterInfoList> ret;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory5> factory = CreateFactory(false, nullptr);
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory5> factory = CreateFactory(false, error);
|
||||
if (!factory)
|
||||
return adapters;
|
||||
return ret;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
|
||||
for (u32 index = 0;; index++)
|
||||
@@ -322,10 +322,13 @@ GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ret.has_value())
|
||||
ret.emplace();
|
||||
|
||||
// Unfortunately we can't get any properties such as feature level without creating the device.
|
||||
// So just assume a max of the D3D11 max across the board.
|
||||
GPUDevice::AdapterInfo ai;
|
||||
ai.name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get(), &ai.driver_type));
|
||||
ai.name = FixupDuplicateAdapterNames(ret.value(), GetAdapterName(adapter.Get(), &ai.driver_type));
|
||||
ai.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
ai.max_multisamples = 8;
|
||||
ai.supports_sample_shading = true;
|
||||
@@ -365,10 +368,16 @@ GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
||||
ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast<unsigned>(hr));
|
||||
}
|
||||
|
||||
adapters.push_back(std::move(ai));
|
||||
ret->push_back(std::move(ai));
|
||||
}
|
||||
|
||||
return adapters;
|
||||
if (!ret.has_value())
|
||||
{
|
||||
Error::SetStringView(error, "No DXGI adapters found.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<DXGI_MODE_DESC>
|
||||
|
||||
@@ -60,7 +60,7 @@ bool CreateD3D12Device(IDXGIAdapter* adapter, D3D_FEATURE_LEVEL feature_level,
|
||||
Microsoft::WRL::ComPtr<ID3DBlob> SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc, Error* error);
|
||||
|
||||
// returns a list of all adapter names
|
||||
GPUDevice::AdapterInfoList GetAdapterInfoList();
|
||||
std::optional<GPUDevice::AdapterInfoList> GetAdapterInfoList(Error* error);
|
||||
|
||||
// returns the fullscreen mode to use for the specified dimensions
|
||||
std::optional<DXGI_MODE_DESC>
|
||||
|
||||
@@ -41,6 +41,7 @@ LOG_CHANNEL(GPUDevice);
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "vulkan_device.h"
|
||||
#include "vulkan_loader.h"
|
||||
#endif
|
||||
|
||||
std::unique_ptr<GPUDevice> g_gpu_device;
|
||||
@@ -328,7 +329,7 @@ GPUDevice::GPUDevice()
|
||||
|
||||
GPUDevice::~GPUDevice() = default;
|
||||
|
||||
RenderAPI GPUDevice::GetPreferredAPI()
|
||||
RenderAPI GPUDevice::GetPreferredAPI(WindowInfoType window_type)
|
||||
{
|
||||
static RenderAPI preferred_renderer = RenderAPI::None;
|
||||
if (preferred_renderer == RenderAPI::None) [[unlikely]]
|
||||
@@ -343,7 +344,7 @@ RenderAPI GPUDevice::GetPreferredAPI()
|
||||
preferred_renderer = RenderAPI::Metal;
|
||||
#elif defined(ENABLE_OPENGL) && defined(ENABLE_VULKAN)
|
||||
// On Linux, if we have both GL and Vulkan, prefer VK if the driver isn't software.
|
||||
preferred_renderer = VulkanDevice::IsSuitableDefaultRenderer() ? RenderAPI::Vulkan : RenderAPI::OpenGL;
|
||||
preferred_renderer = VulkanLoader::IsSuitableDefaultRenderer(window_type) ? RenderAPI::Vulkan : RenderAPI::OpenGL;
|
||||
#elif defined(ENABLE_OPENGL)
|
||||
preferred_renderer = RenderAPI::OpenGL;
|
||||
#elif defined(ENABLE_VULKAN)
|
||||
@@ -413,15 +414,16 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs)
|
||||
(rhs == RenderAPI::OpenGL || rhs == RenderAPI::OpenGLES)));
|
||||
}
|
||||
|
||||
GPUDevice::AdapterInfoList GPUDevice::GetAdapterListForAPI(RenderAPI api)
|
||||
std::optional<GPUDevice::AdapterInfoList> GPUDevice::GetAdapterListForAPI(RenderAPI api, WindowInfoType window_type,
|
||||
Error* error)
|
||||
{
|
||||
AdapterInfoList ret;
|
||||
std::optional<AdapterInfoList> ret;
|
||||
|
||||
switch (api)
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RenderAPI::Vulkan:
|
||||
ret = VulkanDevice::GetAdapterList();
|
||||
ret = VulkanLoader::GetAdapterList(window_type, error);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -429,13 +431,14 @@ GPUDevice::AdapterInfoList GPUDevice::GetAdapterListForAPI(RenderAPI api)
|
||||
case RenderAPI::OpenGL:
|
||||
case RenderAPI::OpenGLES:
|
||||
// No way of querying.
|
||||
ret = AdapterInfoList();
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
case RenderAPI::D3D11:
|
||||
case RenderAPI::D3D12:
|
||||
ret = D3DCommon::GetAdapterInfoList();
|
||||
ret = D3DCommon::GetAdapterInfoList(error);
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -695,7 +695,7 @@ public:
|
||||
virtual ~GPUDevice();
|
||||
|
||||
/// Returns the default/preferred API for the system.
|
||||
static RenderAPI GetPreferredAPI();
|
||||
static RenderAPI GetPreferredAPI(WindowInfoType window_type);
|
||||
|
||||
/// Returns a string representing the specified API.
|
||||
static const char* RenderAPIToString(RenderAPI api);
|
||||
@@ -713,7 +713,7 @@ public:
|
||||
static bool IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs);
|
||||
|
||||
/// Returns a list of adapters for the given API.
|
||||
static AdapterInfoList GetAdapterListForAPI(RenderAPI api);
|
||||
static std::optional<AdapterInfoList> GetAdapterListForAPI(RenderAPI api, WindowInfoType window_type, Error* error);
|
||||
|
||||
/// Dumps out a shader that failed compilation.
|
||||
static void DumpBadShader(std::string_view code, std::string_view errors);
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<ClInclude Include="translation.h" />
|
||||
<ClInclude Include="vulkan_builders.h" />
|
||||
<ClInclude Include="vulkan_device.h" />
|
||||
<ClInclude Include="vulkan_entry_points.h" />
|
||||
<ClInclude Include="vulkan_headers.h" />
|
||||
<ClInclude Include="vulkan_loader.h" />
|
||||
<ClInclude Include="vulkan_pipeline.h" />
|
||||
<ClInclude Include="vulkan_stream_buffer.h" />
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<ClInclude Include="opengl_texture.h" />
|
||||
<ClInclude Include="vulkan_builders.h" />
|
||||
<ClInclude Include="vulkan_device.h" />
|
||||
<ClInclude Include="vulkan_entry_points.h" />
|
||||
<ClInclude Include="vulkan_loader.h" />
|
||||
<ClInclude Include="vulkan_pipeline.h" />
|
||||
<ClInclude Include="vulkan_stream_buffer.h" />
|
||||
@@ -81,6 +80,7 @@
|
||||
<ClInclude Include="imgui_gsvector.h" />
|
||||
<ClInclude Include="translation.h" />
|
||||
<ClInclude Include="core_audio_stream.h" />
|
||||
<ClInclude Include="vulkan_headers.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="state_wrapper.cpp" />
|
||||
|
||||
@@ -118,6 +118,35 @@ void Vulkan::SetErrorObject(Error* errptr, std::string_view prefix, VkResult res
|
||||
Error::SetStringFmt(errptr, "{} (0x{:08X}: {})", prefix, static_cast<unsigned>(res), VkResultToString(res));
|
||||
}
|
||||
|
||||
u32 Vulkan::GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties)
|
||||
{
|
||||
VkImageFormatProperties color_properties = {};
|
||||
vkGetPhysicalDeviceImageFormatProperties(physical_device, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0,
|
||||
&color_properties);
|
||||
VkImageFormatProperties depth_properties = {};
|
||||
vkGetPhysicalDeviceImageFormatProperties(physical_device, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0,
|
||||
&depth_properties);
|
||||
const VkSampleCountFlags combined_properties = properties.limits.framebufferColorSampleCounts &
|
||||
properties.limits.framebufferDepthSampleCounts &
|
||||
color_properties.sampleCounts & depth_properties.sampleCounts;
|
||||
if (combined_properties & VK_SAMPLE_COUNT_64_BIT)
|
||||
return 64;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_32_BIT)
|
||||
return 32;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_16_BIT)
|
||||
return 16;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_8_BIT)
|
||||
return 8;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_4_BIT)
|
||||
return 4;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_2_BIT)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vulkan::DescriptorSetLayoutBuilder::DescriptorSetLayoutBuilder()
|
||||
{
|
||||
Clear();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "gpu_device.h"
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_headers.h"
|
||||
|
||||
#include "common/small_string.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -28,6 +28,8 @@ const char* VkResultToString(VkResult res);
|
||||
void LogVulkanResult(const char* func_name, VkResult res, std::string_view msg);
|
||||
void SetErrorObject(Error* errptr, std::string_view prefix, VkResult res);
|
||||
|
||||
u32 GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties);
|
||||
|
||||
class DescriptorSetLayoutBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "vulkan_device.h"
|
||||
#include "vulkan_builders.h"
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_pipeline.h"
|
||||
#include "vulkan_stream_buffer.h"
|
||||
#include "vulkan_swap_chain.h"
|
||||
@@ -24,11 +25,6 @@
|
||||
#include "fmt/format.h"
|
||||
#include "xxhash.h"
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
@@ -117,9 +113,6 @@ static const VkRenderPass DYNAMIC_RENDERING_RENDER_PASS = ((VkRenderPass) static
|
||||
static u32 s_debug_scope_depth = 0;
|
||||
#endif
|
||||
|
||||
// We need to synchronize instance creation because of adapter enumeration from the UI thread.
|
||||
static std::mutex s_instance_mutex;
|
||||
|
||||
VulkanDevice::VulkanDevice()
|
||||
{
|
||||
m_render_api = RenderAPI::Vulkan;
|
||||
@@ -145,302 +138,6 @@ GPUTextureFormat VulkanDevice::GetFormatForVkFormat(VkFormat format)
|
||||
return GPUTextureFormat::Unknown;
|
||||
}
|
||||
|
||||
VkInstance VulkanDevice::CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils,
|
||||
bool enable_validation_layer)
|
||||
{
|
||||
ExtensionList enabled_extensions;
|
||||
if (!SelectInstanceExtensions(&enabled_extensions, wi, oe, enable_debug_utils))
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
u32 maxApiVersion = VK_API_VERSION_1_0;
|
||||
if (vkEnumerateInstanceVersion)
|
||||
{
|
||||
VkResult res = vkEnumerateInstanceVersion(&maxApiVersion);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceVersion() failed: ");
|
||||
maxApiVersion = VK_API_VERSION_1_0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG("Driver does not provide vkEnumerateInstanceVersion().");
|
||||
}
|
||||
|
||||
// Cap out at 1.1 for consistency.
|
||||
const u32 apiVersion = std::min(maxApiVersion, VK_API_VERSION_1_1);
|
||||
INFO_LOG("Supported instance version: {}.{}.{}, requesting version {}.{}.{}", VK_API_VERSION_MAJOR(maxApiVersion),
|
||||
VK_API_VERSION_MINOR(maxApiVersion), VK_API_VERSION_PATCH(maxApiVersion), VK_API_VERSION_MAJOR(apiVersion),
|
||||
VK_API_VERSION_MINOR(apiVersion), VK_API_VERSION_PATCH(apiVersion));
|
||||
|
||||
// Remember to manually update this every release. We don't pull in svnrev.h here, because
|
||||
// it's only the major/minor version, and rebuilding the file every time something else changes
|
||||
// is unnecessary.
|
||||
VkApplicationInfo app_info = {};
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
app_info.pNext = nullptr;
|
||||
app_info.pApplicationName = "DuckStation";
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||
app_info.pEngineName = "DuckStation";
|
||||
app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||
app_info.apiVersion = apiVersion;
|
||||
|
||||
VkInstanceCreateInfo instance_create_info = {};
|
||||
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instance_create_info.pNext = nullptr;
|
||||
instance_create_info.flags = 0;
|
||||
instance_create_info.pApplicationInfo = &app_info;
|
||||
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
instance_create_info.enabledLayerCount = 0;
|
||||
instance_create_info.ppEnabledLayerNames = nullptr;
|
||||
|
||||
// Enable debug layer on debug builds
|
||||
if (enable_validation_layer)
|
||||
{
|
||||
static const char* layer_names[] = {"VK_LAYER_KHRONOS_validation"};
|
||||
instance_create_info.enabledLayerCount = 1;
|
||||
instance_create_info.ppEnabledLayerNames = layer_names;
|
||||
}
|
||||
|
||||
VkInstance instance;
|
||||
VkResult res = vkCreateInstance(&instance_create_info, nullptr, &instance);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateInstance failed: ");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool VulkanDevice::SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, OptionalExtensions* oe,
|
||||
bool enable_debug_utils)
|
||||
{
|
||||
u32 extension_count = 0;
|
||||
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extension_count == 0)
|
||||
{
|
||||
ERROR_LOG("Vulkan: No extensions supported by instance.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<VkExtensionProperties> available_extension_list(extension_count);
|
||||
res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, available_extension_list.data());
|
||||
DebugAssert(res == VK_SUCCESS);
|
||||
|
||||
auto SupportsExtension = [&](const char* name, bool required) {
|
||||
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
||||
[&](const VkExtensionProperties& properties) {
|
||||
return !strcmp(name, properties.extensionName);
|
||||
}) != available_extension_list.end())
|
||||
{
|
||||
DEV_LOG("Enabling extension: {}", name);
|
||||
extension_list->push_back(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (required)
|
||||
ERROR_LOG("Vulkan: Missing required extension {}.", name);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
if (wi.type == WindowInfoType::Win32 && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true)))
|
||||
return false;
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
if (wi.type == WindowInfoType::XCB && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, true)))
|
||||
return false;
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
if (wi.type == WindowInfoType::Wayland && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true)))
|
||||
return false;
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
if (wi.type == WindowInfoType::MacOS && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
if (wi.type == WindowInfoType::Android && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_SDL)
|
||||
if (wi.type == WindowInfoType::SDL)
|
||||
{
|
||||
Uint32 sdl_extension_count = 0;
|
||||
const char* const* sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extension_count);
|
||||
if (!sdl_extensions)
|
||||
{
|
||||
ERROR_LOG("SDL_Vulkan_GetInstanceExtensions() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < sdl_extension_count; i++)
|
||||
{
|
||||
if (!SupportsExtension(sdl_extensions[i], true))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// VK_EXT_debug_utils
|
||||
if (enable_debug_utils && !SupportsExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false))
|
||||
WARNING_LOG("Vulkan: Debug report requested, but extension is not available.");
|
||||
|
||||
// Needed for exclusive fullscreen control.
|
||||
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
|
||||
|
||||
oe->vk_khr_get_surface_capabilities2 = (wi.type != WindowInfoType::Surfaceless &&
|
||||
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false));
|
||||
oe->vk_ext_surface_maintenance1 =
|
||||
(wi.type != WindowInfoType::Surfaceless && SupportsExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false));
|
||||
oe->vk_ext_swapchain_maintenance1 =
|
||||
(wi.type != WindowInfoType::Surfaceless && SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false));
|
||||
oe->vk_khr_get_physical_device_properties2 =
|
||||
SupportsExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanDevice::GPUList VulkanDevice::EnumerateGPUs(VkInstance instance)
|
||||
{
|
||||
GPUList gpus;
|
||||
|
||||
u32 gpu_count = 0;
|
||||
VkResult res = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
|
||||
if ((res != VK_SUCCESS && res != VK_INCOMPLETE) || gpu_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices (1) failed: ");
|
||||
return gpus;
|
||||
}
|
||||
|
||||
std::vector<VkPhysicalDevice> physical_devices(gpu_count);
|
||||
res = vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices.data());
|
||||
if (res == VK_INCOMPLETE)
|
||||
{
|
||||
WARNING_LOG("First vkEnumeratePhysicalDevices() call returned {} devices, but second returned {}",
|
||||
physical_devices.size(), gpu_count);
|
||||
}
|
||||
else if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices (2) failed: ");
|
||||
return gpus;
|
||||
}
|
||||
|
||||
// Maybe we lost a GPU?
|
||||
if (gpu_count < physical_devices.size())
|
||||
physical_devices.resize(gpu_count);
|
||||
|
||||
gpus.reserve(physical_devices.size());
|
||||
for (VkPhysicalDevice device : physical_devices)
|
||||
{
|
||||
VkPhysicalDeviceProperties2 props = {};
|
||||
VkPhysicalDeviceDriverProperties driver_props = {};
|
||||
|
||||
if (vkGetPhysicalDeviceProperties2)
|
||||
{
|
||||
props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
driver_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
|
||||
Vulkan::AddPointerToChain(&props, &driver_props);
|
||||
vkGetPhysicalDeviceProperties2(device, &props);
|
||||
}
|
||||
|
||||
// just in case the chained version fails
|
||||
vkGetPhysicalDeviceProperties(device, &props.properties);
|
||||
|
||||
VkPhysicalDeviceFeatures available_features = {};
|
||||
vkGetPhysicalDeviceFeatures(device, &available_features);
|
||||
|
||||
AdapterInfo ai;
|
||||
ai.name = props.properties.deviceName;
|
||||
ai.max_texture_size =
|
||||
std::min(props.properties.limits.maxFramebufferWidth, props.properties.limits.maxImageDimension2D);
|
||||
ai.max_multisamples = GetMaxMultisamples(device, props.properties);
|
||||
ai.driver_type = GuessDriverType(props.properties, driver_props);
|
||||
ai.supports_sample_shading = available_features.sampleRateShading;
|
||||
|
||||
// handle duplicate adapter names
|
||||
if (std::any_of(gpus.begin(), gpus.end(), [&ai](const auto& other) { return (ai.name == other.second.name); }))
|
||||
{
|
||||
std::string original_adapter_name = std::move(ai.name);
|
||||
|
||||
u32 current_extra = 2;
|
||||
do
|
||||
{
|
||||
ai.name = fmt::format("{} ({})", original_adapter_name, current_extra);
|
||||
current_extra++;
|
||||
} while (
|
||||
std::any_of(gpus.begin(), gpus.end(), [&ai](const auto& other) { return (ai.name == other.second.name); }));
|
||||
}
|
||||
|
||||
gpus.emplace_back(device, std::move(ai));
|
||||
}
|
||||
|
||||
return gpus;
|
||||
}
|
||||
|
||||
VulkanDevice::GPUList VulkanDevice::EnumerateGPUs()
|
||||
{
|
||||
GPUList ret;
|
||||
std::unique_lock lock(s_instance_mutex);
|
||||
|
||||
// Device shouldn't be torn down since we have the lock.
|
||||
if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::Vulkan && Vulkan::IsVulkanLibraryLoaded())
|
||||
{
|
||||
ret = EnumerateGPUs(VulkanDevice::GetInstance().m_instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Vulkan::LoadVulkanLibrary(nullptr))
|
||||
{
|
||||
OptionalExtensions oe = {};
|
||||
const VkInstance instance = CreateVulkanInstance(WindowInfo(), &oe, false, false);
|
||||
if (instance != VK_NULL_HANDLE)
|
||||
{
|
||||
if (Vulkan::LoadVulkanInstanceFunctions(instance))
|
||||
ret = EnumerateGPUs(instance);
|
||||
|
||||
if (vkDestroyInstance)
|
||||
vkDestroyInstance(instance, nullptr);
|
||||
else
|
||||
ERROR_LOG("Vulkan instance was leaked because vkDestroyInstance() could not be loaded.");
|
||||
}
|
||||
|
||||
Vulkan::UnloadVulkanLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GPUDevice::AdapterInfoList VulkanDevice::GetAdapterList()
|
||||
{
|
||||
AdapterInfoList ret;
|
||||
GPUList gpus = EnumerateGPUs();
|
||||
ret.reserve(gpus.size());
|
||||
for (auto& [physical_device, adapter_info] : gpus)
|
||||
ret.push_back(std::move(adapter_info));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VulkanDevice::EnableOptionalDeviceExtensions(VkPhysicalDevice physical_device,
|
||||
std::span<const VkExtensionProperties> available_extensions,
|
||||
ExtensionList& enabled_extensions,
|
||||
@@ -544,26 +241,6 @@ bool VulkanDevice::EnableOptionalDeviceExtensions(VkPhysicalDevice physical_devi
|
||||
}
|
||||
}
|
||||
|
||||
// we might not have VK_KHR_get_physical_device_properties2...
|
||||
if (!vkGetPhysicalDeviceFeatures2 || !vkGetPhysicalDeviceProperties2 || !vkGetPhysicalDeviceMemoryProperties2)
|
||||
{
|
||||
if (!vkGetPhysicalDeviceFeatures2KHR || !vkGetPhysicalDeviceProperties2KHR ||
|
||||
!vkGetPhysicalDeviceMemoryProperties2KHR)
|
||||
{
|
||||
ERROR_LOG("One or more functions from VK_KHR_get_physical_device_properties2 is missing, disabling extension.");
|
||||
m_optional_extensions.vk_khr_get_physical_device_properties2 = false;
|
||||
vkGetPhysicalDeviceFeatures2 = nullptr;
|
||||
vkGetPhysicalDeviceProperties2 = nullptr;
|
||||
vkGetPhysicalDeviceMemoryProperties2 = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
vkGetPhysicalDeviceFeatures2 = vkGetPhysicalDeviceFeatures2KHR;
|
||||
vkGetPhysicalDeviceProperties2 = vkGetPhysicalDeviceProperties2KHR;
|
||||
vkGetPhysicalDeviceMemoryProperties2 = vkGetPhysicalDeviceMemoryProperties2KHR;
|
||||
}
|
||||
}
|
||||
|
||||
// don't bother querying if we're not actually looking at any features
|
||||
if (vkGetPhysicalDeviceFeatures2 && features2.pNext)
|
||||
vkGetPhysicalDeviceFeatures2(physical_device, &features2);
|
||||
@@ -613,7 +290,7 @@ bool VulkanDevice::EnableOptionalDeviceExtensions(VkPhysicalDevice physical_devi
|
||||
vkGetPhysicalDeviceProperties2(physical_device, &properties2);
|
||||
|
||||
// set driver type
|
||||
SetDriverType(GuessDriverType(m_device_properties, m_device_driver_properties));
|
||||
SetDriverType(VulkanLoader::GuessDriverType(m_device_properties, m_device_driver_properties));
|
||||
|
||||
// check we actually support enough
|
||||
m_optional_extensions.vk_khr_push_descriptor &= (push_descriptor_properties.maxPushDescriptors >= 1);
|
||||
@@ -728,13 +405,10 @@ bool VulkanDevice::EnableOptionalDeviceExtensions(VkPhysicalDevice physical_devi
|
||||
LOG_EXT("VK_EXT_fragment_shader_interlock", vk_ext_fragment_shader_interlock);
|
||||
LOG_EXT("VK_EXT_memory_budget", vk_ext_memory_budget);
|
||||
LOG_EXT("VK_EXT_rasterization_order_attachment_access", vk_ext_rasterization_order_attachment_access);
|
||||
LOG_EXT("VK_EXT_surface_maintenance1", vk_ext_surface_maintenance1);
|
||||
LOG_EXT("VK_EXT_swapchain_maintenance1", vk_ext_swapchain_maintenance1);
|
||||
LOG_EXT("VK_KHR_get_physical_device_properties2", vk_khr_get_physical_device_properties2);
|
||||
LOG_EXT("VK_KHR_driver_properties", vk_khr_driver_properties);
|
||||
LOG_EXT("VK_KHR_dynamic_rendering", vk_khr_dynamic_rendering);
|
||||
LOG_EXT("VK_KHR_dynamic_rendering_local_read", vk_khr_dynamic_rendering_local_read);
|
||||
LOG_EXT("VK_KHR_get_surface_capabilities2", vk_khr_get_surface_capabilities2);
|
||||
LOG_EXT("VK_KHR_maintenance4", vk_khr_maintenance4);
|
||||
LOG_EXT("VK_KHR_maintenance5", vk_khr_maintenance5);
|
||||
LOG_EXT("VK_KHR_push_descriptor", vk_khr_push_descriptor);
|
||||
@@ -750,8 +424,8 @@ bool VulkanDevice::EnableOptionalDeviceExtensions(VkPhysicalDevice physical_devi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanDevice::CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, bool enable_validation_layer,
|
||||
CreateFlags create_flags, Error* error)
|
||||
bool VulkanDevice::CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, CreateFlags create_flags,
|
||||
Error* error)
|
||||
{
|
||||
u32 queue_family_count;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
|
||||
@@ -911,7 +585,8 @@ bool VulkanDevice::CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR s
|
||||
Vulkan::AddPointerToChain(&device_info, &maintenance5_features);
|
||||
}
|
||||
|
||||
res = vkCreateDevice(physical_device, &device_info, nullptr, &m_device);
|
||||
VkDevice device;
|
||||
res = vkCreateDevice(physical_device, &device_info, nullptr, &device);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDevice failed: ");
|
||||
@@ -921,10 +596,19 @@ bool VulkanDevice::CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR s
|
||||
|
||||
// With the device created, we can fill the remaining entry points.
|
||||
m_physical_device = physical_device;
|
||||
if (!Vulkan::LoadVulkanDeviceFunctions(m_device))
|
||||
if (!VulkanLoader::LoadDeviceFunctions(device, error))
|
||||
{
|
||||
if (vkDestroyDevice)
|
||||
vkDestroyDevice(device, nullptr);
|
||||
else
|
||||
ERROR_LOG("Vulkan device leaked because vkDestroyDevice() function pointer was null.");
|
||||
|
||||
VulkanLoader::ResetDeviceFunctions();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Grab the graphics and present queues.
|
||||
m_device = device;
|
||||
vkGetDeviceQueue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
|
||||
if (surface)
|
||||
vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue);
|
||||
@@ -955,7 +639,7 @@ bool VulkanDevice::CreateAllocator()
|
||||
ci.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
|
||||
ci.physicalDevice = m_physical_device;
|
||||
ci.device = m_device;
|
||||
ci.instance = m_instance;
|
||||
ci.instance = VulkanLoader::GetVulkanInstance();
|
||||
|
||||
if (m_optional_extensions.vk_ext_memory_budget)
|
||||
{
|
||||
@@ -1707,78 +1391,6 @@ void VulkanDevice::DeferSamplerDestruction(VkSampler object)
|
||||
[this, object]() { vkDestroySampler(m_device, object, nullptr); });
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL DebugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||
void* pUserData)
|
||||
{
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
|
||||
{
|
||||
ERROR_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
else if (severity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT))
|
||||
{
|
||||
WARNING_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
|
||||
{
|
||||
INFO_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEV_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
bool VulkanDevice::EnableDebugUtils()
|
||||
{
|
||||
// Already enabled?
|
||||
if (m_debug_messenger_callback != VK_NULL_HANDLE)
|
||||
return true;
|
||||
|
||||
// Check for presence of the functions before calling
|
||||
if (!vkCreateDebugUtilsMessengerEXT || !vkDestroyDebugUtilsMessengerEXT || !vkSubmitDebugUtilsMessageEXT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
|
||||
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
nullptr,
|
||||
0,
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT,
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
DebugMessengerCallback,
|
||||
nullptr};
|
||||
|
||||
const VkResult res =
|
||||
vkCreateDebugUtilsMessengerEXT(m_instance, &messenger_info, nullptr, &m_debug_messenger_callback);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDebugUtilsMessengerEXT failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanDevice::DisableDebugUtils()
|
||||
{
|
||||
if (m_debug_messenger_callback != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyDebugUtilsMessengerEXT(m_instance, m_debug_messenger_callback, nullptr);
|
||||
m_debug_messenger_callback = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
VkRenderPass VulkanDevice::CreateCachedRenderPass(RenderPassCacheKey key)
|
||||
{
|
||||
std::array<VkAttachmentReference, MAX_RENDER_TARGETS> color_references;
|
||||
@@ -1929,115 +1541,23 @@ void VulkanDevice::DestroyFramebuffer(VkFramebuffer fbo)
|
||||
VulkanDevice::GetInstance().DeferFramebufferDestruction(fbo);
|
||||
}
|
||||
|
||||
bool VulkanDevice::IsSuitableDefaultRenderer()
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
// No way in hell.
|
||||
return false;
|
||||
#else
|
||||
GPUList gpus = EnumerateGPUs();
|
||||
if (gpus.empty())
|
||||
{
|
||||
// No adapters, not gonna be able to use VK.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the first GPU, should be enough.
|
||||
const AdapterInfo& ainfo = gpus.front().second;
|
||||
INFO_LOG("Using Vulkan GPU '{}' for automatic renderer check.", ainfo.name);
|
||||
|
||||
// Any software rendering (LLVMpipe, SwiftShader).
|
||||
if ((ainfo.driver_type & GPUDriverType::SoftwareFlag) == GPUDriverType::SoftwareFlag)
|
||||
{
|
||||
INFO_LOG("Not using Vulkan for software renderer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
// Intel Ivy Bridge/Haswell/Broadwell drivers are incomplete.
|
||||
if (ainfo.driver_type == GPUDriverType::IntelMesa &&
|
||||
(ainfo.name.find("Ivy Bridge") != std::string::npos || ainfo.name.find("Haswell") != std::string::npos ||
|
||||
ainfo.name.find("Broadwell") != std::string::npos || ainfo.name.find("(IVB") != std::string::npos ||
|
||||
ainfo.name.find("(HSW") != std::string::npos || ainfo.name.find("(BDW") != std::string::npos))
|
||||
{
|
||||
INFO_LOG("Not using Vulkan for Intel GPU with incomplete driver.");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
||||
// V3D is buggy, image copies with larger textures are broken.
|
||||
if (ainfo.driver_type == GPUDriverType::BroadcomMesa)
|
||||
{
|
||||
INFO_LOG("Not using Vulkan for V3D GPU with buggy driver.");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
INFO_LOG("Allowing Vulkan as default renderer.");
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, CreateFlags create_flags,
|
||||
const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||
bool allow_present_throttle,
|
||||
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||
std::optional<bool> exclusive_fullscreen_control, Error* error)
|
||||
{
|
||||
std::unique_lock lock(s_instance_mutex);
|
||||
bool enable_debug_utils = m_debug_device;
|
||||
bool enable_validation_layer = m_debug_device;
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
const bool library_loaded =
|
||||
(wi.type == WindowInfoType::SDL) ? Vulkan::LoadVulkanLibraryFromSDL(error) : Vulkan::LoadVulkanLibrary(error);
|
||||
#else
|
||||
const bool library_loaded = Vulkan::LoadVulkanLibrary(error);
|
||||
#endif
|
||||
if (!library_loaded)
|
||||
if (!VulkanLoader::CreateVulkanInstance(wi.type, &m_debug_device, error))
|
||||
{
|
||||
Error::AddPrefix(error,
|
||||
"Failed to load Vulkan library. Does your GPU and/or driver support Vulkan?\nThe error was:");
|
||||
Error::AddPrefix(error, "Failed to create Vulkan instance. Does your GPU and/or driver support Vulkan?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_instance = CreateVulkanInstance(wi, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
|
||||
if (m_instance == VK_NULL_HANDLE)
|
||||
{
|
||||
if (enable_debug_utils || enable_validation_layer)
|
||||
{
|
||||
// Try again without the validation layer.
|
||||
enable_debug_utils = false;
|
||||
enable_validation_layer = false;
|
||||
m_instance = CreateVulkanInstance(wi, &m_optional_extensions, enable_debug_utils, enable_validation_layer);
|
||||
if (m_instance == VK_NULL_HANDLE)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to create Vulkan instance. Does your GPU and/or driver support Vulkan?");
|
||||
return false;
|
||||
}
|
||||
|
||||
ERROR_LOG("Vulkan validation/debug layers requested but are unavailable. Creating non-debug device.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Vulkan::LoadVulkanInstanceFunctions(m_instance))
|
||||
{
|
||||
ERROR_LOG("Failed to load Vulkan instance functions");
|
||||
Error::SetStringView(error, "Failed to load Vulkan instance functions");
|
||||
|
||||
if (vkDestroyInstance)
|
||||
vkDestroyInstance(std::exchange(m_instance, nullptr), nullptr);
|
||||
else
|
||||
ERROR_LOG("Vulkan instance was leaked because vkDestroyInstance() could not be loaded.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUList gpus = EnumerateGPUs(m_instance);
|
||||
const VulkanLoader::GPUList gpus = VulkanLoader::EnumerateGPUs(error);
|
||||
if (gpus.empty())
|
||||
{
|
||||
Error::SetStringView(error, "No physical devices found. Does your GPU and/or driver support Vulkan?");
|
||||
Error::AddPrefix(error, "No physical devices found. Does your GPU and/or driver support Vulkan?\n");
|
||||
VulkanLoader::ReleaseVulkanInstance();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2067,25 +1587,23 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Create
|
||||
physical_device = gpus[0].first;
|
||||
}
|
||||
|
||||
if (enable_debug_utils)
|
||||
EnableDebugUtils();
|
||||
|
||||
std::unique_ptr<VulkanSwapChain> swap_chain;
|
||||
if (!wi.IsSurfaceless())
|
||||
{
|
||||
swap_chain =
|
||||
std::make_unique<VulkanSwapChain>(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_control);
|
||||
if (!swap_chain->CreateSurface(m_instance, physical_device, error))
|
||||
if (!swap_chain->CreateSurface(physical_device, error))
|
||||
{
|
||||
swap_chain->Destroy(*this, false);
|
||||
VulkanLoader::ReleaseVulkanInstance();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to create the device.
|
||||
if (!CreateDevice(physical_device, swap_chain ? swap_chain->GetSurface() : VK_NULL_HANDLE, enable_validation_layer,
|
||||
create_flags, error))
|
||||
if (!CreateDevice(physical_device, swap_chain ? swap_chain->GetSurface() : VK_NULL_HANDLE, create_flags, error))
|
||||
{
|
||||
VulkanLoader::ReleaseVulkanInstance();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2119,8 +1637,6 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Create
|
||||
|
||||
void VulkanDevice::DestroyDevice()
|
||||
{
|
||||
std::unique_lock lock(s_instance_mutex);
|
||||
|
||||
if (InRenderPass())
|
||||
EndRenderPass();
|
||||
|
||||
@@ -2160,18 +1676,9 @@ void VulkanDevice::DestroyDevice()
|
||||
{
|
||||
vkDestroyDevice(m_device, nullptr);
|
||||
m_device = VK_NULL_HANDLE;
|
||||
VulkanLoader::ResetDeviceFunctions();
|
||||
VulkanLoader::ReleaseVulkanInstance();
|
||||
}
|
||||
|
||||
if (m_debug_messenger_callback != VK_NULL_HANDLE)
|
||||
DisableDebugUtils();
|
||||
|
||||
if (m_instance != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyInstance(m_instance, nullptr);
|
||||
m_instance = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Vulkan::UnloadVulkanLibrary();
|
||||
}
|
||||
|
||||
bool VulkanDevice::ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header, Error* error)
|
||||
@@ -2292,7 +1799,7 @@ std::unique_ptr<GPUSwapChain> VulkanDevice::CreateSwapChain(const WindowInfo& wi
|
||||
{
|
||||
std::unique_ptr<VulkanSwapChain> swap_chain =
|
||||
std::make_unique<VulkanSwapChain>(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_control);
|
||||
if (swap_chain->CreateSurface(m_instance, m_physical_device, error) && swap_chain->CreateSwapChain(*this, error) &&
|
||||
if (swap_chain->CreateSurface(m_physical_device, error) && swap_chain->CreateSwapChain(*this, error) &&
|
||||
swap_chain->CreateSwapChainImages(*this, error))
|
||||
{
|
||||
if (InRenderPass())
|
||||
@@ -2457,35 +1964,6 @@ void VulkanDevice::InsertDebugMessage(const char* msg)
|
||||
|
||||
#endif
|
||||
|
||||
u32 VulkanDevice::GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties)
|
||||
{
|
||||
VkImageFormatProperties color_properties = {};
|
||||
vkGetPhysicalDeviceImageFormatProperties(physical_device, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0,
|
||||
&color_properties);
|
||||
VkImageFormatProperties depth_properties = {};
|
||||
vkGetPhysicalDeviceImageFormatProperties(physical_device, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0,
|
||||
&depth_properties);
|
||||
const VkSampleCountFlags combined_properties = properties.limits.framebufferColorSampleCounts &
|
||||
properties.limits.framebufferDepthSampleCounts &
|
||||
color_properties.sampleCounts & depth_properties.sampleCounts;
|
||||
if (combined_properties & VK_SAMPLE_COUNT_64_BIT)
|
||||
return 64;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_32_BIT)
|
||||
return 32;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_16_BIT)
|
||||
return 16;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_8_BIT)
|
||||
return 8;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_4_BIT)
|
||||
return 4;
|
||||
else if (combined_properties & VK_SAMPLE_COUNT_2_BIT)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VulkanDevice::SetFeatures(CreateFlags create_flags, VkPhysicalDevice physical_device,
|
||||
const VkPhysicalDeviceFeatures& vk_features)
|
||||
{
|
||||
@@ -2494,7 +1972,7 @@ void VulkanDevice::SetFeatures(CreateFlags create_flags, VkPhysicalDevice physic
|
||||
(VK_API_VERSION_MINOR(store_api_version) * 10u) + (VK_API_VERSION_PATCH(store_api_version));
|
||||
m_max_texture_size =
|
||||
std::min(m_device_properties.limits.maxImageDimension2D, m_device_properties.limits.maxFramebufferWidth);
|
||||
m_max_multisamples = static_cast<u16>(GetMaxMultisamples(physical_device, m_device_properties));
|
||||
m_max_multisamples = static_cast<u16>(Vulkan::GetMaxMultisamples(physical_device, m_device_properties));
|
||||
|
||||
m_features.dual_source_blend =
|
||||
!HasCreateFlag(create_flags, CreateFlags::DisableDualSourceBlend) && vk_features.dualSrcBlend;
|
||||
@@ -2547,51 +2025,6 @@ void VulkanDevice::SetFeatures(CreateFlags create_flags, VkPhysicalDevice physic
|
||||
(!HasCreateFlag(create_flags, CreateFlags::DisableCompressedTextures) && vk_features.textureCompressionBC);
|
||||
}
|
||||
|
||||
GPUDriverType VulkanDevice::GuessDriverType(const VkPhysicalDeviceProperties& device_properties,
|
||||
const VkPhysicalDeviceDriverProperties& driver_properties)
|
||||
{
|
||||
static constexpr const std::pair<VkDriverId, GPUDriverType> table[] = {
|
||||
{VK_DRIVER_ID_NVIDIA_PROPRIETARY, GPUDriverType::NVIDIAProprietary},
|
||||
{VK_DRIVER_ID_AMD_PROPRIETARY, GPUDriverType::AMDProprietary},
|
||||
{VK_DRIVER_ID_AMD_OPEN_SOURCE, GPUDriverType::AMDProprietary},
|
||||
{VK_DRIVER_ID_MESA_RADV, GPUDriverType::AMDMesa},
|
||||
{VK_DRIVER_ID_NVIDIA_PROPRIETARY, GPUDriverType::NVIDIAProprietary},
|
||||
{VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, GPUDriverType::IntelProprietary},
|
||||
{VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, GPUDriverType::IntelMesa},
|
||||
{VK_DRIVER_ID_IMAGINATION_PROPRIETARY, GPUDriverType::ImaginationProprietary},
|
||||
{VK_DRIVER_ID_QUALCOMM_PROPRIETARY, GPUDriverType::QualcommProprietary},
|
||||
{VK_DRIVER_ID_ARM_PROPRIETARY, GPUDriverType::ARMProprietary},
|
||||
{VK_DRIVER_ID_GOOGLE_SWIFTSHADER, GPUDriverType::SwiftShader},
|
||||
{VK_DRIVER_ID_GGP_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_BROADCOM_PROPRIETARY, GPUDriverType::BroadcomProprietary},
|
||||
{VK_DRIVER_ID_MESA_LLVMPIPE, GPUDriverType::LLVMPipe},
|
||||
{VK_DRIVER_ID_MOLTENVK, GPUDriverType::AppleProprietary},
|
||||
{VK_DRIVER_ID_COREAVI_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_JUICE_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_VERISILICON_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_MESA_TURNIP, GPUDriverType::QualcommMesa},
|
||||
{VK_DRIVER_ID_MESA_V3DV, GPUDriverType::BroadcomMesa},
|
||||
{VK_DRIVER_ID_MESA_PANVK, GPUDriverType::ARMMesa},
|
||||
{VK_DRIVER_ID_SAMSUNG_PROPRIETARY, GPUDriverType::AMDProprietary},
|
||||
{VK_DRIVER_ID_MESA_VENUS, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_MESA_DOZEN, GPUDriverType::DozenMesa},
|
||||
{VK_DRIVER_ID_MESA_NVK, GPUDriverType::NVIDIAMesa},
|
||||
{VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA, GPUDriverType::ImaginationMesa},
|
||||
{VK_DRIVER_ID_MESA_AGXV, GPUDriverType::AppleMesa},
|
||||
};
|
||||
|
||||
const auto iter = std::find_if(std::begin(table), std::end(table), [&driver_properties](const auto& it) {
|
||||
return (driver_properties.driverID == it.first);
|
||||
});
|
||||
if (iter != std::end(table))
|
||||
return iter->second;
|
||||
|
||||
return GPUDevice::GuessDriverType(
|
||||
device_properties.vendorID, {},
|
||||
std::string_view(device_properties.deviceName,
|
||||
StringUtil::Strnlen(device_properties.deviceName, std::size(device_properties.deviceName))));
|
||||
}
|
||||
|
||||
void VulkanDevice::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
|
||||
GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width,
|
||||
u32 height)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#pragma once
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "gpu_device.h"
|
||||
#include "gpu_framebuffer_manager.h"
|
||||
#include "gpu_texture.h"
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_headers.h"
|
||||
#include "vulkan_stream_buffer.h"
|
||||
|
||||
#include "common/dimensional_array.h"
|
||||
@@ -40,21 +40,14 @@ public:
|
||||
NUM_COMMAND_BUFFERS = 3,
|
||||
};
|
||||
|
||||
struct OptionalInstanceExtensions
|
||||
{
|
||||
bool vk_ext_surface_maintenance1 : 1;
|
||||
bool vk_ext_swapchain_maintenance1 : 1;
|
||||
bool vk_khr_get_surface_capabilities2 : 1;
|
||||
bool vk_khr_get_physical_device_properties2 : 1;
|
||||
};
|
||||
|
||||
struct OptionalExtensions : OptionalInstanceExtensions
|
||||
struct OptionalExtensions
|
||||
{
|
||||
bool vk_ext_external_memory_host : 1;
|
||||
bool vk_ext_fragment_shader_interlock : 1;
|
||||
bool vk_ext_full_screen_exclusive : 1;
|
||||
bool vk_ext_memory_budget : 1;
|
||||
bool vk_ext_rasterization_order_attachment_access : 1;
|
||||
bool vk_ext_swapchain_maintenance1 : 1;
|
||||
bool vk_khr_driver_properties : 1;
|
||||
bool vk_khr_dynamic_rendering : 1;
|
||||
bool vk_khr_dynamic_rendering_local_read : 1;
|
||||
@@ -64,6 +57,8 @@ public:
|
||||
bool vk_khr_shader_non_semantic_info : 1;
|
||||
};
|
||||
|
||||
using ExtensionList = std::vector<const char*>;
|
||||
|
||||
static GPUTextureFormat GetFormatForVkFormat(VkFormat format);
|
||||
|
||||
static const std::array<VkFormat, static_cast<u32>(GPUTextureFormat::MaxCount)> TEXTURE_FORMAT_MAPPING;
|
||||
@@ -72,12 +67,6 @@ public:
|
||||
VulkanDevice();
|
||||
~VulkanDevice() override;
|
||||
|
||||
// Returns a list of Vulkan-compatible GPUs.
|
||||
using GPUList = std::vector<std::pair<VkPhysicalDevice, AdapterInfo>>;
|
||||
static GPUList EnumerateGPUs(VkInstance instance);
|
||||
static GPUList EnumerateGPUs();
|
||||
static AdapterInfoList GetAdapterList();
|
||||
|
||||
std::string GetDriverInfo() const override;
|
||||
|
||||
void FlushCommands() override;
|
||||
@@ -163,7 +152,6 @@ public:
|
||||
|
||||
// Global state accessors
|
||||
ALWAYS_INLINE static VulkanDevice& GetInstance() { return *static_cast<VulkanDevice*>(g_gpu_device.get()); }
|
||||
ALWAYS_INLINE VkInstance GetVulkanInstance() const { return m_instance; }
|
||||
ALWAYS_INLINE VkDevice GetVulkanDevice() const { return m_device; }
|
||||
ALWAYS_INLINE VmaAllocator GetAllocator() const { return m_allocator; }
|
||||
ALWAYS_INLINE VkPhysicalDevice GetVulkanPhysicalDevice() const { return m_physical_device; }
|
||||
@@ -171,9 +159,6 @@ public:
|
||||
ALWAYS_INLINE u32 GetPresentQueueFamilyIndex() const { return m_present_queue_family_index; }
|
||||
ALWAYS_INLINE const OptionalExtensions& GetOptionalExtensions() const { return m_optional_extensions; }
|
||||
|
||||
/// Returns true if Vulkan is suitable as a default for the devices in the system.
|
||||
static bool IsSuitableDefaultRenderer();
|
||||
|
||||
// Helpers for getting constants
|
||||
ALWAYS_INLINE u32 GetBufferCopyOffsetAlignment() const
|
||||
{
|
||||
@@ -326,22 +311,10 @@ private:
|
||||
|
||||
using CleanupObjectFunction = void (*)(VulkanDevice& dev, void* obj);
|
||||
|
||||
// Helper method to create a Vulkan instance.
|
||||
static VkInstance CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils,
|
||||
bool enable_validation_layer);
|
||||
|
||||
bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header, Error* error);
|
||||
void FillPipelineCacheHeader(VK_PIPELINE_CACHE_HEADER* header);
|
||||
|
||||
// Enable/disable debug message runtime.
|
||||
bool EnableDebugUtils();
|
||||
void DisableDebugUtils();
|
||||
|
||||
using ExtensionList = std::vector<const char*>;
|
||||
static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, OptionalExtensions* oe,
|
||||
bool enable_debug_utils);
|
||||
bool CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, bool enable_validation_layer,
|
||||
CreateFlags create_flags, Error* error);
|
||||
bool CreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, CreateFlags create_flags, Error* error);
|
||||
bool EnableOptionalDeviceExtensions(VkPhysicalDevice physical_device,
|
||||
std::span<const VkExtensionProperties> available_extensions,
|
||||
ExtensionList& enabled_extensions, VkPhysicalDeviceFeatures& enabled_features,
|
||||
@@ -349,10 +322,6 @@ private:
|
||||
void SetFeatures(CreateFlags create_flags, VkPhysicalDevice physical_device,
|
||||
const VkPhysicalDeviceFeatures& vk_features);
|
||||
|
||||
static GPUDriverType GuessDriverType(const VkPhysicalDeviceProperties& device_properties,
|
||||
const VkPhysicalDeviceDriverProperties& driver_properties);
|
||||
static u32 GetMaxMultisamples(VkPhysicalDevice physical_device, const VkPhysicalDeviceProperties& properties);
|
||||
|
||||
bool CreateAllocator();
|
||||
void DestroyAllocator();
|
||||
bool CreateCommandBuffers();
|
||||
@@ -403,8 +372,6 @@ private:
|
||||
void EndAndSubmitCommandBuffer(VulkanSwapChain* present_swap_chain, bool explicit_present);
|
||||
void QueuePresent(VulkanSwapChain* present_swap_chain);
|
||||
|
||||
VkInstance m_instance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VmaAllocator m_allocator = VK_NULL_HANDLE;
|
||||
|
||||
@@ -475,7 +442,7 @@ private:
|
||||
m_pipeline_layouts = {};
|
||||
|
||||
// Cold variables.
|
||||
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
|
||||
VkPhysicalDeviceProperties m_device_properties = {};
|
||||
VkPhysicalDeviceDriverProperties m_device_driver_properties = {};
|
||||
VkDebugUtilsMessengerEXT m_debug_messenger_callback = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
73
src/util/vulkan_headers.h
Normal file
73
src/util/vulkan_headers.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#pragma once
|
||||
|
||||
class Error;
|
||||
|
||||
#define VK_NO_PROTOTYPES
|
||||
|
||||
#ifdef _WIN32
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
|
||||
// vulkan.h pulls in windows.h on Windows, so we need to include our replacement header first
|
||||
#include "common/windows_headers.h"
|
||||
#elif defined(__APPLE__)
|
||||
#define VK_USE_PLATFORM_METAL_EXT
|
||||
#elif defined(__ANDROID__)
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#else
|
||||
#ifdef ENABLE_X11
|
||||
#define VK_USE_PLATFORM_XCB_KHR
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// We include vk_mem_alloc globally, so we don't accidentally include it before the vulkan header somewhere.
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnullability-completeness"
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
|
||||
#define VMA_STATIC_VULKAN_FUNCTIONS 1
|
||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
|
||||
#define VMA_STATS_STRING_ENABLED 0
|
||||
#include "vulkan/vk_mem_alloc.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@@ -5,6 +5,10 @@
|
||||
#define VMA_IMPLEMENTATION
|
||||
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_builders.h"
|
||||
#include "vulkan_device.h"
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/dynamic_library.h"
|
||||
@@ -34,80 +38,162 @@ extern "C" {
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
}
|
||||
|
||||
void Vulkan::ResetVulkanLibraryFunctionPointers()
|
||||
{
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
}
|
||||
namespace VulkanLoader {
|
||||
|
||||
static DynamicLibrary s_vulkan_library;
|
||||
static bool LoadVulkanLibrary(WindowInfoType wtype, Error* error);
|
||||
static void ResetModuleFunctions();
|
||||
static bool LoadInstanceFunctions(VkInstance instance, Error* error);
|
||||
static void ResetInstanceFunctions();
|
||||
static void UnloadVulkanLibrary();
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
static bool s_vulkan_library_loaded_from_sdl = false;
|
||||
static bool LoadVulkanLibraryFromSDL(Error* error);
|
||||
static void UnloadVulkanLibraryFromSDL();
|
||||
#endif
|
||||
|
||||
bool Vulkan::IsVulkanLibraryLoaded()
|
||||
static bool LockedCreateVulkanInstance(WindowInfoType wtype, bool* request_debug_instance, Error* error);
|
||||
static void LockedReleaseVulkanInstance();
|
||||
static void LockedDestroyVulkanInstance();
|
||||
|
||||
static bool SelectInstanceExtensions(VulkanDevice::ExtensionList* extension_list, WindowInfoType wtype,
|
||||
bool debug_instance, Error* error);
|
||||
|
||||
VKAPI_ATTR static VkBool32 VKAPI_CALL DebugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||
void* pUserData);
|
||||
|
||||
namespace {
|
||||
struct Locals
|
||||
{
|
||||
~Locals();
|
||||
|
||||
DynamicLibrary library;
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT debug_messenger_callback = VK_NULL_HANDLE;
|
||||
u32 reference_count = 0;
|
||||
OptionalExtensions optional_extensions{};
|
||||
WindowInfoType window_type = WindowInfoType::Surfaceless;
|
||||
bool is_debug_instance = false;
|
||||
#ifdef ENABLE_SDL
|
||||
return (s_vulkan_library.IsOpen() || s_vulkan_library_loaded_from_sdl);
|
||||
#else
|
||||
return s_vulkan_library.IsOpen();
|
||||
bool library_loaded_from_sdl = false;
|
||||
#endif
|
||||
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ALIGN_TO_CACHE_LINE static Locals s_locals;
|
||||
|
||||
} // namespace VulkanLoader
|
||||
|
||||
VulkanLoader::Locals::~Locals()
|
||||
{
|
||||
// Called at process shutdown.
|
||||
if (instance)
|
||||
LockedDestroyVulkanInstance();
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
if (library_loaded_from_sdl)
|
||||
SDL_Vulkan_UnloadLibrary();
|
||||
#endif
|
||||
|
||||
library.Close();
|
||||
}
|
||||
|
||||
bool Vulkan::LoadVulkanLibrary(Error* error)
|
||||
bool VulkanLoader::LoadVulkanLibrary(WindowInfoType wtype, Error* error)
|
||||
{
|
||||
AssertMsg(!s_vulkan_library.IsOpen(), "Vulkan module is not loaded.");
|
||||
#ifdef ENABLE_SDL
|
||||
// Switching to/from SDL?
|
||||
if (wtype == WindowInfoType::SDL)
|
||||
{
|
||||
if (s_locals.library_loaded_from_sdl)
|
||||
return true;
|
||||
|
||||
UnloadVulkanLibrary();
|
||||
if (!LoadVulkanLibraryFromSDL(error))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unload from SDL if we were previously using it.. unlikely.
|
||||
if (s_locals.library_loaded_from_sdl)
|
||||
UnloadVulkanLibraryFromSDL();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s_locals.library.IsOpen())
|
||||
return true;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Check if a path to a specific Vulkan library has been specified.
|
||||
char* libvulkan_env = getenv("LIBVULKAN_PATH");
|
||||
if (libvulkan_env)
|
||||
s_vulkan_library.Open(libvulkan_env, error);
|
||||
if (!s_vulkan_library.IsOpen() &&
|
||||
!s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("MoltenVK").c_str(), error))
|
||||
s_locals.library.Open(libvulkan_env, error);
|
||||
if (!s_locals.library.IsOpen() &&
|
||||
!s_locals.library.Open(DynamicLibrary::GetVersionedFilename("MoltenVK").c_str(), error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// try versioned first, then unversioned.
|
||||
if (!s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("vulkan", 1).c_str(), error) &&
|
||||
!s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("vulkan").c_str(), error))
|
||||
if (!s_locals.library.Open(DynamicLibrary::GetVersionedFilename("vulkan", 1).c_str(), error) &&
|
||||
!s_locals.library.Open(DynamicLibrary::GetVersionedFilename("vulkan").c_str(), error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool required_functions_missing = false;
|
||||
const auto load_function = [&error, &required_functions_missing](PFN_vkVoidFunction* func_ptr, const char* name,
|
||||
bool is_required) {
|
||||
if (!s_locals.library.GetSymbol(name, func_ptr) && is_required && !required_functions_missing)
|
||||
{
|
||||
Error::SetStringFmt(error, "Failed to load required module function {}", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) \
|
||||
if (!s_vulkan_library.GetSymbol(#name, &name) && required) \
|
||||
{ \
|
||||
ERROR_LOG("Vulkan: Failed to load required module function {}", #name); \
|
||||
required_functions_missing = true; \
|
||||
}
|
||||
load_function(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
if (required_functions_missing)
|
||||
{
|
||||
Error::SetStringView(error, "One or more required functions are missing. The log contains more information.");
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
s_vulkan_library.Close();
|
||||
ResetModuleFunctions();
|
||||
s_locals.library.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanLoader::UnloadVulkanLibrary()
|
||||
{
|
||||
#ifdef ENABLE_SDL
|
||||
if (s_locals.library_loaded_from_sdl)
|
||||
{
|
||||
UnloadVulkanLibraryFromSDL();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ResetModuleFunctions();
|
||||
s_locals.library.Close();
|
||||
}
|
||||
|
||||
void VulkanLoader::ResetModuleFunctions()
|
||||
{
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
|
||||
bool Vulkan::LoadVulkanLibraryFromSDL(Error* error)
|
||||
bool VulkanLoader::LoadVulkanLibraryFromSDL(Error* error)
|
||||
{
|
||||
if (!SDL_Vulkan_LoadLibrary(nullptr))
|
||||
{
|
||||
@@ -124,84 +210,684 @@ bool Vulkan::LoadVulkanLibraryFromSDL(Error* error)
|
||||
}
|
||||
|
||||
bool required_functions_missing = false;
|
||||
const auto load_function = [&error, &required_functions_missing](PFN_vkVoidFunction* func_ptr, const char* name,
|
||||
bool is_required) {
|
||||
// vkGetInstanceProcAddr() can't resolve itself until Vulkan 1.2.
|
||||
if (func_ptr == reinterpret_cast<PFN_vkVoidFunction*>(&vkGetInstanceProcAddr))
|
||||
return;
|
||||
|
||||
// vkGetInstanceProcAddr() can't resolve itself until Vulkan 1.2.
|
||||
*func_ptr = vkGetInstanceProcAddr(nullptr, name);
|
||||
if (!(*func_ptr) && is_required && !required_functions_missing)
|
||||
{
|
||||
Error::SetStringFmt(error, "Failed to load required module function {}", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) \
|
||||
if ((reinterpret_cast<const void*>(&name) != reinterpret_cast<const void*>(&vkGetInstanceProcAddr)) && \
|
||||
!(name = reinterpret_cast<decltype(name)>(vkGetInstanceProcAddr(nullptr, #name))) && required) \
|
||||
{ \
|
||||
ERROR_LOG("Vulkan: Failed to load required module function {}", #name); \
|
||||
required_functions_missing = true; \
|
||||
}
|
||||
load_function(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
if (required_functions_missing)
|
||||
{
|
||||
Error::SetStringView(error, "One or more required functions are missing. The log contains more information.");
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
ResetModuleFunctions();
|
||||
SDL_Vulkan_UnloadLibrary();
|
||||
return false;
|
||||
}
|
||||
|
||||
s_vulkan_library_loaded_from_sdl = true;
|
||||
s_locals.library_loaded_from_sdl = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Vulkan::UnloadVulkanLibrary()
|
||||
void VulkanLoader::UnloadVulkanLibraryFromSDL()
|
||||
{
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
|
||||
s_vulkan_library.Close();
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
if (s_vulkan_library_loaded_from_sdl)
|
||||
{
|
||||
s_vulkan_library_loaded_from_sdl = false;
|
||||
SDL_Vulkan_UnloadLibrary();
|
||||
}
|
||||
#endif
|
||||
ResetModuleFunctions();
|
||||
s_locals.library_loaded_from_sdl = false;
|
||||
SDL_Vulkan_UnloadLibrary();
|
||||
}
|
||||
|
||||
bool Vulkan::LoadVulkanInstanceFunctions(VkInstance instance)
|
||||
#endif // ENABLE_SDL
|
||||
|
||||
bool VulkanLoader::LoadInstanceFunctions(VkInstance instance, Error* error)
|
||||
{
|
||||
bool required_functions_missing = false;
|
||||
auto LoadFunction = [&](PFN_vkVoidFunction* func_ptr, const char* name, bool is_required) {
|
||||
const auto load_function = [&instance, &error, &required_functions_missing](PFN_vkVoidFunction* func_ptr,
|
||||
const char* name, bool is_required) {
|
||||
*func_ptr = vkGetInstanceProcAddr(instance, name);
|
||||
if (!(*func_ptr) && is_required)
|
||||
if (!(*func_ptr) && is_required && !required_functions_missing)
|
||||
{
|
||||
ERROR_LOG("Vulkan: Failed to load required instance function {}", name);
|
||||
Error::SetStringFmt(error, "Failed to load required instance function {}", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) \
|
||||
LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
load_function(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
|
||||
// we might not have VK_KHR_get_physical_device_properties2...
|
||||
if (!vkGetPhysicalDeviceFeatures2 || !vkGetPhysicalDeviceProperties2 || !vkGetPhysicalDeviceMemoryProperties2)
|
||||
{
|
||||
if (!vkGetPhysicalDeviceFeatures2KHR || !vkGetPhysicalDeviceProperties2KHR ||
|
||||
!vkGetPhysicalDeviceMemoryProperties2KHR)
|
||||
{
|
||||
ERROR_LOG("One or more functions from VK_KHR_get_physical_device_properties2 is missing, disabling extension.");
|
||||
s_locals.optional_extensions.vk_khr_get_physical_device_properties2 = false;
|
||||
vkGetPhysicalDeviceFeatures2 = nullptr;
|
||||
vkGetPhysicalDeviceProperties2 = nullptr;
|
||||
vkGetPhysicalDeviceMemoryProperties2 = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
vkGetPhysicalDeviceFeatures2 = vkGetPhysicalDeviceFeatures2KHR;
|
||||
vkGetPhysicalDeviceProperties2 = vkGetPhysicalDeviceProperties2KHR;
|
||||
vkGetPhysicalDeviceMemoryProperties2 = vkGetPhysicalDeviceMemoryProperties2KHR;
|
||||
}
|
||||
}
|
||||
|
||||
return !required_functions_missing;
|
||||
}
|
||||
|
||||
bool Vulkan::LoadVulkanDeviceFunctions(VkDevice device)
|
||||
void VulkanLoader::ResetInstanceFunctions()
|
||||
{
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
}
|
||||
|
||||
bool VulkanLoader::LoadDeviceFunctions(VkDevice device, Error* error)
|
||||
{
|
||||
bool required_functions_missing = false;
|
||||
auto LoadFunction = [&](PFN_vkVoidFunction* func_ptr, const char* name, bool is_required) {
|
||||
const auto load_function = [&device, &error, &required_functions_missing](PFN_vkVoidFunction* func_ptr,
|
||||
const char* name, bool is_required) {
|
||||
*func_ptr = vkGetDeviceProcAddr(device, name);
|
||||
if (!(*func_ptr) && is_required)
|
||||
if (!(*func_ptr) && is_required && !required_functions_missing)
|
||||
{
|
||||
ERROR_LOG("Vulkan: Failed to load required device function {}", name);
|
||||
Error::SetStringFmt(error, "Failed to load required device function {}", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) \
|
||||
LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
load_function(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
|
||||
return !required_functions_missing;
|
||||
}
|
||||
|
||||
void VulkanLoader::ResetDeviceFunctions()
|
||||
{
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
}
|
||||
|
||||
bool VulkanLoader::LockedCreateVulkanInstance(WindowInfoType wtype, bool* request_debug_instance, Error* error)
|
||||
{
|
||||
if (s_locals.instance != VK_NULL_HANDLE &&
|
||||
((request_debug_instance && *request_debug_instance != s_locals.is_debug_instance) ||
|
||||
s_locals.window_type != wtype))
|
||||
{
|
||||
// Different debug setting, need to recreate the instance.
|
||||
if (s_locals.reference_count > 0)
|
||||
ERROR_LOG("Cannot change Vulkan instance window type/debug setting while in use.");
|
||||
else
|
||||
LockedDestroyVulkanInstance();
|
||||
}
|
||||
|
||||
if (s_locals.instance != VK_NULL_HANDLE)
|
||||
{
|
||||
s_locals.reference_count++;
|
||||
DEV_LOG("Using cached Vulkan instance, reference count {}", s_locals.reference_count);
|
||||
return s_locals.instance;
|
||||
}
|
||||
|
||||
if (!LoadVulkanLibrary(wtype, error))
|
||||
return false;
|
||||
|
||||
bool debug_instance = request_debug_instance ? *request_debug_instance : g_settings.gpu_use_debug_device;
|
||||
INFO_LOG("Creating new Vulkan instance (debug {}, wtype {})...", debug_instance, static_cast<u32>(wtype));
|
||||
|
||||
VulkanDevice::ExtensionList enabled_extensions;
|
||||
if (!SelectInstanceExtensions(&enabled_extensions, wtype, debug_instance, error))
|
||||
return false;
|
||||
|
||||
u32 maxApiVersion = VK_API_VERSION_1_0;
|
||||
if (vkEnumerateInstanceVersion)
|
||||
{
|
||||
VkResult res = vkEnumerateInstanceVersion(&maxApiVersion);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceVersion() failed: ");
|
||||
maxApiVersion = VK_API_VERSION_1_0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG("Driver does not provide vkEnumerateInstanceVersion().");
|
||||
}
|
||||
|
||||
// Cap out at 1.1 for consistency.
|
||||
const u32 apiVersion = std::min(maxApiVersion, VK_API_VERSION_1_1);
|
||||
INFO_LOG("Supported instance version: {}.{}.{}, requesting version {}.{}.{}", VK_API_VERSION_MAJOR(maxApiVersion),
|
||||
VK_API_VERSION_MINOR(maxApiVersion), VK_API_VERSION_PATCH(maxApiVersion), VK_API_VERSION_MAJOR(apiVersion),
|
||||
VK_API_VERSION_MINOR(apiVersion), VK_API_VERSION_PATCH(apiVersion));
|
||||
|
||||
// Remember to manually update this every release. We don't pull in svnrev.h here, because
|
||||
// it's only the major/minor version, and rebuilding the file every time something else changes
|
||||
// is unnecessary.
|
||||
VkApplicationInfo app_info = {};
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
app_info.pNext = nullptr;
|
||||
app_info.pApplicationName = "DuckStation";
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||
app_info.pEngineName = "DuckStation";
|
||||
app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||
app_info.apiVersion = apiVersion;
|
||||
|
||||
VkInstanceCreateInfo instance_create_info = {};
|
||||
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instance_create_info.pNext = nullptr;
|
||||
instance_create_info.flags = 0;
|
||||
instance_create_info.pApplicationInfo = &app_info;
|
||||
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
instance_create_info.enabledLayerCount = 0;
|
||||
instance_create_info.ppEnabledLayerNames = nullptr;
|
||||
|
||||
// Enable debug layer on debug builds
|
||||
if (debug_instance)
|
||||
{
|
||||
static const char* layer_names[] = {"VK_LAYER_KHRONOS_validation"};
|
||||
instance_create_info.enabledLayerCount = 1;
|
||||
instance_create_info.ppEnabledLayerNames = layer_names;
|
||||
}
|
||||
|
||||
DebugAssert(s_locals.instance == VK_NULL_HANDLE && s_locals.reference_count == 0);
|
||||
VkResult res = vkCreateInstance(&instance_create_info, nullptr, &s_locals.instance);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
// If creation failed, try without the debug flag.
|
||||
if (debug_instance)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateInstance() failed, trying without debug layers: ");
|
||||
debug_instance = false;
|
||||
if (SelectInstanceExtensions(&enabled_extensions, wtype, false, error))
|
||||
{
|
||||
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
instance_create_info.enabledLayerCount = 0;
|
||||
instance_create_info.ppEnabledLayerNames = nullptr;
|
||||
res = vkCreateInstance(&instance_create_info, nullptr, &s_locals.instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkCreateInstance() failed: ", res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LoadInstanceFunctions(s_locals.instance, error))
|
||||
{
|
||||
LockedDestroyVulkanInstance();
|
||||
return false;
|
||||
}
|
||||
|
||||
DEV_LOG("Created new Vulkan instance.");
|
||||
s_locals.reference_count = 1;
|
||||
s_locals.window_type = wtype;
|
||||
s_locals.is_debug_instance = debug_instance;
|
||||
if (request_debug_instance)
|
||||
*request_debug_instance = debug_instance;
|
||||
|
||||
// Check for presence of the functions before calling
|
||||
if (debug_instance)
|
||||
{
|
||||
if (vkCreateDebugUtilsMessengerEXT && vkDestroyDebugUtilsMessengerEXT && vkSubmitDebugUtilsMessageEXT)
|
||||
{
|
||||
const VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
|
||||
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
nullptr,
|
||||
0,
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT,
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
DebugMessengerCallback,
|
||||
nullptr};
|
||||
|
||||
res =
|
||||
vkCreateDebugUtilsMessengerEXT(s_locals.instance, &messenger_info, nullptr, &s_locals.debug_messenger_callback);
|
||||
if (res != VK_SUCCESS)
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDebugUtilsMessengerEXT failed: ");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG("Vulkan: Debug messenger requested, but functions are not available.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanLoader::SelectInstanceExtensions(VulkanDevice::ExtensionList* extension_list, WindowInfoType wtype,
|
||||
bool debug_instance, Error* error)
|
||||
{
|
||||
u32 extension_count = 0;
|
||||
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extension_count == 0)
|
||||
{
|
||||
ERROR_LOG("Vulkan: No extensions supported by instance.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<VkExtensionProperties> available_extension_list(extension_count);
|
||||
res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, available_extension_list.data());
|
||||
DebugAssert(res == VK_SUCCESS);
|
||||
|
||||
const auto SupportsExtension = [&available_extension_list, &extension_list](const char* name, bool required) {
|
||||
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
||||
[&](const VkExtensionProperties& properties) {
|
||||
return (std::strcmp(name, properties.extensionName) == 0);
|
||||
}) != available_extension_list.end())
|
||||
{
|
||||
DEV_LOG("Enabling extension: {}", name);
|
||||
extension_list->push_back(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (required)
|
||||
ERROR_LOG("Vulkan: Missing required extension {}.", name);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
if (wtype == WindowInfoType::Win32 && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
if (wtype == WindowInfoType::XCB && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
if (wtype == WindowInfoType::Wayland && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
if (wtype == WindowInfoType::MacOS && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
if (wtype == WindowInfoType::Android && (!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true) ||
|
||||
!SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_SDL)
|
||||
if (wtype == WindowInfoType::SDL)
|
||||
{
|
||||
Uint32 sdl_extension_count = 0;
|
||||
const char* const* sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extension_count);
|
||||
if (!sdl_extensions)
|
||||
{
|
||||
ERROR_LOG("SDL_Vulkan_GetInstanceExtensions() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < sdl_extension_count; i++)
|
||||
{
|
||||
if (!SupportsExtension(sdl_extensions[i], true))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// VK_EXT_debug_utils
|
||||
if (debug_instance && !SupportsExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false))
|
||||
WARNING_LOG("Vulkan: Debug report requested, but extension is not available.");
|
||||
|
||||
// Needed for exclusive fullscreen control.
|
||||
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
|
||||
|
||||
s_locals.optional_extensions.vk_khr_get_surface_capabilities2 =
|
||||
(wtype != WindowInfoType::Surfaceless &&
|
||||
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false));
|
||||
s_locals.optional_extensions.vk_ext_surface_maintenance1 =
|
||||
(wtype != WindowInfoType::Surfaceless && SupportsExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false));
|
||||
s_locals.optional_extensions.vk_khr_get_physical_device_properties2 =
|
||||
SupportsExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
|
||||
|
||||
#define LOG_EXT(name, field) \
|
||||
GENERIC_LOG(___LogChannel___, Log::Level::Info, \
|
||||
s_locals.optional_extensions.field ? Log::Color::StrongGreen : Log::Color::StrongOrange, name " is {}", \
|
||||
s_locals.optional_extensions.field ? "supported" : "NOT supported")
|
||||
|
||||
LOG_EXT("VK_EXT_surface_maintenance1", vk_ext_surface_maintenance1);
|
||||
LOG_EXT("VK_KHR_get_physical_device_properties2", vk_khr_get_physical_device_properties2);
|
||||
LOG_EXT("VK_KHR_get_surface_capabilities2", vk_khr_get_surface_capabilities2);
|
||||
|
||||
#undef LOG_EXT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanLoader::LockedReleaseVulkanInstance()
|
||||
{
|
||||
Assert(s_locals.reference_count > 0);
|
||||
s_locals.reference_count--;
|
||||
|
||||
// We specifically keep the instance around even after releasing it.
|
||||
// Both AMD on Windows and Mesa leak a few tens of megabytes for every instance...
|
||||
DEV_LOG("Released Vulkan instance, reference count {}", s_locals.reference_count);
|
||||
}
|
||||
|
||||
void VulkanLoader::LockedDestroyVulkanInstance()
|
||||
{
|
||||
DebugAssert(s_locals.reference_count == 0);
|
||||
DebugAssert(s_locals.instance != VK_NULL_HANDLE);
|
||||
|
||||
if (s_locals.debug_messenger_callback != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyDebugUtilsMessengerEXT(s_locals.instance, s_locals.debug_messenger_callback, nullptr);
|
||||
s_locals.debug_messenger_callback = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (vkDestroyInstance)
|
||||
vkDestroyInstance(s_locals.instance, nullptr);
|
||||
else
|
||||
ERROR_LOG("Vulkan instance was leaked because vkDestroyInstance() could not be loaded.");
|
||||
|
||||
s_locals.optional_extensions = {};
|
||||
s_locals.instance = VK_NULL_HANDLE;
|
||||
ResetInstanceFunctions();
|
||||
}
|
||||
|
||||
bool VulkanLoader::CreateVulkanInstance(WindowInfoType window_type, bool* request_debug_instance, Error* error)
|
||||
{
|
||||
const std::lock_guard lock(s_locals.mutex);
|
||||
return LockedCreateVulkanInstance(window_type, request_debug_instance, error);
|
||||
}
|
||||
|
||||
VkInstance VulkanLoader::GetVulkanInstance()
|
||||
{
|
||||
// Doesn't need to be locked, but should have an instance.
|
||||
DebugAssert(s_locals.instance != VK_NULL_HANDLE);
|
||||
return s_locals.instance;
|
||||
}
|
||||
|
||||
void VulkanLoader::ReleaseVulkanInstance()
|
||||
{
|
||||
const std::lock_guard lock(s_locals.mutex);
|
||||
LockedReleaseVulkanInstance();
|
||||
}
|
||||
|
||||
const VulkanLoader::OptionalExtensions& VulkanLoader::GetOptionalExtensions()
|
||||
{
|
||||
return s_locals.optional_extensions;
|
||||
}
|
||||
|
||||
VulkanLoader::GPUList VulkanLoader::EnumerateGPUs(Error* error)
|
||||
{
|
||||
GPUList gpus;
|
||||
|
||||
u32 gpu_count = 0;
|
||||
VkResult res = vkEnumeratePhysicalDevices(s_locals.instance, &gpu_count, nullptr);
|
||||
if ((res != VK_SUCCESS && res != VK_INCOMPLETE) || gpu_count == 0)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkEnumeratePhysicalDevices (1) failed: ", res);
|
||||
return gpus;
|
||||
}
|
||||
|
||||
std::vector<VkPhysicalDevice> physical_devices(gpu_count);
|
||||
res = vkEnumeratePhysicalDevices(s_locals.instance, &gpu_count, physical_devices.data());
|
||||
if (res == VK_INCOMPLETE)
|
||||
{
|
||||
WARNING_LOG("First vkEnumeratePhysicalDevices() call returned {} devices, but second returned {}",
|
||||
physical_devices.size(), gpu_count);
|
||||
}
|
||||
else if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkEnumeratePhysicalDevices (2) failed: ", res);
|
||||
return gpus;
|
||||
}
|
||||
|
||||
if (gpu_count == 0)
|
||||
{
|
||||
Error::SetStringView(error, "No Vulkan physical devices available.");
|
||||
return gpus;
|
||||
}
|
||||
|
||||
// Maybe we lost a GPU?
|
||||
if (gpu_count < physical_devices.size())
|
||||
physical_devices.resize(gpu_count);
|
||||
|
||||
gpus.reserve(physical_devices.size());
|
||||
for (VkPhysicalDevice device : physical_devices)
|
||||
{
|
||||
VkPhysicalDeviceProperties2 props = {};
|
||||
VkPhysicalDeviceDriverProperties driver_props = {};
|
||||
|
||||
if (vkGetPhysicalDeviceProperties2)
|
||||
{
|
||||
props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
driver_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
|
||||
Vulkan::AddPointerToChain(&props, &driver_props);
|
||||
vkGetPhysicalDeviceProperties2(device, &props);
|
||||
}
|
||||
|
||||
// just in case the chained version fails
|
||||
vkGetPhysicalDeviceProperties(device, &props.properties);
|
||||
|
||||
VkPhysicalDeviceFeatures available_features = {};
|
||||
vkGetPhysicalDeviceFeatures(device, &available_features);
|
||||
|
||||
GPUDevice::AdapterInfo ai;
|
||||
ai.name = props.properties.deviceName;
|
||||
ai.max_texture_size =
|
||||
std::min(props.properties.limits.maxFramebufferWidth, props.properties.limits.maxImageDimension2D);
|
||||
ai.max_multisamples = Vulkan::GetMaxMultisamples(device, props.properties);
|
||||
ai.driver_type = GuessDriverType(props.properties, driver_props);
|
||||
ai.supports_sample_shading = available_features.sampleRateShading;
|
||||
|
||||
// handle duplicate adapter names
|
||||
if (std::any_of(gpus.begin(), gpus.end(), [&ai](const auto& other) { return (ai.name == other.second.name); }))
|
||||
{
|
||||
std::string original_adapter_name = std::move(ai.name);
|
||||
|
||||
u32 current_extra = 2;
|
||||
do
|
||||
{
|
||||
ai.name = fmt::format("{} ({})", original_adapter_name, current_extra);
|
||||
current_extra++;
|
||||
} while (
|
||||
std::any_of(gpus.begin(), gpus.end(), [&ai](const auto& other) { return (ai.name == other.second.name); }));
|
||||
}
|
||||
|
||||
gpus.emplace_back(device, std::move(ai));
|
||||
}
|
||||
|
||||
return gpus;
|
||||
}
|
||||
|
||||
bool VulkanLoader::IsSuitableDefaultRenderer(WindowInfoType window_type)
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
// No way in hell.
|
||||
return false;
|
||||
#else
|
||||
const std::optional<GPUDevice::AdapterInfoList> adapter_list = GetAdapterList(window_type, nullptr);
|
||||
if (!adapter_list.has_value() || adapter_list->empty())
|
||||
{
|
||||
// No adapters, not gonna be able to use VK.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the first GPU, should be enough.
|
||||
const GPUDevice::AdapterInfo& ainfo = adapter_list->front();
|
||||
INFO_LOG("Using Vulkan GPU '{}' for automatic renderer check.", ainfo.name);
|
||||
|
||||
// Any software rendering (LLVMpipe, SwiftShader).
|
||||
if ((ainfo.driver_type & GPUDriverType::SoftwareFlag) == GPUDriverType::SoftwareFlag)
|
||||
{
|
||||
INFO_LOG("Not using Vulkan for software renderer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
// Intel Ivy Bridge/Haswell/Broadwell drivers are incomplete.
|
||||
if (ainfo.driver_type == GPUDriverType::IntelMesa &&
|
||||
(ainfo.name.find("Ivy Bridge") != std::string::npos || ainfo.name.find("Haswell") != std::string::npos ||
|
||||
ainfo.name.find("Broadwell") != std::string::npos || ainfo.name.find("(IVB") != std::string::npos ||
|
||||
ainfo.name.find("(HSW") != std::string::npos || ainfo.name.find("(BDW") != std::string::npos))
|
||||
{
|
||||
INFO_LOG("Not using Vulkan for Intel GPU with incomplete driver.");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
||||
// V3D is buggy, image copies with larger textures are broken.
|
||||
if (ainfo.driver_type == GPUDriverType::BroadcomMesa)
|
||||
{
|
||||
INFO_LOG("Not using Vulkan for V3D GPU with buggy driver.");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
INFO_LOG("Allowing Vulkan as default renderer.");
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUDriverType VulkanLoader::GuessDriverType(const VkPhysicalDeviceProperties& device_properties,
|
||||
const VkPhysicalDeviceDriverProperties& driver_properties)
|
||||
{
|
||||
static constexpr const std::pair<VkDriverId, GPUDriverType> table[] = {
|
||||
{VK_DRIVER_ID_NVIDIA_PROPRIETARY, GPUDriverType::NVIDIAProprietary},
|
||||
{VK_DRIVER_ID_AMD_PROPRIETARY, GPUDriverType::AMDProprietary},
|
||||
{VK_DRIVER_ID_AMD_OPEN_SOURCE, GPUDriverType::AMDProprietary},
|
||||
{VK_DRIVER_ID_MESA_RADV, GPUDriverType::AMDMesa},
|
||||
{VK_DRIVER_ID_NVIDIA_PROPRIETARY, GPUDriverType::NVIDIAProprietary},
|
||||
{VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, GPUDriverType::IntelProprietary},
|
||||
{VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, GPUDriverType::IntelMesa},
|
||||
{VK_DRIVER_ID_IMAGINATION_PROPRIETARY, GPUDriverType::ImaginationProprietary},
|
||||
{VK_DRIVER_ID_QUALCOMM_PROPRIETARY, GPUDriverType::QualcommProprietary},
|
||||
{VK_DRIVER_ID_ARM_PROPRIETARY, GPUDriverType::ARMProprietary},
|
||||
{VK_DRIVER_ID_GOOGLE_SWIFTSHADER, GPUDriverType::SwiftShader},
|
||||
{VK_DRIVER_ID_GGP_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_BROADCOM_PROPRIETARY, GPUDriverType::BroadcomProprietary},
|
||||
{VK_DRIVER_ID_MESA_LLVMPIPE, GPUDriverType::LLVMPipe},
|
||||
{VK_DRIVER_ID_MOLTENVK, GPUDriverType::AppleProprietary},
|
||||
{VK_DRIVER_ID_COREAVI_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_JUICE_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_VERISILICON_PROPRIETARY, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_MESA_TURNIP, GPUDriverType::QualcommMesa},
|
||||
{VK_DRIVER_ID_MESA_V3DV, GPUDriverType::BroadcomMesa},
|
||||
{VK_DRIVER_ID_MESA_PANVK, GPUDriverType::ARMMesa},
|
||||
{VK_DRIVER_ID_SAMSUNG_PROPRIETARY, GPUDriverType::AMDProprietary},
|
||||
{VK_DRIVER_ID_MESA_VENUS, GPUDriverType::Unknown},
|
||||
{VK_DRIVER_ID_MESA_DOZEN, GPUDriverType::DozenMesa},
|
||||
{VK_DRIVER_ID_MESA_NVK, GPUDriverType::NVIDIAMesa},
|
||||
{VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA, GPUDriverType::ImaginationMesa},
|
||||
{VK_DRIVER_ID_MESA_AGXV, GPUDriverType::AppleMesa},
|
||||
};
|
||||
|
||||
const auto iter = std::find_if(std::begin(table), std::end(table), [&driver_properties](const auto& it) {
|
||||
return (driver_properties.driverID == it.first);
|
||||
});
|
||||
if (iter != std::end(table))
|
||||
return iter->second;
|
||||
|
||||
return GPUDevice::GuessDriverType(
|
||||
device_properties.vendorID, {},
|
||||
std::string_view(device_properties.deviceName,
|
||||
StringUtil::Strnlen(device_properties.deviceName, std::size(device_properties.deviceName))));
|
||||
}
|
||||
|
||||
std::optional<GPUDevice::AdapterInfoList> VulkanLoader::GetAdapterList(WindowInfoType window_type, Error* error)
|
||||
{
|
||||
std::optional<GPUDevice::AdapterInfoList> ret;
|
||||
GPUList gpus;
|
||||
{
|
||||
const std::lock_guard lock(s_locals.mutex);
|
||||
|
||||
// Prefer re-using the instance if we can to avoid expensive loading.
|
||||
if (s_locals.instance != VK_NULL_HANDLE)
|
||||
{
|
||||
gpus = EnumerateGPUs(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we need to create a temporary instance.
|
||||
// Hold the lock for both creation and querying, otherwise the UI thread could race creation.
|
||||
if (!LockedCreateVulkanInstance(window_type, nullptr, error))
|
||||
return ret;
|
||||
|
||||
gpus = EnumerateGPUs(error);
|
||||
|
||||
LockedReleaseVulkanInstance();
|
||||
}
|
||||
}
|
||||
|
||||
ret.emplace();
|
||||
ret->reserve(gpus.size());
|
||||
for (auto& [physical_device, adapter_info] : gpus)
|
||||
ret->push_back(std::move(adapter_info));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
VkBool32 VulkanLoader::DebugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||
void* pUserData)
|
||||
{
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
|
||||
{
|
||||
ERROR_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
else if (severity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT))
|
||||
{
|
||||
WARNING_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
|
||||
{
|
||||
INFO_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEV_LOG("Vulkan debug report: ({}) {}", pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "",
|
||||
pCallbackData->pMessage);
|
||||
}
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
@@ -1,74 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#pragma once
|
||||
|
||||
class Error;
|
||||
#include "gpu_device.h"
|
||||
#include "vulkan_headers.h"
|
||||
#include "window_info.h"
|
||||
|
||||
#define VK_NO_PROTOTYPES
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// vulkan.h pulls in windows.h on Windows, so we need to include our replacement header first
|
||||
#include "common/windows_headers.h"
|
||||
#elif defined(__APPLE__)
|
||||
#define VK_USE_PLATFORM_METAL_EXT
|
||||
#elif defined(__ANDROID__)
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#else
|
||||
#ifdef ENABLE_X11
|
||||
#define VK_USE_PLATFORM_XCB_KHR
|
||||
#endif
|
||||
namespace VulkanLoader {
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#endif
|
||||
#endif
|
||||
/// @brief List of Vulkan-compatible GPUs and associated adapter information.
|
||||
using GPUList = std::vector<std::pair<VkPhysicalDevice, GPUDevice::AdapterInfo>>;
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
/// @brief Optional extensions for an instance.
|
||||
struct OptionalExtensions
|
||||
{
|
||||
bool vk_ext_surface_maintenance1 : 1;
|
||||
bool vk_khr_get_surface_capabilities2 : 1;
|
||||
bool vk_khr_get_physical_device_properties2 : 1;
|
||||
};
|
||||
|
||||
#include "vulkan_entry_points.h"
|
||||
/// Creates the shared Vulkan instance. If debug_instance is changed, the instance will be recreated.
|
||||
/// @param window_type Window type for selecting required extensions.
|
||||
/// @param request_debug_instance Set to true if a debug instance is requested. May be modified to reflect actual state.
|
||||
/// @param error Error information if the instance could not be created.
|
||||
/// @return The Vulkan instance, or VK_NULL_HANDLE on failure.
|
||||
bool CreateVulkanInstance(WindowInfoType window_type, bool* request_debug_instance, Error* error);
|
||||
|
||||
// We include vk_mem_alloc globally, so we don't accidentally include it before the vulkan header somewhere.
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnullability-completeness"
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
/// Returns the shared Vulkan instance.
|
||||
VkInstance GetVulkanInstance();
|
||||
|
||||
#define VMA_STATIC_VULKAN_FUNCTIONS 1
|
||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
|
||||
#define VMA_STATS_STRING_ENABLED 0
|
||||
#include "vulkan/vk_mem_alloc.h"
|
||||
/// Releases the shared Vulkan instance.
|
||||
void ReleaseVulkanInstance();
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
/// Returns optional extensions for the current instance.
|
||||
const OptionalExtensions& GetOptionalExtensions();
|
||||
|
||||
namespace Vulkan {
|
||||
/// Enumerates Vulkan devices.
|
||||
GPUList EnumerateGPUs(Error* error);
|
||||
|
||||
bool IsVulkanLibraryLoaded();
|
||||
bool LoadVulkanLibrary(Error* error);
|
||||
bool LoadVulkanInstanceFunctions(VkInstance instance);
|
||||
bool LoadVulkanDeviceFunctions(VkDevice device);
|
||||
void UnloadVulkanLibrary();
|
||||
void ResetVulkanLibraryFunctionPointers();
|
||||
/// Safely creates the instance and returns a list of adapters and associated information.
|
||||
std::optional<GPUDevice::AdapterInfoList> GetAdapterList(WindowInfoType window_type, Error* error);
|
||||
|
||||
#ifdef ENABLE_SDL
|
||||
bool LoadVulkanLibraryFromSDL(Error* error);
|
||||
#endif
|
||||
/// Returns true if Vulkan is suitable as a default for the devices in the system.
|
||||
bool IsSuitableDefaultRenderer(WindowInfoType window_type);
|
||||
|
||||
} // namespace Vulkan
|
||||
/// Loads Vulkan device-level functions for the given device.
|
||||
/// @param device The Vulkan device to load functions for.
|
||||
bool LoadDeviceFunctions(VkDevice device, Error* error);
|
||||
|
||||
/// Releases Vulkan device-level functions.
|
||||
void ResetDeviceFunctions();
|
||||
|
||||
/// @brief Guesses the GPU driver type based on device and driver properties.
|
||||
GPUDriverType GuessDriverType(const VkPhysicalDeviceProperties& device_properties,
|
||||
const VkPhysicalDeviceDriverProperties& driver_properties);
|
||||
|
||||
} // namespace VulkanLoader
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "gpu_device.h"
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_headers.h"
|
||||
|
||||
class VulkanDevice;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_headers.h"
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "vulkan_swap_chain.h"
|
||||
#include "vulkan_builders.h"
|
||||
#include "vulkan_device.h"
|
||||
#include "vulkan_loader.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
@@ -82,7 +83,7 @@ VulkanSwapChain::~VulkanSwapChain()
|
||||
Destroy(VulkanDevice::GetInstance(), true);
|
||||
}
|
||||
|
||||
bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physical_device, Error* error)
|
||||
bool VulkanSwapChain::CreateSurface(VkPhysicalDevice physical_device, Error* error)
|
||||
{
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
if (m_window_info.type == WindowInfoType::Win32)
|
||||
@@ -92,7 +93,8 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
||||
.flags = 0,
|
||||
.hinstance = NULL,
|
||||
.hwnd = static_cast<HWND>(m_window_info.window_handle)};
|
||||
const VkResult res = vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||
const VkResult res =
|
||||
vkCreateWin32SurfaceKHR(VulkanLoader::GetVulkanInstance(), &surface_create_info, nullptr, &m_surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkCreateWin32SurfaceKHR() failed: ", res);
|
||||
@@ -114,7 +116,8 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.pLayer = static_cast<const CAMetalLayer*>(m_metal_layer)};
|
||||
const VkResult res = vkCreateMetalSurfaceEXT(instance, &surface_create_info, nullptr, &m_surface);
|
||||
const VkResult res =
|
||||
vkCreateMetalSurfaceEXT(VulkanLoader::GetVulkanInstance(), &surface_create_info, nullptr, &m_surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkCreateMetalSurfaceEXT failed: ", res);
|
||||
@@ -133,7 +136,8 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.window = static_cast<ANativeWindow*>(m_window_info.window_handle)};
|
||||
const VkResult res = vkCreateAndroidSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||
const VkResult res =
|
||||
vkCreateAndroidSurfaceKHR(VulkanLoader::GetVulkanInstance(), &surface_create_info, nullptr, &m_surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkCreateAndroidSurfaceKHR failed: ", res);
|
||||
@@ -153,7 +157,8 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
||||
.flags = 0,
|
||||
.connection = static_cast<xcb_connection_t*>(m_window_info.display_connection),
|
||||
.window = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(m_window_info.window_handle))};
|
||||
const VkResult res = vkCreateXcbSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||
const VkResult res =
|
||||
vkCreateXcbSurfaceKHR(VulkanLoader::GetVulkanInstance(), &surface_create_info, nullptr, &m_surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkCreateXcbSurfaceKHR failed: ", res);
|
||||
@@ -173,7 +178,8 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
||||
.flags = 0,
|
||||
.display = static_cast<struct wl_display*>(m_window_info.display_connection),
|
||||
.surface = static_cast<struct wl_surface*>(m_window_info.window_handle)};
|
||||
VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||
VkResult res =
|
||||
vkCreateWaylandSurfaceKHR(VulkanLoader::GetVulkanInstance(), &surface_create_info, nullptr, &m_surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
Vulkan::SetErrorObject(error, "vkCreateWaylandSurfaceEXT failed: ", res);
|
||||
@@ -187,7 +193,8 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
||||
#if defined(ENABLE_SDL)
|
||||
if (m_window_info.type == WindowInfoType::SDL)
|
||||
{
|
||||
if (!SDL_Vulkan_CreateSurface(static_cast<SDL_Window*>(m_window_info.window_handle), instance, nullptr, &m_surface))
|
||||
if (!SDL_Vulkan_CreateSurface(static_cast<SDL_Window*>(m_window_info.window_handle),
|
||||
VulkanLoader::GetVulkanInstance(), nullptr, &m_surface))
|
||||
{
|
||||
Error::SetStringFmt(error, "SDL_Vulkan_CreateSurface() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
@@ -205,7 +212,7 @@ void VulkanSwapChain::DestroySurface()
|
||||
{
|
||||
if (m_surface != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySurfaceKHR(VulkanDevice::GetInstance().GetVulkanInstance(), m_surface, nullptr);
|
||||
vkDestroySurfaceKHR(VulkanLoader::GetVulkanInstance(), m_surface, nullptr);
|
||||
m_surface = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
@@ -349,8 +356,8 @@ bool VulkanSwapChain::CreateSwapChain(VulkanDevice& dev, Error* error)
|
||||
VkResult res = VK_NOT_READY;
|
||||
|
||||
// The present mode can alter the number of images required. Use VK_KHR_get_surface_capabilities2 to confirm it.
|
||||
if (dev.GetOptionalExtensions().vk_khr_get_surface_capabilities2 &&
|
||||
dev.GetOptionalExtensions().vk_ext_surface_maintenance1)
|
||||
const VulkanLoader::OptionalExtensions& optional_extensions = VulkanLoader::GetOptionalExtensions();
|
||||
if (optional_extensions.vk_khr_get_surface_capabilities2 && optional_extensions.vk_ext_surface_maintenance1)
|
||||
{
|
||||
VkPhysicalDeviceSurfaceInfo2KHR dsi = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, .pNext = nullptr, .surface = m_surface};
|
||||
@@ -858,7 +865,7 @@ bool VulkanSwapChain::RecreateSurface(VulkanDevice& dev, Error* error)
|
||||
DestroySurface();
|
||||
|
||||
// Re-create the surface with the new native handle
|
||||
if (!CreateSurface(dev.GetVulkanInstance(), dev.GetVulkanPhysicalDevice(), error))
|
||||
if (!CreateSurface(dev.GetVulkanPhysicalDevice(), error))
|
||||
return false;
|
||||
|
||||
// The validation layers get angry at us if we don't call this before creating the swapchain.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "gpu_device.h"
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_headers.h"
|
||||
#include "vulkan_texture.h"
|
||||
#include "window_info.h"
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
return &m_images[m_current_image].present_semaphore;
|
||||
}
|
||||
|
||||
bool CreateSurface(VkInstance instance, VkPhysicalDevice physical_device, Error* error);
|
||||
bool CreateSurface(VkPhysicalDevice physical_device, Error* error);
|
||||
bool CreateSwapChain(VulkanDevice& dev, Error* error);
|
||||
bool CreateSwapChainImages(VulkanDevice& dev, Error* error);
|
||||
void Destroy(VulkanDevice& dev, bool wait_for_idle);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "gpu_device.h"
|
||||
#include "gpu_texture.h"
|
||||
#include "vulkan_loader.h"
|
||||
#include "vulkan_headers.h"
|
||||
#include "vulkan_stream_buffer.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
Reference in New Issue
Block a user