summaryrefslogtreecommitdiff
path: root/demos
diff options
context:
space:
mode:
authorIan Elliott <ianelliott@google.com>2017-03-22 08:31:27 -0600
committerIan Elliott <ianelliott@google.com>2017-03-27 14:42:48 -0600
commitcf379cbc2ac59ca03581697ce7c89f9f8edbbd88 (patch)
tree12414ab066bd521f150d8f7c1f3d1e555b95180e /demos
parent57eda737eea95dd728f6dce5c78d6b00fb126076 (diff)
cube: GH1609: Use VK_GOOGLE_display_timing extension
Show how to use the VK_GOOGLE_display_timing extension (currently, only available on the Android O release). Other notes: - There are many diagnostic DbgMsg()'s, which can help show the usage. - This includes a port of the Vulkan CTS (dEQP) time code for use in cube. - Added a "--display_timing" command-line option to turn on use of VK_GOOGLE_display_timing - Compiles and runs on Windows, Linux, and Android, but the feature will only really be used on systems that support the extension.
Diffstat (limited to 'demos')
-rw-r--r--demos/cube.c444
-rw-r--r--demos/gettime.h74
2 files changed, 493 insertions, 25 deletions
diff --git a/demos/cube.c b/demos/cube.c
index 3e2733ee..a429af7e 100644
--- a/demos/cube.c
+++ b/demos/cube.c
@@ -1,31 +1,33 @@
/*
-* Copyright (c) 2015-2016 The Khronos Group Inc.
-* Copyright (c) 2015-2016 Valve Corporation
-* Copyright (c) 2015-2016 LunarG, Inc.
-*
-* 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.
-*
-* Author: Chia-I Wu <olv@lunarg.com>
-* Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
-* Author: Ian Elliott <ian@LunarG.com>
-* Author: Jon Ashburn <jon@lunarg.com>
-* Author: Gwan-gyeong Mun <elongbug@gmail.com>
-* Author: Tony Barbour <tony@LunarG.com>
-* Author: Bill Hollings <bill.hollings@brenwill.com>
-*/
+ * Copyright (c) 2015-2016 The Khronos Group Inc.
+ * Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ *
+ * 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.
+ *
+ * Author: Chia-I Wu <olv@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Ian Elliott <ian@LunarG.com>
+ * Author: Ian Elliott <ianelliott@google.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Gwan-gyeong Mun <elongbug@gmail.com>
+ * Author: Tony Barbour <tony@LunarG.com>
+ * Author: Bill Hollings <bill.hollings@brenwill.com>
+ */
#define _GNU_SOURCE
#include <stdio.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
@@ -53,6 +55,11 @@
#include <vulkan/vk_sdk_platform.h>
#include "linmath.h"
+#include "gettime.h"
+#include "inttypes.h"
+#define MILLION 1000000L
+#define BILLION 1000000000L
+
#define DEMO_TEXTURE_COUNT 1
#define APP_SHORT_NAME "cube"
#define APP_LONG_NAME "The Vulkan Cube Demo Program"
@@ -81,6 +88,13 @@ bool in_callback = false;
if (!demo->suppress_popups) MessageBox(NULL, err_msg, err_class, MB_OK); \
exit(1); \
} while (0)
+void DbgMsg(char *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ printf(fmt, va);
+ fflush(stdout);
+ va_end(va);
+}
#elif defined __ANDROID__
#include <android/log.h>
@@ -89,6 +103,19 @@ bool in_callback = false;
((void)__android_log_print(ANDROID_LOG_INFO, "Cube", err_msg)); \
exit(1); \
} while (0)
+#ifdef VARARGS_WORKS_ON_ANDROID
+void DbgMsg(const char *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ __android_log_print(ANDROID_LOG_INFO, "Cube", fmt, va);
+ va_end(va);
+}
+#else // VARARGS_WORKS_ON_ANDROID
+#define DbgMsg(fmt, ...) \
+ do { \
+ ((void)__android_log_print(ANDROID_LOG_INFO, "Cube", fmt, ##__VA_ARGS__)); \
+ } while (0)
+#endif // VARARGS_WORKS_ON_ANDROID
#else
#define ERR_EXIT(err_msg, err_class) \
do { \
@@ -96,6 +123,13 @@ bool in_callback = false;
fflush(stdout); \
exit(1); \
} while (0)
+void DbgMsg(char *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ printf(fmt, va);
+ fflush(stdout);
+ va_end(va);
+}
#endif
#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
@@ -313,6 +347,16 @@ struct demo {
bool use_staging_buffer;
bool separate_present_queue;
+ bool VK_GOOGLE_display_timing_enabled;
+ bool syncd_with_actual_presents;
+ uint64_t refresh_duration;
+ uint64_t refresh_duration_multiplier;
+ uint64_t target_IPD; // image present duration (inverse of frame rate)
+ uint64_t prev_desired_present_time;
+ uint32_t next_present_id;
+ uint32_t last_early_id; // 0 if no early images
+ uint32_t last_late_id; // 0 if no late images
+
VkInstance inst;
VkPhysicalDevice gpu;
VkDevice device;
@@ -345,6 +389,8 @@ struct demo {
PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
PFN_vkQueuePresentKHR fpQueuePresentKHR;
+ PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE;
+ PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE;
uint32_t swapchainImageCount;
VkSwapchainKHR swapchain;
SwapchainImageResources *swapchain_image_resources;
@@ -475,6 +521,43 @@ VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectType
return false;
}
+bool ActualTimeLate(uint64_t desired, uint64_t actual, uint64_t rdur) {
+ // The desired time was the earliest time that the present should have
+ // occured. In almost every case, the actual time should be later than the
+ // desired time. We should only consider the actual time "late" if it is
+ // after "desired + rdur".
+ if (actual <= desired) {
+ // The actual time was before or equal to the desired time. This will
+ // probably never happen, but in case it does, return false since the
+ // present was obviously NOT late.
+ return false;
+ }
+ uint64_t deadline = actual + rdur;
+ if (actual > deadline) {
+ return true;
+ } else {
+ return false;
+ }
+}
+bool CanPresentEarlier(uint64_t earliest,
+ uint64_t actual,
+ uint64_t margin,
+ uint64_t rdur) {
+ if (earliest < actual) {
+ // Consider whether this present could have occured earlier. Make sure
+ // that earliest time was at least 2msec earlier than actual time, and
+ // that the margin was at least 2msec:
+ uint64_t diff = actual - earliest;
+ if ((diff >= (2 * MILLION)) && (margin >= (2 * MILLION))) {
+ // This present could have occured earlier because both: 1) the
+ // earliest time was at least 2 msec before actual time, and 2) the
+ // margin was at least 2msec.
+ return true;
+ }
+ }
+ return false;
+}
+
// Forward declaration:
static void demo_resize(struct demo *demo);
@@ -737,6 +820,212 @@ void demo_update_data_buffer(struct demo *demo) {
vkUnmapMemory(demo->device, demo->swapchain_image_resources[demo->current_buffer].uniform_memory);
}
+void DemoUpdateTargetIPD(struct demo *demo) {
+ // Look at what happened to previous presents, and make appropriate
+ // adjustments in timing:
+ VkResult U_ASSERT_ONLY err;
+ VkPastPresentationTimingGOOGLE* past = NULL;
+ uint32_t count = 0;
+ DbgMsg("%s(): about to call vkGetPastPresentationTimingGOOGLE\n", __FUNCTION__);
+
+ err = demo->fpGetPastPresentationTimingGOOGLE(demo->device,
+ demo->swapchain,
+ &count,
+ NULL);
+ assert(!err);
+ DbgMsg("%s(): vkGetPastPresentationTimingGOOGLE returned count of %d\n",
+ __FUNCTION__, count);
+ if (count) {
+ past = (VkPastPresentationTimingGOOGLE*) malloc(sizeof(VkPastPresentationTimingGOOGLE) * count);
+ assert(past);
+ DbgMsg("%s(): about to call vkGetPastPresentationTimingGOOGLE again\n", __FUNCTION__);
+ err = demo->fpGetPastPresentationTimingGOOGLE(demo->device,
+ demo->swapchain,
+ &count,
+ past);
+ assert(!err);
+
+ bool early = false;
+ bool late = false;
+ bool calibrate_next = false;
+ static VkPastPresentationTimingGOOGLE prior = {0xffffffff, 0, 0, 0, 0};
+ for (uint32_t i = 0 ; i < count ; i++) {
+ DbgMsg("%s():\n", __FUNCTION__);
+ DbgMsg("%s():\t presentID, desiredPresentTime, actualPresentTime, earliestPresentTime, presentMargin\n", __FUNCTION__);
+ if (prior.presentID != 0xffffffff) {
+ DbgMsg("%s(): prior: %8d, %17"PRId64", %16"PRId64", %18"PRId64", %12"PRId64"\n", __FUNCTION__,
+ prior.presentID,
+ prior.desiredPresentTime,
+ prior.actualPresentTime,
+ prior.earliestPresentTime,
+ prior.presentMargin);
+ }
+ DbgMsg("%s(): cur: %8d, %17"PRId64", %16"PRId64", %18"PRId64", %12"PRId64"\n", __FUNCTION__,
+ past[i].presentID,
+ past[i].desiredPresentTime,
+ past[i].actualPresentTime,
+ past[i].earliestPresentTime,
+ past[i].presentMargin);
+ if (prior.presentID != 0xffffffff) {
+ int64_t diff_desiredPresentTime = past[i].desiredPresentTime - prior.desiredPresentTime;
+ int64_t diff_actualPresentTime = past[i].actualPresentTime - prior.actualPresentTime;
+ int64_t diff_earliestPresentTime = past[i].earliestPresentTime - prior.earliestPresentTime;
+ int64_t diff_presentMargin = past[i].presentMargin - prior.presentMargin;
+ DbgMsg("%s():\t ====================================================================================\n", __FUNCTION__);
+ DbgMsg("%s(): diff:\t %17"PRId64", %16"PRId64", %18"PRId64", %12"PRId64"\n", __FUNCTION__,
+ diff_desiredPresentTime,
+ diff_actualPresentTime,
+ diff_earliestPresentTime,
+ diff_presentMargin);
+ }
+ DbgMsg("%s():\n", __FUNCTION__);
+
+
+ DbgMsg("%s(): actualPresentTime= %16"PRId64", actualPresentTime = %16"PRId64", PRIORactualPresentTime= %16"PRId64"\n",
+ __FUNCTION__, past[i].actualPresentTime, past[i].actualPresentTime, prior.actualPresentTime);
+ DbgMsg("%s():desiredPresentTime= %16"PRId64", earliestPresentTime = %16"PRId64", earliestPresentTime = %16"PRId64"\n",
+ __FUNCTION__, past[i].desiredPresentTime, past[i].earliestPresentTime, past[i].earliestPresentTime);
+ int64_t diffDesireVsActual = past[i].actualPresentTime - past[i].desiredPresentTime;
+ uint64_t diffEarlyVsActual = past[i].actualPresentTime - past[i].earliestPresentTime;
+ uint64_t diffEarlyVsPriorActual = past[i].earliestPresentTime - prior.actualPresentTime;
+ DbgMsg("%s():\t\t\t ================================================================================================\n", __FUNCTION__);
+ DbgMsg("%s(): Amt late (-early) = %12"PRId64", Amt could have been early = %10"PRId64", Sanity check earliest = %16"PRId64"\n",
+ __FUNCTION__, diffDesireVsActual, diffEarlyVsActual, diffEarlyVsPriorActual);
+ DbgMsg("%s(): demo->target_IPD = %"PRId64"\n", __FUNCTION__, demo->target_IPD);
+ DbgMsg("%s(): demo->next_present_id: %d\n", __FUNCTION__, demo->next_present_id);
+ prior.presentID = past[i].presentID;
+ prior.desiredPresentTime = past[i].desiredPresentTime;
+ prior.actualPresentTime = past[i].actualPresentTime;
+ prior.earliestPresentTime = past[i].earliestPresentTime;
+ prior.presentMargin = past[i].presentMargin;
+ if (!demo->syncd_with_actual_presents) {
+ // This is the first time that we've received an
+ // actualPresentTime for this swapchain. In order to not
+ // perceive these early frames as "late", we need to sync-up
+ // our future desiredPresentTime's with the
+ // actualPresentTime(s) that we're receiving now.
+ calibrate_next = true;
+ DbgMsg("%s():\n", __FUNCTION__);
+ DbgMsg("%s(): FIRST TIME GETTING BACK ACTUAL TIME(S)\n", __FUNCTION__);
+ DbgMsg("%s(): Number of TimingInfo's we received was %d\n", __FUNCTION__, count);
+ if (count > 1) {
+ DbgMsg("%s(): past[%d].actualPresentTime = %"PRId64"\n", __FUNCTION__, (count-1), past[count-1].actualPresentTime);
+ DbgMsg("%s(): Last presentID is: %d\n", __FUNCTION__, past[count-1].presentID);
+ }
+ DbgMsg("%s(): demo->prev_desired_present_time= %"PRId64"\n", __FUNCTION__, demo->prev_desired_present_time);
+
+ DbgMsg("%s():\n", __FUNCTION__);
+ DbgMsg("%s():\n", __FUNCTION__);
+ // So that we don't suspect any pending presents as late,
+ // record them all as suspected-late presents:
+ demo->last_late_id = demo->next_present_id - 1;
+ demo->last_early_id = 0;
+ DbgMsg("%s(): Skip past presentID: %d\n", __FUNCTION__, demo->last_late_id);
+
+ demo->syncd_with_actual_presents = true;
+ break;
+ } else if (CanPresentEarlier(past[i].earliestPresentTime,
+ past[i].actualPresentTime,
+ past[i].presentMargin,
+ demo->refresh_duration)) {
+ // This image could have been presented earlier. We don't want
+ // to decrease the target_IPD until we've seen early presents
+ // for at least two seconds.
+ if (demo->last_early_id == past[i].presentID) {
+ // We've now seen two seconds worth of early presents.
+ // Flag it as such, and reset the counter:
+ early = true;
+ demo->last_early_id = 0;
+ } else if (demo->last_early_id == 0) {
+ // This is the first early present we've seen.
+ // Calculate the presentID for two seconds from now.
+ uint64_t lastEarlyTime =
+ past[i].actualPresentTime + (2 * BILLION);
+ uint32_t howManyPresents =
+ (uint32_t)((lastEarlyTime - past[i].actualPresentTime) / demo->target_IPD);
+ demo->last_early_id = past[i].presentID + howManyPresents;
+ } else {
+ // We are in the midst of a set of early images,
+ // and so we won't do anything.
+ }
+ late = false;
+ demo->last_late_id = 0;
+ } else if (ActualTimeLate(past[i].desiredPresentTime,
+ past[i].actualPresentTime,
+ demo->refresh_duration)) {
+ // This image was presented after its desired time. Since
+ // there's a delay between calling vkQueuePresentKHR and when
+ // we get the timing data, several presents may have been late.
+ // Thus, we need to threat all of the outstanding presents as
+ // being likely late, so that we only increase the target_IPD
+ // once for all of those presents.
+ if ((demo->last_late_id == 0) ||
+ (demo->last_late_id < past[i].presentID)) {
+ late = true;
+ // Record the last suspected-late present:
+ demo->last_late_id = demo->next_present_id - 1;
+ } else {
+ // We are in the midst of a set of likely-late images,
+ // and so we won't do anything.
+ }
+ early = false;
+ demo->last_early_id = 0;
+ } else {
+ // Since this image was not presented early or late, reset
+ // any sets of early or late presentIDs:
+ early = false;
+ late = false;
+ calibrate_next = true;
+ demo->last_early_id = 0;
+ demo->last_late_id = 0;
+ }
+ }
+
+ if (early) {
+ // Since we've seen at least two-seconds worth of presnts that
+ // could have occured earlier than desired, let's decrease the
+ // target_IPD (i.e. increase the frame rate):
+ //
+ // TODO(ianelliott): Try to calculate a better target_IPD based
+ // on the most recently-seen present (this is overly-simplistic).
+ demo->refresh_duration_multiplier--;
+ if (demo->refresh_duration_multiplier == 0) {
+ // This should never happen, but in case it does, don't
+ // try to go faster.
+ demo->refresh_duration_multiplier = 1;
+ }
+ demo->target_IPD =
+ demo->refresh_duration * demo->refresh_duration_multiplier;
+ DbgMsg("\t INCREASING FRAME RATE! NEW VALUE OF target_IPD = %"PRId64"\n", demo->target_IPD);
+ }
+ if (late) {
+ // Since we found a new instance of a late present, we want to
+ // increase the target_IPD (i.e. decrease the frame rate):
+ //
+ // TODO(ianelliott): Try to calculate a better target_IPD based
+ // on the most recently-seen present (this is overly-simplistic).
+ demo->refresh_duration_multiplier++;
+ demo->target_IPD =
+ demo->refresh_duration * demo->refresh_duration_multiplier;
+ DbgMsg("\t DECREASING FRAME RATE! NEW VALUE OF target_IPD = %"PRId64"\n", demo->target_IPD);
+ }
+
+ if (calibrate_next) {
+ int64_t multiple = demo->next_present_id - past[count-1].presentID;
+ demo->prev_desired_present_time =
+ (past[count-1].actualPresentTime +
+ (multiple * demo->target_IPD));
+ DbgMsg("%s():\n", __FUNCTION__);
+ DbgMsg("%s(): CALIBRATING!!!\n", __FUNCTION__);
+ DbgMsg("%s(): multiple = %"PRId64"\n", __FUNCTION__, multiple);
+ DbgMsg("%s(): demo->prev_desired_present_time= %"PRId64"\n", __FUNCTION__, demo->prev_desired_present_time);
+ int64_t next_time = demo->prev_desired_present_time + demo->target_IPD;
+ DbgMsg("%s(): past[%d].desiredPresentTime = %"PRId64"\n", __FUNCTION__, demo->next_present_id, next_time);
+ }
+ }
+ DbgMsg("\t\t\t NEW VALUE OF target_IPD = %"PRId64"\n", demo->target_IPD);
+}
+
static void demo_draw(struct demo *demo) {
VkResult U_ASSERT_ONLY err;
@@ -767,6 +1056,18 @@ static void demo_draw(struct demo *demo) {
} else {
assert(!err);
}
+ if (demo->VK_GOOGLE_display_timing_enabled) {
+ // Look at what happened to previous presents, and make appropriate
+ // adjustments in timing:
+ DemoUpdateTargetIPD(demo);
+
+ // Note: a real application would position its geometry to that it's in
+ // the correct locatoin for when the next image is presented. It might
+ // also wait, so that there's less latency between any input and when
+ // the next image is rendered/presented. This demo program is so
+ // simple that it doesn't do either of those.
+ }
+
// Wait for the image acquired semaphore to be signaled to ensure
// that the image won't be rendered to until the presentation
// engine has fully released ownership to the application, and it is
@@ -819,6 +1120,51 @@ static void demo_draw(struct demo *demo) {
.pImageIndices = &demo->current_buffer,
};
+ if (demo->VK_GOOGLE_display_timing_enabled) {
+ VkPresentTimeGOOGLE ptime;
+ if (demo->prev_desired_present_time == 0) {
+ // This must be the first present for this swapchain.
+ //
+ // We don't know where we are relative to the presentation engine's
+ // display's refresh cycle. We also don't know how long rendering
+ // takes. Let's make a grossly-simplified assumption that the
+ // desiredPresentTime should be half way between now and
+ // now+target_IPD. We will adjust over time.
+ uint64_t curtime = getTimeInNanoseconds();
+ if (curtime == 0) {
+ // Since we didn't find out the current time, don't give a
+ // desiredPresentTime:
+ ptime.desiredPresentTime = 0;
+ } else {
+ DbgMsg("Current time (e.g. CLOCK_MONOTONIC) in nanoseconds: %"PRId64"\n",
+ curtime);
+ ptime.desiredPresentTime = curtime + (demo->target_IPD >> 1);
+ }
+ } else {
+ ptime.desiredPresentTime = (demo->prev_desired_present_time +
+ demo->target_IPD);
+ }
+ ptime.presentID = demo->next_present_id++;
+ demo->prev_desired_present_time = ptime.desiredPresentTime;
+ DbgMsg("presentID = %d, desiredPresentTime = %"PRId64"\n",
+ ptime.presentID,
+ ptime.desiredPresentTime);
+
+ VkPresentTimesInfoGOOGLE present_time = {
+ .sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
+ .pNext = present.pNext,
+ .swapchainCount = present.swapchainCount,
+ .pTimes = &ptime,
+ };
+ if (demo->VK_GOOGLE_display_timing_enabled) {
+ present.pNext = &present_time;
+ DbgMsg("present.pNext = %p, present_time = %p, present_time.pNext = %p\n",
+ present.pNext,
+ &present_time,
+ present_time.pNext);
+ }
+ }
+
err = demo->fpQueuePresentKHR(demo->present_queue, &present);
demo->frame_index += 1;
demo->frame_index %= FRAME_LAG;
@@ -1058,6 +1404,25 @@ static void demo_prepare_buffers(struct demo *demo) {
assert(!err);
}
+ if (demo->VK_GOOGLE_display_timing_enabled) {
+ VkRefreshCycleDurationGOOGLE rc_dur;
+ err = demo->fpGetRefreshCycleDurationGOOGLE(demo->device,
+ demo->swapchain,
+ &rc_dur);
+ assert(!err);
+ demo->refresh_duration = rc_dur.refreshDuration;
+
+ demo->syncd_with_actual_presents = false;
+ // Initially target 1X the refresh duration:
+ demo->target_IPD = demo->refresh_duration;
+ demo->refresh_duration_multiplier = 1;
+ demo->prev_desired_present_time = 0;
+ demo->next_present_id = 1;
+
+ DbgMsg("refresh_duration = %"PRId64"\n", demo->refresh_duration);
+ DbgMsg("\t\t\tINITIAL VALUE OF target_IPD = %"PRId64"\n", demo->target_IPD);
+ }
+
if (NULL != presentModes) {
free(presentModes);
}
@@ -3051,6 +3416,27 @@ static void demo_init_vk(struct demo *demo) {
assert(demo->enabled_extension_count < 64);
}
+ if (demo->VK_GOOGLE_display_timing_enabled) {
+ // Even though the user "enabled" the extension via the command
+ // line, we must make sure that it's enumerated for use with the
+ // device. Therefore, disable it here, and re-enable it again if
+ // enumerated.
+ demo->VK_GOOGLE_display_timing_enabled = false;
+ for (uint32_t i = 0; i < device_extension_count; i++) {
+ if (!strcmp(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
+ device_extensions[i].extensionName)) {
+ demo->extension_names[demo->enabled_extension_count++] =
+ VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME;
+ demo->VK_GOOGLE_display_timing_enabled = true;
+ DbgMsg("VK_GOOGLE_display_timing enabled\n");
+ }
+ assert(demo->enabled_extension_count < 64);
+ }
+ if (!demo->VK_GOOGLE_display_timing_enabled) {
+ DbgMsg("VK_GOOGLE_display_timing NOT ENABLED\n");
+ }
+ }
+
free(device_extensions);
}
@@ -3304,6 +3690,10 @@ static void demo_init_vk_swapchain(struct demo *demo) {
GET_DEVICE_PROC_ADDR(demo->device, GetSwapchainImagesKHR);
GET_DEVICE_PROC_ADDR(demo->device, AcquireNextImageKHR);
GET_DEVICE_PROC_ADDR(demo->device, QueuePresentKHR);
+ if (demo->VK_GOOGLE_display_timing_enabled) {
+ GET_DEVICE_PROC_ADDR(demo->device, GetRefreshCycleDurationGOOGLE);
+ GET_DEVICE_PROC_ADDR(demo->device, GetPastPresentationTimingGOOGLE);
+ }
vkGetDeviceQueue(demo->device, demo->graphics_queue_family_index, 0,
&demo->graphics_queue);
@@ -3486,12 +3876,16 @@ static void demo_init(struct demo *demo, int argc, char **argv) {
demo->suppress_popups = true;
continue;
}
+ if (strcmp(argv[i], "--display_timing") == 0) {
+ demo->VK_GOOGLE_display_timing_enabled = true;
+ continue;
+ }
#if defined(ANDROID)
ERR_EXIT("Usage: cube [--validate]\n", "Usage");
#else
fprintf(stderr, "Usage:\n %s [--use_staging] [--validate] [--validate-checks-disabled] [--break] "
- "[--c <framecount>] [--suppress_popups] [--present_mode <present mode enum>]\n"
+ "[--c <framecount>] [--suppress_popups] [--display_timing] [--present_mode <present mode enum>]\n"
"VK_PRESENT_MODE_IMMEDIATE_KHR = %d\n"
"VK_PRESENT_MODE_MAILBOX_KHR = %d\n"
"VK_PRESENT_MODE_FIFO_KHR = %d\n"
diff --git a/demos/gettime.h b/demos/gettime.h
new file mode 100644
index 00000000..a4265cdc
--- /dev/null
+++ b/demos/gettime.h
@@ -0,0 +1,74 @@
+/**************************************************************************
+ *
+ * Copyright 2014, 2017 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.
+ *
+ * Ported from drawElements Utility Library (Google, Inc.)
+ * Port done by: Ian Elliott <ianelliott@google.com>
+ **************************************************************************/
+
+#include <time.h>
+#include <assert.h>
+#include <vulkan/vk_platform.h>
+
+#if defined(_WIN32)
+
+#include <windows.h>
+
+#elif defined(__unix__) || defined(__linux) || defined(__linux__) || defined(__ANDROID__) || defined(__EPOC32__) || defined(__QNX__)
+
+#include <time.h>
+
+#elif defined(__APPLE__)
+
+#include <sys/time.h>
+
+#endif
+
+uint64_t getTimeInNanoseconds(void) {
+#if defined(_WIN32)
+ LARGE_INTEGER freq;
+ LARGE_INTEGER count;
+ QueryPerformanceCounter(&count);
+ QueryPerformanceFrequency(&freq);
+ assert(freq.LowPart != 0 || freq.HighPart != 0);
+
+ if (count.QuadPart < MAXLONGLONG / 1000000) {
+ assert(freq.QuadPart != 0);
+ return count.QuadPart * 1000000 / freq.QuadPart;
+ } else {
+ assert(freq.QuadPart >= 1000000);
+ return count.QuadPart / (freq.QuadPart / 1000000);
+ }
+
+#elif defined(__unix__) || defined(__linux) || defined(__linux__) || defined(__ANDROID__) || defined(__QNX__)
+ struct timespec currTime;
+ clock_gettime(CLOCK_MONOTONIC, &currTime);
+ return (uint64_t)currTime.tv_sec * 1000000 + ((uint64_t)currTime.tv_nsec / 1000);
+
+#elif defined(__EPOC32__)
+ struct timespec currTime;
+ /* Symbian supports only realtime clock for clock_gettime. */
+ clock_gettime(CLOCK_REALTIME, &currTime);
+ return (uint64_t)currTime.tv_sec * 1000000 + ((uint64_t)currTime.tv_nsec / 1000);
+
+#elif defined(__APPLE__)
+ struct timeval currTime;
+ gettimeofday(&currTime, NULL);
+ return (uint64_t)currTime.tv_sec * 1000000 + (uint64_t)currTime.tv_usec;
+
+#else
+#error "Not implemented for target OS"
+#endif
+}