[Vulkan] Define and implement v2 of context negotiation interface (#14890)

* [Vulkan] Add v2 of context negotiation interface.

* [Vulkan] Add vkEnumerateInstanceVersion symbol.

* [Vulkan] Implement v2 context negotiation

* [libretro] Add GET_HW_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT.

Works around issues in v1 interface where it was not possible to
query what frontend would do when faces with newer interface versions.

This env-call gives stronger guarantees how things have to work.

* [runloop] Implement GET_HW_CONTEXT_NEGOATION_INTERFACE_SUPPORT.

Fairly trivial. Just report the latest version.

* [Vulkan] Add stricted wording around get_application_info in v2.

* [Vulkan] Be more defensive about get_application_info() in v1.
This commit is contained in:
Hans-Kristian Arntzen 2023-02-04 16:51:50 +01:00 committed by GitHub
parent 39e5dde973
commit 9f7d0c74d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 436 additions and 130 deletions

View file

@ -1274,7 +1274,7 @@ static bool vulkan_load_device_symbols(gfx_ctx_vulkan_data_t *vk)
return true;
}
static bool vulkan_find_extensions(const char **exts, unsigned num_exts,
static bool vulkan_find_extensions(const char * const *exts, unsigned num_exts,
const VkExtensionProperties *properties, unsigned property_count)
{
unsigned i, ext;
@ -1297,7 +1297,7 @@ static bool vulkan_find_extensions(const char **exts, unsigned num_exts,
return true;
}
static bool vulkan_find_instance_extensions(const char **exts, unsigned num_exts)
static bool vulkan_find_instance_extensions(const char * const *exts, unsigned num_exts)
{
uint32_t property_count;
bool ret = true;
@ -1332,12 +1332,13 @@ end:
}
static bool vulkan_find_device_extensions(VkPhysicalDevice gpu,
const char **enabled, unsigned *enabled_count,
const char **enabled, unsigned *inout_enabled_count,
const char **exts, unsigned num_exts,
const char **optional_exts, unsigned num_optional_exts)
{
uint32_t property_count;
unsigned i;
unsigned count = *inout_enabled_count;
bool ret = true;
VkExtensionProperties *properties = NULL;
@ -1364,15 +1365,16 @@ static bool vulkan_find_device_extensions(VkPhysicalDevice gpu,
goto end;
}
memcpy((void*)enabled, exts, num_exts * sizeof(*exts));
*enabled_count = num_exts;
memcpy(enabled + count, exts, num_exts * sizeof(*exts));
count += num_exts;
for (i = 0; i < num_optional_exts; i++)
if (vulkan_find_extensions(&optional_exts[i], 1, properties, property_count))
enabled[(*enabled_count)++] = optional_exts[i];
enabled[count++] = optional_exts[i];
end:
free(properties);
*inout_enabled_count = count;
return ret;
}
@ -1447,6 +1449,56 @@ static bool vulkan_context_init_gpu(gfx_ctx_vulkan_data_t *vk)
return true;
}
static const char *vulkan_device_extensions[] = {
"VK_KHR_swapchain",
};
static const char *vulkan_optional_device_extensions[] = {
"VK_KHR_sampler_mirror_clamp_to_edge",
};
static VkDevice vulkan_context_create_device_wrapper(
VkPhysicalDevice gpu, void *opaque,
const VkDeviceCreateInfo *create_info)
{
gfx_ctx_vulkan_data_t *vk = (gfx_ctx_vulkan_data_t *)opaque;
VkDeviceCreateInfo info = *create_info;
VkDevice device = VK_NULL_HANDLE;
const char **device_extensions;
VkResult res;
device_extensions = (const char **)malloc(
(info.enabledExtensionCount +
ARRAY_SIZE(vulkan_device_extensions) +
ARRAY_SIZE(vulkan_optional_device_extensions)) * sizeof(const char *));
memcpy(device_extensions, info.ppEnabledExtensionNames, info.enabledExtensionCount * sizeof(const char *));
info.ppEnabledExtensionNames = device_extensions;
if (!(vulkan_find_device_extensions(gpu,
device_extensions, &info.enabledExtensionCount,
vulkan_device_extensions, ARRAY_SIZE(vulkan_device_extensions),
vulkan_optional_device_extensions,
ARRAY_SIZE(vulkan_optional_device_extensions))))
{
RARCH_ERR("[Vulkan]: Could not find required device extensions.\n");
return VK_NULL_HANDLE;
}
/* When we get around to using fancier features we can chain in PDF2 stuff. */
if ((res = vkCreateDevice(gpu, &info, NULL, &device)) != VK_SUCCESS)
{
RARCH_ERR("[Vulkan]: Failed to create device (%d).\n", res);
device = VK_NULL_HANDLE;
goto end;
}
end:
free(device_extensions);
return device;
}
static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
{
uint32_t queue_count;
@ -1461,14 +1513,6 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
const char *enabled_device_extensions[8];
unsigned enabled_device_extension_count = 0;
static const char *device_extensions[] = {
"VK_KHR_swapchain",
};
static const char *optional_device_extensions[] = {
"VK_KHR_sampler_mirror_clamp_to_edge",
};
struct retro_hw_render_context_negotiation_interface_vulkan *iface =
(struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface();
@ -1478,29 +1522,55 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
iface = NULL;
}
if (iface && iface->interface_version != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION)
if (iface && iface->interface_version == 0)
{
RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong interface version.\n");
iface = NULL;
}
if (iface)
RARCH_LOG("[Vulkan]: Got HW context negotiation interface %u.\n", iface->interface_version);
if (!vulkan_context_init_gpu(vk))
return false;
if (!cached_device_vk && iface && iface->create_device)
{
struct retro_vulkan_context context = { 0 };
const VkPhysicalDeviceFeatures features = { 0 };
struct retro_vulkan_context context = { 0 };
bool ret = iface->create_device(&context, vk->context.instance,
vk->context.gpu,
vk->vk_surface,
vulkan_symbol_wrapper_instance_proc_addr(),
device_extensions,
ARRAY_SIZE(device_extensions),
NULL,
0,
&features);
bool ret = false;
if (iface->interface_version >= 2 && iface->create_device2)
{
ret = iface->create_device2(&context, vk->context.instance,
vk->context.gpu,
vk->vk_surface,
vulkan_symbol_wrapper_instance_proc_addr(),
vulkan_context_create_device_wrapper, vk);
if (!ret)
{
RARCH_WARN("[Vulkan]: Failed to create_device2 on provided VkPhysicalDevice, letting core decide which GPU to use.\n");
vk->context.gpu = VK_NULL_HANDLE;
ret = iface->create_device2(&context, vk->context.instance,
vk->context.gpu,
vk->vk_surface,
vulkan_symbol_wrapper_instance_proc_addr(),
vulkan_context_create_device_wrapper, vk);
}
}
else
{
ret = iface->create_device(&context, vk->context.instance,
vk->context.gpu,
vk->vk_surface,
vulkan_symbol_wrapper_instance_proc_addr(),
vulkan_device_extensions,
ARRAY_SIZE(vulkan_device_extensions),
NULL,
0,
&features);
}
if (!ret)
{
@ -1517,6 +1587,7 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
vk->context.queue = context.queue;
vk->context.gpu = context.gpu;
vk->context.graphics_queue_index = context.queue_family_index;
vk->context.queue = context.queue;
if (context.presentation_queue != context.queue)
{
@ -1637,9 +1708,9 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
if (!(vulkan_find_device_extensions(vk->context.gpu,
enabled_device_extensions, &enabled_device_extension_count,
device_extensions, ARRAY_SIZE(device_extensions),
optional_device_extensions,
ARRAY_SIZE(optional_device_extensions))))
vulkan_device_extensions, ARRAY_SIZE(vulkan_device_extensions),
vulkan_optional_device_extensions,
ARRAY_SIZE(vulkan_optional_device_extensions))))
{
RARCH_ERR("[Vulkan]: Could not find required device extensions.\n");
return false;
@ -1652,7 +1723,7 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
device_info.enabledExtensionCount = enabled_device_extension_count;
device_info.ppEnabledExtensionNames = enabled_device_extension_count ? enabled_device_extensions : NULL;
device_info.ppEnabledExtensionNames = enabled_device_extensions;
device_info.pEnabledFeatures = &features;
if (cached_device_vk)
@ -1677,8 +1748,11 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
return false;
}
vkGetDeviceQueue(vk->context.device,
vk->context.graphics_queue_index, 0, &vk->context.queue);
if (vk->context.queue == VK_NULL_HANDLE)
{
vkGetDeviceQueue(vk->context.device,
vk->context.graphics_queue_index, 0, &vk->context.queue);
}
#ifdef HAVE_THREADS
vk->context.queue_lock = slock_new();
@ -1692,23 +1766,117 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
return true;
}
static VkInstance vulkan_context_create_instance_wrapper(void *opaque, const VkInstanceCreateInfo *create_info)
{
gfx_ctx_vulkan_data_t *vk = (gfx_ctx_vulkan_data_t *)opaque;
VkInstanceCreateInfo info = *create_info;
VkInstance instance = VK_NULL_HANDLE;
const char **instance_extensions;
const char **instance_layers;
VkResult res;
uint32_t i;
instance_extensions = (const char **)malloc((info.enabledExtensionCount + 3) * sizeof(const char *));
instance_layers = (const char **)malloc((info.enabledLayerCount + 1) * sizeof(const char *));
memcpy(instance_extensions, info.ppEnabledExtensionNames, info.enabledExtensionCount * sizeof(const char *));
memcpy(instance_layers, info.ppEnabledLayerNames, info.enabledLayerCount * sizeof(const char *));
info.ppEnabledExtensionNames = instance_extensions;
info.ppEnabledLayerNames = instance_layers;
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_surface";
switch (vk->wsi_type)
{
case VULKAN_WSI_WAYLAND:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_wayland_surface";
break;
case VULKAN_WSI_ANDROID:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_android_surface";
break;
case VULKAN_WSI_WIN32:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_win32_surface";
break;
case VULKAN_WSI_XLIB:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_xlib_surface";
break;
case VULKAN_WSI_XCB:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_xcb_surface";
break;
case VULKAN_WSI_MIR:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_mir_surface";
break;
case VULKAN_WSI_DISPLAY:
instance_extensions[info.enabledExtensionCount++] = "VK_KHR_display";
break;
case VULKAN_WSI_MVK_MACOS:
instance_extensions[info.enabledExtensionCount++] = "VK_MVK_macos_surface";
break;
case VULKAN_WSI_MVK_IOS:
instance_extensions[info.enabledExtensionCount++] = "VK_MVK_ios_surface";
break;
case VULKAN_WSI_NONE:
default:
break;
}
#ifdef VULKAN_DEBUG
instance_layers[info.enabledLayerCount++] = "VK_LAYER_KHRONOS_validation";
instance_extensions[info.enabledExtensionCount++] = "VK_EXT_debug_utils";
#endif
VkLayerProperties properties[128];
uint32_t layer_count = ARRAY_SIZE(properties);
vkEnumerateInstanceLayerProperties(&layer_count, properties);
/* Be careful about validating supported instance extensions when using explicit layers.
* If core wants to enable debug layers, we'll have to do deeper validation and query
* supported extensions per-layer which is annoying. vkCreateInstance will validate this on its own anyways. */
if (info.enabledLayerCount == 0 &&
!vulkan_find_instance_extensions(info.ppEnabledExtensionNames, info.enabledExtensionCount))
{
RARCH_ERR("[Vulkan]: Instance does not support required extensions.\n");
goto end;
}
if (info.pApplicationInfo)
{
uint32_t supported_instance_version = VK_API_VERSION_1_0;
if (!vkEnumerateInstanceVersion || vkEnumerateInstanceVersion(&supported_instance_version) != VK_SUCCESS)
supported_instance_version = VK_API_VERSION_1_0;
if (supported_instance_version < info.pApplicationInfo->apiVersion)
{
RARCH_ERR("[Vulkan]: Core requests apiVersion %u.%u, but it is not supported by loader.\n",
VK_VERSION_MAJOR(info.pApplicationInfo->apiVersion),
VK_VERSION_MINOR(info.pApplicationInfo->apiVersion));
goto end;
}
}
if ((res = vkCreateInstance(&info, NULL, &instance)) != VK_SUCCESS)
{
RARCH_ERR("[Vulkan]: Failed to create Vulkan instance (%d).\n", res);
RARCH_ERR("[Vulkan]: If VULKAN_DEBUG=1 is enabled, make sure Vulkan validation layers are installed.\n");
for (i = 0; i < info.enabledLayerCount; i++)
RARCH_ERR("[Vulkan]: Core explicitly enables layer (%s), this might be cause of failure.\n", info.ppEnabledLayerNames[i]);
instance = VK_NULL_HANDLE;
goto end;
}
end:
free(instance_extensions);
free(instance_layers);
return instance;
}
bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
enum vulkan_wsi_type type)
{
unsigned i;
PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
const char *instance_extensions[4];
VkResult res = VK_SUCCESS;
bool use_instance_ext = false;
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
unsigned ext_count = 0;
VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
struct retro_hw_render_context_negotiation_interface_vulkan *iface =
(struct retro_hw_render_context_negotiation_interface_vulkan*)video_driver_get_context_negotiation_interface();
#ifdef VULKAN_DEBUG
static const char *instance_layers[] = { "VK_LAYER_KHRONOS_validation" };
instance_extensions[ext_count++] = "VK_EXT_debug_utils";
#endif
if (iface && iface->interface_type != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN)
{
@ -1716,47 +1884,13 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
iface = NULL;
}
if (iface && iface->interface_version != RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION)
if (iface && iface->interface_version == 0)
{
RARCH_WARN("[Vulkan]: Got HW context negotiation interface, but it's the wrong interface version.\n");
iface = NULL;
}
instance_extensions[ext_count++] = "VK_KHR_surface";
switch (type)
{
case VULKAN_WSI_WAYLAND:
instance_extensions[ext_count++] = "VK_KHR_wayland_surface";
break;
case VULKAN_WSI_ANDROID:
instance_extensions[ext_count++] = "VK_KHR_android_surface";
break;
case VULKAN_WSI_WIN32:
instance_extensions[ext_count++] = "VK_KHR_win32_surface";
break;
case VULKAN_WSI_XLIB:
instance_extensions[ext_count++] = "VK_KHR_xlib_surface";
break;
case VULKAN_WSI_XCB:
instance_extensions[ext_count++] = "VK_KHR_xcb_surface";
break;
case VULKAN_WSI_MIR:
instance_extensions[ext_count++] = "VK_KHR_mir_surface";
break;
case VULKAN_WSI_DISPLAY:
instance_extensions[ext_count++] = "VK_KHR_display";
break;
case VULKAN_WSI_MVK_MACOS:
instance_extensions[ext_count++] = "VK_MVK_macos_surface";
break;
case VULKAN_WSI_MVK_IOS:
instance_extensions[ext_count++] = "VK_MVK_ios_surface";
break;
case VULKAN_WSI_NONE:
default:
break;
}
vk->wsi_type = type;
if (!vulkan_library)
{
@ -1765,9 +1899,9 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
#elif __APPLE__
vulkan_library = dylib_load("libMoltenVK.dylib");
#else
vulkan_library = dylib_load("libvulkan.so");
vulkan_library = dylib_load("libvulkan.so.1");
if (!vulkan_library)
vulkan_library = dylib_load("libvulkan.so.1");
vulkan_library = dylib_load("libvulkan.so");
#endif
}
@ -1796,50 +1930,86 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
return false;
}
use_instance_ext = vulkan_find_instance_extensions(instance_extensions, ext_count);
app.pApplicationName = msg_hash_to_str(MSG_PROGRAM);
app.applicationVersion = 0;
app.pEngineName = msg_hash_to_str(MSG_PROGRAM);
app.engineVersion = 0;
app.apiVersion = VK_API_VERSION_1_0;
app.pApplicationName = msg_hash_to_str(MSG_PROGRAM);
app.applicationVersion = 0;
app.pEngineName = msg_hash_to_str(MSG_PROGRAM);
app.engineVersion = 0;
app.apiVersion = VK_MAKE_VERSION(1, 0, 18);
info.pApplicationInfo = &app;
info.enabledExtensionCount = use_instance_ext ? ext_count : 0;
info.ppEnabledExtensionNames = use_instance_ext ? instance_extensions : NULL;
#ifdef VULKAN_DEBUG
info.enabledLayerCount = ARRAY_SIZE(instance_layers);
info.ppEnabledLayerNames = instance_layers;
#endif
if (iface && !iface->get_application_info && iface->interface_version >= 2)
{
RARCH_ERR("[Vulkan]: Core did not provide application info as required by v2.\n");
return false;
}
if (iface && iface->get_application_info)
{
info.pApplicationInfo = iface->get_application_info();
#ifdef VULKAN_DEBUG
if (info.pApplicationInfo->pApplicationName)
const VkApplicationInfo *app_info = iface->get_application_info();
if (!app_info && iface->interface_version >= 2)
{
RARCH_LOG("[Vulkan]: App: %s (version %u)\n",
info.pApplicationInfo->pApplicationName,
info.pApplicationInfo->applicationVersion);
RARCH_ERR("[Vulkan]: Core did not provide application info as required by v2.\n");
return false;
}
if (info.pApplicationInfo->pEngineName)
if (app_info)
{
RARCH_LOG("[Vulkan]: Engine: %s (version %u)\n",
info.pApplicationInfo->pEngineName,
info.pApplicationInfo->engineVersion);
}
app = *app_info;
#ifdef VULKAN_DEBUG
if (app.pApplicationName)
{
RARCH_LOG("[Vulkan]: App: %s (version %u)\n",
app.pApplicationName, app.applicationVersion);
}
if (app.pEngineName)
{
RARCH_LOG("[Vulkan]: Engine: %s (version %u)\n",
app.pEngineName, app.engineVersion);
}
#endif
}
}
if (app.apiVersion < VK_API_VERSION_1_1)
{
/* Try to upgrade to at least Vulkan 1.1 so that we can more easily make use of advanced features.
* Vulkan 1.0 drivers are completely irrelevant these days. */
uint32_t supported;
if (vkEnumerateInstanceVersion &&
vkEnumerateInstanceVersion(&supported) == VK_SUCCESS &&
supported >= VK_API_VERSION_1_1)
{
app.apiVersion = VK_API_VERSION_1_1;
}
}
if (cached_instance_vk)
{
vk->context.instance = cached_instance_vk;
cached_instance_vk = NULL;
res = VK_SUCCESS;
vk->context.instance = cached_instance_vk;
cached_instance_vk = NULL;
}
else
res = vkCreateInstance(&info, NULL, &vk->context.instance);
{
if (iface && iface->interface_version >= 2 && iface->create_instance)
{
vk->context.instance = iface->create_instance(
GetInstanceProcAddr, &app,
vulkan_context_create_instance_wrapper, vk);
}
else
{
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
info.pApplicationInfo = &app;
vk->context.instance = vulkan_context_create_instance_wrapper(vk, &info);
}
if (vk->context.instance == VK_NULL_HANDLE)
{
RARCH_ERR("Failed to create Vulkan instance.\n");
return false;
}
}
#ifdef VULKAN_DEBUG
VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(vk->context.instance,
@ -1870,22 +2040,6 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
RARCH_LOG("[Vulkan]: Enabling Vulkan debug layers.\n");
#endif
/* Try different API versions if driver has compatible
* but slightly different VK_API_VERSION. */
for (i = 1; i < 4 && res == VK_ERROR_INCOMPATIBLE_DRIVER; i++)
{
info.pApplicationInfo = &app;
app.apiVersion = VK_MAKE_VERSION(1, 0, i);
res = vkCreateInstance(&info, NULL, &vk->context.instance);
}
if (res != VK_SUCCESS)
{
RARCH_ERR("Failed to create Vulkan instance (%d).\n", res);
RARCH_ERR("If VULKAN_DEBUG=1 is enabled, make sure Vulkan validation layers are installed.\n");
return false;
}
if (!vulkan_load_instance_symbols(vk))
{
RARCH_ERR("[Vulkan]: Failed to load instance symbols.\n");
@ -2466,6 +2620,7 @@ void vulkan_context_destroy(gfx_ctx_vulkan_data_t *vk,
vkDestroyDevice(vk->context.device, NULL);
vk->context.device = NULL;
}
if (vk->context.instance)
{
if (vk->context.destroy_device)

View file

@ -209,6 +209,7 @@ typedef struct gfx_ctx_vulkan_data
VkSwapchainKHR swapchain; /* ptr alignment */
struct vulkan_emulated_mailbox mailbox;
uint8_t flags;
enum vulkan_wsi_type wsi_type;
} gfx_ctx_vulkan_data_t;
struct vulkan_display_surface_info

View file

@ -1767,6 +1767,33 @@ enum retro_mod
* (see enum retro_savestate_context)
*/
#define RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT (73 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* struct retro_hw_render_context_negotiation_interface * --
* Before calling SET_HW_RNEDER_CONTEXT_NEGOTIATION_INTERFACE, a core can query
* which version of the interface is supported.
*
* Frontend looks at interface_type and returns the maximum supported
* context negotiation interface version.
* If the interface_type is not supported or recognized by the frontend, a version of 0
* must be returned in interface_version and true is returned by frontend.
*
* If this environment call returns true with interface_version greater than 0,
* a core can always use a negotiation interface version larger than what the frontend returns, but only
* earlier versions of the interface will be used by the frontend.
* A frontend must not reject a negotiation interface version that is larger than
* what the frontend supports. Instead, the frontend will use the older entry points that it recognizes.
* If this is incompatible with a particular core's requirements, it can error out early.
*
* Backwards compatibility note:
* This environment call was introduced after Vulkan v1 context negotiation.
* If this environment call is not supported by frontend - i.e. the environment call returns false -
* only Vulkan v1 context negotiation is supported (if Vulkan HW rendering is supported at all).
* If a core uses Vulkan negotiation interface with version > 1, negotiation may fail unexpectedly.
* All future updates to the context negotiation interface implies that frontend must support
* this environment call to query support.
*/
/* VFS functionality */
/* File paths:

View file

@ -27,7 +27,7 @@
#include <vulkan/vulkan.h>
#define RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION 5
#define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 1
#define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 2
struct retro_vulkan_image
{
@ -64,6 +64,8 @@ struct retro_vulkan_context
uint32_t presentation_queue_family_index;
};
/* This is only used in v1 of the negotiation interface.
* It is deprecated since it cannot express PDF2 features or optional extensions. */
typedef bool (*retro_vulkan_create_device_t)(
struct retro_vulkan_context *context,
VkInstance instance,
@ -78,6 +80,32 @@ typedef bool (*retro_vulkan_create_device_t)(
typedef void (*retro_vulkan_destroy_device_t)(void);
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
typedef VkInstance (*retro_vulkan_create_instance_wrapper_t)(
void *opaque, const VkInstanceCreateInfo *create_info);
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
typedef VkInstance (*retro_vulkan_create_instance_t)(
PFN_vkGetInstanceProcAddr get_instance_proc_addr,
const VkApplicationInfo *app,
retro_vulkan_create_instance_wrapper_t create_instance_wrapper,
void *opaque);
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
typedef VkDevice (*retro_vulkan_create_device_wrapper_t)(
VkPhysicalDevice gpu, void *opaque,
const VkDeviceCreateInfo *create_info);
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
typedef bool (*retro_vulkan_create_device2_t)(
struct retro_vulkan_context *context,
VkInstance instance,
VkPhysicalDevice gpu,
VkSurfaceKHR surface,
PFN_vkGetInstanceProcAddr get_instance_proc_addr,
retro_vulkan_create_device_wrapper_t create_device_wrapper,
void *opaque);
/* Note on thread safety:
* The Vulkan API is heavily designed around multi-threading, and
* the libretro interface for it should also be threading friendly.
@ -89,11 +117,24 @@ struct retro_hw_render_context_negotiation_interface_vulkan
{
/* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN. */
enum retro_hw_render_context_negotiation_interface_type interface_type;
/* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION. */
/* Usually set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
* but can be lower depending on GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT. */
unsigned interface_version;
/* If non-NULL, returns a VkApplicationInfo struct that the frontend can use instead of
* its "default" application info.
* VkApplicationInfo::apiVersion also controls the target core Vulkan version for instance level functionality.
* Lifetime of the returned pointer must remain until the retro_vulkan_context is initialized.
*
* NOTE: For optimal compatibility with e.g. Android which is very slow to update its loader,
* a core version of 1.1 should be requested. Features beyond that can be requested with extensions.
* Vulkan 1.0 is only appropriate for legacy cores, but is still supported.
* A frontend is free to bump the instance creation apiVersion as necessary if the frontend requires more advanced core features.
*
* v2: This function must not be NULL, and must not return NULL.
* v1: It was not clearly defined if this function could return NULL.
* Frontends should be defensive and provide a default VkApplicationInfo
* if this function returns NULL or if this function is NULL.
*/
retro_vulkan_get_application_info_t get_application_info;
@ -102,8 +143,8 @@ struct retro_hw_render_context_negotiation_interface_vulkan
* The core must prepare a designated PhysicalDevice, Device, Queue and queue family index
* which the frontend will use for its internal operation.
*
* If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this PhysicalDevice.
* The core is still free to use other physical devices.
* If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this PhysicalDevice if the call succeeds.
* The core is still free to use other physical devices for other purposes that are private to the core.
*
* The frontend will request certain extensions and layers for a device which is created.
* The core must ensure that the queue and queue_family_index support GRAPHICS and COMPUTE.
@ -132,8 +173,64 @@ struct retro_hw_render_context_negotiation_interface_vulkan
* tearing down its own device resources.
*
* Only auxillary resources should be freed here, i.e. resources which are not part of retro_vulkan_context.
* v2: Auxillary instance resources created during create_instance can also be freed here.
*/
retro_vulkan_destroy_device_t destroy_device;
/* v2 API: If interface_version is < 2, fields below must be ignored.
* If the frontend does not support interface version 2, the v1 entry points will be used instead. */
/* If non-NULL, this is called to create an instance, otherwise a VkInstance is created by the frontend.
* v1 interface bug: The only way to enable instance features is through core versions signalled in VkApplicationInfo.
* The frontend may request that certain extensions and layers
* are enabled on the VkInstance. Application may add additional features.
* If app is non-NULL, apiVersion controls the minimum core version required by the application.
* Return a VkInstance or VK_NULL_HANDLE. The VkInstance is owned by the frontend.
*
* Rather than call vkCreateInstance directly, a core must call the CreateInstance wrapper provided with:
* VkInstance instance = create_instance_wrapper(opaque, &create_info);
* If the core wishes to create a private instance for whatever reason (relying on shared memory for example),
* it may call vkCreateInstance directly. */
retro_vulkan_create_instance_t create_instance;
/* If non-NULL and frontend recognizes negotiation interface >= 2, create_device2 takes precedence over create_device.
* Similar to create_device, but is extended to better understand new core versions and PDF2 feature enablement.
* Requirements for create_device2 are the same as create_device unless a difference is mentioned.
*
* v2 consideration:
* If the chosen gpu by frontend cannot be supported, a core must return false.
*
* NOTE: "Cannot be supported" is intentionally vaguely defined.
* Refusing to run on an iGPU for a very intensive core with desktop GPU as a minimum spec may be in the gray area.
* Not supporting optional features is not a good reason to reject a physical device, however.
*
* On device creation feature with explicit gpu, a frontend should fall back create_device2 with gpu == VK_NULL_HANDLE and let core
* decide on a supported device if possible.
*
* A core must assume that the explicitly provided GPU is the only guaranteed attempt it has to create a device.
* A fallback may not be attempted if there are particular reasons why only a specific physical device can work,
* but these situations should be esoteric and rare in nature, e.g. a libretro frontend is implemented with external memory
* and only LUID matching would work.
* Cores and frontends should ensure "best effort" when negotiating like this and appropriate logging is encouraged.
*
* v1 note: In the v1 version of create_device, it was never expected that create_device would fail like this,
* and frontends are not expected to attempt fall backs.
*
* Rather than call vkCreateDevice directly, a core must call the CreateDevice wrapper provided with:
* VkDevice device = create_device_wrapper(gpu, opaque, &create_info);
* If the core wishes to create a private device for whatever reason (relying on shared memory for example),
* it may call vkCreateDevice directly.
*
* This allows the frontend to add additional extensions that it requires as well as adjust the PDF2 pNext as required.
* It is also possible adjust the queue create infos in case the frontend desires to allocate some private queues.
*
* The get_instance_proc_addr provided in create_device2 must be the same as create_instance.
*
* NOTE: The frontend must not disable features requested by application.
* NOTE: The frontend must not add any robustness features as some API behavior may change (VK_EXT_descriptor_buffer comes to mind).
* I.e. robustBufferAccess and the like. (nullDescriptor from robustness2 is allowed to be enabled).
*/
retro_vulkan_create_device2_t create_device2;
};
struct retro_hw_render_interface_vulkan

View file

@ -11,6 +11,8 @@ extern "C" {
extern PFN_vkCreateInstance vulkan_symbol_wrapper_vkCreateInstance;
#define vkCreateInstance vulkan_symbol_wrapper_vkCreateInstance
extern PFN_vkEnumerateInstanceVersion vulkan_symbol_wrapper_vkEnumerateInstanceVersion;
#define vkEnumerateInstanceVersion vulkan_symbol_wrapper_vkEnumerateInstanceVersion
extern PFN_vkEnumerateInstanceExtensionProperties vulkan_symbol_wrapper_vkEnumerateInstanceExtensionProperties;
#define vkEnumerateInstanceExtensionProperties vulkan_symbol_wrapper_vkEnumerateInstanceExtensionProperties
extern PFN_vkEnumerateInstanceLayerProperties vulkan_symbol_wrapper_vkEnumerateInstanceLayerProperties;

View file

@ -3,6 +3,7 @@
#include <vulkan/vulkan_symbol_wrapper.h>
PFN_vkCreateInstance vulkan_symbol_wrapper_vkCreateInstance;
PFN_vkEnumerateInstanceVersion vulkan_symbol_wrapper_vkEnumerateInstanceVersion;
PFN_vkEnumerateInstanceExtensionProperties vulkan_symbol_wrapper_vkEnumerateInstanceExtensionProperties;
PFN_vkEnumerateInstanceLayerProperties vulkan_symbol_wrapper_vkEnumerateInstanceLayerProperties;
PFN_vkDestroyInstance vulkan_symbol_wrapper_vkDestroyInstance;
@ -189,6 +190,7 @@ VkBool32 vulkan_symbol_wrapper_load_global_symbols(void)
if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(NULL, "vkCreateInstance", vkCreateInstance)) return VK_FALSE;
if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(NULL, "vkEnumerateInstanceExtensionProperties", vkEnumerateInstanceExtensionProperties)) return VK_FALSE;
if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(NULL, "vkEnumerateInstanceLayerProperties", vkEnumerateInstanceLayerProperties)) return VK_FALSE;
VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(NULL, "vkEnumerateInstanceVersion", vkEnumerateInstanceVersion);
return VK_TRUE;
}

View file

@ -72,6 +72,10 @@
#include <encodings/utf.h>
#include <libretro.h>
#ifdef HAVE_VULKAN
#include <libretro_vulkan.h>
#endif
#define VFS_FRONTEND
#include <vfs/vfs_implementation.h>
@ -3344,6 +3348,24 @@ bool runloop_environment_cb(unsigned cmd, void *data)
}
break;
case RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT:
{
struct retro_hw_render_context_negotiation_interface *iface =
(struct retro_hw_render_context_negotiation_interface*)data;
#ifdef HAVE_VULKAN
if (iface->interface_type == RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN)
{
iface->interface_version = RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION;
}
else
#endif
{
iface->interface_version = 0;
}
}
break;
default:
RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd);
return false;