diff --git a/appendices/VK_EXT_present_timing.adoc b/appendices/VK_EXT_present_timing.adoc index 707ce9976..14cfa7b15 100644 --- a/appendices/VK_EXT_present_timing.adoc +++ b/appendices/VK_EXT_present_timing.adoc @@ -7,7 +7,7 @@ include::{generated}/meta/{refprefix}VK_EXT_present_timing.adoc[] === Other Extension Metadata *Last Modified Date*:: - 2023-03-07 + 2024-02-10 *IP Status*:: No known IP claims. *Contributors*:: @@ -171,7 +171,7 @@ In such cases, the special value of zero allows the application to indicate that timing feedback is desired, but that no targetPresentTime is requested. Later, once the application has obtained feedback, it can specify -targetPresentTime by using the results actualPresentTime. +targetPresentTime by using the result's actualPresentTime. 7) How long before an application’s request for new image duration is honored? @@ -198,10 +198,8 @@ the latency was approximately 5 refresh cycles (83.33ms). For higher-frequency displays, the latency may have a larger number of refresh cycles. -Is there value in having a query for the application to know how long it may -have to wait for feedback? Can such a query be reasonably answered by the -driver? Is there other interesting information in this space that we may -wish to capture? +Can such a query be reasonably answered by the driver? Is there other +interesting information in this space that we may wish to capture? 9) Do we have a query(s) about the number of VkPastPresentationTimingEXT @@ -247,3 +245,8 @@ Split it out into its own extension and define the interaction here. * 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 synchronous behavior to vkGetPastPresentationTimingEXT + ** Add stage-local time domain diff --git a/appendices/glossary.adoc b/appendices/glossary.adoc index b8f526185..daf34a996 100644 --- a/appendices/glossary.adoc +++ b/appendices/glossary.adoc @@ -836,7 +836,8 @@ Image Present Duration:: Image Present Rate:: The number of newly-presented images the application intends to present each second (a.k.a. frame rate). - This value should: be a multiple of the refresh rate. + On fixed refresh rate displays, this value should: be a multiple of + the refresh rate. endif::VK_EXT_present_timing,VK_GOOGLE_display_timing[] Image Subresource:: diff --git a/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc b/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc index fe71b7398..9b2abb0d1 100644 --- a/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc +++ b/chapters/VK_EXT_present_timing/PresentTimeInfo.adoc @@ -2,43 +2,35 @@ // // SPDX-License-Identifier: CC-BY-4.0 -[open,refpage='VkPresentTimesInfoEXT',desc='The earliest time each image should be presented',type='structs'] +[open,refpage='VkPresentTimingsInfoEXT',desc='Array of VkPresentTimingInfoEXT to chain with VkPresentInfoKHR',type='structs'] -- When the <> or <> feature is -enabled, additional fields can: be specified that allow an application to -specify the earliest time that an image should: be displayed. -This allows an application to avoid stutter that is caused by an image being -displayed earlier than planned. -Such stuttering might occur with both fixed and variable-refresh-rate -displays, because stuttering occurs when the geometry is not correctly -positioned for when the image is displayed. -An application can: instruct the presentation engine that an image must: -not be displayed earlier than a specified time by including the -sname:VkPresentTimesInfoEXT structure in the pname:pNext chain of the -sname:VkPresentInfoKHR structure. - -The sname:VkPresentTimesInfoEXT structure is defined as: - -include::{generated}/api/structs/VkPresentTimesInfoEXT.adoc[] - - * pname:sType is the type of this structure. +enabled, an application can: instruct the presentation engine that an image +must: not be displayed earlier than a specified time, or for a minimum +duration, by including the sname:VkPresentTimingsInfoEXT structure in the +pname:pNext chain of the slink:VkPresentInfoKHR structure. + +The sname:VkPresentTimingsInfoEXT structure is defined as: + +include::{generated}/api/structs/VkPresentTimingsInfoEXT.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:swapchainCount is the number of swapchains being presented to by this command. - * pname:pTimes is `NULL` or a pointer to an array of - sname:VkPresentTimeEXT elements with pname:swapchainCount entries. - If not `NULL`, each element of pname:pTimes contains the earliest present - time or the minimum present duration for the image corresponding to the - entry in the sname:VkPresentInfoKHR::pname:pImageIndices array. + * pname:pTimingInfos is `NULL` or a pointer to an array of + sname:VkPresentTimingInfoEXT elements with pname:swapchainCount entries. + If not `NULL`, each element of pname:pTimingInfos contains timing + information for the presentation of the image corresponding to the entry + in the sname:VkPresentInfoKHR::pname:pImageIndices array. -The semantics of a specified target present time or duration only apply to -the following present modes: +The semantics of target presentation time or duration vary depending on the presentation mode: * ename:VK_PRESENT_MODE_FIFO_KHR. - Tearing cannot be observed. + Tearing cannot: be observed. The first queued image is dequeued and presented if both: 1) its associated wait semaphore(s) have signaled, and 2) its target present time is less-than or equal-to the current time. @@ -52,25 +44,62 @@ the following present modes: displayed at the start of the vertical blanking period, or may: be treated the same as for ename:VK_PRESENT_MODE_IMMEDIATE_KHR. +Other presentation modes do not support a target presentation time or duration. + .Valid Usage **** * pname:swapchainCount must: be the same value as sname:VkPresentInfoKHR::pname:swapchainCount, where sname:VkPresentInfoKHR is in the pname:pNext chain of this - sname:VkPresentTimesInfoEXT structure. + sname:VkPresentTimingsInfoEXT structure. * For each member of sname:VkPresentInfoKHR::pname:pSwapchains, if the - associated sname:VkPresentTimeEXT is non-zero, the present mode must: - be ename:VK_PRESENT_MODE_FIFO_KHR or + associated sname:VkPresentTimeEXT is non-zero, the current present mode + must: be ename:VK_PRESENT_MODE_FIFO_KHR or ename:VK_PRESENT_MODE_FIFO_RELAXED_KHR. - * For each member of sname:VkPresentInfoKHR::pname:pSwapchains, if the - associated sname:VkPresentTimeEXT is non-zero, the swapchain must: have - been created with a non-zero - sname:VkSwapchainPresentTimingCreateInfoEXT::pname:presentStageTarget **** -include::{generated}/validity/structs/VkPresentTimesInfoEXT.adoc[] +include::{generated}/validity/structs/VkPresentTimingsInfoEXT.adoc[] -- + +[open,refpage='VkPresentTimingInfoEXT',desc='Specifies per-present per-swapchain timing information',type='structs'] +-- +The sname:VkPresentTimingInfoEXT structure is defined as: + +include::{generated}/api/structs/VkPresentTimingInfoEXT.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: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: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: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. + +.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 +**** + +include::{generated}/validity/structs/VkPresentTimingInfoEXT.adoc[] +-- + + [open,refpage='VkPresentTimeEXT',desc='Specifying when an image should be presented',type='structs'] -- @@ -79,27 +108,29 @@ 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 image 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:minPresentDuration, if non-zero, specifies the minimum duration in - nanoseconds before which the next image presentation request can: reach - the target present stage. A value of zero specifies that the - presentation engine may: display the image for any duration allowed by - the current present mode. + 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:minPresentDuration is used. +used. Otherwise, pname:afterPresentDuration is used. pname:targetPresentTime is a time in nanoseconds, according to the -time-domain being used. The presentation engine must: not complete the -target present stage, specified in the associated swapchain -slink:VkSwapchainPresentTimingCreateInfoEXT::pname:presentStageTarget, at an -earlier time. +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 @@ -115,18 +146,6 @@ subtract a small delta from pname:targetPresentTime as a margin for error. include::{generated}/validity/structs/VkPresentTimeEXT.adoc[] -- -[open,refpage='VkPresentStageTimeEXT',desc='Associate a present stage with a timestamp',type='structs'] --- -The sname:VkPresentStageTimeEXT structure is defined as: - -include::{generated}/api/structs/VkPresentStageTimeEXT.adoc[] - - * pname:stage is a tlink:VkPresentStageFlagsEXT value specifying a present stage. - * pname:time is a time in nanoseconds associated with the pname:stage. - -include::{generated}/validity/structs/VkPresentStageTimeEXT.adoc[] --- - [open,refpage='VkPresentStageFlagBitsEXT',desc='Bitmask specifying stages of the image presentation process',type='enums'] -- diff --git a/chapters/VK_EXT_present_timing/queries.adoc b/chapters/VK_EXT_present_timing/queries.adoc index 6d489885d..dc0aa68fd 100644 --- a/chapters/VK_EXT_present_timing/queries.adoc +++ b/chapters/VK_EXT_present_timing/queries.adoc @@ -8,7 +8,8 @@ Traditional game and real-time-animation applications frequently use ename:VK_PRESENT_MODE_FIFO_KHR so that presentable images are updated during the vertical blanking period of a given refresh cycle (RC) of the presentation engine's display. -This avoids the visual anomaly known as tearing. +On fixed refresh rate displays, this avoids the visual anomaly known as +tearing. However, synchronizing the presentation of images with the RC does not prevent all forms of visual anomalies. @@ -19,17 +20,10 @@ others. Sometimes the animation appears to freeze, when the same image is used for more RCs than other images. -In order to minimize stuttering, an application needs to: 1) render -and present images at a consistent rate that is a multiple of the -presentation engine's refresh rate; 2) correctly position its geometry -for when the presentable image will be displayed to the user. -Applications can: benefit from communication of timing information with the -presentation engine and its display. -For example, applications can: determine information about the refresh -rate of the display/compositor, can: specify when an image should be -presented, and can: determine when an image was actually presented. -This allows the application's animation to look smooth to the user, with -no stuttering. +In order to minimize stuttering, an application needs to: 1) render and +present images at a consistent rate that is, on fixed refresh rate displays, +a multiple of the presentation engine's refresh rate; 2) correctly position +its geometry for when the presentable image will be displayed to the user. The ifdef::VK_EXT_present_timing[] `VK_EXT_present_timing` @@ -55,40 +49,26 @@ The sname:VkSwapchainPresentTimingCreateInfoEXT structure is defined as: include::{generated}/api/structs/VkSwapchainPresentTimingCreateInfoEXT.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:presentTimingQueueSize is the size of the swapchain's internal storage holding presentation timing results. - * pname:presentStageQueries is a tlink:VkPresentStageFlagsEXT mask - specifying the stages that will be measured and reported by - flink:vkGetPastPresentationTimingEXT. - * pname:presentStageTarget is zero or a tlink:VkPresentStageFlagsEXT value - specifying which present stage to target when providing - target present times to present operations. * pname:presentAtRelativeTime specifies whether target present times are to be interpreted as relative times. -If pname:presentStageTarget is not zero, a non-zero slink:VkPresentTimeEXT -can: be provided when presenting an image of the swapchain. +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. .Valid Usage **** - * pname:presentStageQueries must: be equal to, or a subset of - slink:VkPresentTimingSurfaceCapabilitiesEXT::pname:presentStageQueries - used to create slink:VkSwapchainCreateInfoKHR::pname:surface - * If the <> - or <> - feature is not enabled, pname:presentStageTarget must: be zero + * If the <> feature is not + enabled, pname:presentTimingQueueSize must: be zero * If the <> feature is not enabled, pname:presentAtRelativeTime must: be ename:VK_FALSE - * If pname:presentStageTarget is not zero, it must: specify one and only - one stage supported in - slink:VkPresentTimingSurfaceCapabilitiesEXT::pname:presentStageTargets - associated with the slink:VkSwapchainCreateInfoKHR::pname:surface - * If pname:presentAtRelativeTime is ename:VK_TRUE, - pname:presentStageTarget must: not zero **** include::{generated}/validity/structs/VkSwapchainPresentTimingCreateInfoEXT.adoc[] @@ -124,7 +104,7 @@ The sname:VkSwapchainTimingPropertiesEXT structure is defined as: include::{generated}/api/structs/VkSwapchainTimingPropertiesEXT.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:refreshDuration is zero; or is an indication of the duration @@ -132,7 +112,7 @@ include::{generated}/api/structs/VkSwapchainTimingPropertiesEXT.adoc[] 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 start of the next refresh cycle. - If the presentation engine is operating as an VRR display + If the presentation engine is operating as a VRR display (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. @@ -157,8 +137,8 @@ The rate at which an application renders and presents new images is known as the image present rate (IPR, a.k.a. frame rate). The inverse of IPR, or the duration between each image present, is the image present duration (IPD). -In order to provide a smooth, stutter-free animation, an application needs -its IPD to be a multiple of pname:refreshDuration. +On FRR displays, in order to provide a smooth, stutter-free animation, an +application needs its IPD to be a multiple of pname:refreshDuration. For example, if a display has a 60Hz refresh rate, pname:refreshDuration will be a value in nanoseconds that is approximately equal to 16.67ms. In such a case, an application will want an IPD of 16.67ms (1X multiplier of @@ -236,7 +216,7 @@ The sname:VkSwapchainTimeDomainPropertiesEXT structure is defined as: include::{generated}/api/structs/VkSwapchainTimeDomainPropertiesEXT.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:timeDomain is a elink:VkTimeDomainEXT value representing a time @@ -245,39 +225,6 @@ include::{generated}/api/structs/VkSwapchainTimeDomainPropertiesEXT.adoc[] include::{generated}/validity/structs/VkSwapchainTimeDomainPropertiesEXT.adoc[] -- -[open,refpage='vkSetSwapchainTimingEXT',desc='Set timing information for a swapchain',type='protos'] --- - -To set timing information for a swapchain, call: - -include::{generated}/api/protos/vkSetSwapchainTimingEXT.adoc[] - - * pname:device is the device associated with pname:swapchain. - * pname:swapchain is the swapchain to obtain timing properties for. - * pname:pSwapchainTimingInfo is `NULL` or a pointer to an instance of - the sname:VkSwapchainTimingInfoEXT structure. If `NULL`, - display timing is disabled for the swapchain, otherwise enables - display timing and specifies which time domain to use. - -include::{generated}/validity/protos/vkSetSwapchainTimingEXT.adoc[] --- - -[open,refpage='VkSwapchainTimingInfoEXT',desc='Specify which of the available time domains to use for a swapchain',type='structs'] --- - -The sname:VkSwapchainTimingInfoEXT structure is defined as: - -include::{generated}/api/structs/VkSwapchainTimingInfoEXT.adoc[] - - * pname:sType is the type of this structure. - * pname:pNext is `NULL` or a pointer to a structure extending this - structure. - * pname:timeDomain is a elink:VkTimeDomainEXT value representing the time - domain that should be used with the swapchain. - -include::{generated}/validity/structs/VkSwapchainTimingInfoEXT.adoc[] --- - [open,refpage='vkGetPastPresentationTimingEXT',desc='Obtain timing of a previously-presented image',type='protos'] -- @@ -300,11 +247,17 @@ include::{generated}/api/protos/vkGetPastPresentationTimingEXT.adoc[] * pname:device is the device associated with pname:swapchain. * pname:swapchain is the swapchain to obtain presentation timing information duration for. + * pname:timeout specifies how long the function waits, in nanoseconds, if + no results are available. * pname:pPresentationTimingCount is a pointer to an integer related to the number of sname:VkPastPresentationTimingEXT structures to query, as described below. - * pname:pPresentationTimings is either `NULL` or a pointer to an an array + * pname:pPresentationTimings is `NULL` or a pointer to an an array of sname:VkPastPresentationTimingEXT structures. + * pname:pTimingPropertiesChanged is `NULL` or a pointer to a + basetype:VkBool32, which is set to ename:VK_TRUE if the swapchain timing + properties have changed since the last call to + fname:vkGetPastPresentationTimingEXT. If pname:pPresentationTimings is `NULL`, then the number of available timing records for the given pname:swapchain is returned in @@ -314,29 +267,48 @@ the user to the number of elements in the pname:pPresentationTimings array, and on return the variable is overwritten with the number of structures actually written to pname:pPresentationTimings. If the value of pname:pPresentationTimingCount is less than the number of -newly-available timing records, at most pname:pPresentationTimingCount -structures are written. -If pname:pPresentationTimingCount is smaller than the number of -newly-available timing records for the given pname:swapchain, -ename:VK_INCOMPLETE is returned instead of ename:VK_SUCCESS to indicate -that not all the available values were returned. -Elements of pname:pPresentationTimings are arranged in ascending order of -present ids. +newly-available timing records for the given pname:swapchain, at most +pname:pPresentationTimingCount structures are written, and +ename:VK_INCOMPLETE is returned instead of ename:VK_SUCCESS to indicate that +not all the available values were returned. + +fname:vkGetPastPresentationTimingEXT may: return incomplete results, +containing only information for a subset of the requested present +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 +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. Timing information may: become available out of order with regards to their associated flink:vkQueuePresentKHR commands. -Because timing information for some present stages may: be available earlier -than for some others, fname:vkGetPastPresentationTimingEXT may: return -incomplete results, containing only information for a subset of the -requested present stages. Further calls to -fname:vkGetPastPresentationTimingEXT will keep providing all available -results for a previously incomplete entry until it is complete. +Results for specific outstanding presentation requests can: be queried by +setting the pname:presentId field of input pname:pPresentationTimings +entries. If pname:timeout is not zero, fname:vkGetPastPresentationTimingEXT +will block for that many nanoseconds or until all the results for the +specified present ids are available. If the timeout expires, +ename:VK_TIMEOUT is returned and no result is written to +pname:pPresentationTimings. If pname:timeout is code:UINT64_MAX, the timeout +period is treated as infinite. Upon return, zero or more slots of the pname:swapchain internal timing -collection queue, equal to the number of completed entries written to -pname:pPresentationTimings for which all present stages have been returned, -are made available for future fname:vkQueuePresentKHR calls. +collection 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. + +There is no requirement for any precise timing relationship between the +completion of a present stage and the availability of any associated timing +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. include::{generated}/validity/protos/vkGetPastPresentationTimingEXT.adoc[] -- @@ -348,89 +320,61 @@ The sname:VkPastPresentationTimingEXT structure is defined as: include::{generated}/api/structs/VkPastPresentationTimingEXT.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:presentId is an application-provided value that was given to a - previous fname:vkQueuePresentKHR command via + * pname:presentId is zero or an application-provided value that was given + to a previous fname:vkQueuePresentKHR command via slink:VkPresentIdKHR::pname:pPresentIds. - It can: be used to uniquely identify a previous present with the - flink:vkQueuePresentKHR command. If no slink:VkPresentIdKHR was provided - or if the specified present id value was zero, pname:presentId is zero. - * pname:targetPresentTime is an application-provided value that was given - to a previous flink:vkQueuePresentKHR command via - slink:VkPresentTimeEXT::pname:targetPresentTime. - If non-zero, it was used by the application to indicate that an image - not be presented any sooner than pname:targetPresentTime, relative to - the stage specified in - slink:VkSwapchainPresentTimingCreateInfoEXT::pname:presentStageTarget. - If the pname:swapchain is using relative present times, it is zero and - the actual present duration can: be obtained by comparing the - pname:pPresentStages results of consecutive presents. * pname:presentStageCount is a count of items contained in pname:pPresentStages. - * pname:pPresentStages is `NULL` or a pointer to an array of - slink:VkPresentStageTimeEXT providing timing information for each of the - present stages requested in - slink:VkSwapchainPresentTimingCreateInfoEXT::pname:presentStageQueries. - * pname:timingPropertiesChanged is ename:VK_TRUE if the swapchain's - timing properties have changed since the last time those - properties were queried with - flink:vkGetSwapchainTimingPropertiesEXT, otherwise the properties - have not changed. - * pname:timeDomainChanged is ename:VK_TRUE if the time domain enabled for - the swapchain is not currently available. + * 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 + 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. +If pname:presentId is not zero, pname:presentStageCount must: be set by the +application to be at least equal to the number of present stages specified +in slink:VkPresentTimingInfoEXT::pname:presentStagesQueries for the +associated flink:vkQueuePresentKHR call. Otherwise, pname:presentStageCount +must: be at least equal to the highest number of present stages queried +among all presentation requests for which there are outstanding results. + Timing information for some present stages may: have a time value of 0, indicating that results for that present stage are not available. -For systems with multiple entities operating downstream of the presentation +For systems with multiple entities operating within the presentation engine, such as multiple displays, pname:pPresentStages will return timing results for at least one entity which has been affected by the presentation. -If pname:pPresentStages is `NULL`, then the number of available present -stages reports is returned in pname:presentStageCount. Otherwise, -pname:presentStageCount must: be set by the application to the number of -elements in the pname:pPresentStages array, and on return the variable is -overwritten with the number of entries actually written to -pname:pPresentStages. If the value of pname:presentStageCount is less than -the number of available present stages reports, at most -pname:presentStageCount will be written to pname:pPresentStages. - -If pname:timeDomainChanged is ename:VK_TRUE, the application must: query -what time domains are available and enable present timing with a -currently-available time domain. If the currently-enabled time domain is the -opaque domain of ename:VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT, it is possible -that ename:VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT will be returned by -flink:vkGetSwapchainTimeDomainsEXT. In such a case, the presentation engine -may: have multiple opaque time domains that it is switching between. - -If any of pname:timeDomainChanged or pname:timingPropertiesChanged is -ename:VK_TRUE, an application must: not compare the values of -pname:pPresentStages with any other values, as the presentation engine may -not be able to provide accurate values. - -The results for a given pname:swapchain and present id are returned from -fname:vkGetPastPresentationTimingEXT until pname:reportComplete is -ename:VK_TRUE and all available results have been reported in -pname:pPresentStages. +pname:timeDomain may: be different than the time domain that was specified +in slink:VkPresentTimingInfoEXT::pname:timeDomain if the requirements for +using this time domain could not be met at the time the presentation engine +processed the presentation request. In such a case, the presentation engine +may: pick a time domain to fall back to and report results in that domain. +Applications can: continue to use this fallback time domain in future +flink:vkQueuePresentKHR calls, or they can: call +flink:vkGetSwapchainTimeDomainsEXT to choose from the currently supported +time domains. include::{generated}/validity/structs/VkPastPresentationTimingEXT.adoc[] [NOTE] .Note ==== -An application can: determine its optimal IPD by using the +An application can: determine its optimal IPD in FRR scenarios by using the sname:VkPastPresentationTimingEXT values. For a given target present stage, if `pPresentStages[targetStageIndex].time` -is one or more refresh cycle durations later than pname:targetPresentTime, -the application knows that the image was presented late. In order to avoid -visual anomalies, it can: increase its IPD by a multiple of the refresh -cycle duration that approximately corresponds to the difference between -`pPresentStages[targetStageIndex].time` and pname:targetPresentTime. +is one or more refresh cycle durations later than the associated present +pname:targetPresentTime, the application knows that the image was presented +late. In order to avoid visual anomalies, it can: increase its IPD by a +multiple of the refresh cycle duration that approximately corresponds to the +difference between `pPresentStages[targetStageIndex].time` and +pname:targetPresentTime. If `pPresentStages[targetStageIndex].time` and pname:targetPresentTime are consistently approximately equal to each other, the application knows that @@ -463,12 +407,23 @@ full-screen, the timing properties may: be VRR. The available time domains for a swapchain may: change for similar or identical reasons. Therefore, it is possible that the same event will cause both -pname:timingPropertiesChanged and pname:timeDomainChanged to become -ename:VK_TRUE. -It is also possible that an event can: cause only -pname:timingPropertiesChanged to become ename:VK_TRUE. +pname:timingPropertiesChanged to become ename:VK_TRUE and and +pname:timeDomain to be different than the time domain requested in +sname:VkPresentTimingInfoEXT. ==== -- + +[open,refpage='VkPresentStageTimeEXT',desc='Associate a present stage with a timestamp',type='structs'] +-- +The sname:VkPresentStageTimeEXT structure is defined as: + +include::{generated}/api/structs/VkPresentStageTimeEXT.adoc[] + + * pname:stage is a tlink:VkPresentStageFlagsEXT value specifying a present stage. + * pname:time is a time in nanoseconds associated with the pname:stage. + +include::{generated}/validity/structs/VkPresentStageTimeEXT.adoc[] +-- endif::VK_EXT_present_timing[] ifdef::VK_GOOGLE_display_timing[] diff --git a/chapters/VK_KHR_swapchain/wsi.adoc b/chapters/VK_KHR_swapchain/wsi.adoc index 78353f203..ba7e518ea 100644 --- a/chapters/VK_KHR_swapchain/wsi.adoc +++ b/chapters/VK_KHR_swapchain/wsi.adoc @@ -1404,13 +1404,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 that swapchain has -enabled the collection of presentation timings via -flink:vkSetSwapchainTimingEXT, one entry of its internal timing queue is -reserved until the results have been collected by -flink:vkGetPastPresentationTimingEXT. + +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. + endif::VK_EXT_present_timing[] include::{generated}/validity/protos/vkQueuePresentKHR.adoc[] diff --git a/proposals/VK_EXT_present_timing.adoc b/proposals/VK_EXT_present_timing.adoc new file mode 100644 index 000000000..b35605de6 --- /dev/null +++ b/proposals/VK_EXT_present_timing.adoc @@ -0,0 +1,494 @@ +// Copyright 2023 The Khronos Group Inc. +// +// SPDX-License-Identifier: CC-BY-4.0 + += VK_EXT_present_timing +:toc: left +:refpage: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/ +:sectnums: + +This extension provides facilities for applications using VK_KHR_swapchain to obtain timing information about the presentation engine's display, presentation statistics for each present operation, and to schedule present operations to happen at a specific time. + +== Problem statement + +As rendering systems have become more complex and more deeply buffered, rendering workloads have grown increasingly independent of the presentation process. Different hardware may even be involved. As a consequence, applications are left without a clear way to align the presentation process with other workloads, particularly rendering. + +This can result in visual anomalies such as stutter, or increased input latency, when the frames aren't being presented to the user at the time the application was expecting it. This effect may be exacerbated in Fixed Refresh Rate (FRR) scenarios when the display refresh rate is not a factor of the application's rendered frame rate; for example, rendering 50 frames per second on a 60Hz monitor, which will result in some frames being visible for multiple refresh cycles. + +To accomplish smooth animation, applications need to predict and schedule when each frame is going to be displayed so that the application's simulation time, which places the geometry and camera within a scene, closely matches the display time. This requires various timing information about the presentation engine, such as when previous presentable images were actually displayed and when they could have been displayed, as well as the presentation engine's refresh cycle duration. + +Multimedia applications also typically require accurate frame timing in order to closely match the content's expected frame rate and synchronize presentation operations with audio output. + +== Solution Space + +Partial solutions exist to address some of the problems described above: + +* Variable Refresh Rate +* `VK_KHR_present_wait` +* `VK_GOOGLE_display_timing` + +Variable Refresh Rate (VRR) technology can mitigate the effects of stutter, because the display may be able to match the variations in present duration, while FRR displays need to wait for a future refresh cycle if an image was not ready in time for its intended present time. Though this limits some of the visual anomalies, it does not address the issue of providing applications feedback and control over the presentation engine timing. + +`VK_KHR_present_wait` is a Vulkan extension which allows the host to synchronously wait for a present operation to complete. This can be used as a tool to implement efficient frame pacing, but lacks important details such as the latency of the present operation itself, and information about the display timing properties. The `VK_KHR_present_wait` specification itself also has rather loose requirements which may result in inconsistent implementations. + +`VK_GOOGLE_display_timing` is currently the only existing extension which provides a solution to this core problem of interacting with the presentation engine's timeline. However, it is not implementable by all vendors, and lacks enough details to support technologies such as VRR systems. The proposal that follows is heavily inspired by all the work and discussions surrounding `VK_GOOGLE_display_timing`, and provides a more granular approach to its features, allowing for wider vendor adoption. + +== Proposal + +=== Features + +`VK_EXT_present_timing` exposes three new physical device features: +[source,c] +---- +typedef struct VkPhysicalDevicePresentTimingFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 presentTiming; + VkBool32 presentAtAbsoluteTime; + VkBool32 presentAtRelativeTime; +} VkPhysicalDevicePresentTimingFeaturesEXT; +---- + +If `VK_EXT_present_timing` is exposed by the device, `presentTiming` is required to be supported. This feature allows applications to query details about presentation timing of a given swapchain, such as the refresh rate or supported time domains, as well as statistics about individual present operations. + +When supported, `presentAtAbsoluteTime` allows applications to specify an absolute time, in a specific time domain, with each `vkQueuePresentKHR` call. `presentAtRelativeTime` allows applications to specify a relative time instead, specifying a minimum duration before a new image can presented. See <>. + +These features are also advertised for each `VkSurfaceKHR` object with: + +[source,c] +---- +typedef struct VkPresentTimingSurfaceCapabilitiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 presentTimingSupported; + VkBool32 presentAtAbsoluteTimeSupported; + VkBool32 presentAtRelativeTimeSupported; + VkPresentStageFlagsEXT presentStageQueries; + VkPresentStageFlagsEXT presentStageTargets; +} VkPresentTimingSurfaceCapabilitiesEXT; +---- + +In addition of the present timing and present scheduling features, surfaces also advertise which <> are available to query timing and schedule presents for. + +=== Present stages [[present_stages]] + +It is difficult to define "presentation" while satisfying all implementations, platforms or even display technologies. Thus, this proposal introduces the concept of "present stages": a set of well-defined discrete steps within typical present pipelines. + +[source,c] +---- +typedef enum VkPresentStageFlagBitsEXT { + VK_PRESENT_STAGE_IMAGE_HANDOFF_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, +} 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. + +* `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_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. + +=== Enabling present timing for a swapchain + +To enable present timing for a swapchain, a new struct must be chained to `VkSwapchainCreateInfoKHR`: + +[source,c] +---- +typedef struct VkSwapchainPresentTimingCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t presentTimingQueueSize; + VkBool32 presentAtRelativeTime; +} VkSwapchainPresentTimingCreateInfoEXT; +---- + +* `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. + +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`. + +=== 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: + +[source,c] +---- +VkResult vkGetSwapchainTimingPropertiesEXT( + VkDevice device, + VkSwapchainKHR swapchain, + VkSwapchainTimingPropertiesEXT* pSwapchainTimingProperties); +---- + +The structure is defined as: + +[source,c] +---- +typedef struct VkSwapchainTimingPropertiesEXT { + VkStructureType sType; + const void* pNext; + uint64_t refreshDuration; + VkBool32 variableRefresh; +} 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. + +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`. + +`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. + +Applications also need to query available time domains using: +[source,c] +---- +VkResult vkGetSwapchainTimeDomainsEXT( + VkDevice device, + VkSwapchainKHR swapchain, + uint32_t* pSwapchainTimeDomainCount, + VkSwapchainTimeDomainPropertiesEXT* pSwapchainTimeDomains); + +typedef struct VkSwapchainTimeDomainPropertiesEXT { + VkStructureType sType; + void* pNext; + VkTimeDomainEXT timeDomain; +} VkSwapchainTimeDomainPropertiesEXT; +---- + +Furthermore, this proposal adds two new `VkTimeDomainEXT` values + +[source,c] +---- +typedef enum VkTimeDomainEXT { + // ... + VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT = 1000208000, + VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT = 1000208001, +} 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. + +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] +---- +typedef struct VkSwapchainCalibratedTimestampInfoEXT { + VkStructureType sType; + const void* pNext; + VkSwapchainKHR swapchain; + VkPresentStageFlagsEXT presentStage; +} 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`. + +=== Presentation timings feedback [[statistics]] + +Applications can obtain timing information about previous presents using: + +[source,c] +---- +VkResult vkGetPastPresentationTimingEXT( + VkDevice device, + VkSwapchainKHR swapchain, + uint64_t timeout, + uint32_t* pPresentationTimingCount, + VkPastPresentationTimingEXT* pPresentationTimings, + VkBool* pTimingPropertiesChanged); +---- + +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. + +The `VkPastPresentationTimingEXT::presentId` of the `pPresentationTimings` entries can be set to ids referencing outstanding presentation request results. When doing so, the function will only return the results for those presentation requests. If timeout is not zero, the function will block until all the results are available, up to `timeout` nanoseconds or until an error occurs. If `timeout` is `UINT64_MAX`, it is treated as infinite. + +Results for presentation requests whose entries in `pPresentationTimings` are marked as complete with `VkPastPresentationTimingEXT::reportComplete` will not be returned anymore. For each of those, a slot in the internal swapchain present timing queue is released. Incomplete results for presentation requests will keep being reported by `vkGetPastPresentationTimingEXT` until complete. + +`pTimingPropertiesChanged` indicates whether the timing properties of the swapchain have changed since the previous call to `vkGetPastPresentationTimingEXT`. If true, applications should query those properties again using `vkGetSwapchainTimingPropertiesEXT`. + +`VkPastPresentationTimingEXT` is defined as: + +[source, c] +---- +typedef struct VkPresentStageTimeEXT { + VkPresentStageFlagsEXT stage; + uint64_t time; +} VkPresentStageTimeEXT; + +typedef struct VkPastPresentationTimingEXT { + VkStructureType sType; + const void* pNext; + uint64_t presentId; + uint32_t presentStageCount; + VkPresentStageTimeEXT* pPresentStages; + VkTimeDomainEXT timeDomain; + 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. +* `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. + +[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). +==== + +[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. +==== + +=== Scheduling presents [[scheduling]] + +A new struct `VkPresentTimingsInfoEXT` can be appended to the `VkPresentInfoKHR` pNext chain to specify present timing properties: + +[source,c] +---- +typedef union VkPresentTimeEXT { + uint64_t targetPresentTime; + uint64_t afterPresentDuration; +} VkPresentTimeEXT; + +typedef struct VkPresentTimingInfoEXT { + VkStructureType sType; + const void* pNext; + VkPresentTimeEXT time; + VkTimeDomainEXT timeDomain; + VkPresentStageFlagsEXT presentStageQueries; + VkPresentStageFlagsEXT targetPresentStage; + VkBool32 presentAtNearestRefreshCycle; +} VkPresentTimingInfoEXT; + +typedef struct VkPresentTimingsInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t swapchainCount; + const VkPresentTimingInfoEXT* pTimingInfos; +} 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`. +* `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. +* `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. + +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 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. + +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 + +=== Enabling present timing for a swapchain + +[source, c] +---- + // Query device features + VkPhysicalDevicePresentTimingFeaturesEXT deviceFeaturesPresentTiming = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT + }; + + VkPhysicalDeviceFeatures2 features2 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &deviceFeaturesPresentTiming + }; + + vkGetPhysicalDeviceFeatures2(physicalDevice, &features2); + + // Create device + // (...) + + // 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 + // (...) + }; + + 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 = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_TIMING_PROPERTIES_EXT, + .pNext = NULL + }; + + vkGetSwapchainTimingPropertiesEXT(device, swapchain, &swapchainTimingProperties); + + uint32_t timeDomainCount = 0; + VkSwapchainTimeDomainPropertiesEXT *timeDomains; + + vkGetSwapchainTimeDomainsEXT(device, swapchain, &timeDomainCount, NULL); + timeDomains = (VkSwapchainTimeDomainPropertiesEXT *) malloc(timeDomainCount * sizeof(VkSwapchainTimeDomainPropertiesEXT)); + vkGetSwapchainTimeDomainsEXT(device, swapchain, &timeDomainCount, timeDomains); + + // (Start presenting...) +---- + +=== Basic present loop + +[source, c] +---- + uint32_t pendingTimingCount = 0; + uint64_t currentPresentId = 1; + VkTimeDomainEXT currentTimeDomain = SelectAvailableTimeDomain(swapchain); + VkPastPresentationTimingEXT timings[maxTimingCount]; + VkPresentStageFlagBitsEXT targetPresentStage = VK_PRESENT_STAGE_IMAGE_LATCHED_BIT_EXT; + + for (i = 0; i < maxTimingCount; ++i) { + timings[i].pPresentStages = (VkPastPresentationTimingEXT *) malloc(maxPresentStageCount * sizeof(VkPastPresentationTimingEXT)); + } + + while (!done) + { + uint32_t timingCount = maxTimingCount; + VkBool timingPropertiesChanged = VK_FALSE; + + for (i = 0; i < maxTimingCount; ++i) { + timings[i].presentId = 0ull; + } + + vkGetPastPresentationTimingEXT(device, swapchain, 0ull, &timingCount, &timings, &timingPropertiesChanged); + + if (timingPropertiesChanged || currentPresentId == 1) { + vkGetSwapchainTimingPropertiesEXT(device, swapchain, &swapchainTimingProperties); + } + + for (i = 0; i < timingCount; ++i) { + if (timings[i].reportComplete) { + if (timings[i].timeDomain == currentTimeDomain) { + // Build a presentation history + pastPresentationTimings[timings[i].presentId % maxPresentHistory] = ParseResult(timings[i]); + } else { + // Handle time domain change. A more sophisticated approach can be + // taken with calibrated timestamps to correlate both time domains. + currentTimeDomain = SelectAvailableTimeDomain(swapchain); + InvalidatePastPresentationTimings(); + } + } + } + + // Process past presentation timings: + // - Determine whether changing the IPD is necessary / desired. + uint64_t targetIPD = ProcessPastPresentationTimings(&swapchainTimingProperties); + + // Based on previous reported times and target IPD, compute the next target present time. + uint64_t targetPresentTime = pastPresentationTimings[mostRecentResultsIndex].latchTime + + (currentPresentId - pastPresentationTimings[mostRecentResultsIndex].presentId) * targetIPD. + + // Position scene geometry / camera for `targetPresentTime' + // (...) + + vkAcquireNextImageKHR(...); + + // Render to acquired swapchain image + // (...) + + VkPresentTimeTargetInfoEXT targetPresentTime = { + .sType = VK_STRUCTURE_TYPE_PRESENT_TIME_TARGET_INFO_EXT, + .pNext = NULL, + .time = targetPresentTime, + .timeDomain = currentTimeDomain, + .presentStageQueries = allStageQueries, + .targetPresentStage = VK_PRESENT_STAGE_IMAGE_LATCHED, + .presentAtNearestRefreshCycle = VK_TRUE + }; + + VkPresentTimesInfoEXT presentTimesInfo = { + .sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_EXT, + .pNext = NULL, + .swapchainCount = 1, + .pTimeInfos = &targetPresentTime + }; + + VkPresentIdKHR presentId = { + .sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR, + .pNext = &presentTimesInfo, + .swapchainCount = 1, + .pPresentIds = ¤tPresentId + } + + VkPresentInfoKHR presentInfo = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = &presentId, + // (...) + }; + + result = vkQueuePresentKHR(queue, &presentInfo); + + 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. + targetPresentTime.presentStageQueries = 0; + result = vkQueuePresentKHR(queue, &presentInfo); + // (...) + break; + // Handle other 'result' values... + // (...) + } + + currentPresentId++; + } +---- + +== Issues + +=== What are the key differences to `VK_GOOGLE_display_timing`? + +The major API changes from `VK_GOOGLE_display_timing` are: + +* Introduction of present stages with `VkPresentStageFlagsEXT` +* Rely on `VK_KHR_present_id` to specify present Ids +* Expose features in physical device and surface features +* Variable refresh rate indicator +* Progressive timings feedback +* Allow time domain selection, with new domains dedicated to swapchains +* Allow for relative present times + +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`? + +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. + +Applications which run into feedback latency issues may have to recreate their swapchains to resize the internal timing queue. + +=== PROPOSED: Do we need an API to synchronously wait for present timing feedback? + +Yes. This provides a way to enforce forward progress guarantees. Much like vkWaitForPresentKHR, this should also lift the external synchronization requirements. diff --git a/xml/vk.xml b/xml/vk.xml index 4c5d3f513..0cce15d80 100755 --- a/xml/vk.xml +++ b/xml/vk.xml @@ -3210,14 +3210,12 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - uint32_t presentTimingQueueSize - VkPresentStageFlagsEXT presentStageQueriespresent stages to provide feedback for - VkPresentStageFlagsEXT presentStageTargetpresent stage used to interpret the target present time + uint32_t presentTimingQueueSizesize of the swapchain's internal timing results queue VkBool32 presentAtRelativeTimeif using target present times, interpret them as relative times VkStructureType sType - const void* pNext + void* pNext uint64_t refreshDurationNumber of nanoseconds from the start of one refresh cycle to the next VkBool32 variableRefresh @@ -3226,35 +3224,37 @@ typedef void* MTLSharedEvent_id; void* pNext VkTimeDomainEXT timeDomainAvailable time domain to use with the swapchain - - VkStructureType sType - const void* pNext - VkTimeDomainEXT timeDomainTime domain selected by the application to be used for the swapchain time values - VkPresentStageFlagsEXT stage uint64_t timeTime in nanoseconds of the associated stage VkStructureType sType - const void* pNext + void* pNext uint64_t presentIdApplication-provided identifier, previously given to vkQueuePresentKHR - uint64_t targetPresentTimeEarliest time an image should have been presented, previously given to vkQueuePresentKHR - uint32_t presentStageCountTBD - VkPresentStageTimeEXT* pPresentStagesTBD - VkBool32 timingPropertiesChangedVK_TRUE if swapchain's timing properties changed since last queried - VkBool32 timeDomainChangedVK_TRUE if the swapchain no longer supports the enabled time domain - VkBool32 reportCompleteVK_TRUE if all the present stages have been reported. - - - VkStructureType sType + uint32_t presentStageCountNumber of present stages results available in pPresentStages + VkPresentStageTimeEXT* pPresentStagesReported timings for each present stage + VkTimeDomainEXT timeDomainTime domain of the present stages + VkBool32 reportCompleteVK_TRUE if all the present stages have been reported + + + VkStructureType sType const void* pNext uint32_t swapchainCountCopy of VkPresentInfoKHR::swapchainCount - const VkPresentTimeEXT* pTimesWhen to present images + const VkPresentTimingInfoEXT* pTimingInfosPresent timing details for each swapchain + + + 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 + VkPresentStageFlagsEXT presentStageQueriesPresent stages to collect timing information for + VkPresentStageFlagsEXT targetPresentStagePresent stage to target with a target present 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 minPresentDuration + uint64_t afterPresentDuration VkStructureType sType @@ -12536,12 +12536,6 @@ typedef void* MTLSharedEvent_id; VkQueue queue const VkPresentInfoKHR* pPresentInfo - - VkResult vkSetSwapchainTimingEXT - VkDevice device - VkSwapchainKHR swapchain - const VkSwapchainTimingInfoEXT* pSwapchainTimingInfo - VkResult vkCreateViSurfaceNN VkInstance instance @@ -13164,12 +13158,14 @@ typedef void* MTLSharedEvent_id; uint32_t set const void* pData - + VkResult vkGetPastPresentationTimingEXT VkDevice device VkSwapchainKHR swapchain + uint64_t timeout uint32_t* pPresentationTimingCount VkPastPresentationTimingEXT* pPresentationTimings + VkBool32* pTimingPropertiesChanged void vkSetHdrMetadataEXT @@ -19996,20 +19992,21 @@ typedef void* MTLSharedEvent_id; - + - + - - + + - + + @@ -20017,9 +20014,9 @@ typedef void* MTLSharedEvent_id; - - + + @@ -20027,7 +20024,6 @@ typedef void* MTLSharedEvent_id; -