From 116c56d0bd72e206c0e600b734ff20f049bc3eef Mon Sep 17 00:00:00 2001 From: Lionel Duc Date: Wed, 15 May 2024 17:01:28 +0200 Subject: [PATCH] Progress towards final version. This change captures a lot of the recent feedback we had during the SI meetings and suggestions from comments in this MR: - Remove VkSwapchainPresentTimingCreateInfo and move relative present timing as per-present parameter. - Internal results queue size is allocated with a new dedicated API: vkSetSwapchainPresentTimingQueueSizeEXT. This allows applications to resize it without recreating the swapchain. - Time domains are associated with a unique identifier to allow multiple swapchain-local time domains of the same scope to coexist. - Variable refresh rate boolean flag is now a duration indicating the delay in "reaction" of the presentation engine to a new image. This is meant to support technologies such as dVRR which can quickly adjust their refresh cycles. - Squash the appendix' revision history. - Add various details in the proposal document --- appendices/VK_EXT_present_timing.adoc | 20 +-- .../PresentTimeInfo.adoc | 94 +++++----- chapters/VK_EXT_present_timing/queries.adoc | 107 +++++------ chapters/VK_KHR_swapchain/wsi.adoc | 30 ++-- chapters/synchronization.adoc | 28 ++- proposals/VK_EXT_present_timing.adoc | 167 ++++++++++-------- xml/vk.xml | 28 +-- 7 files changed, 255 insertions(+), 219 deletions(-) diff --git a/appendices/VK_EXT_present_timing.adoc b/appendices/VK_EXT_present_timing.adoc index 6305583d9..b0807fb4e 100644 --- a/appendices/VK_EXT_present_timing.adoc +++ b/appendices/VK_EXT_present_timing.adoc @@ -233,19 +233,7 @@ Split it out into its own extension and define the interaction here. ** Internal revisions. * Revision 2, 2022-11-30 (Lionel Duc) - ** Major rebase. - ** Add physical device feature flags. - ** Rely on VK_KHR_present_id for present ids. - ** Resolve issue (11): remove VK_PRESENT_MODE_FIFO_LATEST_READY_EXT. - ** Add per-surface capabilities. - - * Revision 3, 2023-01-31 (Lionel Duc) - ** Propose resolution to issue (9): let the application provide the queue size for the - pending results. - - * Revision 4, 2023-05-03 (Lionel Duc) - ** Change target and feedback times to be associated with a well-defined present stage. - - * Revision 5, 2024-02-10 (Lionel Duc) - ** Move swapchain state into per-present struct, removing the need for vkSetSwapchainTimingEXT - ** Add stage-local time domain + ** Rebase for public discussions. + + * Revision 3, 2024-05-29 (Lionel Duc) + ** Public revisions. diff --git a/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc b/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc index 6db5b30a7..48817ff30 100644 --- a/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc +++ b/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc @@ -62,7 +62,7 @@ include::{generated}/validity/structs/VkPresentTimingsInfoEXT.adoc[] -- -[open,refpage='VkPresentTimingInfoEXT',desc='Specifies per-present per-swapchain timing information',type='structs'] +[open,refpage='VkPresentTimingInfoEXT',desc='Specifies per-present timing information',type='structs'] -- The sname:VkPresentTimingInfoEXT structure is defined as: @@ -73,27 +73,52 @@ include::{generated}/api/structs/VkPresentTimingInfoEXT.adoc[] structure. * pname:time is a sname:VkPresentTimeEXT specifying the target present time or duration of the presentation request. - * pname:timeDomain is the time domain used to specify the absolute target - present time and the timing results in a subsequent - flink:vkGetPastPresentationTimingEXT call for the current presentation - request. + * pname:timeDomainId is the id of the time domain used to specify the + absolute target present time and the timing results obtained in a + subsequent flink:vkGetPastPresentationTimingEXT call for the current + presentation request. * pname:presentStageQueries is a valid tlink:VkPresentStageFlagsEXT value indicating which present stages the presentation engine should collect timing information for. * pname:targetPresentStage is zero or a tlink:VkPresentStageFlagsEXT value specifying which present stage pname:time is targeting. + * pname:presentAtRelativeTime specifies whether pname:time is to be + interpreted as an absolute or a relative time value. * pname:presentAtNearestRefreshCycle is a flag indicating that the - application would like to complete pname:targetPresentStage at the start - of the refresh cycle that is closest to pname:time. If - pname:presentAtNearestRefreshCycle is ename:VK_TRUE, the presentation - engine may: complete the target present stage earlier than specified in - pname:time. + application would like the presentation engine to complete + pname:targetPresentStage at the start of the refresh cycle that is + closest to pname:time. + +If pname:targetPresentStage is not zero, the presentation engine must: not +complete the target present stage before the absolute time specified in +pname:time according to the time domain used, unless +pname:presentAtNearestRefreshCycle is ename:VK_TRUE and pname:time falls +within the first half of a refresh cycle. + +[NOTE] +.Note +==== +Applications that use pname:targetPresentTime to specify an absolute present +time should: regularly rebase their calculations for their next target time +on the feedback from flink:vkGetPastPresentationTimingEXT to compensate for +accumulated precision errors or potential clock drift. It is recommended +that when targeting the time of a vertical blanking period, applications use +pname:presentAtNearestRefreshCycle to allow the implementation to compensate +for small precision errors that may cause an image to be displayed one +refresh cycle later than intended. +==== .Valid Usage **** * If pname:presentStageTarget is not zero, it must: specify one and only one stage. - * pname:presentStageTarget must: not be ename:VK_PRESENT_STAGE_IMAGE_HANDOFF_BIT_EXT + * pname:presentStageTarget must: not be + ename:VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT + * If pname:targetPresentStage is not zero, pname:presentAtRelativeTime must: + be ename:VK_FALSE. + * If pname:presentAtRelativeTime is ename:VK_TRUE, the + <> feature + must: be enabled. **** include::{generated}/validity/structs/VkPresentTimingInfoEXT.adoc[] @@ -107,41 +132,14 @@ The sname:VkPresentTimeEXT union is defined as: include::{generated}/api/structs/VkPresentTimeEXT.adoc[] - * pname:targetPresentTime, if non-zero, specifies the earliest time the - application wants the presentation engine to complete the swapchain's - target present stage. A value of zero specifies that the presentation - engine may: display the image at any time allowed by the current present - mode. - * pname:afterPresentDuration, if non-zero, specifies the minimum duration - in nanoseconds before which the next image presentation request can: - reach the target present stage, relative to the previous presentation - reaching that same stage. A value of zero specifies that the - presentation engine may: display the previous image for any duration - allowed by the current present mode. - -The contents of the sname:VkPresentTimeEXT union is interpreted depending on -the associated sname:VkPresentInfoKHR::pname:pSwapchains member creation -values. If -sname:VkSwapchainPresentTimingCreateInfoEXT::pname:presentAtRelativeTime was -ename:VK_FALSE when creating the pname:swapchain, pname:targetPresentTime is -used. Otherwise, pname:afterPresentDuration is used. - -pname:targetPresentTime is a time in nanoseconds, according to the -time-domain specified in the associated -slink:VkPresentTimingInfoEXT::pname:timeDomain. The presentation engine -must: not complete the target present stage specified in -slink:VkPresentTimingInfoEXT::pname:targetPresentStage at an earlier time. - -[NOTE] -.Note -==== -Applications that use pname:targetPresentTime to specify an absolute present -time should: regularly rebase their calculations for their next target time -on the feedback from flink:vkGetPastPresentationTimingEXT to compensate for -accumulated precision errors or potential clock drift. It is recommended -that when targeting the time of a vertical blanking period, applications -subtract a small delta from pname:targetPresentTime as a margin for error. -==== + * pname:targetPresentTime, if non-zero, specifies the earliest time in + nanoseconds the application wants the presentation engine to complete a + specified target present stage. A value of zero specifies that the + presentation engine may: display the image at any time allowed by the + current present mode. + * pname:presentDuration specifies the minimum duration in nanoseconds that + must: separate the ename:VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT + present stages of the current and the next presentation requests, if any. include::{generated}/validity/structs/VkPresentTimeEXT.adoc[] -- @@ -174,10 +172,6 @@ The set of queue operations delimited by ename:VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT includes the wait for the semaphores specified in slink:VkPresentInfoKHR::pname:pWaitSemaphores, if any, and any work implicitly enqueued by the implementation. - -On some platforms, when the present mode allows for it, presentation engines -may: proactively latch an image before this set of queue operations has -completed. ==== -- diff --git a/chapters/VK_EXT_present_timing/queries.adoc b/chapters/VK_EXT_present_timing/queries.adoc index 063f795b5..0e426646f 100644 --- a/chapters/VK_EXT_present_timing/queries.adoc +++ b/chapters/VK_EXT_present_timing/queries.adoc @@ -39,39 +39,33 @@ displayed to the user on a periodic basis. This period may: be fixed (Fixed Refresh Rate, FRR) or variable (Variable Refresh Rate, VRR). ifdef::VK_EXT_present_timing[] -[open,refpage='VkSwapchainPresentTimingCreateInfoEXT',desc='Specify present timing properties to initialize a swapchain with',type='structs'] +[open,refpage='vkSetSwapchainPresentTimingQueueSizeEXT',desc='Allocate memory for the swapchain-internal timing results queue',type='protos'] -- -To enable present timing for a swapchain, add a -sname:VkSwapchainPresentTimingCreateInfoEXT structure to the pname:pNext -chain of slink:VkSwapchainCreateInfoKHR. +In order to collect timing information about presentation, a swapchain needs +an internal queue to store asynchronously updated results until applications +collect them. -The sname:VkSwapchainPresentTimingCreateInfoEXT structure is defined as: +To allocate the swapchain's internal timing results queue, call: -include::{generated}/api/structs/VkSwapchainPresentTimingCreateInfoEXT.adoc[] +include::{generated}/api/protos/vkSetSwapchainPresentTimingQueueSizeEXT.adoc[] - * pname:sType is a elink:VkStructureType value identifying this structure. - * pname:pNext is `NULL` or a pointer to a structure extending this - structure. - * pname:presentTimingQueueSize is the size of the swapchain's internal - storage holding presentation timing results. - * pname:presentAtRelativeTime specifies whether target present times are - to be interpreted as relative times. + * pname:device is the device associated with pname:swapchain. + * pname:swapchain is the swapchain to allocate a results queue for. + * pname:size is the requested number of slots in the internal results queue. -When timing collection is enabled via the <> feature, resources may: be allocated by the -implementation for an internal queue to collect results. The size of that -queue is specified by pname:presentTimingQueueSize. +If this function is called multiple times, the internal queue is reallocated +to fit the new pname:size. If the new pname:size is less than the current +number of outstanding results, ename:VK_NOT_READY is returned and no allocation +is performed. .Valid Usage **** - * If the <> feature is not - enabled, pname:presentTimingQueueSize must: be zero - * If the <> - feature is not enabled, pname:presentAtRelativeTime must: be - ename:VK_FALSE + * pname:swapchain must: have been created with + sname:VkSwapchainCreateInfoKHR::pname:flags containing + ename:VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT **** -include::{generated}/validity/structs/VkSwapchainPresentTimingCreateInfoEXT.adoc[] +include::{generated}/validity/protos/vkSetSwapchainPresentTimingQueueSizeEXT.adoc[] -- [open,refpage='vkGetSwapchainTimingPropertiesEXT',desc='Obtain the display timing properties of the PE\'s display',type='protos'] @@ -88,18 +82,11 @@ include::{generated}/api/protos/vkGetSwapchainTimingPropertiesEXT.adoc[] * pname:device is the device associated with pname:swapchain. * pname:swapchain is the swapchain to obtain timing properties for. * pname:pSwapchainTimingProperties is a pointer to an instance of the - sname:VkSwapchainTimingPropertiesEXT structure. + slink:VkSwapchainTimingPropertiesEXT structure. * pname:pSwapchainTimingPropertiesCounter is `NULL` or a pointer to a 64-bit unsigned integer set by the implementation to the current value of the swapchain's internal timing properties counter. -.Valid Usage -**** - * pname:swapchain must: have been created with a - slink:VkSwapchainPresentTimingCreateInfoEXT specified in the - slink:VkSwapchainCreateInfoKHR::pname:pNext chain. -**** - include::{generated}/validity/protos/vkGetSwapchainTimingPropertiesEXT.adoc[] -- @@ -113,7 +100,7 @@ include::{generated}/api/structs/VkSwapchainTimingPropertiesEXT.adoc[] * pname:sType is a elink:VkStructureType value identifying this structure. * pname:pNext is `NULL` or a pointer to a structure extending this structure. - * pname:refreshDuration is zero; or is an indication of the duration + * pname:refreshDuration is zero or an indication of the duration of a refresh cycle. If the presentation engine is operating as an FRR display, this is the number of nanoseconds from the start of one refresh cycle to the @@ -122,10 +109,13 @@ include::{generated}/api/structs/VkSwapchainTimingPropertiesEXT.adoc[] (i.e. refresh cycles may: have variable length), this is the minimum number of nanoseconds from the start of one refresh cycle to the start of the next refresh cycle. - * pname:variableRefresh is undefined: if pname:refreshDuration is - zero; otherwise it is ename:VK_FALSE if the presentation engine is - operating as a FRR display, or ename:VK_TRUE if the presentation - engine is operating as a VRR display. + * pname:variableRefreshDelay is undefined: if pname:refreshDuration is + zero; otherwise it is a duration in nanoseconds indicating the maximum + theoretical delay for the presentation engine to start a new refresh + cycle upon processing a presentation request. + +If pname:variableRefreshDelay is the same as pname:refreshDuration, the +presentation engine is operating as an FRR display. Some platforms may: not provide timing properties until after at least one image has been presented to the pname:swapchain. If timing properties @@ -180,7 +170,7 @@ feedback of slink:VkPastPresentationTimingEXT values indicates that the target IPD can be durably achieved. ==== -[open,refpage='vkGetSwapchainTimeDomainsEXT',desc='Obtain the time domain used by the PE for the swapchain',type='protos'] +[open,refpage='vkGetSwapchainTimeDomainsEXT',desc='Obtain the time domains supported by the PE for the swapchain',type='protos'] -- To query the time domain used by the presentation engine for a given swapchain, @@ -227,17 +217,26 @@ include::{generated}/api/structs/VkSwapchainTimeDomainPropertiesEXT.adoc[] structure. * pname:timeDomain is a elink:VkTimeDomainEXT value representing a time domain that is available for the swapchain. + * pname:timeDomainId is a unique identifier for this time domain within a + swapchain's namespace. + +[NOTE] +.Note +==== +Due to the dynamic nature of their underlying sname:VkSurfaceKHR properties, +swapchains may need to expose multiple swapchain-local opaque time domains +using the same elink:VkTimeDomainEXT value, for example when a surface is +moved from one display hardware to another. Arbitrary identifiers, provided +in pname:timeDomainId, are used by the implementation to differentiate opaque +time domains of identical scopes. +==== include::{generated}/validity/structs/VkSwapchainTimeDomainPropertiesEXT.adoc[] -- -[open,refpage='vkGetPastPresentationTimingEXT',desc='Obtain timing of a previously-presented image',type='protos'] +[open,refpage='vkGetPastPresentationTimingEXT',desc='Obtain timing of previously-presented images',type='protos'] -- -The implementation maintains a limited amount of history of timing -information about previous presents, specified by -slink:VkSwapchainPresentTimingCreateInfoEXT::pname:presentTimingQueueSize -when creating a swapchain. Because of the asynchronous nature of the presentation engine, the timing information for a given flink:vkQueuePresentKHR command only becomes available some time later. @@ -254,13 +253,16 @@ include::{generated}/api/protos/vkGetPastPresentationTimingEXT.adoc[] * pname:swapchain is the swapchain to obtain presentation timing information duration for. * pname:pPresentationTimingCount is a pointer to an integer related to the - number of sname:VkPastPresentationTimingEXT structures to query, as + number of slink:VkPastPresentationTimingEXT structures to query, as described below. * pname:pPresentationTimings is `NULL` or a pointer to an an array - of sname:VkPastPresentationTimingEXT structures. + of slink:VkPastPresentationTimingEXT structures. * pname:pSwapchainTimingPropertiesCounter is `NULL` or a pointer to a 64-bit unsigned integer set by the implementation to the current value of the swapchain's internal timing properties counter. + * pname:pTimeDomainsChanged is `NULL` or a pointer to a boolean value + indicating if the list of supported time domains supported by the + pname:swapchain has changed since the last call to this function. If pname:pPresentationTimings is `NULL`, then the number of available timing records for the given pname:swapchain is returned in @@ -281,16 +283,19 @@ stages. Further calls to fname:vkGetPastPresentationTimingEXT will keep providing all available results for a previously incomplete entry until it is complete. -The implementation must: return a sname:VkPastPresentationTimingEXT for +The implementation must: return a slink:VkPastPresentationTimingEXT for every flink:vkQueuePresentKHR referencing pname:swapchain where a non-zero slink:VkPresentTimingInfoEXT::pname:presentStageQueries was specified and at least one present stage has available results. +If pname:pTimeDomainsChanged is ename:VK_TRUE, applications should: query +the new list of available time domains with flink:vkGetSwapchainTimeDomainsEXT. + Timing information may: become available out of order with regards to their associated presentation request submission order. Upon return, zero or more slots of the pname:swapchain internal timing -collection queue, equal to the number of entries written to +results queue, equal to the number of entries written to pname:pPresentationTimings for which pname:reportComplete is ename:VK_TRUE, are made available for future fname:vkQueuePresentKHR calls. Elements of pname:pPresentationTimings are arranged in ascending order of present ids. @@ -301,8 +306,10 @@ information. As an exception to the normal rules for objects which are externally synchronized, pname:swapchain may: be simultaneously used by other threads -in calls to functions other than flink:vkDestroySwapchainKHR. Access to the -swapchain timing information must: be atomic within the implementation. +in calls to functions other than flink:vkDestroySwapchainKHR and +flink:vkCreateSwapchainKHR with pname:swapchain used as an +pname:oldSwapchain. Access to the swapchain timing information must: be +atomic within the implementation. include::{generated}/validity/protos/vkGetPastPresentationTimingEXT.adoc[] -- @@ -325,7 +332,7 @@ include::{generated}/api/structs/VkPastPresentationTimingEXT.adoc[] * pname:pPresentStages a pointer to an array of slink:VkPresentStageTimeEXT providing timing information for the presentation request associated with pname:presentId. - * pname:timeDomain is the ename:VkTimeDomainEXT used by the presentation + * pname:timeDomainId is the id of the time domain used by the presentation engine to report times in pname:pPresentStages. * pname:reportComplete is ename:VK_TRUE if the presentation engine has reported all the requested results in pname:pPresentStages. @@ -403,7 +410,7 @@ identical reasons. Therefore, it is possible that the same event will cause both pname:timingPropertiesChanged to become ename:VK_TRUE and and pname:timeDomain to be different than the time domain requested in -sname:VkPresentTimingInfoEXT. +slink:VkPresentTimingInfoEXT. ==== -- diff --git a/chapters/VK_KHR_swapchain/wsi.adoc b/chapters/VK_KHR_swapchain/wsi.adoc index ba7e518ea..cf2839c8f 100644 --- a/chapters/VK_KHR_swapchain/wsi.adoc +++ b/chapters/VK_KHR_swapchain/wsi.adoc @@ -578,9 +578,11 @@ ifndef::VK_EXT_image_compression_control_swapchain[The] slink:VkImageCompressionControlEXT structure endif::VK_EXT_image_compression_control[] ifdef::VK_EXT_present_timing[] - * If the <> - feature is not enabled, the pname:pNext chain must: not include an - slink:VkSwapchainPresentTimingCreateInfoEXT structure + * If none of the <>, + <>, or + <> + features are enabled, pname:flags must: not contain + ename:VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT endif::VK_EXT_present_timing[] **** ifdef::VKSC_VERSION_1_0[] @@ -638,6 +640,12 @@ ifdef::VK_EXT_swapchain_maintenance1[] ifdef::VK_VERSION_1_1,VK_KHR_device_group[or flink:vkAcquireNextImage2KHR] for the first time. endif::VK_EXT_swapchain_maintenance1[] +ifdef::VK_EXT_present_timing[] + * ename:VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT specifies that features + supported by the swapchain device in + slink:VkPhysicalDevicePresentTimingFeaturesEXT can: be used to collect + timing information or schedule presentation requests at specific times. +endif::VK_EXT_present_timing[] -- [open,refpage='VkSwapchainCreateFlagsKHR',desc='Bitmask of VkSwapchainCreateFlagBitsKHR',type='flags'] @@ -1404,14 +1412,14 @@ implementation-specific reasons outside of the application's control. endif::VK_EXT_full_screen_exclusive[] ifdef::VK_EXT_present_timing[] - -For any pname:pSwapchains member of pname:pPresentInfo, if a slink: is -provided in the pNext chain with a non-zero pname:presentStageQueries, one -entry of its internal timing queue is reserved until the results have been -collected by flink:vkGetPastPresentationTimingEXT. -ename:VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT is returned if a swapchain is -unable to reserve an entry in its queue. - +For any pname:pSwapchains member of pname:pPresentInfo, if a +slink:VkPresentTimingInfoEXT is provided in the pNext chain with a non-zero +pname:presentStageQueries, one entry of its internal timing results queue is +reserved until the results have been collected by +flink:vkGetPastPresentationTimingEXT. Implementations must: guarantee +forward progress by providing timing results in a finite time. +ename:VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT is returned if a +pname:swapchain is unable to reserve an entry in its queue. endif::VK_EXT_present_timing[] include::{generated}/validity/protos/vkQueuePresentKHR.adoc[] diff --git a/chapters/synchronization.adoc b/chapters/synchronization.adoc index c087f105f..3f0ff41d0 100644 --- a/chapters/synchronization.adoc +++ b/chapters/synchronization.adoc @@ -7701,9 +7701,9 @@ endif::VK_EXT_calibrated_timestamps[] pname:timeDomain must: be one of the elink:VkTimeDomainKHR values returned by flink:vkGetPhysicalDeviceCalibrateableTimeDomainsKHR ifdef::VK_EXT_present_timing[] - * If pname:timeDomain is ename:VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT, - the pname:pNext chain must: include a - slink:VkSwapchainCalibratedTimestampInfoEXT structure. + * If pname:timeDomain is ename:VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT or + ename:VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT, the pname:pNext chain + must: include a slink:VkSwapchainCalibratedTimestampInfoEXT structure. endif::VK_EXT_present_timing[] **** include::{generated}/validity/structs/VkCalibratedTimestampInfoKHR.adoc[] @@ -7800,11 +7800,27 @@ The sname:VkSwapchainCalibratedTimestampInfoEXT structure is defined as: include::{generated}/api/structs/VkSwapchainCalibratedTimestampInfoEXT.adoc[] - * pname:sType is the type of this structure. + * pname:sType is a elink:VkStructureType value identifying this structure. * pname:pNext is `NULL` or a pointer to a structure extending this structure. - * pname:swapchain is the swapchain to retrieve the - ename:VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT timestamp from. + * pname:swapchain is the swapchain to retrieve the swapchain-local timestamp from. + * pname:presentStage is zero or a tlink:VkPresentStageFlagsEXT value used + to identify a single present stage when calibrating a timestamp in the + ename:VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT time domain. + * pname:timeDomainId is the id for the opaque time domain being calibrated. + +pname:timeDomainId must: be an id previously reported by +flink:vkGetSwapchainTimeDomainsEXT for pname:swapchain. If the +pname:timeDomainId is no longer supported by the pname:swapchain, +implementations may: report zero as the calibrated timestamp value. + +.Valid Usage +**** + * If the pname:timeDomain member of the slink:VkCalibratedTimestampInfoKHR structure + in this structure's pname:pNext chain is ename:VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT, + pname:presentStage must: specify one and only one present stage. +**** + include::{generated}/validity/structs/VkSwapchainCalibratedTimestampInfoEXT.adoc[] -- diff --git a/proposals/VK_EXT_present_timing.adoc b/proposals/VK_EXT_present_timing.adoc index d0e2b83ca..a50139db4 100644 --- a/proposals/VK_EXT_present_timing.adoc +++ b/proposals/VK_EXT_present_timing.adoc @@ -1,4 +1,4 @@ -// Copyright 2023 The Khronos Group Inc. +// Copyright 2023-2024 The Khronos Group Inc. // // SPDX-License-Identifier: CC-BY-4.0 @@ -77,7 +77,7 @@ It is difficult to define "presentation" while satisfying all implementations, p [source,c] ---- typedef enum VkPresentStageFlagBitsEXT { - VK_PRESENT_STAGE_IMAGE_HANDOFF_BIT_EXT = 0x00000001, + VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT = 0x00000001, VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT = 0x00000002, VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT = 0x00000004, VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT = 0x00000008, @@ -86,39 +86,36 @@ typedef enum VkPresentStageFlagBitsEXT { When queueing a presentation request for a swapchain, a set of present stages is specified to inform the implementation that timing for all those stages is desired. See <>. -Similarly, when using `presentAtAbsoluteTime` or `presentAtRelativeTime` feature to schedule presents at specific times, a present stage must be specified as a target. +Similarly, when using `presentAtAbsoluteTime` feature to schedule presents at specific times, a present stage must be specified as a target. -* `VK_PRESENT_STAGE_IMAGE_HANDOFF_BIT_EXT` marks the end of the set of queue operations enqueued by `vkQueuePresentKHR` on the provided `VkQueue`. These queue operations are implementation-specific; the usual example is a blit to a system-specific internal surface suited for presentation. +* `VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT` marks the end of the set of queue operations enqueued by `vkQueuePresentKHR` on the provided `VkQueue`. These queue operations are implementation-specific; the usual example is a blit to a system-specific internal surface suited for presentation. * `VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT` is the step after which the image associated with the presentation request has been latched by the presentation engine to create the presentation of a future refresh cycle. For example, in a flip-model scenario, this is the time the presentation request's image has been selected for the next refresh cycle. * `VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_OUT_BIT_EXT` is the stage after which data for the first pixel of the presentation request associated with the image has left the presentation engine for a display hardware. * `VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT` is the stage after which a display hardware has made the first pixel visible for the presentation request associated with the image to be presented. -Implementations are required to support at least `VK_PRESENT_STAGE_IMAGE_HANDOFF_BIT_EXT` in `VkSurfacePresentTimingCapabilitiesEXT::presentStageQueries` if `presentTimingSupported` is `VK_TRUE` for the surface. +Implementations are required to support at least `VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT` in `VkSurfacePresentTimingCapabilitiesEXT::presentStageQueries` if `presentTimingSupported` is `VK_TRUE` for the surface. === Enabling present timing for a swapchain -To enable present timing for a swapchain, a new struct must be chained to `VkSwapchainCreateInfoKHR`: +To enable present timing for a swapchain, a new flag must be specified in `VkSwapchainCreateInfoKHR::flags`: `VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT`. + +To provide presentation timing results, implementations need to allocate an internal queue and other resources to collect the necessary timestamps. The size of that queue must be specified by the application with a new function: [source,c] ---- -typedef struct VkSwapchainPresentTimingCreateInfoEXT { - VkStructureType sType; - const void* pNext; - uint32_t presentTimingQueueSize; - VkBool32 presentAtRelativeTime; -} VkSwapchainPresentTimingCreateInfoEXT; +VkResult vkSetSwapchainPresentTimingQueueSizeEXT( + VkDevice device, + VkSwapchainKHR swapchain, + uint32_t size); ---- -* `presentTimingQueueSize` is the size of the internal queue which contains the timing data. -* `presentAtRelativeTime` enables relative present timing if it is supported. See <>. - -When enabling presentation statistics, implementations are likely required to allocate an internal queue and other resources to collect the necessary timestamps. The size of that queue must be specified by the application in `presentTimingQueueSize`. Calling `vkQueuePresentKHR` with non-zero stage queries allocates a slot in that internal queue, while `vkGetPastPresentationTimingEXT` releases slots when complete results are returned. +Calling this function multiple times causes the results queue to be reallocated to the new size. If the new size cannot hold all the current outstanding results, `VK_NOT_READY` is returned. -When timing collection is enabled via the `presentTiming` feature, resources are allocated by the implementation for an internal queue to collect results. The size of that queue must be specified by the application in `presentTimingQueueSize`. Slots of this internal queue are reserved when calling `vkQueuePresentKHR` and released when calling `vkGetPastPresentationTimingEXT` if the corresponding report is marked as complete by `VkPastPresentationTimingEXT::reportComplete`. +Calling `vkQueuePresentKHR` with non-zero stage queries allocates a slot in that internal queue, while `vkGetPastPresentationTimingEXT` releases slots when complete results are returned. -=== Swapchain Timing information +=== Swapchain Timing Information -For timing to be meaningful, the application needs to be aware of various time-related properties. Basic properties are exposed in a new structure, which can be retrieved with: +For timing to be meaningful, the application needs to be aware of various properties. Basic properties are exposed in a new structure, `VkSwapchainTimingPropertiesEXT`, which can be retrieved with: [source,c] ---- @@ -139,16 +136,18 @@ typedef struct VkSwapchainTimingPropertiesEXT { VkStructureType sType; const void* pNext; uint64_t refreshDuration; - VkBool32 variableRefresh; + uint64_t variableRefreshDelay; } VkSwapchainTimingPropertiesEXT; ---- * `refreshDuration` is the duration in nanoseconds of the refresh cycle the presentation engine is operating at. -* `variableRefresh` indicates whether refresh duration may be variable, in which case `refreshDuration` is the minimum duration of a refresh cycle. +* `variableRefreshDelay` is a duration in nanoseconds indicating the maximum theoretical delay for the presentation engine to start a new refresh cycle upon receiving a presentation request. If this value is the same as `refreshDuration`, the presentation engine is operating in FRR mode. -Those properties may change at any time during an application's runtime without prior notification, in order to satisfy various system constraints, or simply user input. For example, enabling power-saving mode on a device may cause it to lower the display panel's refresh rate. Such changes are communicated back to the application when querying presentation timings via `vkGetSwapchainTimingPropertiesEXT`. +Those properties may change at any time during an application's runtime without prior notification, in order to satisfy various system constraints or user input. For example, enabling power-saving mode on a device may cause it to lower the display panel's refresh rate. Such changes are communicated back to the application when querying presentation timings via `vkGetSwapchainTimingPropertiesEXT`. -`refreshDuration` may also be zero, because some platforms may not provide timing properties until after at least one image has been presented to the swapchain. If timing properties of the swapchain change, updated results may again only be provided until after at least one additional image has been presented. +When the presentation engine is operating in VRR mode, `refreshDuration` is the minimum refresh duration. + +`refreshDuration` may be zero, because some platforms may not provide timing properties until after at least one image has been presented to the swapchain. If timing properties of the swapchain change, updated results may again only be provided until after at least one additional image has been presented. Applications also need to query available time domains using: [source,c] @@ -163,11 +162,13 @@ typedef struct VkSwapchainTimeDomainPropertiesEXT { VkStructureType sType; void* pNext; VkTimeDomainEXT timeDomain; + uint64_t timeDomainId; } VkSwapchainTimeDomainPropertiesEXT; ---- -Furthermore, this proposal adds two new `VkTimeDomainEXT` values +* `timeDomainId` is a unique identifier for this time domain in a swapchain-local namespace. This is used to differentiate between multiple swapchain-local time domains that have the same `VkTimeDomainEXT` scope. +Swapchain-local time domains are added in this proposal as two new `VkTimeDomainEXT` values: [source,c] ---- typedef enum VkTimeDomainEXT { @@ -180,6 +181,8 @@ typedef enum VkTimeDomainEXT { * `VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT` is a stage-local and swapchain-local time domain. It allows platforms where different presentation stages are handled by independent hardware to report timings in their own time domain. It is required to be supported. * `VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT` is a swapchain-local time domain, shared by all present stages. +Time domains are assigned a unique identifier within a swapchain by the implementation. This id is used to differentiate between multiple swapchain-local time domains. + To calibrate a swapchain-local or stage-local timestamp with another time domain, a new structure can be chained to `VkCalibratedTimestampInfoKHR` and passed to `vkGetCalibratedTimestampsKHR`: [source,c] ---- @@ -188,10 +191,11 @@ typedef struct VkSwapchainCalibratedTimestampInfoEXT { const void* pNext; VkSwapchainKHR swapchain; VkPresentStageFlagsEXT presentStage; + uint64_t timeDomainId; } VkSwapchainCalibratedTimestampInfoEXT; ---- -A single present stage can be specified in `presentStage` to calibrate a `VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT` timestamp from that stage. If `presentStage` is zero, the time domain used is `VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT`. +A single present stage can be specified in `presentStage` to calibrate a `VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT` timestamp from that stage. === Presentation timings feedback [[statistics]] @@ -204,7 +208,8 @@ VkResult vkGetPastPresentationTimingEXT( VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingEXT* pPresentationTimings, - uint64_t* pSwapchainTimingPropertiesCounter); + uint64_t* pSwapchainTimingPropertiesCounter, + VkBool32* pTimeDomainsChanged); ---- If the value of `pPresentationTimingCount` is 0, the implementation sets it to the number of pending results available in the swapchain's internal queue. Otherwise, it contains the number of entries written to `pPresentationTimings` upon return. If the implementation is not able to write all the available results in the provided `pPresentationTimings` array, `VK_INCOMPLETE` is returned. @@ -213,8 +218,9 @@ Results for presentation requests whose entries in `pPresentationTimings` are ma If `pSwapchainTimingPropertiesCounter` is not `NULL`, the implementation sets it to the current internal counter of the swapchain's timing properties. If its value is different than the last known counter value (from a previous call to `vkGetPastPresentationTimingEXT` or `vkGetSwapchainTimingPropertiesEXT`), applications should query those properties again using `vkGetSwapchainTimingPropertiesEXT`. -`VkPastPresentationTimingEXT` is defined as: +`pTimeDomainsChanged` is `VK_TRUE` if the swapchain's list of supported time domains has changed since the last call to `vkGetPastPresentationTimingEXT`. +`VkPastPresentationTimingEXT` is defined as: [source, c] ---- typedef struct VkPresentStageTimeEXT { @@ -228,28 +234,28 @@ typedef struct VkPastPresentationTimingEXT { uint64_t presentId; uint32_t presentStageCount; VkPresentStageTimeEXT* pPresentStages; - VkTimeDomainEXT timeDomain; + uint64_t timeDomainId; VkBool32 reportComplete; } VkPastPresentationTimingEXT; ---- * `presentId` is a present id provided to `vkQueuePresentKHR` by adding a `VkPresentIdKHR` to the `VkPresentInfoKHR` pNext chain. Timing results can be correlated to specific presents using this value. * `presentStageCount` and `pPresentStages` contain the timing information for the present stages that were specified in the `VkPresentTimeTargetInfoEXT` passed to the corresponding `vkQueuePresentKHR`. -* `timeDomain` is the time domain used for `pPresentStages` result times. It may be different than the time domain specified for the associated `vkQueuePresentKHR` if that time domain was unavailable when the presentation request was processed. In this case, `timeDomain` is a time domain the presentation engine used as a preferred fallback. +* `timeDomainId` is the id of the time domain used for `pPresentStages` result times. It may be different than the time domain specified for the associated `vkQueuePresentKHR` if that time domain was unavailable when the presentation request was processed. In this case, `timeDomainId` refers to the time domain the presentation engine used as a preferred fallback. * `reportComplete` indicates whether results for all present stages have been reported. `presentStageCount` only reports the number of stages which contain definitive results. However, time values in completed `pPresentStages` can still be 0 for multiple reasons. Most notably, it is possible for a presentation request to never reach some present stages, for example if using a present mode that allows images to be replaced in the queue, such as `VK_PRESENT_MODE_MAILBOX_KHR`. Platform-specific events can also cause results for some present stages to be unavailable for a specific presentation request. -To accommodate for the difference in query latency among the different present stages, timing results can be reported as incomplete when multiple present stages were specified in `VkSwapchainPresentTimingCreateInfoEXT::presentStageQueries`. For example, in more complex topologies of the display system, such as network-based configurations, results for the `VK_PRESENT_STAGE_IMAGE_HANDOFF_BIT_EXT` present stage can be available much earlier than for subsequent stages. +To accommodate for the difference in query latency among the different present stages, timing results can be reported as incomplete when multiple present stages were specified in `VkSwapchainPresentTimingCreateInfoEXT::presentStageQueries`. For example, in more complex topologies of the display system, such as network-based configurations, results for the `VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT` present stage can be available much earlier than for subsequent stages. [NOTE] ==== -Tracking the timing of multiple present stages allows applications to deduce various useful information about the present pipeline. For example, tracking both `VK_PRESENT_STAGE_IMAGE_HANDOFF_BIT_EXT` and `VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT` reveals how early a presentation request was before its image got latched by the presentation engine. Applications can use this "headroom" value to determine whether they can durably shorten their Image Present Duration (IPD). +Tracking the timing of multiple present stages allows applications to deduce various useful information about the present pipeline. For example, tracking both `VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT` and `VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT` reveals how early a presentation request was before its image got latched by the presentation engine. Applications can use this "headroom" value to determine whether they can durably shorten their Image Present Duration (IPD). ==== [NOTE] ==== -One key aspect that is notably missing from this proposal is the ability to collect timing information from individual "nodes" of the display topology. A typical example would be a system connected to two displays, running in "mirror" mode so that both will display the swapchain contents; in this case, this API does not provide any way to know which monitor the timings correspond to: the only requirement is that the timings are from an entity that is affected by the presentation. There are security considerations to providing such details that are best covered by system-specific extensions. +One key aspect that is notably missing from this extension is the ability to collect timing information from individual "nodes" of the display topology. A typical example would be a system connected to two displays, running in "mirror" mode so that both will display the swapchain contents; in this case, this API does not provide any way to know which monitor the timings correspond to: the only requirement is that the timings are from an entity that is affected by the presentation. There are security considerations to providing such details that are best covered by system-specific extensions. ==== === Scheduling presents [[scheduling]] @@ -260,16 +266,17 @@ A new struct `VkPresentTimingsInfoEXT` can be appended to the `VkPresentInfoKHR` ---- typedef union VkPresentTimeEXT { uint64_t targetPresentTime; - uint64_t afterPresentDuration; + uint64_t presentDuration; } VkPresentTimeEXT; typedef struct VkPresentTimingInfoEXT { VkStructureType sType; const void* pNext; VkPresentTimeEXT time; - VkTimeDomainEXT timeDomain; + uint64_t timeDomainId; VkPresentStageFlagsEXT presentStageQueries; VkPresentStageFlagsEXT targetPresentStage; + VkBool32 presentAtRelativeTime; VkBool32 presentAtNearestRefreshCycle; } VkPresentTimingInfoEXT; @@ -282,22 +289,28 @@ typedef struct VkPresentTimingsInfoEXT { ---- For each swapchain referenced in `VkPresentInfoKHR`, a `VkPresentTimingInfoEXT` is specified: -* `time` is the absolute or relative time the application would like the presentation to complete the target present stage, in the specified `timeDomain`. +* `time` is the absolute or relative time used to schedule this presentation request. +* `timeDomainId` is the id of the time domain used to specify `time` and to query timing results. * `presentStageQueries` is a bitmask specifying all the present stages the application would like timings for. * `targetPresentStage` is a present stage which cannot be completed before the target time has elapsed. +* `presentAtRelativeTime` specifies whether `time` is to be interpreted as an absolute or a relative time value. * `presentAtNearestRefreshCycle` specifies that the application would like to present at the refresh cycle that is nearest to the target present time. -`VkPresentTimeEXT` is interpreted depending on the swapchain configuration set by the `VkSwapchainPresentTimingCreateInfoEXT::presentAtRelativeTime` flag specified at swapchain creation: -* `targetPresentTime` specifies the time in nanoseconds the application would like the image to complete the swapchain's target present stage. -* `afterPresentDuration` specifies the minimum duration in nanoseconds before which the new presentation request can reach the target present stage, relative to the previous presentation request's completion of the same stage. +`VkPresentTimeEXT` is interpreted according to the `VkPresentTimingInfoEXT::presentAtRelativeTime` flag: +* `targetPresentTime` specifies the earliest time in nanoseconds the presentation engine can complete the swapchain's target present stage. +* `presentDuration` specifies the minimum duration in nanoseconds the application would like +the image to be visible. If `presentStageQueries` is not zero, and the swapchain's internal timing queue is full, calling `vkQueuePresentKHR` yields a new error: `VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT`. -The presentation engine must not complete the target present stage earlier than the specified `time`, unless `presentAtNearestRefreshCycle` is set to `VK_TRUE`. In that case, the presentation engine may complete `targetPresentStage` at an earlier time which coincides with the beginning of a refresh cycle, if `time` is within the first half of that refresh cycle. In FRR scenarios, this can help work around clock drift or clock precision issues, which could cause the presentation engine to otherwise skip a refresh cycle for a presentation request. +The presentation engine must not complete the target present stage earlier than the specified `time`, unless `presentAtNearestRefreshCycle` is set to `VK_TRUE`. In that case, the presentation engine may complete `targetPresentStage` at an earlier time matching the beginning of a refresh cycle, if `time` is within the first half of that refresh cycle. In FRR scenarios, this can help work around clock drift or clock precision issues, which could cause the presentation engine to otherwise skip a refresh cycle for a presentation request. -The semantics of specifying a target present time or duration only apply to FIFO present modes (`VK_PRESENT_MODE_FIFO_KHR` and `VK_PRESENT_MODE_FIFO_RELAXED_KHR`). When attempting to dequeue a presentation request from the FIFO queue, the presentation engine will also check the current time against the target time. +The semantics of specifying a target present time or duration only apply to FIFO present modes (`VK_PRESENT_MODE_FIFO_KHR` and `VK_PRESENT_MODE_FIFO_RELAXED_KHR`). When attempting to dequeue a presentation request from the FIFO queue, the presentation engine checks the current time against the target time. +[NOTE] +==== To maintain a constant IPD, applications should use timing information collected via `vkGetPastPresentationTimingEXT` to determine the target time or duration of each present. If the presentation engine is operating with a fixed refresh rate, the application's image present duration (IPD) should be a multiple of `VkSwapchainTimingPropertiesEXT::refreshDuration`. That is, the quanta for changing the IPD is `refreshDuration`. For example, if `refreshDuration` is 16.67ms, the IPD can be 16.67ms, 33.33ms, 50.0ms, etc. +==== == Examples @@ -321,23 +334,16 @@ To maintain a constant IPD, applications should use timing information collected // (...) // Create swapchain - VkSwapchainPresentTimingCreateInfoEXT swapchainCreateInfoPresentTiming = { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_TIMING_CREATE_INFO_EXT - .pNext = NULL, - .presentTimingQueueSize = maxTimingCount, // e.g. swapchainImageCount * 4 - .presentAtRelativeTime = VK_FALSE - }; - VkSwapchainCreateInfoKHR swapchainCreateInfo = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = &swapchainCreateInfoPresentTiming + .pNext = NULL, + .flags = VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT // (...) }; - vkCreateSwapchainKHR(device, &swapchainCreateInfo, NULL, &swapchain); + result = vkCreateSwapchainKHR(device, &swapchainCreateInfo, NULL, &swapchain); // Query timing properties and time domains - // Note: On some systems, this may only be available after some // presentation requests have been processed. VkSwapchainTimingPropertiesEXT swapchainTimingProperties = { @@ -346,14 +352,21 @@ To maintain a constant IPD, applications should use timing information collected }; uint64_t currentTimingPropertiesCounter = 0; - vkGetSwapchainTimingPropertiesEXT(device, swapchain, &swapchainTimingProperties, ¤tTimingPropertiesCounter); + result = vkGetSwapchainTimingPropertiesEXT(device, swapchain, &swapchainTimingProperties, ¤tTimingPropertiesCounter); uint32_t timeDomainCount = 0; VkSwapchainTimeDomainPropertiesEXT *timeDomains; - vkGetSwapchainTimeDomainsEXT(device, swapchain, &timeDomainCount, NULL); + result = vkGetSwapchainTimeDomainsEXT(device, swapchain, &timeDomainCount, NULL); timeDomains = (VkSwapchainTimeDomainPropertiesEXT *) malloc(timeDomainCount * sizeof(VkSwapchainTimeDomainPropertiesEXT)); - vkGetSwapchainTimeDomainsEXT(device, swapchain, &timeDomainCount, timeDomains); + result = vkGetSwapchainTimeDomainsEXT(device, swapchain, &timeDomainCount, timeDomains); + + // Find the ID of the current VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT time domain + uint64_t currentTimeDomainId = FindTimeDomain(timeDomains, timeDomainCount, VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT); + + // Allocate internal queue to collect present timing results + const uint32_t maxTimingCount = GetMaxTimingCount(); // e.g. swapchainImageCount * 2 + result = vkSetSwapchainPresentTimingQueueSizeEXT(device, swapchain, maxTimingCount); // (Start presenting...) ---- @@ -362,31 +375,28 @@ To maintain a constant IPD, applications should use timing information collected [source, c] ---- - uint32_t pendingTimingCount = 0; + uint32_t maxPresentStageCount = 4; uint64_t currentPresentId = 1; - VkTimeDomainEXT currentTimeDomain = SelectAvailableTimeDomain(swapchain); - VkPastPresentationTimingEXT timings[maxTimingCount]; + VkPastPresentationTimingEXT *timings = NULL; VkPresentStageFlagBitsEXT targetPresentStage = VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT; - for (i = 0; i < maxTimingCount; ++i) { - timings[i].pPresentStages = (VkPastPresentationTimingEXT *) malloc(maxPresentStageCount * sizeof(VkPastPresentationTimingEXT)); + timings = (VkPastPresentationTimingEXT *) malloc(maxTimingCount * sizeof(VkPastPresentationTimingEXT)); + for (uint32_t i = 0; i < maxTimingCount; ++i) { + timings[i].pPresentStages = (VkPresentStageTimeEXT *) malloc(maxPresentStageCount * sizeof(VkPastPresentationTimingEXT)); } while (!done) { uint32_t timingCount = maxTimingCount; uint64_t newTimingPropertiesCounter = 0; - for (i = 0; i < maxTimingCount; ++i) { - timings[i].presentId = 0ull; - } - - vkGetPastPresentationTimingEXT(device, swapchain, &timingCount, &timings, &newTimingPropertiesCounter); + result = vkGetPastPresentationTimingEXT(device, swapchain, &timingCount, &timings, &newTimingPropertiesCounter); - if (newTimingPropertiesCounter != currentTimingPropertiesCounter || currentPresentId == 1) { - vkGetSwapchainTimingPropertiesEXT(device, swapchain, &swapchainTimingProperties, ¤tTimingPropertiesCounter); + if (newTimingPropertiesCounter != currentTimingPropertiesCounter) { + result = vkGetSwapchainTimingPropertiesEXT(device, swapchain, &swapchainTimingProperties, ¤tTimingPropertiesCounter); + currentTimingPropertiesCounter = newTimingPropertiesCounter; } - for (i = 0; i < timingCount; ++i) { + for (uint32_t i = 0; i < timingCount; ++i) { if (timings[i].reportComplete) { if (timings[i].timeDomain == currentTimeDomain) { // Build a presentation history @@ -410,16 +420,18 @@ To maintain a constant IPD, applications should use timing information collected // Position scene geometry / camera for `targetPresentTime' // (...) - vkAcquireNextImageKHR(...); + result = vkAcquireNextImageKHR(...); // Render to acquired swapchain image // (...) + result = vkQueueSubmit(...); + VkPresentTimingInfoEXT targetPresentTime = { .sType = VK_STRUCTURE_TYPE_PRESENT_TIME_TARGET_INFO_EXT, .pNext = NULL, .time = targetPresentTime, - .timeDomain = currentTimeDomain, + .timeDomainId = currentTimeDomain, .presentStageQueries = allStageQueries, .targetPresentStage = VK_PRESENT_STAGE_IMAGE_LATCHED, .presentAtNearestRefreshCycle = VK_TRUE @@ -450,8 +462,8 @@ To maintain a constant IPD, applications should use timing information collected switch (result) { case VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT: // We're presenting faster than results are coming in. We can either - // wait using a synchronous vkGetPastPresentationTimingEXT call, - // or present again without asking for present timing data. + // wait, reallocate the results queue, or present again without asking + // for present timing data. targetPresentTime.presentStageQueries = 0; result = vkQueuePresentKHR(queue, &presentInfo); // (...) @@ -480,14 +492,19 @@ The major API changes from `VK_GOOGLE_display_timing` are: Compared to `VK_GOOGLE_display_timing`, stricter specification language is also used to allow for more consistent and wider adoption among implementors. -=== RESOLVED: How does the application choose the value for `VkSwapchainPresentTimingCreateInfoEXT::presentTimingQueueSize`? +=== RESOLVED: How does the application choose the internal queue size to pass in `vkSetSwapchainPresentTimingQueueSize`? Use reasonable default values, such as a multiple of the swapchain image count. -Because presenting when the swapchain's internal timing queue is full is considered an error, the latency of the timing results effectively can end up throttling the present rate if the internal queue is small enough. The topology of the presentation engine usually being generally opaque to applications, there is no indication of the feedback latency before the application starts presenting. +Because presenting when the swapchain's internal timing queue is full is considered an error, the latency of the timing results effectively can end up throttling the present rate if the internal queue is small enough. The topology of the presentation engine being generally opaque to applications, there is no indication of the feedback latency before the application starts presenting. + +Applications which run into feedback latency issues can resize the internal timing queue. + +=== RESOLVED: Do we need an API to synchronously wait for present timing feedback? -Applications which run into feedback latency issues may have to recreate their swapchains to resize the internal timing queue. +No, because some implementations cannot provide a synchronous wait on those results, but allow applications to call vkGetPastPresentationTimingEXT without external synchronization. -=== PROPOSED: Do we need an API to synchronously wait for present timing feedback? +=== PROPOSED: How do we handle dynamic surface properties updates? -Yes. This provides a way to enforce forward progress guarantees. Much like vkWaitForPresentKHR, this should also lift the external synchronization requirements. +`VkSurfaceKHR` objects capabilities are dynamic and can respond to a lot of different events. For example, when an application user moves a window to another monitor, it is possible for the underlying surface's capabilities to change. In the context of this extension, this means that some of the parameters set in a `VkPresentTimingInfoEXT` struct and passed to `vkQueuePresentKHR`, for example, may not be valid by the time the presentation engine processes the presentation request. +The implementation must thus be able to handle parameters that have become invalid without the application's knowledge. In those cases, the specification provides sane "fallback" behaviors, e.g. reporting timestamps in a different time domain, reporting 0 values when unavailable, etc. diff --git a/xml/vk.xml b/xml/vk.xml index 2c56f856f..412c758d3 100755 --- a/xml/vk.xml +++ b/xml/vk.xml @@ -3207,22 +3207,17 @@ typedef void* MTLSharedEvent_id; VkPresentStageFlagsEXT presentStageQueriespresent stages that can be queried VkPresentStageFlagsEXT presentStageTargetspresent stages that can be used as a present target - - VkStructureType sType - const void* pNext - uint32_t presentTimingQueueSizesize of the swapchain's internal timing results queue - VkBool32 presentAtRelativeTimeif using target present times, interpret them as relative times - VkStructureType sType void* pNext uint64_t refreshDurationNumber of nanoseconds from the start of one refresh cycle to the next - VkBool32 variableRefresh + uint64_t variableRefreshDelayNumber of nanoseconds for variable refresh displays to react to IPD changes VkStructureType sType void* pNext VkTimeDomainEXT timeDomainAvailable time domain to use with the swapchain + uint64_t timeDomainIdUnique identifier for this time domain VkPresentStageFlagsEXT stage @@ -3234,7 +3229,7 @@ typedef void* MTLSharedEvent_id; uint64_t presentIdApplication-provided identifier, previously given to vkQueuePresentKHR uint32_t presentStageCountNumber of present stages results available in pPresentStages VkPresentStageTimeEXT* pPresentStagesReported timings for each present stage - VkTimeDomainEXT timeDomainTime domain of the present stages + uint64_t timeDomainIdTime domain of the present stages VkBool32 reportCompleteVK_TRUE if all the present stages have been reported @@ -3247,19 +3242,22 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkPresentTimeEXT timetarget present time or duration - VkTimeDomainEXT timeDomainTime domain to interpret the target present time and collect present stages timings with + uint32_t timeDomainIdTime domain to interpret the target present time and collect present stages timings with VkPresentStageFlagsEXT presentStageQueriesPresent stages to collect timing information for VkPresentStageFlagsEXT targetPresentStagePresent stage to target with a target present time + VkBool32 presentAtRelativeTimeWhether the specified VkPresentTimeEXT is a relative time VkBool32 presentAtNearestRefreshCycleWhether the presentation engine is allowed to round down the target present time to the nearest refresh cycle uint64_t targetPresentTime - uint64_t afterPresentDuration + uint64_t presentDuration VkStructureType sType const void* pNext VkSwapchainKHR swapchain + VkPresentStageFlagsEXT presentStage + uint64_t timeDomainId Display primary in chromaticity coordinates @@ -13165,6 +13163,7 @@ typedef void* MTLSharedEvent_id; uint32_t* pPresentationTimingCount VkPastPresentationTimingEXT* pPresentationTimings uint64_t* pSwapchainTimingPropertiesCounter + VkBool32* pTimeDomainsChanged void vkSetHdrMetadataEXT @@ -15316,6 +15315,12 @@ typedef void* MTLSharedEvent_id; const VkShaderStageFlagBits* pStages const VkShaderEXT* pShaders + + VkResult vkSetSwapchainPresentTimingQueueSizeEXT + VkDevice device + VkSwapchainKHR swapchain + uint32_t size + VkResult vkGetSwapchainTimingPropertiesEXT VkDevice device @@ -20008,10 +20013,10 @@ typedef void* MTLSharedEvent_id; + - @@ -20021,6 +20026,7 @@ typedef void* MTLSharedEvent_id; +