Timestamps
We’re Back
I spent a few days locked in a hellish battle against a software implementation of doubles (64-bit floats) for ARB_gpu_shader_fp64 in the raging summer heat. This scholarly infographic roughly documents my progress:
Instead of dwelling on it, I’m going to go back to something very quick and less brain-damaging, namely ARB_timer_query. This extension provides functionality for performing time queries on the gpu, both for elapsed time and just getting regular time values.
In gallium, this is represented by PIPE_CAP_QUERY_TIME_ELAPSED
and PIPE_CAP_QUERY_TIMESTAMP
capabilities in a driver.
Extensions
Obviously there’s Vulkan extensions to go with this. VK_EXT_calibrated_timestamps is the one that’s needed, as this provides the functionality for retrieving time values directly from the gpu without going through a command buffer. There’s the usual copy-and-paste dance for this during driver init, which includes:
- checking for the extension name
- checking for the feature
- enabling the feature
- enabling the extension
- checking for the needed extensions-specific features (in this case,
VK_TIME_DOMAIN_DEVICE_EXT
)
Query Time
What followed, in this case, a lot of wrangling existing query code to remove asserts and finish some stubs where timestamp queries had been partially implemented. The most important part was this function:
static void
timestamp_to_nanoseconds(struct zink_screen *screen, uint64_t *timestamp)
{
/* The number of valid bits in a timestamp value is determined by
* the VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is written.
* - 17.5. Timestamp Queries
*/
*timestamp &= ((1ull << screen->timestampValidBits) - 1);;
/* The number of nanoseconds it takes for a timestamp value to be incremented by 1
* can be obtained from VkPhysicalDeviceLimits::timestampPeriod
* - 17.5. Timestamp Queries
*/
*timestamp *= screen->props.limits.timestampPeriod;
}
All time values from the gpu are returned in “ticks”, which are a unit decided by the underlying driver. Applications using Zink want nanoseconds, however, so this needs to be converted.
Side Query
But then also there’s the case where a timestamp can be retrieved directly, for example:
glGetInteger64v(GL_TIMESTAMP, &time);
In Zink and other gallium drivers, this uses either a struct pipe_screen
or struct pipe_context
hook:
/**
* Query a timestamp in nanoseconds. The returned value should match
* PIPE_QUERY_TIMESTAMP. This function returns immediately and doesn't
* wait for rendering to complete (which cannot be achieved with queries).
*/
uint64_t (*get_timestamp)(struct pipe_screen *);
/**
* Query a timestamp in nanoseconds. This is completely equivalent to
* pipe_screen::get_timestamp() but takes a context handle for drivers
* that require a context.
*/
uint64_t (*get_timestamp)(struct pipe_context *);
The current implementation looks like:
static uint64_t
zink_get_timestamp(struct pipe_context *pctx)
{
struct zink_screen *screen = zink_screen(pctx->screen);
uint64_t timestamp, deviation;
assert(screen->have_EXT_calibrated_timestamps);
VkCalibratedTimestampInfoEXT cti = {};
cti.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
cti.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
screen->vk_GetCalibratedTimestampsEXT(screen->dev, 1, &cti, ×tamp, &deviation);
timestamp_to_nanoseconds(screen, ×tamp);
return timestamp;
}
Using the VK_EXT_calibrated_timestamps
functionality from earlier, this is very simple and straightforward, unlike software implementations of ARB_gpu_shader_fp64
.
It’s a bit of a leap forward, but that’s another GL 3.3 feature done.