libretro: Vulkan renderer support
This commit is contained in:
parent
218f6721d3
commit
b9ffca1ddf
|
@ -1,7 +1,7 @@
|
|||
# DuckStation - PlayStation 1, aka. PSX Emulator
|
||||
**Discord Server:** https://discord.gg/Buktv3t
|
||||
|
||||
**Latest Windows and Linux (AppImage) Builds:** https://github.com/stenzek/duckstation/releases/tag/latest
|
||||
**Latest Windows, Linux (AppImage), and Libretro Builds:** https://github.com/stenzek/duckstation/releases/tag/latest
|
||||
|
||||
**Game Compatibility List:** https://docs.google.com/spreadsheets/d/1H66MxViRjjE5f8hOl5RQmF5woS1murio2dsLn14kEqo/edit?usp=sharing
|
||||
|
||||
|
@ -11,6 +11,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c
|
|||
|
||||
## Latest News
|
||||
|
||||
- 2020/07/04: Vulkan renderer now available in libretro core.
|
||||
- 2020/07/02: Now available as a libretro core.
|
||||
- 2020/07/01: Lightgun support with custom crosshairs.
|
||||
- 2020/06/19: Vulkan hardware renderer added.
|
||||
|
@ -197,7 +198,7 @@ Hotkeys:
|
|||
|
||||
## Libretro Core
|
||||
|
||||
DuckStation is available as a libretro core, which can be loaded into a frontend such as RetroArch. Currently, only the D3D11 and OpenGL renderers are available, Vulkan will be available soon. It supports most features of the full frontend, within the constraints and limitations of being a libretro core.
|
||||
DuckStation is available as a libretro core, which can be loaded into a frontend such as RetroArch. It supports most features of the full frontend, within the constraints and limitations of being a libretro core.
|
||||
|
||||
Prebuilt binaries for Windows and 64-bit Linux can be found on the releases page. Direct links:
|
||||
- 64-bit Windows: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-libretro-windows-x64-release.7z
|
||||
|
|
|
@ -97,5 +97,6 @@ bool LoadVulkanLibrary();
|
|||
bool LoadVulkanInstanceFunctions(VkInstance instance);
|
||||
bool LoadVulkanDeviceFunctions(VkDevice device);
|
||||
void UnloadVulkanLibrary();
|
||||
void ResetVulkanLibraryFunctionPointers();
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
namespace Vulkan {
|
||||
static void ResetVulkanLibraryFunctionPointers()
|
||||
|
||||
void ResetVulkanLibraryFunctionPointers()
|
||||
{
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) name = nullptr;
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue