From 2bf9dc71daa7661d2dc7d849069fe1176c344812 Mon Sep 17 00:00:00 2001 From: "Matias N. Goldberg" Date: Fri, 28 Feb 2025 15:01:07 -0300 Subject: [PATCH] Update to latest version of Swappy Fixes #103294 (cherry picked from commit 89ea5b3d005126a2f7c463f7f487c5001d1b949e) --- .github/workflows/android_builds.yml | 4 +- .gitignore | 5 + platform/android/detect.py | 2 +- .../common/gamesdk_common.h | 7 +- thirdparty/swappy-frame-pacing/swappyVk.h | 105 +++++++--- .../swappy-frame-pacing/swappy_common.h | 193 +++++++++--------- 6 files changed, 187 insertions(+), 129 deletions(-) diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index f1c721fb638..865f826bc0f 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -62,8 +62,8 @@ jobs: - name: Download pre-built Android Swappy Frame Pacing Library uses: dsaltares/fetch-gh-release-asset@1.1.2 with: - repo: darksylinc/godot-swappy - version: tags/v2023.3.0.0 + repo: godotengine/godot-swappy + version: tags/from-source-2025-01-31 file: godot-swappy.7z target: swappy/godot-swappy.7z diff --git a/.gitignore b/.gitignore index 2c3bf742ba2..8adc7b786c6 100644 --- a/.gitignore +++ b/.gitignore @@ -263,6 +263,11 @@ bld/ !thirdparty/**/arm/ !thirdparty/**/arm64/ +thirdparty/swappy-frame-pacing/arm64-v8a/abi.json +thirdparty/swappy-frame-pacing/armeabi-v7a/abi.json +thirdparty/swappy-frame-pacing/x86/abi.json +thirdparty/swappy-frame-pacing/x86_64/abi.json + # Visual Studio 2015/2017 cache/options directory .vs/ diff --git a/platform/android/detect.py b/platform/android/detect.py index 571cfd9819c..cac7c3f48d4 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -187,7 +187,7 @@ def configure(env: "SConsEnvironment"): has_swappy = detect_swappy() if not has_swappy: print_warning( - "Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/darksylinc/godot-swappy/releases and extract it so that the following files can be found:\n" + "Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/godotengine/godot-swappy/releases and extract it so that the following files can be found:\n" + " thirdparty/swappy-frame-pacing/arm64-v8a/libswappy_static.a\n" + " thirdparty/swappy-frame-pacing/armeabi-v7a/libswappy_static.a\n" + " thirdparty/swappy-frame-pacing/x86/libswappy_static.a\n" diff --git a/thirdparty/swappy-frame-pacing/common/gamesdk_common.h b/thirdparty/swappy-frame-pacing/common/gamesdk_common.h index d29ac01af38..25ce813968f 100644 --- a/thirdparty/swappy-frame-pacing/common/gamesdk_common.h +++ b/thirdparty/swappy-frame-pacing/common/gamesdk_common.h @@ -31,11 +31,12 @@ // There are separate versions for each GameSDK component that use this format: #define ANDROID_GAMESDK_PACKED_VERSION(MAJOR, MINOR, BUGFIX) \ - ((MAJOR << 16) | (MINOR << 8) | (BUGFIX)) + ((MAJOR << 16) | (MINOR << 8) | (BUGFIX)) // Accessors #define ANDROID_GAMESDK_MAJOR_VERSION(PACKED) ((PACKED) >> 16) #define ANDROID_GAMESDK_MINOR_VERSION(PACKED) (((PACKED) >> 8) & 0xff) #define ANDROID_GAMESDK_BUGFIX_VERSION(PACKED) ((PACKED) & 0xff) -#define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX, GIT) \ -#MAJOR "." #MINOR "." #BUGFIX "." #GIT +#define AGDK_STRINGIFY(NUMBER) #NUMBER +#define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX) \ + AGDK_STRINGIFY(MAJOR) "." AGDK_STRINGIFY(MINOR) "." AGDK_STRINGIFY(BUGFIX) diff --git a/thirdparty/swappy-frame-pacing/swappyVk.h b/thirdparty/swappy-frame-pacing/swappyVk.h index 020683cbc43..654fcbf4a4a 100644 --- a/thirdparty/swappy-frame-pacing/swappyVk.h +++ b/thirdparty/swappy-frame-pacing/swappyVk.h @@ -281,30 +281,30 @@ void SwappyVk_uninjectTracer(const SwappyTracer* tracer); * Usage of this functionality is optional. */ typedef struct SwappyVkFunctionProvider { - /** - * @brief Callback to initialize the function provider. - * - * This function is called by Swappy before any functions are requested. - * E.g. so you can call dlopen on the Vulkan library. - */ - bool (*init)(); + /** + * @brief Callback to initialize the function provider. + * + * This function is called by Swappy before any functions are requested. + * E.g. so you can call dlopen on the Vulkan library. + */ + bool (*init)(); - /** - * @brief Callback to get the address of a function. - * - * This function is called by Swappy to get the address of a Vulkan - * function. - * @param name The null-terminated name of the function. - */ - void* (*getProcAddr)(const char* name); + /** + * @brief Callback to get the address of a function. + * + * This function is called by Swappy to get the address of a Vulkan + * function. + * @param name The null-terminated name of the function. + */ + void* (*getProcAddr)(const char* name); - /** - * @brief Callback to close any resources owned by the function provider. - * - * This function is called by Swappy when no more functions will be - * requested, e.g. so you can call dlclose on the Vulkan library. - */ - void (*close)(); + /** + * @brief Callback to close any resources owned by the function provider. + * + * This function is called by Swappy when no more functions will be + * requested, e.g. so you can call dlclose on the Vulkan library. + */ + void (*close)(); } SwappyVkFunctionProvider; /** @@ -384,7 +384,8 @@ void SwappyVk_enableStats(VkSwapchainKHR swapchain, bool enabled); * @see SwappyVk_enableStats. */ -void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain, uint32_t image); +void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain, + uint32_t image); /** * @brief Returns the stats collected, if statistics collection was toggled on. @@ -396,11 +397,11 @@ void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain, uint32_t * conditions. * * @param[in] swapchain - The swapchain for which stats are being queried. - * @param swappyStats - Pointer to a SwappyStats that will be populated with - * the collected stats. Cannot be NULL. + * @param swappyStats - Pointer to a SwappyStats that will be populated + * with the collected stats. Cannot be NULL. * @see SwappyStats */ -void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats *swappyStats); +void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats* swappyStats); /** * @brief Clears the frame statistics collected so far. @@ -413,6 +414,58 @@ void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats *swappyStats); */ void SwappyVk_clearStats(VkSwapchainKHR swapchain); +/** + * @brief Reset the swappy pacing mechanism + * + * In cases where the frame timing history is irrelevant (for example during + * scene/level transitions or after loading screens), calling this would + * remove all the history for frame pacing. Calling this entry point + * would reset the frame rate to the initial state at the end of the current + * frame. Then swappy would just pace as normal with fresh state from next + * frame. There are no error conditions associated with this call. + * + * @param[in] swapchain - The swapchain for which frame pacing is reset. + */ +void SwappyVk_resetFramePacing(VkSwapchainKHR swapchain); + +/** + * @brief Enable/Disable the swappy pacing mechanism + * + * By default frame pacing is enabled when swappy is used, however it can be + * disabled at runtime by calling `SwappyVk_enableFramePacing(swapchain, + * false)`. When the frame pacing is disabled, ::SwappyVk_enableBlockingWait + * will control whether + * ::SwappyVk_queuePresent will return immediately, or will block until the + * previous frame's GPU work is completed. All enabling/disabling effects take + * place from next frame. Once disabled, `SwappyVk_enableFramePacing(swapchain, + * true)` will enable frame pacing again with a fresh frame time state - + * equivalent of calling ::SwappyVk_resetFramePacing(). + * + * @param[in] swapchain - The swapchain for which stats are being queried. + * @param enable - If true, enables frame pacing otherwise disables it + */ +void SwappyVk_enableFramePacing(VkSwapchainKHR swapchain, bool enable); + +/** + * @brief Enable/Disable blocking wait when frame-pacing is disabled + * + * By default ::SwappyVk_queuePresent will do a blocking wait until previous + * frame's GPU work is completed. However when frame pacing is disabled, calling + * `SwappyVk_enableBlockingWait(swapchain, false)` will ensure that + * ::SwappyVk_queuePresent returns without waiting for previous frame's GPU + * work. This behaviour impacts the GPU time returned in the + * ::SwappyPostWaitCallback callback. If the last frame's GPU work is done by + * the time ::SwappyVk_queuePresent for this frame is called, then the previous + * frame's GPU time will be returned otherwise `-1` will be delivered in the + * callback for the frame. This setting has no impact when frame pacing is + * enabled. + * + * @param[in] swapchain - The swapchain for which stats are being queried. + * @param enable - If true, ::SwappyVk_queuePresent will block until + * the previous frame's GPU work is complete. + */ +void SwappyVk_enableBlockingWait(VkSwapchainKHR swapchain, bool enable); + #ifdef __cplusplus } // extern "C" #endif diff --git a/thirdparty/swappy-frame-pacing/swappy_common.h b/thirdparty/swappy-frame-pacing/swappy_common.h index b711ca910fb..acceb4243df 100644 --- a/thirdparty/swappy-frame-pacing/swappy_common.h +++ b/thirdparty/swappy-frame-pacing/swappy_common.h @@ -48,22 +48,22 @@ // Internal macros to track Swappy version, do not use directly. #define SWAPPY_MAJOR_VERSION 2 -#define SWAPPY_MINOR_VERSION 0 +#define SWAPPY_MINOR_VERSION 2 #define SWAPPY_BUGFIX_VERSION 0 -#define SWAPPY_PACKED_VERSION \ - ANDROID_GAMESDK_PACKED_VERSION(SWAPPY_MAJOR_VERSION, SWAPPY_MINOR_VERSION, \ - SWAPPY_BUGFIX_VERSION) +#define SWAPPY_PACKED_VERSION \ + ANDROID_GAMESDK_PACKED_VERSION(SWAPPY_MAJOR_VERSION, SWAPPY_MINOR_VERSION, \ + SWAPPY_BUGFIX_VERSION) // Internal macros to generate a symbol to track Swappy version, do not use // directly. #define SWAPPY_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT) \ - PREFIX##_##MAJOR##_##MINOR##_##BUGFIX##_##GITCOMMIT + PREFIX##_##MAJOR##_##MINOR##_##BUGFIX##_##GITCOMMIT #define SWAPPY_VERSION_CONCAT(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT) \ - SWAPPY_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT) -#define SWAPPY_VERSION_SYMBOL \ - SWAPPY_VERSION_CONCAT(Swappy_version, SWAPPY_MAJOR_VERSION, \ - SWAPPY_MINOR_VERSION, SWAPPY_BUGFIX_VERSION, \ - AGDK_GIT_COMMIT) + SWAPPY_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT) +#define SWAPPY_VERSION_SYMBOL \ + SWAPPY_VERSION_CONCAT(Swappy_version, SWAPPY_MAJOR_VERSION, \ + SWAPPY_MINOR_VERSION, SWAPPY_BUGFIX_VERSION, \ + AGDK_GIT_COMMIT) // Define this to 1 to enable all logging from Swappy, by default it is // disabled in a release build and enabled in a debug build. @@ -83,29 +83,29 @@ typedef uint64_t SwappyThreadId; * Usage of this functionality is optional. */ typedef struct SwappyThreadFunctions { - /** @brief Thread start callback. - * - * This function is called by Swappy to start thread_func on a new thread. - * @param user_data A value to be passed the thread function. - * If the thread was started, this function should set the thread_id and - * return 0. If the thread was not started, this function should return a - * non-zero value. - */ - int (*start)(SwappyThreadId* thread_id, void* (*thread_func)(void*), - void* user_data); + /** @brief Thread start callback. + * + * This function is called by Swappy to start thread_func on a new thread. + * @param user_data A value to be passed the thread function. + * If the thread was started, this function should set the thread_id and + * return 0. If the thread was not started, this function should return a + * non-zero value. + */ + int (*start)(SwappyThreadId* thread_id, void* (*thread_func)(void*), + void* user_data); - /** @brief Thread join callback. - * - * This function is called by Swappy to join the thread with given id. - */ - void (*join)(SwappyThreadId thread_id); + /** @brief Thread join callback. + * + * This function is called by Swappy to join the thread with given id. + */ + void (*join)(SwappyThreadId thread_id); - /** @brief Thread joinable callback. - * - * This function is called by Swappy to discover whether the thread with the - * given id is joinable. - */ - bool (*joinable)(SwappyThreadId thread_id); + /** @brief Thread joinable callback. + * + * This function is called by Swappy to discover whether the thread with the + * given id is joinable. + */ + bool (*joinable)(SwappyThreadId thread_id); } SwappyThreadFunctions; #ifdef __cplusplus @@ -138,47 +138,46 @@ const char* Swappy_versionString(); * ::SwappyGL_enableStats or ::SwappyVk_enableStats. */ typedef struct SwappyStats { - /** @brief Total frames swapped by swappy */ - uint64_t totalFrames; + /** @brief Total frames swapped by swappy */ + uint64_t totalFrames; - /** @brief Histogram of the number of screen refreshes a frame waited in the - * compositor queue after rendering was completed. - * - * For example: - * if a frame waited 2 refresh periods in the compositor queue after - * rendering was done, the frame will be counted in idleFrames[2] - */ - uint64_t idleFrames[MAX_FRAME_BUCKETS]; + /** @brief Histogram of the number of screen refreshes a frame waited in the + * compositor queue after rendering was completed. + * + * For example: + * if a frame waited 2 refresh periods in the compositor queue after + * rendering was done, the frame will be counted in idleFrames[2] + */ + uint64_t idleFrames[MAX_FRAME_BUCKETS]; - /** @brief Histogram of the number of screen refreshes passed between the - * requested presentation time and the actual present time. - * - * For example: - * if a frame was presented 2 refresh periods after the requested - * timestamp swappy set, the frame will be counted in lateFrames[2] - */ - uint64_t lateFrames[MAX_FRAME_BUCKETS]; + /** @brief Histogram of the number of screen refreshes passed between the + * requested presentation time and the actual present time. + * + * For example: + * if a frame was presented 2 refresh periods after the requested + * timestamp swappy set, the frame will be counted in lateFrames[2] + */ + uint64_t lateFrames[MAX_FRAME_BUCKETS]; - /** @brief Histogram of the number of screen refreshes passed between two - * consecutive frames - * - * For example: - * if frame N was presented 2 refresh periods after frame N-1 - * frame N will be counted in offsetFromPreviousFrame[2] - */ - uint64_t offsetFromPreviousFrame[MAX_FRAME_BUCKETS]; + /** @brief Histogram of the number of screen refreshes passed between two + * consecutive frames + * + * For example: + * if frame N was presented 2 refresh periods after frame N-1 + * frame N will be counted in offsetFromPreviousFrame[2] + */ + uint64_t offsetFromPreviousFrame[MAX_FRAME_BUCKETS]; - /** @brief Histogram of the number of screen refreshes passed between the - * call to Swappy_recordFrameStart and the actual present time. - * - * For example: - * if a frame was presented 2 refresh periods after the call to - * `Swappy_recordFrameStart` the frame will be counted in latencyFrames[2] - */ - uint64_t latencyFrames[MAX_FRAME_BUCKETS]; + /** @brief Histogram of the number of screen refreshes passed between the + * call to Swappy_recordFrameStart and the actual present time. + * + * For example: + * if a frame was presented 2 refresh periods after the call to + * `Swappy_recordFrameStart` the frame will be counted in latencyFrames[2] + */ + uint64_t latencyFrames[MAX_FRAME_BUCKETS]; } SwappyStats; - #ifdef __cplusplus } // extern "C" #endif @@ -236,43 +235,43 @@ typedef void (*SwappySwapIntervalChangedCallback)(void*); * Injection of these is optional. */ typedef struct SwappyTracer { - /** - * Callback called before waiting to queue the frame to the composer. - */ - SwappyPreWaitCallback preWait; + /** + * Callback called before waiting to queue the frame to the composer. + */ + SwappyPreWaitCallback preWait; - /** - * Callback called after wait to queue the frame to the composer is done. - */ - SwappyPostWaitCallback postWait; + /** + * Callback called after wait to queue the frame to the composer is done. + */ + SwappyPostWaitCallback postWait; - /** - * Callback called before calling the function to queue the frame to the - * composer. - */ - SwappyPreSwapBuffersCallback preSwapBuffers; + /** + * Callback called before calling the function to queue the frame to the + * composer. + */ + SwappyPreSwapBuffersCallback preSwapBuffers; - /** - * Callback called after calling the function to queue the frame to the - * composer. - */ - SwappyPostSwapBuffersCallback postSwapBuffers; + /** + * Callback called after calling the function to queue the frame to the + * composer. + */ + SwappyPostSwapBuffersCallback postSwapBuffers; - /** - * Callback called at the start of a frame. - */ - SwappyStartFrameCallback startFrame; + /** + * Callback called at the start of a frame. + */ + SwappyStartFrameCallback startFrame; - /** - * Pointer to some arbitrary data that will be passed as the first argument - * of callbacks. - */ - void* userData; + /** + * Pointer to some arbitrary data that will be passed as the first argument + * of callbacks. + */ + void* userData; - /** - * Callback called when the swap interval was changed. - */ - SwappySwapIntervalChangedCallback swapIntervalChanged; + /** + * Callback called when the swap interval was changed. + */ + SwappySwapIntervalChangedCallback swapIntervalChanged; } SwappyTracer; /** @} */