1 
2 #include "Common/GPU/Vulkan/VulkanLoader.h"
3 #include "Common/GPU/Vulkan/VulkanContext.h"
4 #include "Common/GPU/Vulkan/VulkanDebug.h"
5 #include "Common/Log.h"
6 #include "Core/Config.h"
7 #include "Core/ConfigValues.h"
8 #include "Core/System.h"
9 #include "GPU/GPUInterface.h"
10 #include "Common/Data/Text/Parsers.h"
11 
12 #include "libretro/LibretroVulkanContext.h"
13 #include "libretro/libretro_vulkan.h"
14 #include <GPU/Vulkan/VulkanRenderManager.h>
15 
16 static VulkanContext *vk;
17 
18 void vk_libretro_init(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);
19 void vk_libretro_shutdown();
20 void vk_libretro_set_hwrender_interface(retro_hw_render_interface *hw_render_interface);
21 void vk_libretro_wait_for_presentation();
22 
LibretroVulkanContext()23 LibretroVulkanContext::LibretroVulkanContext()
24 	: LibretroHWRenderContext(RETRO_HW_CONTEXT_VULKAN, VK_MAKE_VERSION(1, 0, 18)) {}
25 
SwapBuffers()26 void LibretroVulkanContext::SwapBuffers() {
27 	vk_libretro_wait_for_presentation();
28 	LibretroHWRenderContext::SwapBuffers();
29 }
30 
create_device(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)31 static bool create_device(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) {
32 	init_glslang();
33 
34 	if (!VulkanLoad()) {
35 		// TODO: In the context of RetroArch, someone has already loaded the functions.
36 		// But grabbing the pointers for ourselves can't really be a bad thing.
37 		ERROR_LOG(G3D, "RetroArch called the Vulkan entry point without Vulkan available???");
38 		return false;
39 	}
40 
41 	vk = new VulkanContext();
42 
43    vk_libretro_init(instance, gpu, surface, get_instance_proc_addr, required_device_extensions, num_required_device_extensions, required_device_layers, num_required_device_layers, required_features);
44 
45 	// TODO: Here we'll inject the instance and all of the stuff into the VulkanContext.
46 
47 	vk->CreateInstance({});
48 
49 	int physical_device = 0;
50 	while (gpu && vk->GetPhysicalDevice(physical_device) != gpu) {
51 		physical_device++;
52 	}
53 
54 	if (!gpu) {
55 		physical_device = vk->GetBestPhysicalDevice();
56 	}
57 
58 	vk->ChooseDevice(physical_device);
59 	vk->CreateDevice();
60 #ifdef _WIN32
61 	vk->InitSurface(WINDOWSYSTEM_WIN32, nullptr, nullptr);
62 #elif defined(__ANDROID__)
63 	vk->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr);
64 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
65 	vk->InitSurface(WINDOWSYSTEM_XLIB, nullptr, nullptr);
66 #elif defined(VK_USE_PLATFORM_XCB_KHR)
67 	vk->InitSurface(WINDOWSYSTEM_XCB, nullptr, nullptr);
68 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
69 	vk->InitSurface(WINDOWSYSTEM_WAYLAND, nullptr, nullptr);
70 #elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
71 	vk->InitSurface(WINDOWSYSTEM_DISPLAY, nullptr, nullptr);
72 #endif
73 
74 	context->gpu = vk->GetPhysicalDevice(physical_device);
75 	context->device = vk->GetDevice();
76 	context->queue = vk->GetGraphicsQueue();
77 	context->queue_family_index = vk->GetGraphicsQueueFamilyIndex();
78 	context->presentation_queue = context->queue;
79 	context->presentation_queue_family_index = context->queue_family_index;
80 #ifdef _DEBUG
81 	fflush(stdout);
82 #endif
83 	return true;
84 }
85 
GetApplicationInfo(void)86 static const VkApplicationInfo *GetApplicationInfo(void) {
87 	static VkApplicationInfo app_info{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
88 	app_info.pApplicationName = "PPSSPP";
89 	app_info.applicationVersion = Version(PPSSPP_GIT_VERSION).ToInteger();
90 	app_info.pEngineName = "PPSSPP";
91 	app_info.engineVersion = 2;
92 	app_info.apiVersion = VK_API_VERSION_1_0;
93 	return &app_info;
94 }
95 
Init()96 bool LibretroVulkanContext::Init() {
97 	if (!LibretroHWRenderContext::Init(false)) {
98 		return false;
99 	}
100 
101 	static const struct retro_hw_render_context_negotiation_interface_vulkan iface = {
102       RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN,
103       RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
104       GetApplicationInfo,
105       create_device,  // Callback above.
106       nullptr,
107    };
108 	Libretro::environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void *)&iface);
109 
110 	g_Config.iGPUBackend = (int)GPUBackend::VULKAN;
111 	return true;
112 }
113 
ContextReset()114 void LibretroVulkanContext::ContextReset() {
115    retro_hw_render_interface *vulkan;
116    if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&vulkan) || !vulkan) {
117       ERROR_LOG(G3D, "Failed to get HW rendering interface!\n");
118       return;
119    }
120    if (vulkan->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION) {
121       ERROR_LOG(G3D, "HW render interface mismatch, expected %u, got %u!\n", RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION, vulkan->interface_version);
122       return;
123    }
124    vk_libretro_set_hwrender_interface(vulkan);
125 
126    LibretroHWRenderContext::ContextReset();
127 }
128 
ContextDestroy()129 void LibretroVulkanContext::ContextDestroy() {
130    INFO_LOG(G3D, "LibretroVulkanContext::ContextDestroy()");
131 
132    LostBackbuffer();
133    gpu->DeviceLost();
134 }
135 
CreateDrawContext()136 void LibretroVulkanContext::CreateDrawContext() {
137    vk->ReinitSurface();
138 
139    if (!vk->InitSwapchain()) {
140       return;
141    }
142 
143    draw_ = Draw::T3DCreateVulkanContext(vk, false);
144    ((VulkanRenderManager*)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER))->SetInflightFrames(g_Config.iInflightFrames);
145    SetGPUBackend(GPUBackend::VULKAN);
146 }
147 
Shutdown()148 void LibretroVulkanContext::Shutdown() {
149 	LibretroHWRenderContext::Shutdown();
150 
151 	if (!vk) {
152 		return;
153 	}
154 
155 	vk->WaitUntilQueueIdle();
156 
157 	vk->DestroySwapchain();
158 	vk->DestroySurface();
159 	vk->DestroyDevice();
160 	vk->DestroyInstance();
161 	delete vk;
162 	vk = nullptr;
163 
164 	finalize_glslang();
165 	vk_libretro_shutdown();
166 }
167 
GetAPIContext()168 void *LibretroVulkanContext::GetAPIContext() { return vk; }
169