| 1 | /* Copyright (C) 2010-2020 The RetroArch team |
| 2 | * |
| 3 | * --------------------------------------------------------------------------------------------- |
| 4 | * The following license statement only applies to this libretro API header (libretro_vulkan.h) |
| 5 | * --------------------------------------------------------------------------------------------- |
| 6 | * |
| 7 | * Permission is hereby granted, free of charge, |
| 8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), |
| 9 | * to deal in the Software without restriction, including without limitation the rights to |
| 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, |
| 11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
| 16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 21 | */ |
| 22 | |
| 23 | #ifndef LIBRETRO_VULKAN_H__ |
| 24 | #define LIBRETRO_VULKAN_H__ |
| 25 | |
| 26 | #include <libretro.h> |
| 27 | #include <vulkan/vulkan.h> |
| 28 | |
| 29 | #define RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION 5 |
| 30 | #define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 2 |
| 31 | |
| 32 | struct retro_vulkan_image |
| 33 | { |
| 34 | VkImageView image_view; |
| 35 | VkImageLayout image_layout; |
| 36 | VkImageViewCreateInfo create_info; |
| 37 | }; |
| 38 | |
| 39 | typedef void (*retro_vulkan_set_image_t)(void *handle, |
| 40 | const struct retro_vulkan_image *image, |
| 41 | uint32_t num_semaphores, |
| 42 | const VkSemaphore *semaphores, |
| 43 | uint32_t src_queue_family); |
| 44 | |
| 45 | typedef uint32_t (*retro_vulkan_get_sync_index_t)(void *handle); |
| 46 | typedef uint32_t (*retro_vulkan_get_sync_index_mask_t)(void *handle); |
| 47 | typedef void (*retro_vulkan_set_command_buffers_t)(void *handle, |
| 48 | uint32_t num_cmd, |
| 49 | const VkCommandBuffer *cmd); |
| 50 | typedef void (*retro_vulkan_wait_sync_index_t)(void *handle); |
| 51 | typedef void (*retro_vulkan_lock_queue_t)(void *handle); |
| 52 | typedef void (*retro_vulkan_unlock_queue_t)(void *handle); |
| 53 | typedef void (*retro_vulkan_set_signal_semaphore_t)(void *handle, VkSemaphore semaphore); |
| 54 | |
| 55 | typedef const VkApplicationInfo *(*retro_vulkan_get_application_info_t)(void); |
| 56 | |
| 57 | struct retro_vulkan_context |
| 58 | { |
| 59 | VkPhysicalDevice gpu; |
| 60 | VkDevice device; |
| 61 | VkQueue queue; |
| 62 | uint32_t queue_family_index; |
| 63 | VkQueue presentation_queue; |
| 64 | uint32_t presentation_queue_family_index; |
| 65 | }; |
| 66 | |
| 67 | /* This is only used in v1 of the negotiation interface. |
| 68 | * It is deprecated since it cannot express PDF2 features or optional extensions. */ |
| 69 | typedef bool (*retro_vulkan_create_device_t)( |
| 70 | struct retro_vulkan_context *context, |
| 71 | VkInstance instance, |
| 72 | VkPhysicalDevice gpu, |
| 73 | VkSurfaceKHR surface, |
| 74 | PFN_vkGetInstanceProcAddr get_instance_proc_addr, |
| 75 | const char **required_device_extensions, |
| 76 | unsigned num_required_device_extensions, |
| 77 | const char **required_device_layers, |
| 78 | unsigned num_required_device_layers, |
| 79 | const VkPhysicalDeviceFeatures *required_features); |
| 80 | |
| 81 | typedef void (*retro_vulkan_destroy_device_t)(void); |
| 82 | |
| 83 | /* v2 CONTEXT_NEGOTIATION_INTERFACE only. */ |
| 84 | typedef VkInstance (*retro_vulkan_create_instance_wrapper_t)( |
| 85 | void *opaque, const VkInstanceCreateInfo *create_info); |
| 86 | |
| 87 | /* v2 CONTEXT_NEGOTIATION_INTERFACE only. */ |
| 88 | typedef VkInstance (*retro_vulkan_create_instance_t)( |
| 89 | PFN_vkGetInstanceProcAddr get_instance_proc_addr, |
| 90 | const VkApplicationInfo *app, |
| 91 | retro_vulkan_create_instance_wrapper_t create_instance_wrapper, |
| 92 | void *opaque); |
| 93 | |
| 94 | /* v2 CONTEXT_NEGOTIATION_INTERFACE only. */ |
| 95 | typedef VkDevice (*retro_vulkan_create_device_wrapper_t)( |
| 96 | VkPhysicalDevice gpu, void *opaque, |
| 97 | const VkDeviceCreateInfo *create_info); |
| 98 | |
| 99 | /* v2 CONTEXT_NEGOTIATION_INTERFACE only. */ |
| 100 | typedef bool (*retro_vulkan_create_device2_t)( |
| 101 | struct retro_vulkan_context *context, |
| 102 | VkInstance instance, |
| 103 | VkPhysicalDevice gpu, |
| 104 | VkSurfaceKHR surface, |
| 105 | PFN_vkGetInstanceProcAddr get_instance_proc_addr, |
| 106 | retro_vulkan_create_device_wrapper_t create_device_wrapper, |
| 107 | void *opaque); |
| 108 | |
| 109 | /* Note on thread safety: |
| 110 | * The Vulkan API is heavily designed around multi-threading, and |
| 111 | * the libretro interface for it should also be threading friendly. |
| 112 | * A core should be able to build command buffers and submit |
| 113 | * command buffers to the GPU from any thread. |
| 114 | */ |
| 115 | |
| 116 | struct retro_hw_render_context_negotiation_interface_vulkan |
| 117 | { |
| 118 | /* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN. */ |
| 119 | enum retro_hw_render_context_negotiation_interface_type interface_type; |
| 120 | /* Usually set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION, |
| 121 | * but can be lower depending on GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT. */ |
| 122 | unsigned interface_version; |
| 123 | |
| 124 | /* If non-NULL, returns a VkApplicationInfo struct that the frontend can use instead of |
| 125 | * its "default" application info. |
| 126 | * VkApplicationInfo::apiVersion also controls the target core Vulkan version for instance level functionality. |
| 127 | * Lifetime of the returned pointer must remain until the retro_vulkan_context is initialized. |
| 128 | * |
| 129 | * NOTE: For optimal compatibility with e.g. Android which is very slow to update its loader, |
| 130 | * a core version of 1.1 should be requested. Features beyond that can be requested with extensions. |
| 131 | * Vulkan 1.0 is only appropriate for legacy cores, but is still supported. |
| 132 | * A frontend is free to bump the instance creation apiVersion as necessary if the frontend requires more advanced core features. |
| 133 | * |
| 134 | * v2: This function must not be NULL, and must not return NULL. |
| 135 | * v1: It was not clearly defined if this function could return NULL. |
| 136 | * Frontends should be defensive and provide a default VkApplicationInfo |
| 137 | * if this function returns NULL or if this function is NULL. |
| 138 | */ |
| 139 | retro_vulkan_get_application_info_t get_application_info; |
| 140 | |
| 141 | /* If non-NULL, the libretro core will choose one or more physical devices, |
| 142 | * create one or more logical devices and create one or more queues. |
| 143 | * The core must prepare a designated PhysicalDevice, Device, Queue and queue family index |
| 144 | * which the frontend will use for its internal operation. |
| 145 | * |
| 146 | * If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this PhysicalDevice if the call succeeds. |
| 147 | * The core is still free to use other physical devices for other purposes that are private to the core. |
| 148 | * |
| 149 | * The frontend will request certain extensions and layers for a device which is created. |
| 150 | * The core must ensure that the queue and queue_family_index support GRAPHICS and COMPUTE. |
| 151 | * |
| 152 | * If surface is not VK_NULL_HANDLE, the core must consider presentation when creating the queues. |
| 153 | * If presentation to "surface" is supported on the queue, presentation_queue must be equal to queue. |
| 154 | * If not, a second queue must be provided in presentation_queue and presentation_queue_index. |
| 155 | * If surface is not VK_NULL_HANDLE, the instance from frontend will have been created with supported for |
| 156 | * VK_KHR_surface extension. |
| 157 | * |
| 158 | * The core is free to set its own queue priorities. |
| 159 | * Device provided to frontend is owned by the frontend, but any additional device resources must be freed by core |
| 160 | * in destroy_device callback. |
| 161 | * |
| 162 | * If this function returns true, a PhysicalDevice, Device and Queues are initialized. |
| 163 | * If false, none of the above have been initialized and the frontend will attempt |
| 164 | * to fallback to "default" device creation, as if this function was never called. |
| 165 | */ |
| 166 | retro_vulkan_create_device_t create_device; |
| 167 | |
| 168 | /* If non-NULL, this callback is called similar to context_destroy for HW_RENDER_INTERFACE. |
| 169 | * However, it will be called even if context_reset was not called. |
| 170 | * This can happen if the context never succeeds in being created. |
| 171 | * destroy_device will always be called before the VkInstance |
| 172 | * of the frontend is destroyed if create_device was called successfully so that the core has a chance of |
| 173 | * tearing down its own device resources. |
| 174 | * |
| 175 | * Only auxillary resources should be freed here, i.e. resources which are not part of retro_vulkan_context. |
| 176 | * v2: Auxillary instance resources created during create_instance can also be freed here. |
| 177 | */ |
| 178 | retro_vulkan_destroy_device_t destroy_device; |
| 179 | |
| 180 | /* v2 API: If interface_version is < 2, fields below must be ignored. |
| 181 | * If the frontend does not support interface version 2, the v1 entry points will be used instead. */ |
| 182 | |
| 183 | /* If non-NULL, this is called to create an instance, otherwise a VkInstance is created by the frontend. |
| 184 | * v1 interface bug: The only way to enable instance features is through core versions signalled in VkApplicationInfo. |
| 185 | * The frontend may request that certain extensions and layers |
| 186 | * are enabled on the VkInstance. Application may add additional features. |
| 187 | * If app is non-NULL, apiVersion controls the minimum core version required by the application. |
| 188 | * Return a VkInstance or VK_NULL_HANDLE. The VkInstance is owned by the frontend. |
| 189 | * |
| 190 | * Rather than call vkCreateInstance directly, a core must call the CreateInstance wrapper provided with: |
| 191 | * VkInstance instance = create_instance_wrapper(opaque, &create_info); |
| 192 | * If the core wishes to create a private instance for whatever reason (relying on shared memory for example), |
| 193 | * it may call vkCreateInstance directly. */ |
| 194 | retro_vulkan_create_instance_t create_instance; |
| 195 | |
| 196 | /* If non-NULL and frontend recognizes negotiation interface >= 2, create_device2 takes precedence over create_device. |
| 197 | * Similar to create_device, but is extended to better understand new core versions and PDF2 feature enablement. |
| 198 | * Requirements for create_device2 are the same as create_device unless a difference is mentioned. |
| 199 | * |
| 200 | * v2 consideration: |
| 201 | * If the chosen gpu by frontend cannot be supported, a core must return false. |
| 202 | * |
| 203 | * NOTE: "Cannot be supported" is intentionally vaguely defined. |
| 204 | * Refusing to run on an iGPU for a very intensive core with desktop GPU as a minimum spec may be in the gray area. |
| 205 | * Not supporting optional features is not a good reason to reject a physical device, however. |
| 206 | * |
| 207 | * On device creation feature with explicit gpu, a frontend should fall back create_device2 with gpu == VK_NULL_HANDLE and let core |
| 208 | * decide on a supported device if possible. |
| 209 | * |
| 210 | * A core must assume that the explicitly provided GPU is the only guaranteed attempt it has to create a device. |
| 211 | * A fallback may not be attempted if there are particular reasons why only a specific physical device can work, |
| 212 | * but these situations should be esoteric and rare in nature, e.g. a libretro frontend is implemented with external memory |
| 213 | * and only LUID matching would work. |
| 214 | * Cores and frontends should ensure "best effort" when negotiating like this and appropriate logging is encouraged. |
| 215 | * |
| 216 | * v1 note: In the v1 version of create_device, it was never expected that create_device would fail like this, |
| 217 | * and frontends are not expected to attempt fall backs. |
| 218 | * |
| 219 | * Rather than call vkCreateDevice directly, a core must call the CreateDevice wrapper provided with: |
| 220 | * VkDevice device = create_device_wrapper(gpu, opaque, &create_info); |
| 221 | * If the core wishes to create a private device for whatever reason (relying on shared memory for example), |
| 222 | * it may call vkCreateDevice directly. |
| 223 | * |
| 224 | * This allows the frontend to add additional extensions that it requires as well as adjust the PDF2 pNext as required. |
| 225 | * It is also possible adjust the queue create infos in case the frontend desires to allocate some private queues. |
| 226 | * |
| 227 | * The get_instance_proc_addr provided in create_device2 must be the same as create_instance. |
| 228 | * |
| 229 | * NOTE: The frontend must not disable features requested by application. |
| 230 | * NOTE: The frontend must not add any robustness features as some API behavior may change (VK_EXT_descriptor_buffer comes to mind). |
| 231 | * I.e. robustBufferAccess and the like. (nullDescriptor from robustness2 is allowed to be enabled). |
| 232 | */ |
| 233 | retro_vulkan_create_device2_t create_device2; |
| 234 | }; |
| 235 | |
| 236 | struct retro_hw_render_interface_vulkan |
| 237 | { |
| 238 | /* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN. */ |
| 239 | enum retro_hw_render_interface_type interface_type; |
| 240 | /* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION. */ |
| 241 | unsigned interface_version; |
| 242 | |
| 243 | /* Opaque handle to the Vulkan backend in the frontend |
| 244 | * which must be passed along to all function pointers |
| 245 | * in this interface. |
| 246 | * |
| 247 | * The rationale for including a handle here (which libretro v1 |
| 248 | * doesn't currently do in general) is: |
| 249 | * |
| 250 | * - Vulkan cores should be able to be freely threaded without lots of fuzz. |
| 251 | * This would break frontends which currently rely on TLS |
| 252 | * to deal with multiple cores loaded at the same time. |
| 253 | * - Fixing this in general is TODO for an eventual libretro v2. |
| 254 | */ |
| 255 | void *handle; |
| 256 | |
| 257 | /* The Vulkan instance the context is using. */ |
| 258 | VkInstance instance; |
| 259 | /* The physical device used. */ |
| 260 | VkPhysicalDevice gpu; |
| 261 | /* The logical device used. */ |
| 262 | VkDevice device; |
| 263 | |
| 264 | /* Allows a core to fetch all its needed symbols without having to link |
| 265 | * against the loader itself. */ |
| 266 | PFN_vkGetDeviceProcAddr get_device_proc_addr; |
| 267 | PFN_vkGetInstanceProcAddr get_instance_proc_addr; |
| 268 | |
| 269 | /* The queue the core must use to submit data. |
| 270 | * This queue and index must remain constant throughout the lifetime |
| 271 | * of the context. |
| 272 | * |
| 273 | * This queue will be the queue that supports graphics and compute |
| 274 | * if the device supports compute. |
| 275 | */ |
| 276 | VkQueue queue; |
| 277 | unsigned queue_index; |
| 278 | |
| 279 | /* Before calling retro_video_refresh_t with RETRO_HW_FRAME_BUFFER_VALID, |
| 280 | * set which image to use for this frame. |
| 281 | * |
| 282 | * If num_semaphores is non-zero, the frontend will wait for the |
| 283 | * semaphores provided to be signaled before using the results further |
| 284 | * in the pipeline. |
| 285 | * |
| 286 | * Semaphores provided by a single call to set_image will only be |
| 287 | * waited for once (waiting for a semaphore resets it). |
| 288 | * E.g. set_image, video_refresh, and then another |
| 289 | * video_refresh without set_image, |
| 290 | * but same image will only wait for semaphores once. |
| 291 | * |
| 292 | * For this reason, ownership transfer will only occur if semaphores |
| 293 | * are waited on for a particular frame in the frontend. |
| 294 | * |
| 295 | * Using semaphores is optional for synchronization purposes, |
| 296 | * but if not using |
| 297 | * semaphores, an image memory barrier in vkCmdPipelineBarrier |
| 298 | * should be used in the graphics_queue. |
| 299 | * Example: |
| 300 | * |
| 301 | * vkCmdPipelineBarrier(cmd, |
| 302 | * srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, |
| 303 | * dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, |
| 304 | * image_memory_barrier = { |
| 305 | * srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| 306 | * dstAccessMask = VK_ACCESS_SHADER_READ_BIT, |
| 307 | * }); |
| 308 | * |
| 309 | * The use of pipeline barriers instead of semaphores is encouraged |
| 310 | * as it is simpler and more fine-grained. A layout transition |
| 311 | * must generally happen anyways which requires a |
| 312 | * pipeline barrier. |
| 313 | * |
| 314 | * The image passed to set_image must have imageUsage flags set to at least |
| 315 | * VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_SAMPLED_BIT. |
| 316 | * The core will naturally want to use flags such as |
| 317 | * VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT and/or |
| 318 | * VK_IMAGE_USAGE_TRANSFER_DST_BIT depending |
| 319 | * on how the final image is created. |
| 320 | * |
| 321 | * The image must also have been created with MUTABLE_FORMAT bit set if |
| 322 | * 8-bit formats are used, so that the frontend can reinterpret sRGB |
| 323 | * formats as it sees fit. |
| 324 | * |
| 325 | * Images passed to set_image should be created with TILING_OPTIMAL. |
| 326 | * The image layout should be transitioned to either |
| 327 | * VK_IMAGE_LAYOUT_GENERIC or VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL. |
| 328 | * The actual image layout used must be set in image_layout. |
| 329 | * |
| 330 | * The image must be a 2D texture which may or not be layered |
| 331 | * and/or mipmapped. |
| 332 | * |
| 333 | * The image must be suitable for linear sampling. |
| 334 | * While the image_view is typically the only field used, |
| 335 | * the frontend may want to reinterpret the texture as sRGB vs. |
| 336 | * non-sRGB for example so the VkImageViewCreateInfo used to |
| 337 | * create the image view must also be passed in. |
| 338 | * |
| 339 | * The data in the pointer to the image struct will not be copied |
| 340 | * as the pNext field in create_info cannot be reliably deep-copied. |
| 341 | * The image pointer passed to set_image must be valid until |
| 342 | * retro_video_refresh_t has returned. |
| 343 | * |
| 344 | * If frame duping is used when passing NULL to retro_video_refresh_t, |
| 345 | * the frontend is free to either use the latest image passed to |
| 346 | * set_image or reuse the older pointer passed to set_image the |
| 347 | * frame RETRO_HW_FRAME_BUFFER_VALID was last used. |
| 348 | * |
| 349 | * Essentially, the lifetime of the pointer passed to |
| 350 | * retro_video_refresh_t should be extended if frame duping is used |
| 351 | * so that the frontend can reuse the older pointer. |
| 352 | * |
| 353 | * The image itself however, must not be touched by the core until |
| 354 | * wait_sync_index has been completed later. The frontend may perform |
| 355 | * layout transitions on the image, so even read-only access is not defined. |
| 356 | * The exception to read-only rule is if GENERAL layout is used for the image. |
| 357 | * In this case, the frontend is not allowed to perform any layout transitions, |
| 358 | * so concurrent reads from core and frontend are allowed. |
| 359 | * |
| 360 | * If frame duping is used, or if set_command_buffers is used, |
| 361 | * the frontend will not wait for any semaphores. |
| 362 | * |
| 363 | * The src_queue_family is used to specify which queue family |
| 364 | * the image is currently owned by. If using multiple queue families |
| 365 | * (e.g. async compute), the frontend will need to acquire ownership of the |
| 366 | * image before rendering with it and release the image afterwards. |
| 367 | * |
| 368 | * If src_queue_family is equal to the queue family (queue_index), |
| 369 | * no ownership transfer will occur. |
| 370 | * Similarly, if src_queue_family is VK_QUEUE_FAMILY_IGNORED, |
| 371 | * no ownership transfer will occur. |
| 372 | * |
| 373 | * The frontend will always release ownership back to src_queue_family. |
| 374 | * Waiting for frontend to complete with wait_sync_index() ensures that |
| 375 | * the frontend has released ownership back to the application. |
| 376 | * Note that in Vulkan, transfering ownership is a two-part process. |
| 377 | * |
| 378 | * Example frame: |
| 379 | * - core releases ownership from src_queue_index to queue_index with VkImageMemoryBarrier. |
| 380 | * - core calls set_image with src_queue_index. |
| 381 | * - Frontend will acquire the image with src_queue_index -> queue_index as well, completing the ownership transfer. |
| 382 | * - Frontend renders the frame. |
| 383 | * - Frontend releases ownership with queue_index -> src_queue_index. |
| 384 | * - Next time image is used, core must acquire ownership from queue_index ... |
| 385 | * |
| 386 | * Since the frontend releases ownership, we cannot necessarily dupe the frame because |
| 387 | * the core needs to make the roundtrip of ownership transfer. |
| 388 | */ |
| 389 | retro_vulkan_set_image_t set_image; |
| 390 | |
| 391 | /* Get the current sync index for this frame which is obtained in |
| 392 | * frontend by calling e.g. vkAcquireNextImageKHR before calling |
| 393 | * retro_run(). |
| 394 | * |
| 395 | * This index will correspond to which swapchain buffer is currently |
| 396 | * the active one. |
| 397 | * |
| 398 | * Knowing this index is very useful for maintaining safe asynchronous CPU |
| 399 | * and GPU operation without stalling. |
| 400 | * |
| 401 | * The common pattern for synchronization is to receive fences when |
| 402 | * submitting command buffers to Vulkan (vkQueueSubmit) and add this fence |
| 403 | * to a list of fences for frame number get_sync_index(). |
| 404 | * |
| 405 | * Next time we receive the same get_sync_index(), we can wait for the |
| 406 | * fences from before, which will usually return immediately as the |
| 407 | * frontend will generally also avoid letting the GPU run ahead too much. |
| 408 | * |
| 409 | * After the fence has signaled, we know that the GPU has completed all |
| 410 | * GPU work related to work submitted in the frame we last saw get_sync_index(). |
| 411 | * |
| 412 | * This means we can safely reuse or free resources allocated in this frame. |
| 413 | * |
| 414 | * In theory, even if we wait for the fences correctly, it is not technically |
| 415 | * safe to write to the image we earlier passed to the frontend since we're |
| 416 | * not waiting for the frontend GPU jobs to complete. |
| 417 | * |
| 418 | * The frontend will guarantee that the appropriate pipeline barrier |
| 419 | * in graphics_queue has been used such that |
| 420 | * VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT cannot |
| 421 | * start until the frontend is done with the image. |
| 422 | */ |
| 423 | retro_vulkan_get_sync_index_t get_sync_index; |
| 424 | |
| 425 | /* Returns a bitmask of how many swapchain images we currently have |
| 426 | * in the frontend. |
| 427 | * |
| 428 | * If bit #N is set in the return value, get_sync_index can return N. |
| 429 | * Knowing this value is useful for preallocating per-frame management |
| 430 | * structures ahead of time. |
| 431 | * |
| 432 | * While this value will typically remain constant throughout the |
| 433 | * applications lifecycle, it may for example change if the frontend |
| 434 | * suddently changes fullscreen state and/or latency. |
| 435 | * |
| 436 | * If this value ever changes, it is safe to assume that the device |
| 437 | * is completely idle and all synchronization objects can be deleted |
| 438 | * right away as desired. |
| 439 | */ |
| 440 | retro_vulkan_get_sync_index_mask_t get_sync_index_mask; |
| 441 | |
| 442 | /* Instead of submitting the command buffer to the queue first, the core |
| 443 | * can pass along its command buffer to the frontend, and the frontend |
| 444 | * will submit the command buffer together with the frontends command buffers. |
| 445 | * |
| 446 | * This has the advantage that the overhead of vkQueueSubmit can be |
| 447 | * amortized into a single call. For this mode, semaphores in set_image |
| 448 | * will be ignored, so vkCmdPipelineBarrier must be used to synchronize |
| 449 | * the core and frontend. |
| 450 | * |
| 451 | * The command buffers in set_command_buffers are only executed once, |
| 452 | * even if frame duping is used. |
| 453 | * |
| 454 | * If frame duping is used, set_image should be used for the frames |
| 455 | * which should be duped instead. |
| 456 | * |
| 457 | * Command buffers passed to the frontend with set_command_buffers |
| 458 | * must not actually be submitted to the GPU until retro_video_refresh_t |
| 459 | * is called. |
| 460 | * |
| 461 | * The frontend must submit the command buffer before submitting any |
| 462 | * other command buffers provided by set_command_buffers. */ |
| 463 | retro_vulkan_set_command_buffers_t set_command_buffers; |
| 464 | |
| 465 | /* Waits on CPU for device activity for the current sync index to complete. |
| 466 | * This is useful since the core will not have a relevant fence to sync with |
| 467 | * when the frontend is submitting the command buffers. */ |
| 468 | retro_vulkan_wait_sync_index_t wait_sync_index; |
| 469 | |
| 470 | /* If the core submits command buffers itself to any of the queues provided |
| 471 | * in this interface, the core must lock and unlock the frontend from |
| 472 | * racing on the VkQueue. |
| 473 | * |
| 474 | * Queue submission can happen on any thread. |
| 475 | * Even if queue submission happens on the same thread as retro_run(), |
| 476 | * the lock/unlock functions must still be called. |
| 477 | * |
| 478 | * NOTE: Queue submissions are heavy-weight. */ |
| 479 | retro_vulkan_lock_queue_t lock_queue; |
| 480 | retro_vulkan_unlock_queue_t unlock_queue; |
| 481 | |
| 482 | /* Sets a semaphore which is signaled when the image in set_image can safely be reused. |
| 483 | * The semaphore is consumed next call to retro_video_refresh_t. |
| 484 | * The semaphore will be signalled even for duped frames. |
| 485 | * The semaphore will be signalled only once, so set_signal_semaphore should be called every frame. |
| 486 | * The semaphore may be VK_NULL_HANDLE, which disables semaphore signalling for next call to retro_video_refresh_t. |
| 487 | * |
| 488 | * This is mostly useful to support use cases where you're rendering to a single image that |
| 489 | * is recycled in a ping-pong fashion with the frontend to save memory (but potentially less throughput). |
| 490 | */ |
| 491 | retro_vulkan_set_signal_semaphore_t set_signal_semaphore; |
| 492 | }; |
| 493 | |
| 494 | #endif |