219 lines
7.6 KiB
C++
219 lines
7.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.
|
|
*/
|
|
|
|
/***************************************************************************************************
|
|
*
|
|
* Per-Device abstract base class.
|
|
*
|
|
***************************************************************************************************/
|
|
|
|
/**
|
|
* Abstract base class that calls the Vulkan API.
|
|
*
|
|
* It is expected that one concrete class will be instantiated per VkDevice, and
|
|
* that all VkSwapchainKHR's for a given VkDevice will share the same instance.
|
|
*
|
|
* Base class members are used by the derived classes to unify the behavior
|
|
* across implementations:
|
|
* @mThread - Thread used for getting Choreographer events.
|
|
* @mTreadRunning - Used to signal the tread to exit
|
|
* @mNextPresentID - unique ID for frame presentation.
|
|
* @mNextDesiredPresentTime - Holds the time in nanoseconds for the next frame
|
|
* to be presented.
|
|
* @mNextPresentIDToCheck - Used to determine whether presentation time needs
|
|
* to be adjusted.
|
|
* @mFrameID - Keeps track of how many Choreographer callbacks received.
|
|
* @mLastframeTimeNanos - Holds the last frame time reported by Choreographer.
|
|
* @mSumRefreshTime - Used together with @mSamples to calculate refresh rate
|
|
* based on Choreographer.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <dlfcn.h>
|
|
#include <inttypes.h>
|
|
#include <pthread.h>
|
|
#include <swappy/swappyVk.h>
|
|
#include <unistd.h>
|
|
|
|
#include <condition_variable>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <list>
|
|
#include <map>
|
|
#include <mutex>
|
|
|
|
#include "ChoreographerShim.h"
|
|
#include "Settings.h"
|
|
#include "SwappyCommon.h"
|
|
#include "Trace.h"
|
|
|
|
namespace swappy {
|
|
|
|
constexpr uint32_t kThousand = 1000;
|
|
constexpr uint32_t kMillion = 1000000;
|
|
constexpr uint32_t kBillion = 1000000000;
|
|
constexpr uint32_t k16_6msec = 16666666;
|
|
|
|
constexpr uint32_t kTooCloseToVsyncBoundary = 3000000;
|
|
constexpr uint32_t kTooFarAwayFromVsyncBoundary = 7000000;
|
|
constexpr uint32_t kNudgeWithinVsyncBoundaries = 2000000;
|
|
|
|
// AChoreographer is supported from API 24. To allow compilation for minSDK < 24
|
|
// and still use AChoreographer for SDK >= 24 we need runtime support to call
|
|
// AChoreographer APIs.
|
|
|
|
using PFN_AChoreographer_getInstance = AChoreographer* (*)();
|
|
|
|
using PFN_AChoreographer_postFrameCallback =
|
|
void (*)(AChoreographer* choreographer,
|
|
AChoreographer_frameCallback callback, void* data);
|
|
|
|
using PFN_AChoreographer_postFrameCallbackDelayed = void (*)(
|
|
AChoreographer* choreographer, AChoreographer_frameCallback callback,
|
|
void* data, long delayMillis);
|
|
|
|
extern PFN_vkCreateCommandPool vkCreateCommandPool;
|
|
extern PFN_vkDestroyCommandPool vkDestroyCommandPool;
|
|
extern PFN_vkCreateFence vkCreateFence;
|
|
extern PFN_vkDestroyFence vkDestroyFence;
|
|
extern PFN_vkWaitForFences vkWaitForFences;
|
|
extern PFN_vkResetFences vkResetFences;
|
|
extern PFN_vkCreateSemaphore vkCreateSemaphore;
|
|
extern PFN_vkDestroySemaphore vkDestroySemaphore;
|
|
extern PFN_vkCreateEvent vkCreateEvent;
|
|
extern PFN_vkDestroyEvent vkDestroyEvent;
|
|
extern PFN_vkCmdSetEvent vkCmdSetEvent;
|
|
extern PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
|
|
extern PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
|
|
extern PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
|
|
extern PFN_vkEndCommandBuffer vkEndCommandBuffer;
|
|
extern PFN_vkQueueSubmit vkQueueSubmit;
|
|
|
|
void LoadVulkanFunctions(const SwappyVkFunctionProvider* pFunctionProvider);
|
|
|
|
class SwappyVkBase {
|
|
public:
|
|
SwappyVkBase(JNIEnv* env, jobject jactivity,
|
|
VkPhysicalDevice physicalDevice, VkDevice device,
|
|
const SwappyVkFunctionProvider* pFunctionProvider);
|
|
|
|
virtual ~SwappyVkBase();
|
|
|
|
virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
|
|
uint64_t* pRefreshDuration) = 0;
|
|
|
|
virtual VkResult doQueuePresent(VkQueue queue, uint32_t queueFamilyIndex,
|
|
const VkPresentInfoKHR* pPresentInfo) = 0;
|
|
|
|
void doSetWindow(ANativeWindow* window);
|
|
void doSetSwapInterval(VkSwapchainKHR swapchain, uint64_t swapNs);
|
|
|
|
VkResult injectFence(VkQueue queue, const VkPresentInfoKHR* pPresentInfo,
|
|
VkSemaphore* pSemaphore);
|
|
|
|
bool isEnabled() { return mEnabled; }
|
|
|
|
void setAutoSwapInterval(bool enabled);
|
|
void setAutoPipelineMode(bool enabled);
|
|
|
|
void setMaxAutoSwapDuration(std::chrono::nanoseconds swapMaxNS);
|
|
|
|
void setFenceTimeout(std::chrono::nanoseconds duration);
|
|
std::chrono::nanoseconds getFenceTimeout() const;
|
|
std::chrono::nanoseconds getSwapInterval();
|
|
|
|
void addTracer(const SwappyTracer* tracer);
|
|
void removeTracer(const SwappyTracer* tracer);
|
|
|
|
VkDevice getDevice() const { return mDevice; }
|
|
|
|
int getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
|
|
int allocated_entries);
|
|
|
|
virtual void enableStats(bool enabled) = 0;
|
|
virtual void getStats(SwappyStats* swappyStats) = 0;
|
|
virtual void recordFrameStart(VkQueue queue, uint32_t image) = 0;
|
|
virtual void clearStats() = 0;
|
|
|
|
void resetFramePacing();
|
|
void enableFramePacing(bool enable);
|
|
void enableBlockingWait(bool enable);
|
|
|
|
protected:
|
|
struct VkSync {
|
|
VkFence fence;
|
|
VkSemaphore semaphore;
|
|
VkCommandBuffer command;
|
|
VkEvent event;
|
|
};
|
|
|
|
struct ThreadContext {
|
|
ThreadContext(VkQueue queue) : queue(queue) {}
|
|
|
|
Thread thread;
|
|
bool running GUARDED_BY(lock) = true;
|
|
bool hasPendingWork GUARDED_BY(lock);
|
|
std::mutex lock;
|
|
std::condition_variable_any condition;
|
|
VkQueue queue GUARDED_BY(lock);
|
|
};
|
|
|
|
SwappyCommon mCommonBase;
|
|
VkPhysicalDevice mPhysicalDevice;
|
|
VkDevice mDevice;
|
|
const SwappyVkFunctionProvider* mpFunctionProvider;
|
|
bool mInitialized;
|
|
bool mEnabled;
|
|
|
|
uint32_t mNextPresentID = 0;
|
|
uint32_t mNextPresentIDToCheck = 2;
|
|
|
|
PFN_vkGetDeviceProcAddr mpfnGetDeviceProcAddr = nullptr;
|
|
PFN_vkQueuePresentKHR mpfnQueuePresentKHR = nullptr;
|
|
#if (not defined ANDROID_NDK_VERSION) || ANDROID_NDK_VERSION >= 15
|
|
PFN_vkGetRefreshCycleDurationGOOGLE mpfnGetRefreshCycleDurationGOOGLE =
|
|
nullptr;
|
|
PFN_vkGetPastPresentationTimingGOOGLE mpfnGetPastPresentationTimingGOOGLE =
|
|
nullptr;
|
|
#endif
|
|
// Holds VKSync objects ready to be used
|
|
std::map<VkQueue, std::list<VkSync>> mFreeSyncPool;
|
|
|
|
// Holds VKSync objects queued and but signaled yet
|
|
std::map<VkQueue, std::list<VkSync>> mWaitingSyncs;
|
|
|
|
// Holds VKSync objects that were signaled
|
|
std::map<VkQueue, std::list<VkSync>> mSignaledSyncs;
|
|
|
|
std::map<VkQueue, VkCommandPool> mCommandPool;
|
|
std::map<VkQueue, std::unique_ptr<ThreadContext>> mThreads;
|
|
|
|
static constexpr int MAX_PENDING_FENCES = 2;
|
|
|
|
std::atomic<std::chrono::nanoseconds> mLastFenceTime = {};
|
|
|
|
void initGoogExtension();
|
|
VkResult initializeVkSyncObjects(VkQueue queue, uint32_t queueFamilyIndex);
|
|
void destroyVkSyncObjects();
|
|
void reclaimSignaledFences(VkQueue queue);
|
|
bool lastFrameIsCompleted(VkQueue queue);
|
|
std::chrono::nanoseconds getLastFenceTime(VkQueue queue);
|
|
void waitForFenceThreadMain(ThreadContext& thread);
|
|
};
|
|
|
|
} // namespace swappy
|