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 +}