-
Notifications
You must be signed in to change notification settings - Fork 812
Add simple API sample for VK_EXT_present_timing. #1535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nvlduc
wants to merge
1
commit into
KhronosGroup:main
Choose a base branch
from
nvlduc:present_timing_api
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Copyright (c) 2026, NVIDIA CORPORATION | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 the "License"; | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
|
|
||
| get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) | ||
| get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH) | ||
| get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME) | ||
|
|
||
| add_sample( | ||
| ID ${FOLDER_NAME} | ||
| CATEGORY ${CATEGORY_NAME} | ||
| AUTHOR "NVIDIA" | ||
| NAME "Swapchain Present Timing" | ||
| DESCRIPTION "Presentation using VK_EXT_present_timing" | ||
| SHADER_FILES_GLSL | ||
| "${FOLDER_NAME}/glsl/fullscreen_triangle.vert" | ||
| "${FOLDER_NAME}/glsl/circle.frag" | ||
| SHADER_FILES_HLSL | ||
| "${FOLDER_NAME}/hlsl/fullscreen_triangle.vert.hlsl" | ||
| "${FOLDER_NAME}/hlsl/circle.frag.hlsl" | ||
| SHADER_FILES_SLANG | ||
| "${FOLDER_NAME}/slang/fullscreen_triangle.vert.slang" | ||
| "${FOLDER_NAME}/slang/circle.frag.slang") | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| //// | ||
| - Copyright (c) 2026, NVIDIA CORPORATION | ||
| - | ||
| - SPDX-License-Identifier: Apache-2.0 | ||
| - | ||
| - Licensed under the Apache License, Version 2.0 the "License"; | ||
| - you may not use this file except in compliance with the License. | ||
| - You may obtain a copy of the License at | ||
| - | ||
| - http://www.apache.org/licenses/LICENSE-2.0 | ||
| - | ||
| - Unless required by applicable law or agreed to in writing, software | ||
| - distributed under the License is distributed on an "AS IS" BASIS, | ||
| - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| - See the License for the specific language governing permissions and | ||
| - limitations under the License. | ||
| - | ||
| //// | ||
|
|
||
| = Swapchain Present Timing | ||
|
|
||
| ifdef::site-gen-antora[] | ||
| TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/swapchain_present_timing[Khronos Vulkan samples github repository]. | ||
| endif::[] | ||
|
|
||
| Frame pacing is an important aspect of achieving smooth rendering and synchronization in real-time applications. This sample demonstrates how to use the VK_EXT_present_timing extension to implement frame pacing with time-based presentation. | ||
|
|
||
| The sample renders a circle moving linearly across the viewport, which is a good visual test for detecting micro-stutters and verifying smooth presentation. It queries past presentation timing information and uses the last known display time along with the swapchain's refresh cycle duration to predict when the next frame should be presented. | ||
|
|
||
| Press 'P' on the keyboard to toggle the use of present timing feature. When present timing is disabled, animations will use the current CPU time during the application's update routine to simulate geometry placement. This is a common approach in realtime applications and can exhibit visual artifacts when frame times are inconsistent, e.g. when rendering complex very dynamic scenes. | ||
|
|
||
| == Extension Features | ||
|
|
||
| VK_EXT_present_timing exposes 3 features at the physical device level: | ||
| * `presentTiming` is required for the extension to be exposed, and allows the application to query past presentation timings. | ||
| * `presentAtAbsoluteTime` and `presentAtRelativeTime` allow applications to precisely control the time or duration of a presentation request. | ||
|
|
||
| However, hardware support is only one part of the requirements to access those features. Presentation capabilities can vary greatly depending on the kind of VkSurfaceKHR used, since they are in large part also dictated by the system APIs available to the implementation. Surface capabilities may then only expose a subset of the physical device's and need to be queried as well to make sure the implementation can provide present timing features in its runtime environment. | ||
|
|
||
| In addition to the "present-at" features, surfaces capabilities also expose the present stages the implementation is able to provide timing information for. Present stages represent steps in common presentation pipelines, so that different systems which provide time measurements for different things under a vague "display time" term can still express them in a common framework. For example, some systems might be able to accurately measure the time at which the display actually lit pixels, while others can only report when the system made the image available for the display. Having access to timing information from multiple present stages can also be useful to figure out the latency of the presentation engine. | ||
|
|
||
| == Swapchain Setup | ||
|
|
||
| After creating a swapchain with a capable `VkSurfaceKHR` and the `VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT` flag bit, applications should first query important properties about the swapchain's timing. | ||
|
|
||
| === Timing Properties | ||
|
|
||
| `VkSwapchainTimingPropertiesEXT` exposes a `refreshDuration` and a `refreshInterval` value. These two fields put together describe the behavior of the presentation engine: | ||
| * If both values are equal, the presentation engine is operating in a fixed refresh rate mode (FRR), and the value indicates the length of a refresh cycle in nanoseconds. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably an empty line before this one, again? |
||
| * If `refreshInterval` is `UINT64_MAX`, it means variable refresh rate (VRR) is active, and `refreshDuration` is the minimum duration of a refresh cycle achievable (i.e. the maximum framerate). | ||
| * If both values are different (but non-zero), then the presentation engine is operating in FRR mode, but with the ability to adjust its refresh duration by a factor of `refreshInterval` nanoseconds, sometimes referred to as Adaptive Refresh Rate (ARR). | ||
| * Finally, any value of zero means the implementation was not able to determine how the presentation engine operates. | ||
|
|
||
| In this example which has trivial rendering, we simply use the `refreshDuration` value as a fixed time interval, or default to a 60Hz refresh rate if that is not available. | ||
|
|
||
| === Time Domains | ||
|
|
||
| The time values are all expressed in a time domain chosen by the application among a list of candidates exposed by the swapchain. `VK_EXT_present_timing` introduces new opaque times domains that are local to a given swapchain: `VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT`, which all implementations must support, and `VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. times domains -> time domains ? |
||
|
|
||
| Because these are opaque time domains, it is possible for an implementation to expose more than one of the same kind, for example when a window is moved from one display to another. For this reason, time domains are also assigned a unique id by the implementation. | ||
|
|
||
| * `VK_TIME_DOMAIN_SWAPCHAIN_LOCAL_EXT` refers to a time domain that is specific to a swapchain, but common across all present stages. | ||
| * `VK_TIME_DOMAIN_PRESENT_STAGE_LOCAL_EXT` must be associated with both a swapchain and a present stage. This is useful on platforms where present stages might be handled by different hardware, each with their own time domain. | ||
|
|
||
| If available, choosing a wider time domain such as `VK_TIME_DOMAIN_DEVICE_KHR` can simplify the usage of the extension's API. | ||
|
|
||
| === Timing Results Queue | ||
|
|
||
| Finally, the swapchain must be given the opportunity to allocate internal resources that are used to store the timing results until the application can collect them. This is done with `vkSetSwapchainPresentTimingQueueSizeEXT`. A common question is figuring out how much space to allocate. There is unfortunately no good way of figuring this out other than trying, as it depends on the latency of the presentation engine to fill those results, which cannot be known beforehand. In this example, we choose a multiple of the swapchain's image count, betting on results to be available within a few frames. | ||
|
|
||
| If the internal timing results queue cannot hold any more data, calling `vkQueuePresentKHR` and requesting timing results returns a new error, `VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT`. Applications can recover from this error by allocating more space in the queue, or stop results requests until more space has been made available. | ||
|
|
||
| == Collecting Presentation Timings Data | ||
|
|
||
| Timing results are retrieved by the application by calling `vkGetPastPresentationTimingEXT`. | ||
|
|
||
| Results can be correlated to every `vkQueuePresentKHR` by using present IDs assigned with `VK_KHR_present_id2`. This allows applications to build a history of present timing statistics which can then be used to drive their frame pacing strategy. This sample shows an example of collecting those statistics, though it does not try to use them. | ||
|
|
||
| Unexpected system events, such as those triggered by power management-related features, can cause the presentation engine to change its behavior, for example by throttling the presentation rate. Such changes are communicated to the application via the `VkPastPresentationTimingPropertiesEXT::timingPropertiesCounter` when they are related to timing properties, and `VkPastPresentationTimingPropertiesEXT::timeDomainsCounter` when time domains are affected. These "counter" values should be checked against the last known value returned from `vkGetSwapchainTimingPropertiesEXT` and `vkGetSwapchainTimeDomainPropertiesEXT` respectively. If they do not match, applications should query those properties again. | ||
|
|
||
| == Rendering & Presentation | ||
|
|
||
| === Absolute vs. Relative Present Time | ||
|
|
||
| `VK_EXT_present_timing` supports two methods for specifying when a frame should be presented: | ||
|
|
||
| * Absolute Time (`presentAtAbsoluteTime`): `VkPresentTimingInfoEXT::targetTime` represents a timestamp in the selected time domain at which the image should be displayed. | ||
|
|
||
| * Relative Time (`presentAtRelativeTime`): The `VkPresentTimingInfoEXT::targetTime` is the minimum duration the image should be visible. | ||
|
|
||
| This sample provides examples for both methods. To compute the absolute time, it adds the current refresh duration to the last available display time. For relative time, the sample simply uses the current refresh duration as well. | ||
|
|
||
| The `VK_PRESENT_TIMING_INFO_PRESENT_AT_NEAREST_REFRESH_CYCLE_BIT_EXT` flag instructs the implementation to align the requested time or duration to the nearest refresh cycle boundary. This is useful when working with fixed refresh rates, so that small errors in calculations won't cause a presentation request to miss a full refresh cycle, causing in turn micro-stutters. | ||
|
|
||
| === Adaptive Frame Pacing | ||
|
|
||
| While this sample uses a fixed target duration, production applications can implement more sophisticated strategies by analyzing the present statistics: | ||
|
|
||
| * Latency detection: Compare `present_ready_time` (when GPU finished the queue operations enqueued by `vkQueuePresentKHR`) with `present_display_time` to measure the full presentation latency from the device's perspective. | ||
| * Missed deadlines: Detect when `targetTime` is earlier than `present_display_time` by more than a full refresh cycle, indicating the frame missed its target. | ||
| * Headroom: Compare `present_ready_time` with `present_dequeued_time` to measure how much headroom is available. If consistently finishing early with enough headroom, the application may target a higher framerate. | ||
| * Micro-stutter Detection: Look for irregular spacing between consecutive `present_display_time` values | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want an empty line before this one.