108 lines
4.6 KiB
C++
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
|