3535#include " thirdparty/misc/smolv.h"
3636#include " vulkan_hooks.h"
3737
38+ #if defined(ANDROID_ENABLED)
39+ #include " platform/android/java_godot_wrapper.h"
40+ #include " platform/android/os_android.h"
41+ #include " platform/android/thread_jandroid.h"
42+ #endif
43+
44+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
45+ #include " thirdparty/swappy-frame-pacing/swappyVk.h"
46+ #endif
47+
3848#define ARRAY_SIZE (a ) (sizeof (a) / sizeof (a[0 ]))
3949
4050#define PRINT_NATIVE_COMMANDS 0
@@ -538,6 +548,37 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
538548 err = vkEnumerateDeviceExtensionProperties (physical_device, nullptr , &device_extension_count, device_extensions.ptr ());
539549 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
540550
551+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
552+ if (swappy_frame_pacer_enable) {
553+ char **swappy_required_extensions;
554+ uint32_t swappy_required_extensions_count = 0 ;
555+ // Determine number of extensions required by Swappy frame pacer.
556+ SwappyVk_determineDeviceExtensions (physical_device, device_extension_count, device_extensions.ptr (), &swappy_required_extensions_count, nullptr );
557+
558+ if (swappy_required_extensions_count < device_extension_count) {
559+ // Determine the actual extensions.
560+ swappy_required_extensions = (char **)malloc (swappy_required_extensions_count * sizeof (char *));
561+ char *pRequiredExtensionsData = (char *)malloc (swappy_required_extensions_count * (VK_MAX_EXTENSION_NAME_SIZE + 1 ));
562+ for (uint32_t i = 0 ; i < swappy_required_extensions_count; i++) {
563+ swappy_required_extensions[i] = &pRequiredExtensionsData[i * (VK_MAX_EXTENSION_NAME_SIZE + 1 )];
564+ }
565+ SwappyVk_determineDeviceExtensions (physical_device, device_extension_count,
566+ device_extensions.ptr (), &swappy_required_extensions_count, swappy_required_extensions);
567+
568+ // Enable extensions requested by Swappy.
569+ for (uint32_t i = 0 ; i < swappy_required_extensions_count; i++) {
570+ CharString extension_name (swappy_required_extensions[i]);
571+ if (requested_device_extensions.has (extension_name)) {
572+ enabled_device_extension_names.insert (extension_name);
573+ }
574+ }
575+
576+ free (pRequiredExtensionsData);
577+ free (swappy_required_extensions);
578+ }
579+ }
580+ #endif
581+
541582#ifdef DEV_ENABLED
542583 for (uint32_t i = 0 ; i < device_extension_count; i++) {
543584 print_verbose (String (" VULKAN: Found device extension " ) + String::utf8 (device_extensions[i].extensionName ));
@@ -1379,6 +1420,18 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
13791420 breadcrumb_buffer = buffer_create (2u * sizeof (uint32_t ) * BREADCRUMB_BUFFER_ENTRIES, BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
13801421#endif
13811422
1423+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
1424+ swappy_frame_pacer_enable = GLOBAL_GET (" display/window/frame_pacing/android/enable_frame_pacing" );
1425+ swappy_mode = GLOBAL_GET (" display/window/frame_pacing/android/swappy_mode" );
1426+
1427+ if (VulkanHooks::get_singleton () != nullptr ) {
1428+ // Hooks control device creation & possibly presentation
1429+ // (e.g. OpenXR) thus it's too risky to use Swappy.
1430+ swappy_frame_pacer_enable = false ;
1431+ OS::get_singleton ()->print (" VulkanHooks detected (e.g. OpenXR): Force-disabling Swappy Frame Pacing.\n " );
1432+ }
1433+ #endif
1434+
13821435 return OK;
13831436}
13841437
@@ -2364,6 +2417,14 @@ RDD::CommandQueueID RenderingDeviceDriverVulkan::command_queue_create(CommandQue
23642417
23652418 ERR_FAIL_COND_V_MSG (picked_queue_index >= queue_family.size (), CommandQueueID (), " A queue in the picked family could not be found." );
23662419
2420+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2421+ if (swappy_frame_pacer_enable) {
2422+ VkQueue selected_queue;
2423+ vkGetDeviceQueue (vk_device, family_index, picked_queue_index, &selected_queue);
2424+ SwappyVk_setQueueFamilyIndex (vk_device, selected_queue, family_index);
2425+ }
2426+ #endif
2427+
23672428 // Create the virtual queue.
23682429 CommandQueue *command_queue = memnew (CommandQueue);
23692430 command_queue->queue_family = family_index;
@@ -2509,7 +2570,16 @@ Error RenderingDeviceDriverVulkan::command_queue_execute_and_present(CommandQueu
25092570 present_info.pResults = results.ptr ();
25102571
25112572 device_queue.submit_mutex .lock ();
2573+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2574+ if (swappy_frame_pacer_enable) {
2575+ err = SwappyVk_queuePresent (device_queue.queue , &present_info);
2576+ } else {
2577+ err = device_functions.QueuePresentKHR (device_queue.queue , &present_info);
2578+ }
2579+ #else
25122580 err = device_functions.QueuePresentKHR (device_queue.queue , &present_info);
2581+ #endif
2582+
25132583 device_queue.submit_mutex .unlock ();
25142584
25152585 // Set the index to an invalid value. If any of the swap chains returned out of date, indicate it should be resized the next time it's acquired.
@@ -2691,6 +2761,14 @@ void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
26912761 swap_chain->framebuffers .clear ();
26922762
26932763 if (swap_chain->vk_swapchain != VK_NULL_HANDLE) {
2764+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2765+ if (swappy_frame_pacer_enable) {
2766+ // Swappy has a bug where the ANativeWindow will be leaked if we call
2767+ // SwappyVk_destroySwapchain, so we must release it by hand.
2768+ SwappyVk_setWindow (vk_device, swap_chain->vk_swapchain , nullptr );
2769+ SwappyVk_destroySwapchain (vk_device, swap_chain->vk_swapchain );
2770+ }
2771+ #endif
26942772 device_functions.DestroySwapchainKHR (vk_device, swap_chain->vk_swapchain , VKC::get_allocation_callbacks (VK_OBJECT_TYPE_SWAPCHAIN_KHR));
26952773 swap_chain->vk_swapchain = VK_NULL_HANDLE;
26962774 }
@@ -2807,6 +2885,20 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
28072885 VkResult err = functions.GetPhysicalDeviceSurfaceCapabilitiesKHR (physical_device, surface->vk_surface , &surface_capabilities);
28082886 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
28092887
2888+ // No swapchain yet, this is the first time we're creating it.
2889+ if (!swap_chain->vk_swapchain ) {
2890+ uint32_t width = surface_capabilities.currentExtent .width ;
2891+ uint32_t height = surface_capabilities.currentExtent .height ;
2892+ if (surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
2893+ surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
2894+ // Swap to get identity width and height.
2895+ surface_capabilities.currentExtent .height = width;
2896+ surface_capabilities.currentExtent .width = height;
2897+ }
2898+
2899+ native_display_size = surface_capabilities.currentExtent ;
2900+ }
2901+
28102902 VkExtent2D extent;
28112903 if (surface_capabilities.currentExtent .width == 0xFFFFFFFF ) {
28122904 // The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities.
@@ -2871,15 +2963,8 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
28712963 desired_swapchain_images = MIN (desired_swapchain_images, surface_capabilities.maxImageCount );
28722964 }
28732965
2874- // Prefer identity transform if it's supported, use the current transform otherwise.
2875- // This behavior is intended as Godot does not supported native rotation in platforms that use these bits.
28762966 // Refer to the comment in command_queue_present() for more details.
2877- VkSurfaceTransformFlagBitsKHR surface_transform_bits;
2878- if (surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
2879- surface_transform_bits = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
2880- } else {
2881- surface_transform_bits = surface_capabilities.currentTransform ;
2882- }
2967+ VkSurfaceTransformFlagBitsKHR surface_transform_bits = surface_capabilities.currentTransform ;
28832968
28842969 VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
28852970 if (OS::get_singleton ()->is_layered_allowed () || !(surface_capabilities.supportedCompositeAlpha & composite_alpha)) {
@@ -2906,7 +2991,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
29062991 swap_create_info.minImageCount = desired_swapchain_images;
29072992 swap_create_info.imageFormat = swap_chain->format ;
29082993 swap_create_info.imageColorSpace = swap_chain->color_space ;
2909- swap_create_info.imageExtent = extent ;
2994+ swap_create_info.imageExtent = native_display_size ;
29102995 swap_create_info.imageArrayLayers = 1 ;
29112996 swap_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
29122997 swap_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -2917,6 +3002,39 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
29173002 err = device_functions.CreateSwapchainKHR (vk_device, &swap_create_info, VKC::get_allocation_callbacks (VK_OBJECT_TYPE_SWAPCHAIN_KHR), &swap_chain->vk_swapchain );
29183003 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
29193004
3005+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
3006+ if (swappy_frame_pacer_enable) {
3007+ const double max_fps = Engine::get_singleton ()->get_max_fps ();
3008+ const uint64_t max_time = max_fps > 0 ? uint64_t ((1000.0 * 1000.0 * 1000.0 ) / max_fps) : 0 ;
3009+
3010+ SwappyVk_initAndGetRefreshCycleDuration (get_jni_env (), static_cast <OS_Android *>(OS::get_singleton ())->get_godot_java ()->get_activity (), physical_device,
3011+ vk_device, swap_chain->vk_swapchain , &swap_chain->refresh_duration );
3012+ SwappyVk_setWindow (vk_device, swap_chain->vk_swapchain , static_cast <OS_Android *>(OS::get_singleton ())->get_native_window ());
3013+ SwappyVk_setSwapIntervalNS (vk_device, swap_chain->vk_swapchain , MAX (swap_chain->refresh_duration , max_time));
3014+
3015+ enum SwappyModes {
3016+ PIPELINE_FORCED_ON,
3017+ AUTO_FPS_PIPELINE_FORCED_ON,
3018+ AUTO_FPS_AUTO_PIPELINE,
3019+ };
3020+
3021+ switch (swappy_mode) {
3022+ case PIPELINE_FORCED_ON:
3023+ SwappyVk_setAutoSwapInterval (true );
3024+ SwappyVk_setAutoPipelineMode (true );
3025+ break ;
3026+ case AUTO_FPS_PIPELINE_FORCED_ON:
3027+ SwappyVk_setAutoSwapInterval (true );
3028+ SwappyVk_setAutoPipelineMode (false );
3029+ break ;
3030+ case AUTO_FPS_AUTO_PIPELINE:
3031+ SwappyVk_setAutoSwapInterval (false );
3032+ SwappyVk_setAutoPipelineMode (false );
3033+ break ;
3034+ }
3035+ }
3036+ #endif
3037+
29203038 uint32_t image_count = 0 ;
29213039 err = device_functions.GetSwapchainImagesKHR (vk_device, swap_chain->vk_swapchain , &image_count, nullptr );
29223040 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
@@ -3064,6 +3182,22 @@ RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p
30643182 }
30653183}
30663184
3185+ void RenderingDeviceDriverVulkan::swap_chain_set_max_fps (SwapChainID p_swap_chain, int p_max_fps) {
3186+ DEV_ASSERT (p_swap_chain.id != 0 );
3187+
3188+ #ifdef SWAPPY_FRAME_PACING_ENABLED
3189+ if (!swappy_frame_pacer_enable) {
3190+ return ;
3191+ }
3192+
3193+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id );
3194+ if (swap_chain->vk_swapchain != VK_NULL_HANDLE) {
3195+ const uint64_t max_time = p_max_fps > 0 ? uint64_t ((1000.0 * 1000.0 * 1000.0 ) / p_max_fps) : 0 ;
3196+ SwappyVk_setSwapIntervalNS (vk_device, swap_chain->vk_swapchain , MAX (swap_chain->refresh_duration , max_time));
3197+ }
3198+ #endif
3199+ }
3200+
30673201void RenderingDeviceDriverVulkan::swap_chain_free (SwapChainID p_swap_chain) {
30683202 DEV_ASSERT (p_swap_chain.id != 0 );
30693203
0 commit comments