Initial work on supporting VK_GOOGLE_display_timing. Not working yet.

This commit is contained in:
Henrik Rydgård 2023-08-03 11:11:16 +02:00
parent 1a1a6fe15d
commit e16cac6548
7 changed files with 70 additions and 11 deletions

View File

@ -25,4 +25,8 @@ struct FrameTimeData {
double firstSubmit; double firstSubmit;
double queuePresent; double queuePresent;
double actualPresent; double actualPresent;
double desiredPresentTime;
double actualPresentTime;
double earliestPresentTime;
double presentMargin;
}; };

View File

@ -692,9 +692,11 @@ VkResult VulkanContext::CreateDevice() {
extensionsLookup_.EXT_fragment_shader_interlock = EnableDeviceExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME); extensionsLookup_.EXT_fragment_shader_interlock = EnableDeviceExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME);
extensionsLookup_.ARM_rasterization_order_attachment_access = EnableDeviceExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME); extensionsLookup_.ARM_rasterization_order_attachment_access = EnableDeviceExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME);
extensionsLookup_.KHR_present_id = EnableDeviceExtension(VK_KHR_PRESENT_ID_EXTENSION_NAME);
extensionsLookup_.KHR_present_wait = EnableDeviceExtension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
extensionsLookup_.GOOGLE_display_timing = EnableDeviceExtension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME); extensionsLookup_.GOOGLE_display_timing = EnableDeviceExtension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME);
if (!extensionsLookup_.GOOGLE_display_timing) {
extensionsLookup_.KHR_present_id = EnableDeviceExtension(VK_KHR_PRESENT_ID_EXTENSION_NAME);
extensionsLookup_.KHR_present_wait = EnableDeviceExtension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);
}
VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };

View File

@ -110,11 +110,18 @@ VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared)
present.pWaitSemaphores = &shared.renderingCompleteSemaphore; present.pWaitSemaphores = &shared.renderingCompleteSemaphore;
present.waitSemaphoreCount = 1; present.waitSemaphoreCount = 1;
// Can't move these into the if.
VkPresentIdKHR presentID{ VK_STRUCTURE_TYPE_PRESENT_ID_KHR }; VkPresentIdKHR presentID{ VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
VkPresentTimesInfoGOOGLE presentGOOGLE{ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE };
VkPresentTimeGOOGLE presentTimeGOOGLE{ (uint32_t)frameID, 0 }; // it's ok to truncate this. it'll wrap around and work (if we ever reach 4 billion frames..)
if (vulkan->Extensions().KHR_present_id && vulkan->GetDeviceFeatures().enabled.presentId.presentId) { if (vulkan->Extensions().KHR_present_id && vulkan->GetDeviceFeatures().enabled.presentId.presentId) {
presentID.pPresentIds = &frameID; presentID.pPresentIds = &frameID;
presentID.swapchainCount = 1; presentID.swapchainCount = 1;
present.pNext = &presentID; present.pNext = &presentID;
} else if (vulkan->Extensions().GOOGLE_display_timing) {
presentGOOGLE.pTimes = &presentTimeGOOGLE;
presentGOOGLE.swapchainCount = 1;
present.pNext = &presentGOOGLE;
} }
return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present); return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);

View File

@ -582,6 +582,24 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile
} }
vkResetFences(device, 1, &frameData.fence); vkResetFences(device, 1, &frameData.fence);
// Poll for information about completed frames.
if (vulkan_->Extensions().GOOGLE_display_timing) {
uint32_t count = 0;
vkGetPastPresentationTimingGOOGLE(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &count, nullptr);
if (count > 0) {
VkPastPresentationTimingGOOGLE *timings = new VkPastPresentationTimingGOOGLE[count];
vkGetPastPresentationTimingGOOGLE(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &count, timings);
for (uint32_t i = 0; i < count; i++) {
uint64_t presentId = timings[i].presentID;
frameTimeData_[presentId].actualPresent = from_time_raw(timings[i].actualPresentTime);
frameTimeData_[presentId].desiredPresentTime = from_time_raw(timings[i].desiredPresentTime);
frameTimeData_[presentId].earliestPresentTime = from_time_raw(timings[i].earliestPresentTime);
frameTimeData_[presentId].presentMargin = from_time_raw(timings[i].earliestPresentTime);
}
delete[] timings;
}
}
int validBits = vulkan_->GetQueueFamilyProperties(vulkan_->GetGraphicsQueueFamilyIndex()).timestampValidBits; int validBits = vulkan_->GetQueueFamilyProperties(vulkan_->GetGraphicsQueueFamilyIndex()).timestampValidBits;
// Can't set this until after the fence. // Can't set this until after the fence.

View File

@ -21,6 +21,9 @@
// TODO: https://github.com/floooh/sokol/blob/9a6237fcdf213e6da48e4f9201f144bcb2dcb46f/sokol_time.h#L229-L248 // TODO: https://github.com/floooh/sokol/blob/9a6237fcdf213e6da48e4f9201f144bcb2dcb46f/sokol_time.h#L229-L248
static const double micros = 1000000.0;
static const double nanos = 1000000000.0;
#ifdef _WIN32 #ifdef _WIN32
static LARGE_INTEGER frequency; static LARGE_INTEGER frequency;
@ -41,9 +44,17 @@ double time_now_d() {
// Fake, but usable in a pinch. Don't, though. // Fake, but usable in a pinch. Don't, though.
uint64_t time_now_raw() { uint64_t time_now_raw() {
return (uint64_t)(time_now_d() * 1000000000.0); return (uint64_t)(time_now_d() * nanos);
} }
double from_time_raw(uint64_t raw_time) {
if (raw_time == 0) {
return 0.0; // invalid time
}
return (double)raw_time * (1.0 / nanos);
}
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(LINUX) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) #elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(LINUX) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
// The only intended use is to match the timings in VK_GOOGLE_display_timing // The only intended use is to match the timings in VK_GOOGLE_display_timing
@ -53,13 +64,18 @@ uint64_t time_now_raw() {
return tp.tv_sec * 1000000000ULL + tp.tv_nsec; return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
} }
static uint64_t g_startTime;
double from_time_raw(uint64_t raw_time) {
return (double)(raw_time - g_startTime) * (1.0 / nanos);
}
double time_now_d() { double time_now_d() {
static uint64_t start;
uint64_t raw_time = time_now_raw(); uint64_t raw_time = time_now_raw();
if (start == 0) { if (g_startTime == 0) {
start = raw_time; g_startTime = raw_time;
} }
return (double)(raw_time - start) * (1.0 / 1000000000.0); return from_time_raw(raw_time);
} }
#else #else
@ -71,12 +87,16 @@ double time_now_d() {
if (start == 0) { if (start == 0) {
start = tv.tv_sec; start = tv.tv_sec;
} }
return (double)(tv.tv_sec - start) + (double)tv.tv_usec * (1.0 / 1000000.0); return (double)(tv.tv_sec - start) + (double)tv.tv_usec * (1.0 / micros);
} }
// Fake, but usable in a pinch. Don't, though. // Fake, but usable in a pinch. Don't, though.
uint64_t time_now_raw() { uint64_t time_now_raw() {
return (uint64_t)(time_now_d() * 1000000000.0); return (uint64_t)(time_now_d() * nanos);
}
double from_time_raw(uint64_t raw_time) {
return (double)raw_time * (1.0 / nanos);
} }
#endif #endif

View File

@ -9,6 +9,9 @@ double time_now_d();
// The only intended use is to match the timings from VK_GOOGLE_display_timing. // The only intended use is to match the timings from VK_GOOGLE_display_timing.
uint64_t time_now_raw(); uint64_t time_now_raw();
// This is only interesting for Linux, in relation to VK_GOOGLE_display_timing.
double from_time_raw(uint64_t raw_time);
// Sleep. Does not necessarily have millisecond granularity, especially on Windows. // Sleep. Does not necessarily have millisecond granularity, especially on Windows.
void sleep_ms(int ms); void sleep_ms(int ms);

View File

@ -113,12 +113,17 @@ static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) {
double submitLatency_s = data.firstSubmit - data.frameBegin; double submitLatency_s = data.firstSubmit - data.frameBegin;
double queuePresentLatency_s = data.queuePresent - data.frameBegin; double queuePresentLatency_s = data.queuePresent - data.frameBegin;
double actualPresentLatency_s = data.actualPresent - data.frameBegin; double actualPresentLatency_s = data.actualPresent - data.frameBegin;
double margin = data.presentMargin;
double computedMargin = data.actualPresent - data.queuePresent;
char presentStats[256] = ""; char presentStats[256] = "";
if (data.actualPresent != 0.0) { if (data.actualPresent != 0.0) {
snprintf(presentStats, sizeof(presentStats), snprintf(presentStats, sizeof(presentStats),
"* Actual present: %0.1f ms\n", "* Actual present: %0.1f ms\n"
actualPresentLatency_s * 1000.0); "* Margin: %0.1f ms (%0.1f computed)\n",
actualPresentLatency_s * 1000.0,
margin * 1000.0,
computedMargin * 1000.0);
} }
snprintf(statBuf, sizeof(statBuf), snprintf(statBuf, sizeof(statBuf),
"Time from start of frame to event:\n" "Time from start of frame to event:\n"