mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-13 16:55:46 -04:00
libretro: Vulkan renderer support
This commit is contained in:
@ -19,8 +19,8 @@ std::unique_ptr<Vulkan::Context> g_vulkan_context;
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
Context::Context(VkInstance instance, VkPhysicalDevice physical_device)
|
||||
: m_instance(instance), m_physical_device(physical_device)
|
||||
Context::Context(VkInstance instance, VkPhysicalDevice physical_device, bool owns_device)
|
||||
: m_instance(instance), m_physical_device(physical_device), m_owns_device(owns_device)
|
||||
{
|
||||
// Read device physical memory properties, we need it for allocating buffers
|
||||
vkGetPhysicalDeviceProperties(physical_device, &m_device_properties);
|
||||
@ -44,13 +44,17 @@ Context::~Context()
|
||||
DestroyGlobalDescriptorPool();
|
||||
DestroyCommandBuffers();
|
||||
|
||||
if (m_device != VK_NULL_HANDLE)
|
||||
if (m_owns_device && m_device != VK_NULL_HANDLE)
|
||||
vkDestroyDevice(m_device, nullptr);
|
||||
|
||||
if (m_debug_report_callback != VK_NULL_HANDLE)
|
||||
DisableDebugReports();
|
||||
|
||||
vkDestroyInstance(m_instance, nullptr);
|
||||
if (m_owns_device)
|
||||
{
|
||||
vkDestroyInstance(m_instance, nullptr);
|
||||
Vulkan::UnloadVulkanLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
bool Context::CheckValidationLayerAvailablility()
|
||||
@ -344,14 +348,14 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu
|
||||
return false;
|
||||
}
|
||||
|
||||
g_vulkan_context.reset(new Context(instance, gpus[gpu_index]));
|
||||
g_vulkan_context.reset(new Context(instance, gpus[gpu_index], true));
|
||||
|
||||
// Enable debug reports if the "Host GPU" log category is enabled.
|
||||
if (enable_debug_reports)
|
||||
g_vulkan_context->EnableDebugReports();
|
||||
|
||||
// Attempt to create the device.
|
||||
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer) ||
|
||||
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer, nullptr, 0, nullptr, 0, nullptr) ||
|
||||
!g_vulkan_context->CreateGlobalDescriptorPool() || !g_vulkan_context->CreateCommandBuffers() ||
|
||||
(enable_surface && (*out_swap_chain = SwapChain::Create(wi_copy, surface, true)) == nullptr))
|
||||
{
|
||||
@ -359,7 +363,33 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
vkDestroySurfaceKHR(instance, surface, nullptr);
|
||||
|
||||
Vulkan::UnloadVulkanLibrary();
|
||||
g_vulkan_context.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Context::CreateFromExistingInstance(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface,
|
||||
bool take_ownership, bool enable_validation_layer, bool enable_debug_reports,
|
||||
const char** required_device_extensions /* = nullptr */,
|
||||
u32 num_required_device_extensions /* = 0 */,
|
||||
const char** required_device_layers /* = nullptr */,
|
||||
u32 num_required_device_layers /* = 0 */,
|
||||
const VkPhysicalDeviceFeatures* required_features /* = nullptr */)
|
||||
{
|
||||
g_vulkan_context.reset(new Context(instance, gpu, take_ownership));
|
||||
|
||||
// Enable debug reports if the "Host GPU" log category is enabled.
|
||||
if (enable_debug_reports)
|
||||
g_vulkan_context->EnableDebugReports();
|
||||
|
||||
// Attempt to create the device.
|
||||
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer, required_device_extensions,
|
||||
num_required_device_extensions, required_device_layers,
|
||||
num_required_device_layers, required_features) ||
|
||||
!g_vulkan_context->CreateGlobalDescriptorPool() || !g_vulkan_context->CreateCommandBuffers())
|
||||
{
|
||||
g_vulkan_context.reset();
|
||||
return false;
|
||||
}
|
||||
@ -403,8 +433,13 @@ bool Context::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_
|
||||
return !strcmp(name, properties.extensionName);
|
||||
}) != available_extension_list.end())
|
||||
{
|
||||
Log_InfoPrintf("Enabling extension: %s", name);
|
||||
extension_list->push_back(name);
|
||||
if (std::none_of(extension_list->begin(), extension_list->end(),
|
||||
[&](const char* existing_name) { return (std::strcmp(existing_name, name) == 0); }))
|
||||
{
|
||||
Log_InfoPrintf("Enabling extension: %s", name);
|
||||
extension_list->push_back(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -420,7 +455,7 @@ bool Context::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Context::SelectDeviceFeatures()
|
||||
bool Context::SelectDeviceFeatures(const VkPhysicalDeviceFeatures* required_features)
|
||||
{
|
||||
VkPhysicalDeviceFeatures available_features;
|
||||
vkGetPhysicalDeviceFeatures(m_physical_device, &available_features);
|
||||
@ -431,6 +466,9 @@ bool Context::SelectDeviceFeatures()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (required_features)
|
||||
std::memcpy(&m_device_features, required_features, sizeof(m_device_features));
|
||||
|
||||
// Enable the features we use.
|
||||
m_device_features.dualSrcBlend = available_features.dualSrcBlend;
|
||||
m_device_features.geometryShader = available_features.geometryShader;
|
||||
@ -438,7 +476,9 @@ bool Context::SelectDeviceFeatures()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Context::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
|
||||
bool Context::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, const char** required_device_extensions,
|
||||
u32 num_required_device_extensions, const char** required_device_layers,
|
||||
u32 num_required_device_layers, const VkPhysicalDeviceFeatures* required_features)
|
||||
{
|
||||
u32 queue_family_count;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, nullptr);
|
||||
@ -536,16 +576,18 @@ bool Context::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
|
||||
device_info.pQueueCreateInfos = queue_infos.data();
|
||||
|
||||
ExtensionList enabled_extensions;
|
||||
for (u32 i = 0; i < num_required_device_extensions; i++)
|
||||
enabled_extensions.emplace_back(required_device_extensions[i]);
|
||||
if (!SelectDeviceExtensions(&enabled_extensions, surface != VK_NULL_HANDLE))
|
||||
return false;
|
||||
|
||||
device_info.enabledLayerCount = 0;
|
||||
device_info.ppEnabledLayerNames = nullptr;
|
||||
device_info.enabledLayerCount = num_required_device_layers;
|
||||
device_info.ppEnabledLayerNames = required_device_layers;
|
||||
device_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
device_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
|
||||
// Check for required features before creating.
|
||||
if (!SelectDeviceFeatures())
|
||||
if (!SelectDeviceFeatures(required_features))
|
||||
return false;
|
||||
|
||||
device_info.pEnabledFeatures = &m_device_features;
|
||||
|
@ -46,6 +46,15 @@ public:
|
||||
static bool Create(std::string_view gpu_name, const WindowInfo* wi, std::unique_ptr<SwapChain>* out_swap_chain,
|
||||
bool enable_debug_reports, bool enable_validation_layer);
|
||||
|
||||
// Creates a new context from a pre-existing instance.
|
||||
static bool CreateFromExistingInstance(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface,
|
||||
bool take_ownership, bool enable_validation_layer, bool enable_debug_reports,
|
||||
const char** required_device_extensions = nullptr,
|
||||
u32 num_required_device_extensions = 0,
|
||||
const char** required_device_layers = nullptr,
|
||||
u32 num_required_device_layers = 0,
|
||||
const VkPhysicalDeviceFeatures* required_features = nullptr);
|
||||
|
||||
// Destroys context.
|
||||
static void Destroy();
|
||||
|
||||
@ -162,13 +171,15 @@ public:
|
||||
void WaitForGPUIdle();
|
||||
|
||||
private:
|
||||
Context(VkInstance instance, VkPhysicalDevice physical_device);
|
||||
Context(VkInstance instance, VkPhysicalDevice physical_device, bool owns_device);
|
||||
|
||||
using ExtensionList = std::vector<const char*>;
|
||||
static bool SelectInstanceExtensions(ExtensionList* extension_list, bool enable_surface, bool enable_debug_report);
|
||||
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
||||
bool SelectDeviceFeatures();
|
||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
||||
bool SelectDeviceFeatures(const VkPhysicalDeviceFeatures* required_features);
|
||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, const char** required_device_extensions,
|
||||
u32 num_required_device_extensions, const char** required_device_layers,
|
||||
u32 num_required_device_layers, const VkPhysicalDeviceFeatures* required_features);
|
||||
|
||||
bool CreateCommandBuffers();
|
||||
void DestroyCommandBuffers();
|
||||
@ -210,6 +221,7 @@ private:
|
||||
u64 m_completed_fence_counter = 0;
|
||||
u32 m_current_frame;
|
||||
|
||||
bool m_owns_device = false;
|
||||
bool m_last_present_failed = false;
|
||||
|
||||
// Render pass cache
|
||||
|
@ -232,6 +232,15 @@ void SafeDestroySampler(VkSampler& samp)
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroySemaphore(VkSemaphore& sem)
|
||||
{
|
||||
if (sem != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySemaphore(g_vulkan_context->GetDevice(), sem, nullptr);
|
||||
sem = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeFreeGlobalDescriptorSet(VkDescriptorSet& ds)
|
||||
{
|
||||
if (ds != VK_NULL_HANDLE)
|
||||
|
@ -41,6 +41,7 @@ void SafeDestroyPipelineLayout(VkPipelineLayout& pl);
|
||||
void SafeDestroyDescriptorSetLayout(VkDescriptorSetLayout& dsl);
|
||||
void SafeDestroyBufferView(VkBufferView& bv);
|
||||
void SafeDestroySampler(VkSampler& samp);
|
||||
void SafeDestroySemaphore(VkSemaphore& sem);
|
||||
void SafeFreeGlobalDescriptorSet(VkDescriptorSet& ds);
|
||||
|
||||
void SetViewport(VkCommandBuffer command_buffer, int x, int y, int width, int height, float min_depth = 0.0f,
|
||||
|
@ -9,6 +9,8 @@ add_library(duckstation-libretro SHARED
|
||||
libretro_opengl_host_display.h
|
||||
libretro_settings_interface.cpp
|
||||
libretro_settings_interface.h
|
||||
libretro_vulkan_host_display.cpp
|
||||
libretro_vulkan_host_display.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
@ -19,5 +21,5 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(duckstation-libretro PRIVATE core common imgui glad scmversion frontend-common libretro-common)
|
||||
target_link_libraries(duckstation-libretro PRIVATE core common imgui glad scmversion frontend-common vulkan-loader libretro-common)
|
||||
|
||||
|
@ -35,6 +35,9 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\dep\vulkan-loader\vulkan-loader.vcxproj">
|
||||
<Project>{9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||
</ProjectReference>
|
||||
@ -54,6 +57,7 @@
|
||||
<ClCompile Include="libretro_host_display.cpp" />
|
||||
<ClCompile Include="libretro_host_interface.cpp" />
|
||||
<ClCompile Include="libretro_settings_interface.cpp" />
|
||||
<ClCompile Include="libretro_vulkan_host_display.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="libretro_opengl_host_display.cpp" />
|
||||
</ItemGroup>
|
||||
@ -64,6 +68,7 @@
|
||||
<ClInclude Include="libretro_host_interface.h" />
|
||||
<ClInclude Include="libretro_settings_interface.h" />
|
||||
<ClInclude Include="libretro_opengl_host_display.h" />
|
||||
<ClInclude Include="libretro_vulkan_host_display.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}</ProjectGuid>
|
||||
@ -211,7 +216,7 @@
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -232,7 +237,7 @@
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -253,7 +258,7 @@
|
||||
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@ -277,7 +282,7 @@
|
||||
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@ -300,7 +305,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -323,7 +328,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -347,7 +352,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -370,7 +375,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\libretro-common\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -8,6 +8,7 @@
|
||||
<ClCompile Include="libretro_settings_interface.cpp" />
|
||||
<ClCompile Include="libretro_opengl_host_display.cpp" />
|
||||
<ClCompile Include="libretro_d3d11_host_display.cpp" />
|
||||
<ClCompile Include="libretro_vulkan_host_display.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="libretro_host_interface.h" />
|
||||
@ -16,5 +17,6 @@
|
||||
<ClInclude Include="libretro_settings_interface.h" />
|
||||
<ClInclude Include="libretro_opengl_host_display.h" />
|
||||
<ClInclude Include="libretro_d3d11_host_display.h" />
|
||||
<ClInclude Include="libretro_vulkan_host_display.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -4,7 +4,7 @@
|
||||
#include "common/d3d11/shader_compiler.h"
|
||||
#include "common/log.h"
|
||||
#include "libretro_host_interface.h"
|
||||
Log_SetChannel(D3D11HostDisplay);
|
||||
Log_SetChannel(LibretroD3D11HostDisplay);
|
||||
|
||||
#define HAVE_D3D11
|
||||
#include "libretro_d3d.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "libretro_host_display.h"
|
||||
#include "libretro_opengl_host_display.h"
|
||||
#include "libretro_settings_interface.h"
|
||||
#include "libretro_vulkan_host_display.h"
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <tuple>
|
||||
@ -344,6 +345,7 @@ static std::array<retro_core_option_definition, 22> s_option_definitions = {{
|
||||
{"D3D11", "Hardware (D3D11)"},
|
||||
#endif
|
||||
{"OpenGL", "Hardware (OpenGL)"},
|
||||
{"Vulkan", "Hardware (Vulkan)"},
|
||||
{"Software", "Software"}},
|
||||
#ifdef WIN32
|
||||
"D3D11"
|
||||
@ -658,16 +660,14 @@ static std::optional<GPURenderer> RetroHwContextToRenderer(retro_hw_context_type
|
||||
case RETRO_HW_CONTEXT_OPENGLES_VERSION:
|
||||
return GPURenderer::HardwareOpenGL;
|
||||
|
||||
case RETRO_HW_CONTEXT_VULKAN:
|
||||
return GPURenderer::HardwareVulkan;
|
||||
|
||||
#ifdef WIN32
|
||||
case RETRO_HW_CONTEXT_DIRECT3D:
|
||||
return GPURenderer::HardwareD3D11;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
case RETRO_HW_CONTEXT_VULKAN:
|
||||
return GPURenderer::HardwareVulkan;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -720,6 +720,10 @@ bool LibretroHostInterface::RequestHardwareRendererContext()
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GPURenderer::HardwareVulkan:
|
||||
m_hw_render_callback_valid = LibretroVulkanHostDisplay::RequestHardwareRendererContext(&m_hw_render_callback);
|
||||
break;
|
||||
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
m_hw_render_callback_valid = LibretroOpenGLHostDisplay::RequestHardwareRendererContext(&m_hw_render_callback);
|
||||
break;
|
||||
@ -758,6 +762,10 @@ void LibretroHostInterface::SwitchToHardwareRenderer()
|
||||
display = std::make_unique<LibretroOpenGLHostDisplay>();
|
||||
break;
|
||||
|
||||
case GPURenderer::HardwareVulkan:
|
||||
display = std::make_unique<LibretroVulkanHostDisplay>();
|
||||
break;
|
||||
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
display = std::make_unique<LibretroD3D11HostDisplay>();
|
||||
|
261
src/duckstation-libretro/libretro_vulkan_host_display.cpp
Normal file
261
src/duckstation-libretro/libretro_vulkan_host_display.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
#include "libretro_vulkan_host_display.h"
|
||||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/vulkan/builders.h"
|
||||
#include "common/vulkan/context.h"
|
||||
#include "common/vulkan/shader_cache.h"
|
||||
#include "common/vulkan/util.h"
|
||||
#include "libretro_host_interface.h"
|
||||
#include "vulkan_loader.h"
|
||||
Log_SetChannel(LibretroVulkanHostDisplay);
|
||||
|
||||
LibretroVulkanHostDisplay::LibretroVulkanHostDisplay() = default;
|
||||
|
||||
LibretroVulkanHostDisplay::~LibretroVulkanHostDisplay() = default;
|
||||
|
||||
void LibretroVulkanHostDisplay::SetVSync(bool enabled)
|
||||
{
|
||||
// The libretro frontend controls this.
|
||||
Log_DevPrintf("Ignoring SetVSync(%u)", BoolToUInt32(enabled));
|
||||
}
|
||||
|
||||
static bool LoadModuleFunctions(VkInstance instance, PFN_vkGetInstanceProcAddr get_instance_proc_addr)
|
||||
{
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) \
|
||||
if (!name && (name = reinterpret_cast<decltype(name)>(get_instance_proc_addr(instance, #name))) == nullptr) \
|
||||
{ \
|
||||
Log_ErrorPrintf("Could not get function pointer for '%s'", #name); \
|
||||
return false; \
|
||||
}
|
||||
#include "vulkan_entry_points.inl"
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool RetroCreateVulkanDevice(struct retro_vulkan_context* context, VkInstance instance, VkPhysicalDevice gpu,
|
||||
VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
||||
const char** required_device_extensions, unsigned num_required_device_extensions,
|
||||
const char** required_device_layers, unsigned num_required_device_layers,
|
||||
const VkPhysicalDeviceFeatures* required_features)
|
||||
{
|
||||
// We need some module functions.
|
||||
vkGetInstanceProcAddr = get_instance_proc_addr;
|
||||
if (!LoadModuleFunctions(instance, get_instance_proc_addr))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to load Vulkan module functions");
|
||||
Vulkan::ResetVulkanLibraryFunctionPointers();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Vulkan::LoadVulkanInstanceFunctions(instance))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to load Vulkan instance functions");
|
||||
Vulkan::ResetVulkanLibraryFunctionPointers();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gpu == VK_NULL_HANDLE)
|
||||
{
|
||||
Vulkan::Context::GPUList gpus = Vulkan::Context::EnumerateGPUs(instance);
|
||||
if (gpus.empty())
|
||||
{
|
||||
g_libretro_host_interface.ReportError("No GPU provided and none available, cannot create device");
|
||||
Vulkan::ResetVulkanLibraryFunctionPointers();
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_InfoPrintf("No GPU provided, using first/default");
|
||||
gpu = gpus[0];
|
||||
}
|
||||
|
||||
if (!Vulkan::Context::CreateFromExistingInstance(
|
||||
instance, gpu, surface, false, false, false, required_device_extensions, num_required_device_extensions,
|
||||
required_device_layers, num_required_device_layers, required_features))
|
||||
{
|
||||
Vulkan::ResetVulkanLibraryFunctionPointers();
|
||||
return false;
|
||||
}
|
||||
|
||||
context->gpu = g_vulkan_context->GetPhysicalDevice();
|
||||
context->device = g_vulkan_context->GetDevice();
|
||||
context->queue = g_vulkan_context->GetGraphicsQueue();
|
||||
context->queue_family_index = g_vulkan_context->GetGraphicsQueueFamilyIndex();
|
||||
context->presentation_queue = g_vulkan_context->GetPresentQueue();
|
||||
context->presentation_queue_family_index = g_vulkan_context->GetPresentQueueFamilyIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
static retro_hw_render_context_negotiation_interface_vulkan s_vulkan_context_negotiation_interface = {
|
||||
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN, // interface_type
|
||||
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION, // interface_version
|
||||
nullptr, // get_application_info
|
||||
RetroCreateVulkanDevice, // create_device
|
||||
nullptr // destroy_device
|
||||
};
|
||||
|
||||
bool LibretroVulkanHostDisplay::RequestHardwareRendererContext(retro_hw_render_callback* cb)
|
||||
{
|
||||
cb->cache_context = true;
|
||||
cb->bottom_left_origin = false;
|
||||
cb->context_type = RETRO_HW_CONTEXT_VULKAN;
|
||||
return g_retro_environment_callback(RETRO_ENVIRONMENT_SET_HW_RENDER, cb) &&
|
||||
g_retro_environment_callback(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE,
|
||||
&s_vulkan_context_negotiation_interface);
|
||||
}
|
||||
|
||||
bool LibretroVulkanHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name,
|
||||
bool debug_device)
|
||||
{
|
||||
retro_hw_render_interface* ri = nullptr;
|
||||
if (!g_retro_environment_callback(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, &ri))
|
||||
{
|
||||
Log_ErrorPrint("Failed to get HW render interface");
|
||||
return false;
|
||||
}
|
||||
else if (ri->interface_type != RETRO_HW_RENDER_INTERFACE_VULKAN ||
|
||||
ri->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION)
|
||||
{
|
||||
Log_ErrorPrintf("Unexpected HW interface - type %u version %u", static_cast<unsigned>(ri->interface_type),
|
||||
static_cast<unsigned>(ri->interface_version));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_vulkan_context)
|
||||
{
|
||||
Log_ErrorPrintf("Vulkan context was not negotiated/created");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Grab queue? it should be the same
|
||||
m_ri = reinterpret_cast<const retro_hw_render_interface_vulkan*>(ri);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibretroVulkanHostDisplay::DestroyRenderDevice()
|
||||
{
|
||||
VulkanHostDisplay::DestroyRenderDevice();
|
||||
Vulkan::ResetVulkanLibraryFunctionPointers();
|
||||
}
|
||||
|
||||
bool LibretroVulkanHostDisplay::CreateResources()
|
||||
{
|
||||
m_frame_render_pass = g_vulkan_context->GetRenderPass(FRAMEBUFFER_FORMAT, VK_FORMAT_UNDEFINED, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR);
|
||||
if (m_frame_render_pass == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
return VulkanHostDisplay::CreateResources();
|
||||
}
|
||||
|
||||
void LibretroVulkanHostDisplay::DestroyResources()
|
||||
{
|
||||
VulkanHostDisplay::DestroyResources();
|
||||
Vulkan::Util::SafeDestroyFramebuffer(m_frame_framebuffer);
|
||||
m_frame_texture.Destroy();
|
||||
}
|
||||
|
||||
VkRenderPass LibretroVulkanHostDisplay::GetRenderPassForDisplay() const
|
||||
{
|
||||
return m_frame_render_pass;
|
||||
}
|
||||
|
||||
void LibretroVulkanHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
m_window_info.surface_width = static_cast<u32>(new_window_width);
|
||||
m_window_info.surface_height = static_cast<u32>(new_window_height);
|
||||
}
|
||||
|
||||
bool LibretroVulkanHostDisplay::Render()
|
||||
{
|
||||
const u32 resolution_scale = g_libretro_host_interface.GetResolutionScale();
|
||||
const u32 display_width = static_cast<u32>(m_display_width) * resolution_scale;
|
||||
const u32 display_height = static_cast<u32>(m_display_height) * resolution_scale;
|
||||
if (!CheckFramebufferSize(display_width, display_height))
|
||||
return false;
|
||||
|
||||
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
m_frame_texture.OverrideImageLayout(m_frame_view.image_layout);
|
||||
m_frame_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
const VkClearValue clear_value = {};
|
||||
const VkRenderPassBeginInfo rp = {
|
||||
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, m_frame_render_pass, m_frame_framebuffer,
|
||||
{{0, 0}, {display_width, display_height}}, 1u, &clear_value};
|
||||
vkCmdBeginRenderPass(cmdbuffer, &rp, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
if (HasDisplayTexture())
|
||||
{
|
||||
const auto [left, top, width, height] = CalculateDrawRect(display_width, display_height, 0);
|
||||
RenderDisplay(left, top, width, height, m_display_texture_handle, m_display_texture_width, m_display_texture_height,
|
||||
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
|
||||
m_display_texture_view_height, m_display_linear_filtering);
|
||||
}
|
||||
|
||||
if (HasSoftwareCursor())
|
||||
{
|
||||
// TODO: Scale mouse x/y
|
||||
const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(m_mouse_position_x, m_mouse_position_y);
|
||||
RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get());
|
||||
}
|
||||
|
||||
vkCmdEndRenderPass(cmdbuffer);
|
||||
m_frame_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_frame_view.image_layout = m_frame_texture.GetLayout();
|
||||
m_ri->set_image(m_ri->handle, &m_frame_view, 0, nullptr, VK_QUEUE_FAMILY_IGNORED);
|
||||
|
||||
// TODO: We can't use this because it doesn't support passing fences...
|
||||
// m_ri->set_command_buffers(m_ri->handle, 1, &cmdbuffer);
|
||||
m_ri->lock_queue(m_ri->handle);
|
||||
g_vulkan_context->SubmitCommandBuffer();
|
||||
m_ri->unlock_queue(m_ri->handle);
|
||||
g_vulkan_context->MoveToNextCommandBuffer();
|
||||
|
||||
g_retro_video_refresh_callback(RETRO_HW_FRAME_BUFFER_VALID, display_width, display_height, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibretroVulkanHostDisplay::CheckFramebufferSize(u32 width, u32 height)
|
||||
{
|
||||
static constexpr VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
static constexpr VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D;
|
||||
static constexpr VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
|
||||
if (m_frame_texture.GetWidth() == width && m_frame_texture.GetHeight() == height)
|
||||
return true;
|
||||
|
||||
g_vulkan_context->DeferFramebufferDestruction(m_frame_framebuffer);
|
||||
m_frame_texture.Destroy(true);
|
||||
|
||||
if (!m_frame_texture.Create(width, height, 1, 1, FRAMEBUFFER_FORMAT, VK_SAMPLE_COUNT_1_BIT, view_type, tiling, usage))
|
||||
return false;
|
||||
|
||||
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
m_frame_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
static constexpr VkClearColorValue cc = {};
|
||||
static constexpr VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
vkCmdClearColorImage(cmdbuf, m_frame_texture.GetImage(), m_frame_texture.GetLayout(), &cc, 1, &range);
|
||||
|
||||
Vulkan::FramebufferBuilder fbb;
|
||||
fbb.SetRenderPass(m_frame_render_pass);
|
||||
fbb.AddAttachment(m_frame_texture.GetView());
|
||||
fbb.SetSize(width, height, 1);
|
||||
m_frame_framebuffer = fbb.Create(g_vulkan_context->GetDevice(), false);
|
||||
if (m_frame_framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
m_frame_view = {};
|
||||
m_frame_view.image_view = m_frame_texture.GetView();
|
||||
m_frame_view.image_layout = m_frame_texture.GetLayout();
|
||||
m_frame_view.create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
m_frame_view.create_info.image = m_frame_texture.GetImage();
|
||||
m_frame_view.create_info.viewType = view_type;
|
||||
m_frame_view.create_info.format = FRAMEBUFFER_FORMAT;
|
||||
m_frame_view.create_info.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A};
|
||||
m_frame_view.create_info.subresourceRange = range;
|
||||
return true;
|
||||
}
|
42
src/duckstation-libretro/libretro_vulkan_host_display.h
Normal file
42
src/duckstation-libretro/libretro_vulkan_host_display.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include "common/vulkan/texture.h"
|
||||
#include "frontend-common/vulkan_host_display.h"
|
||||
#include "libretro.h"
|
||||
|
||||
#define HAVE_VULKAN
|
||||
#include "libretro_vulkan.h"
|
||||
|
||||
class LibretroVulkanHostDisplay final : public FrontendCommon::VulkanHostDisplay
|
||||
{
|
||||
public:
|
||||
LibretroVulkanHostDisplay();
|
||||
~LibretroVulkanHostDisplay();
|
||||
|
||||
static bool RequestHardwareRendererContext(retro_hw_render_callback* cb);
|
||||
|
||||
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override;
|
||||
void DestroyRenderDevice() override;
|
||||
|
||||
void ResizeRenderWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
bool Render() override;
|
||||
|
||||
protected:
|
||||
bool CreateResources() override;
|
||||
void DestroyResources() override;
|
||||
VkRenderPass GetRenderPassForDisplay() const override;
|
||||
|
||||
private:
|
||||
static constexpr VkFormat FRAMEBUFFER_FORMAT = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
bool CheckFramebufferSize(u32 width, u32 height);
|
||||
|
||||
const retro_hw_render_interface_vulkan* m_ri = nullptr;
|
||||
|
||||
Vulkan::Texture m_frame_texture;
|
||||
retro_vulkan_image m_frame_view = {};
|
||||
VkFramebuffer m_frame_framebuffer = VK_NULL_HANDLE;
|
||||
VkRenderPass m_frame_render_pass = VK_NULL_HANDLE;
|
||||
};
|
@ -266,6 +266,11 @@ bool VulkanHostDisplay::HasRenderSurface() const
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
}
|
||||
|
||||
VkRenderPass VulkanHostDisplay::GetRenderPassForDisplay() const
|
||||
{
|
||||
return m_swap_chain->GetClearRenderPass();
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::CreateResources()
|
||||
{
|
||||
static constexpr char fullscreen_quad_vertex_shader[] = R"(
|
||||
@ -348,7 +353,7 @@ void main()
|
||||
gpbuilder.SetNoBlendingState();
|
||||
gpbuilder.SetDynamicViewportAndScissorState();
|
||||
gpbuilder.SetPipelineLayout(m_pipeline_layout);
|
||||
gpbuilder.SetRenderPass(m_swap_chain->GetClearRenderPass(), 0);
|
||||
gpbuilder.SetRenderPass(GetRenderPassForDisplay(), 0);
|
||||
|
||||
m_display_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
if (m_display_pipeline == VK_NULL_HANDLE)
|
||||
|
@ -60,6 +60,9 @@ protected:
|
||||
float src_rect_height;
|
||||
};
|
||||
|
||||
// Can be overridden by frontends.
|
||||
virtual VkRenderPass GetRenderPassForDisplay() const;
|
||||
|
||||
virtual bool CreateResources();
|
||||
virtual void DestroyResources();
|
||||
|
||||
|
Reference in New Issue
Block a user