Add frame pacing functions

Use the following functions to use Android Frame Pacing with a rendering engine based on the Vulkan API.

Identify required extensions for creation

To gather the set of extensions necessary to create an instance of Android Frame Pacing when using Vulkan, complete the steps shown in the following code snippet:

VkPhysicalDevice physicalDevice;
uint32_t availableExtensionCount;
VkExtensionProperties* pAvailableExtensions;
uint32_t requiredExtensionCount;
char** pRequiredExtensions;

// Determine the number of extensions available for this device.
vkEnumerateDeviceExtensionProperties(physicalDevice, layerName, &availableExtensionCount,
    pAvailableExtensions);

// Determine the number of required extensions.
SwappyVk_determineDeviceExtensions(physicalDevice, availableExtensionCount,
    pAvailableExtensions, &requiredExtensionCount, nullptr);

// Determine the required extensions.
pRequiredExtensions = (char**)malloc(requiredExtensionCount * sizeof(char*));
pRequiredExtensionsData = (char*)malloc(requiredExtensionCount * (VK_MAX_EXTENSION_NAME_SIZE + 1));
for (uint32_t i = 0; i < requiredExtensionCount; i++) {
    pRequiredExtensions[i] = &pRequiredExtensionsData[i * (VK_MAX_EXTENSION_NAME_SIZE + 1)];
}
SwappyVk_determineDeviceExtensions(physicalDevice, availableExtensionCount,
    pAvailableExtensions, &requiredExtensionCount, pRequiredExtensions);

You can then start Android Frame Pacing by calling vkCreateDevice(). The 2nd argument, a struct of type VkDeviceCreateInfo*, should have its enabledExtensionCount member set to the number of required extensions.

Identify queue family

To present the correct display queue, Android Frame Pacing needs to know which queue family Vulkan is using. To determine the correct family, complete the steps shown in the following code snippet:

// Reusing local variables from previous snippets:
// VkPhysicalDevice physicalDevice;

const VkDeviceCreateInfo createInfo;
const VkAllocationCallbacks allocator;
VkDevice device;
uint32_t queueFamilyIndex;
uint32_t queueIndex;
VkQueue deviceQueue;

// Values of "device" and "deviceQueue" set in the 1st and 2nd function
// calls, respectively.
vkCreateDevice(physicalDevice, &createInfo, &allocator, &device);
vkGetDeviceQueue(device, queueFamilyIndex, queueIndex, &deviceQueue);
SwappyVk_setQueueFamilyIndex(device, deviceQueue, queueFamilyIndex);

Define framerate for swapchain

To initialize Android Frame Pacing for a given physical device and swapchain, complete the steps shown in the following code snippet:

// Reusing local variables from previous snippets:
// VkPhysicalDevice physicalDevice;
// VkDevice device;

// Assume that the JNI environment is available in:
// JNIEnv *env;
// jobject jactivity;

// Assume that swapchain is already known.
VkSwapchainKHR swapchain;
uint64_t refreshDuration; // in nanoseconds

// Determine duration between vertical-blanking periods.
// Example: 60 FPS sets "refreshDuration" to 16,666,666.
SwappyVk_initAndGetRefreshCycleDuration(env, jactivity, physicalDevice,
        device, swapchain, &refreshDuration);

This determines the swap duration in nanoseconds. There are helper macros defined in swappy_common.h for common swap durations (for example, SWAPPY_SWAP_60FPS).

Next, you need to supply the swap duration in nanoseconds.

// Declare the periods in nanoseconds that should elapse before refreshing one
// image with the next image. There are helper macros defined in swappy_common.h
// for common swap durations.
// This example shows what to do when you want to render your game at 30 FPS.

SwappyVk_setSwapIntervalNS(device, swapchain, SWAPPY_SWAP_30FPS);

Setting the ANativeWindow

Swappy needs the handle of ANativeWindow in order to perform ANativeWindow-specific operation, such as calling ANativeWindow_setFrameRate(). Call SwappyVk_setWindow() when your Android display surface has changed and you have a new ANativeWindow handle (see the Cube sample for an example).

Auto Modes

Android Frame Pacing adjusts the swap duration and pipeline mode based on the average duration of previous frames. You can control this behavior with the following functions:

Present a frame

To present a frame of your game to Android Frame Pacing, call SwappyVk_queuePresent(). This function calls vkQueuePresentKHR() on behalf of your game.

Destroy the swapchain

To destroy the SwappyVk data associated with a given swapchain, complete the steps shown in the following code snippet:

// Reusing local variables from previous snippets:
// VkDevice device;
// VkSwapchainKHR swapchain;
// const VkAllocationCallbacks allocator;

SwappyVk_destroySwapchain(device, swapchain);
vkDestroySwapchainKHR(device, swapchain, &allocator);