Files
2025-05-18 13:04:45 +08:00

108 lines
4.6 KiB
C++

/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "FrameStatistics.h"
#include "SwappyVkBase.h"
namespace swappy {
/***************************************************************************************************
*
* Per-Device concrete/derived class for using VK_GOOGLE_display_timing.
*
* This class uses the VK_GOOGLE_display_timing in order to present frames at a
*muliple (the "swap interval") of a fixed refresh-cycle duration (i.e. the time
*between successive vsync's).
*
* In order to reduce complexity, some simplifying assumptions are made:
*
* - We assume a fixed refresh-rate (FRR) display that's between 60 Hz and 120
*Hz.
*
* - While Vulkan allows applications to create and use multiple
*VkSwapchainKHR's per VkDevice, and to re-create VkSwapchainKHR's, we assume
*that the application uses a single VkSwapchainKHR, and never re-creates it.
*
* - The values reported back by the VK_GOOGLE_display_timing extension (which
*comes from lower-level Android interfaces) are not precise, and that values
*can drift over time. For example, the refresh-cycle duration for a 60 Hz
*display should be 16,666,666 nsec; but the value reported back by the
*extension won't be precisely this. Also, the differences betweeen the times
*of two successive frames won't be an exact multiple of 16,666,666 nsec. This
*can make it difficult to precisely predict when a future vsync will be (it can
*appear to drift overtime). Therefore, we try to give a desiredPresentTime for
*each image that is between 3 and 7 msec before vsync. We look at the
*actualPresentTime for previously-presented images, and nudge the future
*desiredPresentTime back within those 3-7 msec boundaries.
*
* - There can be a few frames of latency between when an image is presented and
*when the actualPresentTime is available for that image. Therefore, we
*initially just pick times based upon CLOCK_MONOTONIC (which is the time domain
*for VK_GOOGLE_display_timing). After we get past-present times, we nudge the
*desiredPresentTime, we wait for a few presents before looking again to see
*whether we need to nudge again.
*
* - If, for some reason, an application can't keep up with its chosen swap
*interval (e.g. it's designed for 30FPS on a premium device and is now running
*on a slow device; or it's running on a 120Hz display), this algorithm may not
*be able to make up for this (i.e. smooth rendering at a targeted frame rate
*may not be possible with an application that can't render fast enough).
*
***************************************************************************************************/
class SwappyVkGoogleDisplayTiming : public SwappyVkBase {
public:
SwappyVkGoogleDisplayTiming(JNIEnv* env, jobject jactivity,
VkPhysicalDevice physicalDevice,
VkDevice device,
const SwappyVkFunctionProvider* provider);
bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
uint64_t* pRefreshDuration) override final;
VkResult doQueuePresent(
VkQueue queue, uint32_t queueFamilyIndex,
const VkPresentInfoKHR* pPresentInfo) override final;
void enableStats(bool enabled) override final;
void recordFrameStart(VkQueue queue, uint32_t image) override final;
void getStats(SwappyStats* swappyStats) override final;
void clearStats() override final;
private:
static constexpr int MAX_FRAME_LAG = 10;
// Vulkan loader does not give any frame timings unless they are 5 frames
// old. We use this internally to not waste calls.
static constexpr int MIN_FRAME_LAG = 5;
uint32_t mPresentID = 0;
VkSwapchainKHR mSwapchain;
struct VKFrame {
uint32_t id;
uint64_t startFrameTime;
int pastTimingIndex;
};
std::vector<VKFrame> mPendingFrames;
// Storage for querying past presentation frames, allocated upfront.
VkPastPresentationTimingGOOGLE mPastTimes[MAX_FRAME_LAG];
FrameStatistics mFrameStatisticsCommon;
};
} // namespace swappy