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
@@ -533,6 +543,37 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
533543 err = vkEnumerateDeviceExtensionProperties (physical_device, nullptr , &device_extension_count, device_extensions.ptr ());
534544 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
535545
546+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
547+ if (swappy_frame_pacer_enable) {
548+ char **swappy_required_extensions;
549+ uint32_t swappy_required_extensions_count = 0 ;
550+ // Determine number of extensions required by Swappy frame pacer.
551+ SwappyVk_determineDeviceExtensions (physical_device, device_extension_count, device_extensions.ptr (), &swappy_required_extensions_count, nullptr );
552+
553+ if (swappy_required_extensions_count < device_extension_count) {
554+ // Determine the actual extensions.
555+ swappy_required_extensions = (char **)malloc (swappy_required_extensions_count * sizeof (char *));
556+ char *pRequiredExtensionsData = (char *)malloc (swappy_required_extensions_count * (VK_MAX_EXTENSION_NAME_SIZE + 1 ));
557+ for (uint32_t i = 0 ; i < swappy_required_extensions_count; i++) {
558+ swappy_required_extensions[i] = &pRequiredExtensionsData[i * (VK_MAX_EXTENSION_NAME_SIZE + 1 )];
559+ }
560+ SwappyVk_determineDeviceExtensions (physical_device, device_extension_count,
561+ device_extensions.ptr (), &swappy_required_extensions_count, swappy_required_extensions);
562+
563+ // Enable extensions requested by Swappy.
564+ for (uint32_t i = 0 ; i < swappy_required_extensions_count; i++) {
565+ CharString extension_name (swappy_required_extensions[i]);
566+ if (requested_device_extensions.has (extension_name)) {
567+ enabled_device_extension_names.insert (extension_name);
568+ }
569+ }
570+
571+ free (pRequiredExtensionsData);
572+ free (swappy_required_extensions);
573+ }
574+ }
575+ #endif
576+
536577#ifdef DEV_ENABLED
537578 for (uint32_t i = 0 ; i < device_extension_count; i++) {
538579 print_verbose (String (" VULKAN: Found device extension " ) + String::utf8 (device_extensions[i].extensionName ));
@@ -1371,6 +1412,18 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
13711412 max_descriptor_sets_per_pool = GLOBAL_GET (" rendering/rendering_device/vulkan/max_descriptors_per_pool" );
13721413 breadcrumb_buffer = buffer_create (sizeof (uint32_t ), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
13731414
1415+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
1416+ swappy_frame_pacer_enable = GLOBAL_GET (" display/window/frame_pacing/android/enable_frame_pacing" );
1417+ swappy_mode = GLOBAL_GET (" display/window/frame_pacing/android/swappy_mode" );
1418+
1419+ if (VulkanHooks::get_singleton () != nullptr ) {
1420+ // Hooks control device creation & possibly presentation
1421+ // (e.g. OpenXR) thus it's too risky to use Swappy.
1422+ swappy_frame_pacer_enable = false ;
1423+ OS::get_singleton ()->print (" VulkanHooks detected (e.g. OpenXR): Force-disabling Swappy Frame Pacing.\n " );
1424+ }
1425+ #endif
1426+
13741427 return OK;
13751428}
13761429
@@ -2356,6 +2409,14 @@ RDD::CommandQueueID RenderingDeviceDriverVulkan::command_queue_create(CommandQue
23562409
23572410 ERR_FAIL_COND_V_MSG (picked_queue_index >= queue_family.size (), CommandQueueID (), " A queue in the picked family could not be found." );
23582411
2412+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2413+ if (swappy_frame_pacer_enable) {
2414+ VkQueue selected_queue;
2415+ vkGetDeviceQueue (vk_device, family_index, picked_queue_index, &selected_queue);
2416+ SwappyVk_setQueueFamilyIndex (vk_device, selected_queue, family_index);
2417+ }
2418+ #endif
2419+
23592420 // Create the virtual queue.
23602421 CommandQueue *command_queue = memnew (CommandQueue);
23612422 command_queue->queue_family = family_index;
@@ -2501,7 +2562,16 @@ Error RenderingDeviceDriverVulkan::command_queue_execute_and_present(CommandQueu
25012562 present_info.pResults = results.ptr ();
25022563
25032564 device_queue.submit_mutex .lock ();
2565+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2566+ if (swappy_frame_pacer_enable) {
2567+ err = SwappyVk_queuePresent (device_queue.queue , &present_info);
2568+ } else {
2569+ err = device_functions.QueuePresentKHR (device_queue.queue , &present_info);
2570+ }
2571+ #else
25042572 err = device_functions.QueuePresentKHR (device_queue.queue , &present_info);
2573+ #endif
2574+
25052575 device_queue.submit_mutex .unlock ();
25062576
25072577 // 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.
@@ -2681,6 +2751,14 @@ void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
26812751 swap_chain->framebuffers .clear ();
26822752
26832753 if (swap_chain->vk_swapchain != VK_NULL_HANDLE) {
2754+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2755+ if (swappy_frame_pacer_enable) {
2756+ // Swappy has a bug where the ANativeWindow will be leaked if we call
2757+ // SwappyVk_destroySwapchain, so we must release it by hand.
2758+ SwappyVk_setWindow (vk_device, swap_chain->vk_swapchain , nullptr );
2759+ SwappyVk_destroySwapchain (vk_device, swap_chain->vk_swapchain );
2760+ }
2761+ #endif
26842762 device_functions.DestroySwapchainKHR (vk_device, swap_chain->vk_swapchain , VKC::get_allocation_callbacks (VK_OBJECT_TYPE_SWAPCHAIN_KHR));
26852763 swap_chain->vk_swapchain = VK_NULL_HANDLE;
26862764 }
@@ -2797,6 +2875,20 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
27972875 VkResult err = functions.GetPhysicalDeviceSurfaceCapabilitiesKHR (physical_device, surface->vk_surface , &surface_capabilities);
27982876 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
27992877
2878+ // No swapchain yet, this is the first time we're creating it.
2879+ if (!swap_chain->vk_swapchain ) {
2880+ uint32_t width = surface_capabilities.currentExtent .width ;
2881+ uint32_t height = surface_capabilities.currentExtent .height ;
2882+ if (surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
2883+ surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
2884+ // Swap to get identity width and height.
2885+ surface_capabilities.currentExtent .height = width;
2886+ surface_capabilities.currentExtent .width = height;
2887+ }
2888+
2889+ native_display_size = surface_capabilities.currentExtent ;
2890+ }
2891+
28002892 VkExtent2D extent;
28012893 if (surface_capabilities.currentExtent .width == 0xFFFFFFFF ) {
28022894 // The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities.
@@ -2863,15 +2955,8 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
28632955 desired_swapchain_images = MIN (desired_swapchain_images, surface_capabilities.maxImageCount );
28642956 }
28652957
2866- // Prefer identity transform if it's supported, use the current transform otherwise.
2867- // This behavior is intended as Godot does not supported native rotation in platforms that use these bits.
28682958 // Refer to the comment in command_queue_present() for more details.
2869- VkSurfaceTransformFlagBitsKHR surface_transform_bits;
2870- if (surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
2871- surface_transform_bits = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
2872- } else {
2873- surface_transform_bits = surface_capabilities.currentTransform ;
2874- }
2959+ VkSurfaceTransformFlagBitsKHR surface_transform_bits = surface_capabilities.currentTransform ;
28752960
28762961 VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
28772962 if (OS::get_singleton ()->is_layered_allowed () || !(surface_capabilities.supportedCompositeAlpha & composite_alpha)) {
@@ -2898,7 +2983,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
28982983 swap_create_info.minImageCount = desired_swapchain_images;
28992984 swap_create_info.imageFormat = swap_chain->format ;
29002985 swap_create_info.imageColorSpace = swap_chain->color_space ;
2901- swap_create_info.imageExtent = extent ;
2986+ swap_create_info.imageExtent = native_display_size ;
29022987 swap_create_info.imageArrayLayers = 1 ;
29032988 swap_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
29042989 swap_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -2909,6 +2994,39 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
29092994 err = device_functions.CreateSwapchainKHR (vk_device, &swap_create_info, VKC::get_allocation_callbacks (VK_OBJECT_TYPE_SWAPCHAIN_KHR), &swap_chain->vk_swapchain );
29102995 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
29112996
2997+ #if defined(SWAPPY_FRAME_PACING_ENABLED)
2998+ if (swappy_frame_pacer_enable) {
2999+ const double max_fps = Engine::get_singleton ()->get_max_fps ();
3000+ const uint64_t max_time = max_fps > 0 ? uint64_t ((1000.0 * 1000.0 * 1000.0 ) / max_fps) : 0 ;
3001+
3002+ SwappyVk_initAndGetRefreshCycleDuration (get_jni_env (), static_cast <OS_Android *>(OS::get_singleton ())->get_godot_java ()->get_activity (), physical_device,
3003+ vk_device, swap_chain->vk_swapchain , &swap_chain->refresh_duration );
3004+ SwappyVk_setWindow (vk_device, swap_chain->vk_swapchain , static_cast <OS_Android *>(OS::get_singleton ())->get_native_window ());
3005+ SwappyVk_setSwapIntervalNS (vk_device, swap_chain->vk_swapchain , MAX (swap_chain->refresh_duration , max_time));
3006+
3007+ enum SwappyModes {
3008+ PIPELINE_FORCED_ON,
3009+ AUTO_FPS_PIPELINE_FORCED_ON,
3010+ AUTO_FPS_AUTO_PIPELINE,
3011+ };
3012+
3013+ switch (swappy_mode) {
3014+ case PIPELINE_FORCED_ON:
3015+ SwappyVk_setAutoSwapInterval (true );
3016+ SwappyVk_setAutoPipelineMode (true );
3017+ break ;
3018+ case AUTO_FPS_PIPELINE_FORCED_ON:
3019+ SwappyVk_setAutoSwapInterval (true );
3020+ SwappyVk_setAutoPipelineMode (false );
3021+ break ;
3022+ case AUTO_FPS_AUTO_PIPELINE:
3023+ SwappyVk_setAutoSwapInterval (false );
3024+ SwappyVk_setAutoPipelineMode (false );
3025+ break ;
3026+ }
3027+ }
3028+ #endif
3029+
29123030 uint32_t image_count = 0 ;
29133031 err = device_functions.GetSwapchainImagesKHR (vk_device, swap_chain->vk_swapchain , &image_count, nullptr );
29143032 ERR_FAIL_COND_V (err != VK_SUCCESS, ERR_CANT_CREATE);
@@ -3049,6 +3167,22 @@ RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p
30493167 }
30503168}
30513169
3170+ void RenderingDeviceDriverVulkan::swap_chain_set_max_fps (SwapChainID p_swap_chain, int p_max_fps) {
3171+ DEV_ASSERT (p_swap_chain.id != 0 );
3172+
3173+ #ifdef SWAPPY_FRAME_PACING_ENABLED
3174+ if (!swappy_frame_pacer_enable) {
3175+ return ;
3176+ }
3177+
3178+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id );
3179+ if (swap_chain->vk_swapchain != VK_NULL_HANDLE) {
3180+ const uint64_t max_time = p_max_fps > 0 ? uint64_t ((1000.0 * 1000.0 * 1000.0 ) / p_max_fps) : 0 ;
3181+ SwappyVk_setSwapIntervalNS (vk_device, swap_chain->vk_swapchain , MAX (swap_chain->refresh_duration , max_time));
3182+ }
3183+ #endif
3184+ }
3185+
30523186void RenderingDeviceDriverVulkan::swap_chain_free (SwapChainID p_swap_chain) {
30533187 DEV_ASSERT (p_swap_chain.id != 0 );
30543188
0 commit comments