diff --git a/demos/cube.c b/demos/cube.c
index 3e2733ee287f2ead9115e4443ff1828b89693f48..a429af7eb1c63c60892cdae46b68a8d53674565a 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 0000000000000000000000000000000000000000..a4265cdcf58337afff1a8cad84a09ab7a8a2c182
--- /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
+}