• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifdef ANDROID
18 #define SWAPPYVK_USE_WRAPPER
19 #endif
20 #include <swappyVk/SwappyVk.h>
21 
22 #include <map>
23 #include <condition_variable>
24 #include <cstring>
25 #include <unistd.h>
26 
27 #include <dlfcn.h>
28 #include <cstdlib>
29 
30 #include <inttypes.h>
31 
32 #ifdef ANDROID
33 #include <mutex>
34 #include <pthread.h>
35 #include <list>
36 #include <android/looper.h>
37 #include <android/log.h>
38 #include "Trace.h"
39 #include "ChoreographerShim.h"
40 
41 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "SwappyVk", __VA_ARGS__)
42 #define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "SwappyVk", __VA_ARGS__)
43 #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "SwappyVk", __VA_ARGS__)
44 #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "SwappyVk", __VA_ARGS__)
45 #define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "SwappyVk", __VA_ARGS__)
46 #else
47 #define ATRACE_CALL() ((void)0)
48 #define ALOGE(...)    ((void)0)
49 #define ALOGW(...)    ((void)0)
50 #define ALOGD(...)    ((void)0)
51 #define ALOGV(...)    ((void)0)
52 #endif
53 
54 
55 constexpr uint32_t kThousand = 1000;
56 constexpr uint32_t kMillion  = 1000000;
57 constexpr uint32_t kBillion  = 1000000000;
58 constexpr uint32_t k16_6msec = 16666666;
59 
60 constexpr uint32_t kTooCloseToVsyncBoundary     = 3000000;
61 constexpr uint32_t kTooFarAwayFromVsyncBoundary = 7000000;
62 constexpr uint32_t kNudgeWithinVsyncBoundaries  = 2000000;
63 
64 // Note: The API functions is at the botton of the file.  Those functions call methods of the
65 // singleton SwappyVk class.  Those methods call virtual methods of the abstract SwappyVkBase
66 // class, which is actually implemented by one of the derived/concrete classes:
67 //
68 // - SwappyVkGoogleDisplayTiming
69 // - SwappyVkVulkanFallback
70 // - SwappyVkAndroidFallback
71 
72 // Forward declarations:
73 class SwappyVk;
74 
75 // AChoreographer is supported from API 24. To allow compilation for minSDK < 24
76 // and still use AChoreographer for SDK >= 24 we need runtime support to call
77 // AChoreographer APIs.
78 
79 using PFN_AChoreographer_getInstance = AChoreographer* (*)();
80 
81 using PFN_AChoreographer_postFrameCallback = void (*)(AChoreographer* choreographer,
82                                                   AChoreographer_frameCallback callback,
83                                                   void* data);
84 
85 using PFN_AChoreographer_postFrameCallbackDelayed = void (*)(AChoreographer* choreographer,
86                                                         AChoreographer_frameCallback callback,
87                                                         void* data,
88                                                         long delayMillis);
89 
90 /***************************************************************************************************
91  *
92  * Per-Device abstract base class.
93  *
94  ***************************************************************************************************/
95 
96 /**
97  * Abstract base class that calls the Vulkan API.
98  *
99  * It is expected that one concrete class will be instantiated per VkDevice, and that all
100  * VkSwapchainKHR's for a given VkDevice will share the same instance.
101  *
102  * Base class members are used by the derived classes to unify the behavior across implementaitons:
103  *  @mThread - Thread used for getting Choreographer events.
104  *  @mTreadRunning - Used to signal the tread to exit
105  *  @mNextPresentID - unique ID for frame presentation.
106  *  @mNextDesiredPresentTime - Holds the time in nanoseconds for the next frame to be presented.
107  *  @mNextPresentIDToCheck - Used to determine whether presentation time needs to be adjusted.
108  *  @mFrameID - Keeps track of how many Choreographer callbacks received.
109  *  @mLastframeTimeNanos - Holds the last frame time reported by Choreographer.
110  *  @mSumRefreshTime - Used together with @mSamples to calculate refresh rate based on Choreographer.
111  */
112 class SwappyVkBase
113 {
114 public:
SwappyVkBase(VkPhysicalDevice physicalDevice,VkDevice device,uint64_t refreshDur,uint32_t interval,SwappyVk & swappyVk,void * libVulkan)115     SwappyVkBase(VkPhysicalDevice physicalDevice,
116                  VkDevice         device,
117                  uint64_t         refreshDur,
118                  uint32_t         interval,
119                  SwappyVk         &swappyVk,
120                  void             *libVulkan) :
121             mPhysicalDevice(physicalDevice), mDevice(device), mRefreshDur(refreshDur),
122             mInterval(interval), mSwappyVk(swappyVk), mLibVulkan(libVulkan),
123             mInitialized(false)
124     {
125 #ifdef ANDROID
126         InitVulkan();
127 #endif
128         mpfnGetDeviceProcAddr =
129                 reinterpret_cast<PFN_vkGetDeviceProcAddr>(
130                     dlsym(mLibVulkan, "vkGetDeviceProcAddr"));
131         mpfnQueuePresentKHR =
132                 reinterpret_cast<PFN_vkQueuePresentKHR>(
133                     mpfnGetDeviceProcAddr(mDevice, "vkQueuePresentKHR"));
134 
135         mLibAndroid = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL);
136         if (mLibAndroid == nullptr) {
137             ALOGE("FATAL: cannot open libandroid.so: %s", strerror(errno));
138             abort();
139         }
140 
141         mAChoreographer_getInstance =
142                 reinterpret_cast<PFN_AChoreographer_getInstance >(
143                     dlsym(mLibAndroid, "AChoreographer_getInstance"));
144 
145         mAChoreographer_postFrameCallback =
146                 reinterpret_cast<PFN_AChoreographer_postFrameCallback >(
147                         dlsym(mLibAndroid, "AChoreographer_postFrameCallback"));
148 
149         mAChoreographer_postFrameCallbackDelayed =
150                 reinterpret_cast<PFN_AChoreographer_postFrameCallbackDelayed >(
151                         dlsym(mLibAndroid, "AChoreographer_postFrameCallbackDelayed"));
152         if (!mAChoreographer_getInstance ||
153             !mAChoreographer_postFrameCallback ||
154             !mAChoreographer_postFrameCallbackDelayed) {
155             ALOGE("FATAL: cannot get AChoreographer symbols");
156             abort();
157         }
158     }
~SwappyVkBase()159     virtual ~SwappyVkBase() {
160         if(mLibAndroid)
161             dlclose(mLibAndroid);
162     }
163     virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
164                                            uint64_t*      pRefreshDuration) = 0;
doSetSwapInterval(VkSwapchainKHR swapchain,uint32_t interval)165     void doSetSwapInterval(VkSwapchainKHR swapchain,
166                            uint32_t       interval)
167     {
168         mInterval = interval;
169     }
170     virtual VkResult doQueuePresent(VkQueue                 queue,
171                                     uint32_t                queueFamilyIndex,
172                                     const VkPresentInfoKHR* pPresentInfo) = 0;
173 protected:
174     VkPhysicalDevice mPhysicalDevice;
175     VkDevice         mDevice;
176     uint64_t         mRefreshDur;
177     uint32_t         mInterval;
178     SwappyVk         &mSwappyVk;
179     void             *mLibVulkan;
180     bool             mInitialized;
181     pthread_t mThread = 0;
182     ALooper *mLooper = nullptr;
183     bool mTreadRunning = false;
184     AChoreographer *mChoreographer = nullptr;
185     std::mutex mWaitingMutex;
186     std::condition_variable mWaitingCondition;
187     uint32_t mNextPresentID = 0;
188     uint64_t mNextDesiredPresentTime = 0;
189     uint32_t mNextPresentIDToCheck = 2;
190 
191     PFN_vkGetDeviceProcAddr mpfnGetDeviceProcAddr = nullptr;
192     PFN_vkQueuePresentKHR   mpfnQueuePresentKHR = nullptr;
193     PFN_vkGetRefreshCycleDurationGOOGLE mpfnGetRefreshCycleDurationGOOGLE = nullptr;
194     PFN_vkGetPastPresentationTimingGOOGLE mpfnGetPastPresentationTimingGOOGLE = nullptr;
195 
196     void *mLibAndroid = nullptr;
197     PFN_AChoreographer_getInstance mAChoreographer_getInstance = nullptr;
198     PFN_AChoreographer_postFrameCallback mAChoreographer_postFrameCallback = nullptr;
199     PFN_AChoreographer_postFrameCallbackDelayed mAChoreographer_postFrameCallbackDelayed = nullptr;
200 
201     long mFrameID = 0;
202     long mTargetFrameID = 0;
203     uint64_t mLastframeTimeNanos = 0;
204     long mSumRefreshTime = 0;
205     long mSamples = 0;
206     long mCallbacksBeforeIdle = 0;
207 
208     static constexpr int MAX_SAMPLES = 5;
209     static constexpr int MAX_CALLBACKS_BEFORE_IDLE = 10;
210 
initGoogExtention()211     void initGoogExtention()
212     {
213         mpfnGetRefreshCycleDurationGOOGLE =
214                 reinterpret_cast<PFN_vkGetRefreshCycleDurationGOOGLE>(
215                         mpfnGetDeviceProcAddr(mDevice, "vkGetRefreshCycleDurationGOOGLE"));
216         mpfnGetPastPresentationTimingGOOGLE =
217                 reinterpret_cast<PFN_vkGetPastPresentationTimingGOOGLE>(
218                         mpfnGetDeviceProcAddr(mDevice, "vkGetPastPresentationTimingGOOGLE"));
219     }
220 
221     void startChoreographerThread();
222     void stopChoreographerThread();
223     static void *looperThreadWrapper(void *data);
224     void *looperThread();
225     static void frameCallback(long frameTimeNanos, void *data);
226     void onDisplayRefresh(long frameTimeNanos);
227     void calcRefreshRate(uint64_t currentTime);
228     void postChoreographerCallback();
229 };
230 
startChoreographerThread()231 void SwappyVkBase::startChoreographerThread() {
232     std::unique_lock<std::mutex> lock(mWaitingMutex);
233     // create a new ALooper thread to get Choreographer events
234     mTreadRunning = true;
235     pthread_create(&mThread, NULL, looperThreadWrapper, this);
236     mWaitingCondition.wait(lock, [&]() { return mChoreographer != nullptr; });
237 }
238 
stopChoreographerThread()239 void SwappyVkBase::stopChoreographerThread() {
240     if (mLooper) {
241         ALooper_acquire(mLooper);
242         mTreadRunning = false;
243         ALooper_wake(mLooper);
244         ALooper_release(mLooper);
245         pthread_join(mThread, NULL);
246     }
247 }
248 
looperThreadWrapper(void * data)249 void *SwappyVkBase::looperThreadWrapper(void *data) {
250     SwappyVkBase *me = reinterpret_cast<SwappyVkBase *>(data);
251     return me->looperThread();
252 }
253 
looperThread()254 void *SwappyVkBase::looperThread() {
255     int outFd, outEvents;
256     void *outData;
257 
258     mLooper = ALooper_prepare(0);
259     if (!mLooper) {
260         ALOGE("ALooper_prepare failed");
261         return NULL;
262     }
263 
264     mChoreographer = mAChoreographer_getInstance();
265     if (!mChoreographer) {
266         ALOGE("AChoreographer_getInstance failed");
267         return NULL;
268     }
269     mWaitingCondition.notify_all();
270 
271     while (mTreadRunning) {
272         ALooper_pollAll(-1, &outFd, &outEvents, &outData);
273     }
274 
275     return NULL;
276 }
277 
frameCallback(long frameTimeNanos,void * data)278 void SwappyVkBase::frameCallback(long frameTimeNanos, void *data) {
279     SwappyVkBase *me = reinterpret_cast<SwappyVkBase *>(data);
280     me->onDisplayRefresh(frameTimeNanos);
281 }
282 
onDisplayRefresh(long frameTimeNanos)283 void SwappyVkBase::onDisplayRefresh(long frameTimeNanos) {
284     std::lock_guard<std::mutex> lock(mWaitingMutex);
285     struct timespec currTime;
286     clock_gettime(CLOCK_MONOTONIC, &currTime);
287     uint64_t currentTime =
288             ((uint64_t) currTime.tv_sec * kBillion) + (uint64_t) currTime.tv_nsec;
289 
290     calcRefreshRate(currentTime);
291     mLastframeTimeNanos = currentTime;
292     mFrameID++;
293     mWaitingCondition.notify_all();
294 
295     // queue the next frame callback
296     if (mCallbacksBeforeIdle > 0) {
297         mCallbacksBeforeIdle--;
298         mAChoreographer_postFrameCallbackDelayed(mChoreographer, frameCallback, this, 1);
299     }
300 }
301 
postChoreographerCallback()302 void SwappyVkBase::postChoreographerCallback() {
303     if (mCallbacksBeforeIdle == 0) {
304         mAChoreographer_postFrameCallbackDelayed(mChoreographer, frameCallback, this, 1);
305     }
306     mCallbacksBeforeIdle = MAX_CALLBACKS_BEFORE_IDLE;
307 }
308 
calcRefreshRate(uint64_t currentTime)309 void SwappyVkBase::calcRefreshRate(uint64_t currentTime) {
310     long refresh_nano = currentTime - mLastframeTimeNanos;
311 
312     if (mRefreshDur != 0 || mLastframeTimeNanos == 0) {
313         return;
314     }
315 
316     mSumRefreshTime += refresh_nano;
317     mSamples++;
318 
319     if (mSamples == MAX_SAMPLES) {
320         mRefreshDur = mSumRefreshTime / mSamples;
321     }
322 }
323 
324 
325 /***************************************************************************************************
326  *
327  * Per-Device concrete/derived class for using VK_GOOGLE_display_timing.
328  *
329  * This class uses the VK_GOOGLE_display_timing in order to present frames at a muliple (the "swap
330  * interval") of a fixed refresh-cycle duration (i.e. the time between successive vsync's).
331  *
332  * In order to reduce complexity, some simplifying assumptions are made:
333  *
334  * - We assume a fixed refresh-rate (FRR) display that's between 60 Hz and 120 Hz.
335  *
336  * - While Vulkan allows applications to create and use multiple VkSwapchainKHR's per VkDevice, and
337  *   to re-create VkSwapchainKHR's, we assume that the application uses a single VkSwapchainKHR,
338  *   and never re-creates it.
339  *
340  * - The values reported back by the VK_GOOGLE_display_timing extension (which comes from
341  *   lower-level Android interfaces) are not precise, and that values can drift over time.  For
342  *   example, the refresh-cycle duration for a 60 Hz display should be 16,666,666 nsec; but the
343  *   value reported back by the extension won't be precisely this.  Also, the differences betweeen
344  *   the times of two successive frames won't be an exact multiple of 16,666,666 nsec.  This can
345  *   make it difficult to precisely predict when a future vsync will be (it can appear to drift
346  *   overtime).  Therefore, we try to give a desiredPresentTime for each image that is between 3
347  *   and 7 msec before vsync.  We look at the actualPresentTime for previously-presented images,
348  *   and nudge the future desiredPresentTime back within those 3-7 msec boundaries.
349  *
350  * - There can be a few frames of latency between when an image is presented and when the
351  *   actualPresentTime is available for that image.  Therefore, we initially just pick times based
352  *   upon CLOCK_MONOTONIC (which is the time domain for VK_GOOGLE_display_timing).  After we get
353  *   past-present times, we nudge the desiredPresentTime, we wait for a few presents before looking
354  *   again to see whether we need to nudge again.
355  *
356  * - If, for some reason, an application can't keep up with its chosen swap interval (e.g. it's
357  *   designed for 30FPS on a premium device and is now running on a slow device; or it's running on
358  *   a 120Hz display), this algorithm may not be able to make up for this (i.e. smooth rendering at
359  *   a targetted frame rate may not be possible with an application that can't render fast enough).
360  *
361  ***************************************************************************************************/
362 
363 /**
364  * Concrete/derived class that sits on top of VK_GOOGLE_display_timing
365  */
366 class SwappyVkGoogleDisplayTiming : public SwappyVkBase
367 {
368 public:
SwappyVkGoogleDisplayTiming(VkPhysicalDevice physicalDevice,VkDevice device,SwappyVk & swappyVk,void * libVulkan)369     SwappyVkGoogleDisplayTiming(VkPhysicalDevice physicalDevice,
370                                 VkDevice         device,
371                                 SwappyVk         &swappyVk,
372                                 void             *libVulkan) :
373             SwappyVkBase(physicalDevice, device, k16_6msec, 1, swappyVk, libVulkan)
374     {
375         initGoogExtention();
376     }
doGetRefreshCycleDuration(VkSwapchainKHR swapchain,uint64_t * pRefreshDuration)377     virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
378                                            uint64_t*      pRefreshDuration) override
379     {
380         VkRefreshCycleDurationGOOGLE refreshCycleDuration;
381         VkResult res = mpfnGetRefreshCycleDurationGOOGLE(mDevice, swapchain, &refreshCycleDuration);
382         if (res != VK_SUCCESS) {
383             // This should never occur, but in case it does, return 16,666,666ns:
384             mRefreshDur = k16_6msec;
385         } else {
386             mRefreshDur = refreshCycleDuration.refreshDuration;
387         }
388 
389         // TEMP CODE: LOG REFRESH DURATION AND RATE:
390         double refreshRate = mRefreshDur;
391         refreshRate = 1.0 / (refreshRate / 1000000000.0);
392 
393         ALOGD("Returning refresh duration of %" PRIu64 " nsec (approx %f Hz)", mRefreshDur, refreshRate);
394 
395         *pRefreshDuration = mRefreshDur;
396         return true;
397     }
398     virtual VkResult doQueuePresent(VkQueue                 queue,
399                                     uint32_t                queueFamilyIndex,
400                                     const VkPresentInfoKHR* pPresentInfo) override;
401 
402 private:
403     void calculateNextDesiredPresentTime(VkSwapchainKHR swapchain);
404     void checkPastPresentTiming(VkSwapchainKHR swapchain);
405 };
406 
doQueuePresent(VkQueue queue,uint32_t queueFamilyIndex,const VkPresentInfoKHR * pPresentInfo)407 VkResult SwappyVkGoogleDisplayTiming::doQueuePresent(VkQueue                 queue,
408                                                      uint32_t                queueFamilyIndex,
409                                                      const VkPresentInfoKHR* pPresentInfo)
410 {
411     VkResult ret = VK_SUCCESS;
412 
413     calculateNextDesiredPresentTime(pPresentInfo->pSwapchains[0]);
414 
415     // Setup the new structures to pass:
416     VkPresentTimeGOOGLE *pPresentTimes =
417             reinterpret_cast<VkPresentTimeGOOGLE*>(malloc(sizeof(VkPresentTimeGOOGLE) *
418                                                           pPresentInfo->swapchainCount));
419     for (uint32_t i = 0 ; i < pPresentInfo->swapchainCount ; i++) {
420         pPresentTimes[i].presentID = mNextPresentID;
421         pPresentTimes[i].desiredPresentTime = mNextDesiredPresentTime;
422     }
423     mNextPresentID++;
424 
425     VkPresentTimesInfoGOOGLE presentTimesInfo = {VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
426                                                  pPresentInfo->pNext, pPresentInfo->swapchainCount,
427                                                  pPresentTimes};
428     VkPresentInfoKHR replacementPresentInfo = {pPresentInfo->sType, &presentTimesInfo,
429                                                pPresentInfo->waitSemaphoreCount,
430                                                pPresentInfo->pWaitSemaphores,
431                                                pPresentInfo->swapchainCount,
432                                                pPresentInfo->pSwapchains,
433                                                pPresentInfo->pImageIndices, pPresentInfo->pResults};
434     ret = mpfnQueuePresentKHR(queue, &replacementPresentInfo);
435     free(pPresentTimes);
436     return ret;
437 }
438 
calculateNextDesiredPresentTime(VkSwapchainKHR swapchain)439 void SwappyVkGoogleDisplayTiming::calculateNextDesiredPresentTime(VkSwapchainKHR swapchain)
440 {
441     struct timespec currTime;
442     clock_gettime(CLOCK_MONOTONIC, &currTime);
443     uint64_t currentTime =
444             ((uint64_t) currTime.tv_sec * kBillion) + (uint64_t) currTime.tv_nsec;
445 
446 
447     // Determine the desiredPresentTime:
448     if (!mNextDesiredPresentTime) {
449         mNextDesiredPresentTime = currentTime + mRefreshDur;
450     } else {
451         // Look at the timing of past presents, and potentially adjust mNextDesiredPresentTime:
452         checkPastPresentTiming(swapchain);
453         mNextDesiredPresentTime += mRefreshDur * mInterval;
454 
455         // Make sure the calculated time is not before the current time to present
456         if (mNextDesiredPresentTime < currentTime) {
457             mNextDesiredPresentTime = currentTime + mRefreshDur;
458         }
459     }
460 }
461 
checkPastPresentTiming(VkSwapchainKHR swapchain)462 void SwappyVkGoogleDisplayTiming::checkPastPresentTiming(VkSwapchainKHR swapchain)
463 {
464     VkResult ret = VK_SUCCESS;
465 
466     if (mNextPresentID <= mNextPresentIDToCheck) {
467         return;
468     }
469     // Check the timing of past presents to see if we need to adjust mNextDesiredPresentTime:
470     uint32_t pastPresentationTimingCount = 0;
471     VkResult err = mpfnGetPastPresentationTimingGOOGLE(mDevice, swapchain,
472                                                        &pastPresentationTimingCount, NULL);
473     if (!pastPresentationTimingCount) {
474         return;
475     }
476     // TODO: don't allocate memory for the timestamps every time.
477     VkPastPresentationTimingGOOGLE *past =
478             reinterpret_cast<VkPastPresentationTimingGOOGLE*>(
479                     malloc(sizeof(VkPastPresentationTimingGOOGLE) *
480                            pastPresentationTimingCount));
481     err = mpfnGetPastPresentationTimingGOOGLE(mDevice, swapchain,
482                                               &pastPresentationTimingCount, past);
483     for (uint32_t i = 0; i < pastPresentationTimingCount; i++) {
484         // Note: On Android, actualPresentTime can actually be before desiredPresentTime
485         // (which shouldn't be possible.  Therefore, this must be a signed integer.
486         int64_t amountEarlyBy =
487                 (int64_t) past[i].actualPresentTime - (int64_t)past[i].desiredPresentTime;
488         if (amountEarlyBy < kTooCloseToVsyncBoundary) {
489             // We're getting too close to vsync.  Nudge the next present back
490             // towards/in the boundaries, and check back after a few more presents:
491             mNextDesiredPresentTime -= kNudgeWithinVsyncBoundaries;
492             mNextPresentIDToCheck = mNextPresentID + 7;
493             break;
494         }
495         if (amountEarlyBy > kTooFarAwayFromVsyncBoundary) {
496             // We're getting too far away from vsync.  Nudge the next present back
497             // towards/in the boundaries, and check back after a few more presents:
498             mNextDesiredPresentTime += kNudgeWithinVsyncBoundaries;
499             mNextPresentIDToCheck = mNextPresentID + 7;
500             break;
501         }
502     }
503     free(past);
504 }
505 
506 /**
507  * Concrete/derived class that sits on top of VK_GOOGLE_display_timing
508  */
509 class SwappyVkGoogleDisplayTimingAndroid : public SwappyVkGoogleDisplayTiming
510 {
511 public:
SwappyVkGoogleDisplayTimingAndroid(VkPhysicalDevice physicalDevice,VkDevice device,SwappyVk & swappyVk,void * libVulkan)512     SwappyVkGoogleDisplayTimingAndroid(VkPhysicalDevice physicalDevice,
513                                 VkDevice         device,
514                                 SwappyVk         &swappyVk,
515                                 void             *libVulkan) :
516             SwappyVkGoogleDisplayTiming(physicalDevice, device, swappyVk,libVulkan) {
517         startChoreographerThread();
518     }
519 
~SwappyVkGoogleDisplayTimingAndroid()520     ~SwappyVkGoogleDisplayTimingAndroid() {
521         stopChoreographerThread();
522         destroyVkSyncObjects();
523     }
524 
doGetRefreshCycleDuration(VkSwapchainKHR swapchain,uint64_t * pRefreshDuration)525     virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
526                                           uint64_t*      pRefreshDuration) override {
527         bool res = SwappyVkGoogleDisplayTiming::doGetRefreshCycleDuration(swapchain, pRefreshDuration);
528         return res;
529     }
530 
531 
532 
533     virtual VkResult doQueuePresent(VkQueue queue,
534                                     uint32_t queueFamilyIndex,
535                                     const VkPresentInfoKHR *pPresentInfo) override;
536 
537 private:
538     VkResult initializeVkSyncObjects(VkQueue queue, uint32_t queueFamilyIndex);
539     void destroyVkSyncObjects();
540 
541     void waitForFenceChoreographer(VkQueue queue);
542 
543     struct VkSync {
544         VkFence fence;
545         VkSemaphore semaphore;
546         VkCommandBuffer command;
547         VkEvent event;
548     };
549 
550     std::map<VkQueue, std::list<VkSync>> mFreeSync;
551     std::map<VkQueue, std::list<VkSync>> mPendingSync;
552     std::map<VkQueue, VkCommandPool> mCommandPool;
553 
554     static constexpr int MAX_PENDING_FENCES = 1;
555 };
556 
initializeVkSyncObjects(VkQueue queue,uint32_t queueFamilyIndex)557 VkResult SwappyVkGoogleDisplayTimingAndroid::initializeVkSyncObjects(VkQueue   queue,
558                                                                      uint32_t  queueFamilyIndex)
559 {
560     if (mCommandPool.find(queue) != mCommandPool.end()) {
561         return VK_SUCCESS;
562     }
563 
564     VkSync sync;
565 
566     const VkCommandPoolCreateInfo cmd_pool_info = {
567             .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
568             .pNext = NULL,
569             .queueFamilyIndex = queueFamilyIndex,
570             .flags = 0,
571     };
572 
573     VkResult res = vkCreateCommandPool(mDevice, &cmd_pool_info, NULL, &mCommandPool[queue]);
574     if (res) {
575         ALOGE("vkCreateCommandPool failed %d", res);
576         return res;
577     }
578     const VkCommandBufferAllocateInfo present_cmd_info = {
579             .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
580             .pNext = NULL,
581             .commandPool = mCommandPool[queue],
582             .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
583             .commandBufferCount = 1,
584     };
585 
586     for(int i = 0; i < MAX_PENDING_FENCES; i++) {
587         VkFenceCreateInfo fence_ci =
588                 {.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = 0};
589 
590         res = vkCreateFence(mDevice, &fence_ci, NULL, &sync.fence);
591         if (res) {
592             ALOGE("failed to create fence: %d", res);
593             return res;
594         }
595 
596         VkSemaphoreCreateInfo semaphore_ci =
597                 {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = NULL, .flags = 0};
598 
599         res = vkCreateSemaphore(mDevice, &semaphore_ci, NULL, &sync.semaphore);
600         if (res) {
601             ALOGE("failed to create semaphore: %d", res);
602             return res;
603         }
604 
605 
606         res = vkAllocateCommandBuffers(mDevice, &present_cmd_info, &sync.command);
607         if (res) {
608             ALOGE("vkAllocateCommandBuffers failed %d", res);
609             return res;
610         }
611 
612         const VkCommandBufferBeginInfo cmd_buf_info = {
613                 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
614                 .pNext = NULL,
615                 .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
616                 .pInheritanceInfo = NULL,
617         };
618 
619         res = vkBeginCommandBuffer(sync.command, &cmd_buf_info);
620         if (res) {
621             ALOGE("vkAllocateCommandBuffers failed %d", res);
622             return res;
623         }
624 
625         VkEventCreateInfo event_info = {
626                 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
627                 .pNext = NULL,
628                 .flags = 0,
629         };
630 
631         res = vkCreateEvent(mDevice, &event_info, NULL, &sync.event);
632         if (res) {
633             ALOGE("vkCreateEvent failed %d", res);
634             return res;
635         }
636 
637         vkCmdSetEvent(sync.command, sync.event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
638 
639         res = vkEndCommandBuffer(sync.command);
640         if (res) {
641             ALOGE("vkCreateEvent failed %d", res);
642             return res;
643         }
644 
645         mFreeSync[queue].push_back(sync);
646     }
647 
648     return VK_SUCCESS;
649 }
650 
destroyVkSyncObjects()651 void SwappyVkGoogleDisplayTimingAndroid::destroyVkSyncObjects() {
652     for (auto it = mPendingSync.begin(); it != mPendingSync.end(); it++) {
653         while (mPendingSync[it->first].size() > 0) {
654             VkSync sync = mPendingSync[it->first].front();
655             mPendingSync[it->first].pop_front();
656             vkWaitForFences(mDevice, 1, &sync.fence, VK_TRUE, UINT64_MAX);
657             vkResetFences(mDevice, 1, &sync.fence);
658             mFreeSync[it->first].push_back(sync);
659         }
660 
661         while (mFreeSync[it->first].size() > 0) {
662             VkSync sync = mFreeSync[it->first].front();
663             mFreeSync[it->first].pop_front();
664             vkFreeCommandBuffers(mDevice, mCommandPool[it->first], 1, &sync.command);
665             vkDestroyEvent(mDevice, sync.event, NULL);
666             vkDestroySemaphore(mDevice, sync.semaphore, NULL);
667             vkDestroyFence(mDevice, sync.fence, NULL);
668         }
669 
670         vkDestroyCommandPool(mDevice, mCommandPool[it->first], NULL);
671     }
672 }
673 
waitForFenceChoreographer(VkQueue queue)674 void SwappyVkGoogleDisplayTimingAndroid::waitForFenceChoreographer(VkQueue queue)
675 {
676     std::unique_lock<std::mutex> lock(mWaitingMutex);
677     VkSync sync = mPendingSync[queue].front();
678     mPendingSync[queue].pop_front();
679     mWaitingCondition.wait(lock, [&]() {
680         if (vkWaitForFences(mDevice, 1, &sync.fence, VK_TRUE, 0) == VK_TIMEOUT) {
681             postChoreographerCallback();
682 
683             // adjust the target frame here as we are waiting additional frame for the fence
684             mTargetFrameID++;
685             return false;
686         }
687         return true;
688     });
689 
690     vkResetFences(mDevice, 1, &sync.fence);
691     mFreeSync[queue].push_back(sync);
692 }
693 
doQueuePresent(VkQueue queue,uint32_t queueFamilyIndex,const VkPresentInfoKHR * pPresentInfo)694 VkResult SwappyVkGoogleDisplayTimingAndroid::doQueuePresent(VkQueue                 queue,
695                                                      uint32_t                queueFamilyIndex,
696                                                      const VkPresentInfoKHR* pPresentInfo)
697 {
698     VkResult ret = initializeVkSyncObjects(queue, queueFamilyIndex);
699     if (ret) {
700         return ret;
701     }
702 
703     {
704         std::unique_lock<std::mutex> lock(mWaitingMutex);
705         mWaitingCondition.wait(lock, [&]() {
706             if (mFrameID < mTargetFrameID) {
707                 postChoreographerCallback();
708                 return false;
709             }
710             return true;
711         });
712     }
713 
714     if (mPendingSync[queue].size() >= MAX_PENDING_FENCES) {
715         waitForFenceChoreographer(queue);
716     }
717 
718     // Adjust the presentation time based on the current frameID we are at.
719     if(mFrameID < mTargetFrameID) {
720         ALOGE("Bad frame ID %ld < target %ld", mFrameID, mTargetFrameID);
721         mTargetFrameID = mFrameID;
722     }
723     mNextDesiredPresentTime += (mFrameID - mTargetFrameID) * mRefreshDur;
724 
725     // Setup the new structures to pass:
726     VkPresentTimeGOOGLE pPresentTimes[pPresentInfo->swapchainCount];
727     for (uint32_t i = 0 ; i < pPresentInfo->swapchainCount ; i++) {
728         pPresentTimes[i].presentID = mNextPresentID;
729         pPresentTimes[i].desiredPresentTime = mNextDesiredPresentTime;
730     }
731     mNextPresentID++;
732 
733     VkSync sync = mFreeSync[queue].front();
734     mFreeSync[queue].pop_front();
735     mPendingSync[queue].push_back(sync);
736 
737     VkPipelineStageFlags pipe_stage_flags;
738     VkSubmitInfo submit_info;
739     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
740     submit_info.pNext = NULL;
741     submit_info.pWaitDstStageMask = &pipe_stage_flags;
742     pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
743     submit_info.waitSemaphoreCount = pPresentInfo->waitSemaphoreCount;
744     submit_info.pWaitSemaphores = pPresentInfo->pWaitSemaphores;
745     submit_info.commandBufferCount = 1;
746     submit_info.pCommandBuffers = &sync.command;
747     submit_info.signalSemaphoreCount = 1;
748     submit_info.pSignalSemaphores = &sync.semaphore;
749     ret = vkQueueSubmit(queue, 1, &submit_info, sync.fence);
750     if (ret) {
751         ALOGE("Failed to vkQueueSubmit %d", ret);
752         return ret;
753     }
754 
755     VkPresentTimesInfoGOOGLE presentTimesInfo = {VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
756                                                  pPresentInfo->pNext, pPresentInfo->swapchainCount,
757                                                  pPresentTimes};
758     VkPresentInfoKHR replacementPresentInfo = {pPresentInfo->sType, &presentTimesInfo,
759                                                1,
760                                                &sync.semaphore,
761                                                pPresentInfo->swapchainCount,
762                                                pPresentInfo->pSwapchains,
763                                                pPresentInfo->pImageIndices, pPresentInfo->pResults};
764     ret = mpfnQueuePresentKHR(queue, &replacementPresentInfo);
765 
766     // next present time is going to be 2 intervals from now, leaving 1 interval for cpu work
767     // and 1 interval for gpu work
768     mNextDesiredPresentTime = mLastframeTimeNanos + 2 * mRefreshDur * mInterval;
769     mTargetFrameID = mFrameID + mInterval;
770 
771     return ret;
772 }
773 
774 /***************************************************************************************************
775  *
776  * Per-Device concrete/derived class for the "Android fallback" path (uses
777  * Choreographer to try to get presents to occur at the desired time).
778  *
779  ***************************************************************************************************/
780 
781 /**
782  * Concrete/derived class that sits on top of the Vulkan API
783  */
784 #ifdef ANDROID
785 class SwappyVkAndroidFallback : public SwappyVkBase
786 {
787 public:
SwappyVkAndroidFallback(VkPhysicalDevice physicalDevice,VkDevice device,SwappyVk & swappyVk,void * libVulkan)788     SwappyVkAndroidFallback(VkPhysicalDevice physicalDevice,
789                             VkDevice         device,
790                             SwappyVk         &swappyVk,
791                             void             *libVulkan) :
792             SwappyVkBase(physicalDevice, device, 0, 1, swappyVk, libVulkan) {
793         startChoreographerThread();
794     }
795 
~SwappyVkAndroidFallback()796         ~SwappyVkAndroidFallback() {
797             stopChoreographerThread();
798     }
799 
doGetRefreshCycleDuration(VkSwapchainKHR swapchain,uint64_t * pRefreshDuration)800     virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
801                                                uint64_t*      pRefreshDuration) override
802     {
803         std::unique_lock<std::mutex> lock(mWaitingMutex);
804         mWaitingCondition.wait(lock, [&]() {
805             if (mRefreshDur == 0) {
806                 postChoreographerCallback();
807                 return false;
808             }
809             return true;
810         });
811 
812         *pRefreshDuration = mRefreshDur;
813 
814         double refreshRate = mRefreshDur;
815         refreshRate = 1.0 / (refreshRate / 1000000000.0);
816         ALOGI("Returning refresh duration of %" PRIu64 " nsec (approx %f Hz)", mRefreshDur, refreshRate);
817         return true;
818     }
819 
doQueuePresent(VkQueue queue,uint32_t queueFamilyIndex,const VkPresentInfoKHR * pPresentInfo)820     virtual VkResult doQueuePresent(VkQueue                 queue,
821                                     uint32_t                queueFamilyIndex,
822                                     const VkPresentInfoKHR* pPresentInfo) override
823     {
824         {
825             std::unique_lock<std::mutex> lock(mWaitingMutex);
826 
827             mWaitingCondition.wait(lock, [&]() {
828                 if (mFrameID < mTargetFrameID) {
829                     postChoreographerCallback();
830                     return false;
831                 }
832                 return true;
833             });
834         }
835         mTargetFrameID = mFrameID + mInterval;
836         return mpfnQueuePresentKHR(queue, pPresentInfo);
837     }
838 };
839 #endif
840 
841 /***************************************************************************************************
842  *
843  * Per-Device concrete/derived class for the "Vulkan fallback" path (i.e. no API/OS timing support;
844  * just generic Vulkan)
845  *
846  ***************************************************************************************************/
847 
848 /**
849  * Concrete/derived class that sits on top of the Vulkan API
850  */
851 class SwappyVkVulkanFallback : public SwappyVkBase
852 {
853 public:
SwappyVkVulkanFallback(VkPhysicalDevice physicalDevice,VkDevice device,SwappyVk & swappyVk,void * libVulkan)854     SwappyVkVulkanFallback(VkPhysicalDevice physicalDevice,
855                             VkDevice         device,
856                             SwappyVk         &swappyVk,
857                             void             *libVulkan) :
858             SwappyVkBase(physicalDevice, device, k16_6msec, 1, swappyVk, libVulkan) {}
doGetRefreshCycleDuration(VkSwapchainKHR swapchain,uint64_t * pRefreshDuration)859     virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
860                                            uint64_t*      pRefreshDuration) override
861     {
862         *pRefreshDuration = mRefreshDur;
863         return true;
864     }
doQueuePresent(VkQueue queue,uint32_t queueFamilyIndex,const VkPresentInfoKHR * pPresentInfo)865     virtual VkResult doQueuePresent(VkQueue                 queue,
866                                     uint32_t                queueFamilyIndex,
867                                     const VkPresentInfoKHR* pPresentInfo) override
868     {
869         return mpfnQueuePresentKHR(queue, pPresentInfo);
870     }
871 };
872 
873 
874 
875 
876 /***************************************************************************************************
877  *
878  * Singleton class that provides the high-level implementation of the Swappy entrypoints.
879  *
880  ***************************************************************************************************/
881 /**
882  * Singleton class that provides the high-level implementation of the Swappy entrypoints.
883  *
884  * This class determines which low-level implementation to use for each physical
885  * device, and then calls that class's do-method for the entrypoint.
886  */
887 class SwappyVk
888 {
889 public:
getInstance()890     static SwappyVk& getInstance()
891     {
892         static SwappyVk instance;
893         return instance;
894     }
~SwappyVk()895     ~SwappyVk() {
896         if(mLibVulkan)
897             dlclose(mLibVulkan);
898     }
899 
900     void swappyVkDetermineDeviceExtensions(VkPhysicalDevice       physicalDevice,
901                                            uint32_t               availableExtensionCount,
902                                            VkExtensionProperties* pAvailableExtensions,
903                                            uint32_t*              pRequiredExtensionCount,
904                                            char**                 pRequiredExtensions);
905     void SetQueueFamilyIndex(VkDevice   device,
906                              VkQueue    queue,
907                              uint32_t   queueFamilyIndex);
908     bool GetRefreshCycleDuration(VkPhysicalDevice physicalDevice,
909                                  VkDevice         device,
910                                  VkSwapchainKHR   swapchain,
911                                  uint64_t*        pRefreshDuration);
912     void SetSwapInterval(VkDevice       device,
913                          VkSwapchainKHR swapchain,
914                          uint32_t       interval);
915     VkResult QueuePresent(VkQueue                 queue,
916                           const VkPresentInfoKHR* pPresentInfo);
917     void DestroySwapchain(VkDevice                device,
918                           VkSwapchainKHR          swapchain);
919 
920 private:
921     std::map<VkPhysicalDevice, bool> doesPhysicalDeviceHaveGoogleDisplayTiming;
922     std::map<VkDevice, std::shared_ptr<SwappyVkBase>> perDeviceImplementation;
923     std::map<VkSwapchainKHR, std::shared_ptr<SwappyVkBase>> perSwapchainImplementation;
924 
925     struct QueueFamilyIndex {
926         VkDevice device;
927         uint32_t queueFamilyIndex;
928     };
929     std::map<VkQueue, QueueFamilyIndex> perQueueFamilyIndex;
930 
931     void *mLibVulkan     = nullptr;
932 
933 private:
SwappyVk()934     SwappyVk() {} // Need to implement this constructor
935     SwappyVk(SwappyVk const&); // Don't implement a copy constructor--no copies
936     void operator=(SwappyVk const&); // Don't implement--no copies
937 };
938 
939 /**
940  * Generic/Singleton implementation of swappyVkDetermineDeviceExtensions.
941  */
swappyVkDetermineDeviceExtensions(VkPhysicalDevice physicalDevice,uint32_t availableExtensionCount,VkExtensionProperties * pAvailableExtensions,uint32_t * pRequiredExtensionCount,char ** pRequiredExtensions)942 void SwappyVk::swappyVkDetermineDeviceExtensions(
943     VkPhysicalDevice       physicalDevice,
944     uint32_t               availableExtensionCount,
945     VkExtensionProperties* pAvailableExtensions,
946     uint32_t*              pRequiredExtensionCount,
947     char**                 pRequiredExtensions)
948 {
949     // TODO: Refactor this to be more concise:
950     if (!pRequiredExtensions) {
951         for (uint32_t i = 0; i < availableExtensionCount; i++) {
952             if (!strcmp(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
953                         pAvailableExtensions[i].extensionName)) {
954                 (*pRequiredExtensionCount)++;
955             }
956         }
957     } else {
958         doesPhysicalDeviceHaveGoogleDisplayTiming[physicalDevice] = false;
959         for (uint32_t i = 0, j = 0; i < availableExtensionCount; i++) {
960             if (!strcmp(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
961                         pAvailableExtensions[i].extensionName)) {
962                 if (j < *pRequiredExtensionCount) {
963                     strcpy(pRequiredExtensions[j++], VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME);
964                     doesPhysicalDeviceHaveGoogleDisplayTiming[physicalDevice] = true;
965                 }
966             }
967         }
968     }
969 }
970 
SetQueueFamilyIndex(VkDevice device,VkQueue queue,uint32_t queueFamilyIndex)971 void SwappyVk::SetQueueFamilyIndex(VkDevice   device,
972                                     VkQueue    queue,
973                                     uint32_t   queueFamilyIndex)
974 {
975     perQueueFamilyIndex[queue] = {device, queueFamilyIndex};
976 }
977 
978 
979 /**
980  * Generic/Singleton implementation of swappyVkGetRefreshCycleDuration.
981  */
GetRefreshCycleDuration(VkPhysicalDevice physicalDevice,VkDevice device,VkSwapchainKHR swapchain,uint64_t * pRefreshDuration)982 bool SwappyVk::GetRefreshCycleDuration(VkPhysicalDevice physicalDevice,
983                                        VkDevice         device,
984                                        VkSwapchainKHR   swapchain,
985                                        uint64_t*        pRefreshDuration)
986 {
987     auto& pImplementation = perDeviceImplementation[device];
988     if (!pImplementation) {
989         // We have not seen this device yet.
990         if (!mLibVulkan) {
991             // This is the first time we've been called--initialize function pointers:
992             mLibVulkan = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
993             if (!mLibVulkan)
994             {
995                 // If Vulkan doesn't exist, bail-out early:
996                 return false;
997             }
998         }
999 
1000         // First, based on whether VK_GOOGLE_display_timing is available
1001         // (determined and cached by swappyVkDetermineDeviceExtensions),
1002         // determine which derived class to use to implement the rest of the API
1003         if (doesPhysicalDeviceHaveGoogleDisplayTiming[physicalDevice]) {
1004 #ifdef ANDROID
1005             pImplementation = std::make_shared<SwappyVkGoogleDisplayTimingAndroid>
1006                     (physicalDevice, device, getInstance(), mLibVulkan);
1007             ALOGV("SwappyVk initialized for VkDevice %p using VK_GOOGLE_display_timing on Android", device);
1008 #else
1009             // Instantiate the class that sits on top of VK_GOOGLE_display_timing
1010             pImplementation = std::make_shared<SwappyVkGoogleDisplayTiming>
1011                     (physicalDevice, device, getInstance(), mLibVulkan);
1012             ALOGV("SwappyVk initialized for VkDevice %p using VK_GOOGLE_display_timing", device);
1013 #endif
1014         } else {
1015             // Instantiate the class that sits on top of the basic Vulkan APIs
1016 #ifdef ANDROID
1017             pImplementation = std::make_shared<SwappyVkAndroidFallback>
1018                     (physicalDevice, device, getInstance(), mLibVulkan);
1019             ALOGV("SwappyVk initialized for VkDevice %p using Android fallback", device);
1020 #else  // ANDROID
1021             pImplementation = std::make_shared<SwappyVkVulkanFallback>
1022                     (physicalDevice, device, getInstance(), mLibVulkan);
1023             ALOGV("SwappyVk initialized for VkDevice %p using Vulkan-only fallback", device);
1024 #endif // ANDROID
1025         }
1026 
1027         if (!pImplementation) {
1028             // This shouldn't happen, but if it does, something is really wrong.
1029             return false;
1030         }
1031     }
1032 
1033     // Cache the per-swapchain pointer to the derived class:
1034     perSwapchainImplementation[swapchain] = pImplementation;
1035 
1036     // Now, call that derived class to get the refresh duration to return
1037     return pImplementation->doGetRefreshCycleDuration(swapchain, pRefreshDuration);
1038 }
1039 
1040 
1041 /**
1042  * Generic/Singleton implementation of swappyVkSetSwapInterval.
1043  */
SetSwapInterval(VkDevice device,VkSwapchainKHR swapchain,uint32_t interval)1044 void SwappyVk::SetSwapInterval(VkDevice       device,
1045                                VkSwapchainKHR swapchain,
1046                                uint32_t       interval)
1047 {
1048     auto& pImplementation = perDeviceImplementation[device];
1049     if (!pImplementation) {
1050         return;
1051     }
1052     pImplementation->doSetSwapInterval(swapchain, interval);
1053 }
1054 
1055 
1056 /**
1057  * Generic/Singleton implementation of swappyVkQueuePresent.
1058  */
QueuePresent(VkQueue queue,const VkPresentInfoKHR * pPresentInfo)1059 VkResult SwappyVk::QueuePresent(VkQueue                 queue,
1060                                 const VkPresentInfoKHR* pPresentInfo)
1061 {
1062     if (perQueueFamilyIndex.find(queue) == perQueueFamilyIndex.end()) {
1063         ALOGE("Unknown queue %p. Did you call SwappyVkSetQueueFamilyIndex ?", queue);
1064         return VK_INCOMPLETE;
1065     }
1066 
1067     // This command doesn't have a VkDevice.  It should have at least one VkSwapchainKHR's.  For
1068     // this command, all VkSwapchainKHR's will have the same VkDevice and VkQueue.
1069     if ((pPresentInfo->swapchainCount == 0) || (!pPresentInfo->pSwapchains)) {
1070         // This shouldn't happen, but if it does, something is really wrong.
1071         return VK_ERROR_DEVICE_LOST;
1072     }
1073     auto& pImplementation = perSwapchainImplementation[*pPresentInfo->pSwapchains];
1074     if (pImplementation) {
1075         return pImplementation->doQueuePresent(queue,
1076                                                perQueueFamilyIndex[queue].queueFamilyIndex,
1077                                                pPresentInfo);
1078     } else {
1079         // This should only happen if the API was used wrong (e.g. they never
1080         // called swappyVkGetRefreshCycleDuration).
1081         // NOTE: Technically, a Vulkan library shouldn't protect a user from
1082         // themselves, but we'll be friendlier
1083         return VK_ERROR_DEVICE_LOST;
1084     }
1085 }
1086 
DestroySwapchain(VkDevice device,VkSwapchainKHR swapchain)1087 void SwappyVk::DestroySwapchain(VkDevice                device,
1088                                         VkSwapchainKHR          swapchain) {
1089     auto it = perQueueFamilyIndex.begin();
1090     while (it != perQueueFamilyIndex.end()) {
1091         if (it->second.device == device) {
1092             it = perQueueFamilyIndex.erase(it);
1093         } else {
1094             ++it;
1095         }
1096     }
1097 
1098     perDeviceImplementation[device] = nullptr;
1099     perSwapchainImplementation[swapchain] = nullptr;
1100 }
1101 
1102 
1103 /***************************************************************************************************
1104  *
1105  * API ENTRYPOINTS
1106  *
1107  ***************************************************************************************************/
1108 
1109 extern "C" {
1110 
SwappyVk_determineDeviceExtensions(VkPhysicalDevice physicalDevice,uint32_t availableExtensionCount,VkExtensionProperties * pAvailableExtensions,uint32_t * pRequiredExtensionCount,char ** pRequiredExtensions)1111 void SwappyVk_determineDeviceExtensions(
1112     VkPhysicalDevice       physicalDevice,
1113     uint32_t               availableExtensionCount,
1114     VkExtensionProperties* pAvailableExtensions,
1115     uint32_t*              pRequiredExtensionCount,
1116     char**                 pRequiredExtensions)
1117 {
1118     TRACE_CALL();
1119     SwappyVk& swappy = SwappyVk::getInstance();
1120     swappy.swappyVkDetermineDeviceExtensions(physicalDevice,
1121                                              availableExtensionCount, pAvailableExtensions,
1122                                              pRequiredExtensionCount, pRequiredExtensions);
1123 }
1124 
SwappyVk_setQueueFamilyIndex(VkDevice device,VkQueue queue,uint32_t queueFamilyIndex)1125 void SwappyVk_setQueueFamilyIndex(
1126         VkDevice    device,
1127         VkQueue     queue,
1128         uint32_t    queueFamilyIndex)
1129 {
1130     TRACE_CALL();
1131     SwappyVk& swappy = SwappyVk::getInstance();
1132     swappy.SetQueueFamilyIndex(device, queue, queueFamilyIndex);
1133 }
1134 
SwappyVk_initAndGetRefreshCycleDuration(VkPhysicalDevice physicalDevice,VkDevice device,VkSwapchainKHR swapchain,uint64_t * pRefreshDuration)1135 bool SwappyVk_initAndGetRefreshCycleDuration(
1136         VkPhysicalDevice physicalDevice,
1137         VkDevice         device,
1138         VkSwapchainKHR   swapchain,
1139         uint64_t*        pRefreshDuration)
1140 {
1141     TRACE_CALL();
1142     SwappyVk& swappy = SwappyVk::getInstance();
1143     return swappy.GetRefreshCycleDuration(physicalDevice, device, swapchain, pRefreshDuration);
1144 }
1145 
SwappyVk_setSwapInterval(VkDevice device,VkSwapchainKHR swapchain,uint32_t interval)1146 void SwappyVk_setSwapInterval(
1147         VkDevice       device,
1148         VkSwapchainKHR swapchain,
1149         uint32_t       interval)
1150 {
1151     TRACE_CALL();
1152     SwappyVk& swappy = SwappyVk::getInstance();
1153     swappy.SetSwapInterval(device, swapchain, interval);
1154 }
1155 
SwappyVk_queuePresent(VkQueue queue,const VkPresentInfoKHR * pPresentInfo)1156 VkResult SwappyVk_queuePresent(
1157         VkQueue                 queue,
1158         const VkPresentInfoKHR* pPresentInfo)
1159 {
1160     TRACE_CALL();
1161     SwappyVk& swappy = SwappyVk::getInstance();
1162     return swappy.QueuePresent(queue, pPresentInfo);
1163 }
1164 
SwappyVk_destroySwapchain(VkDevice device,VkSwapchainKHR swapchain)1165 void SwappyVk_destroySwapchain(
1166         VkDevice                device,
1167         VkSwapchainKHR          swapchain)
1168 {
1169     TRACE_CALL();
1170     SwappyVk& swappy = SwappyVk::getInstance();
1171     swappy.DestroySwapchain(device, swapchain);
1172 }
1173 
1174 }  // extern "C"
1175