mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-01 12:46:12 +00:00
c/main: Hookup up new frame timing code
This commit is contained in:
parent
a40c2e7d50
commit
fac1ce4a5a
|
@ -111,116 +111,52 @@ compositor_end_session(struct xrt_compositor *xc)
|
|||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Utility for waiting (for rendering purposes) until the next vsync or a
|
||||
* specified time point, whichever comes first.
|
||||
*
|
||||
* Only for rendering - this will busy-wait if needed.
|
||||
*
|
||||
* @return true if we waited until the time indicated
|
||||
*
|
||||
* @todo In the future, this may differ between platforms since some have ways
|
||||
* to directly wait on a vsync.
|
||||
*/
|
||||
static bool
|
||||
compositor_wait_vsync_or_time(struct comp_compositor *c, int64_t wake_up_time)
|
||||
{
|
||||
|
||||
int64_t now_ns = os_monotonic_get_ns();
|
||||
/*!
|
||||
* @todo this is not accurate, but it serves the purpose of not letting
|
||||
* us sleep longer than the next vsync usually
|
||||
*/
|
||||
int64_t next_vsync = now_ns + c->settings.nominal_frame_interval_ns / 2;
|
||||
|
||||
bool ret = true;
|
||||
// Sleep until the sooner of vsync or our deadline.
|
||||
if (next_vsync < wake_up_time) {
|
||||
ret = false;
|
||||
wake_up_time = next_vsync;
|
||||
}
|
||||
int64_t wait_duration = wake_up_time - now_ns;
|
||||
if (wait_duration <= 0) {
|
||||
// Don't wait at all
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wait_duration > 1000000) {
|
||||
os_nanosleep(wait_duration - (wait_duration % 1000000));
|
||||
}
|
||||
// Busy-wait for fine-grained delays.
|
||||
while (now_ns < wake_up_time) {
|
||||
now_ns = os_monotonic_get_ns();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xrt_result_t
|
||||
compositor_wait_frame(struct xrt_compositor *xc,
|
||||
int64_t *out_frame_id,
|
||||
uint64_t *predicted_display_time,
|
||||
uint64_t *predicted_display_period)
|
||||
uint64_t *out_predicted_display_time_ns,
|
||||
uint64_t *out_predicted_display_period_ns)
|
||||
{
|
||||
struct comp_compositor *c = comp_compositor(xc);
|
||||
|
||||
// A little bit easier to read.
|
||||
int64_t interval_ns = (int64_t)c->settings.nominal_frame_interval_ns;
|
||||
uint64_t interval_ns = (int64_t)c->settings.nominal_frame_interval_ns;
|
||||
|
||||
int64_t now_ns = os_monotonic_get_ns();
|
||||
comp_target_update_timings(c->target);
|
||||
|
||||
COMP_SPEW(c, "WAIT_FRAME at %8.3fms", ns_to_ms(now_ns));
|
||||
assert(c->frame.waited.id == -1);
|
||||
|
||||
if (c->last_next_display_time == 0) {
|
||||
// First frame, we'll just assume we will display immediately
|
||||
int64_t frame_id = -1;
|
||||
uint64_t wake_up_time_ns = 0;
|
||||
uint64_t present_slop_ns = 0;
|
||||
uint64_t desired_present_time_ns = 0;
|
||||
uint64_t predicted_display_time_ns = 0;
|
||||
comp_target_calc_frame_timings(c->target, //
|
||||
&frame_id, //
|
||||
&wake_up_time_ns, //
|
||||
&desired_present_time_ns, //
|
||||
&present_slop_ns, //
|
||||
&predicted_display_time_ns); //
|
||||
|
||||
*predicted_display_period = interval_ns;
|
||||
c->last_next_display_time = now_ns + interval_ns;
|
||||
*predicted_display_time = c->last_next_display_time;
|
||||
*out_frame_id = c->last_next_display_time;
|
||||
c->frame.waited.id = frame_id;
|
||||
c->frame.waited.desired_present_time_ns = desired_present_time_ns;
|
||||
c->frame.waited.present_slop_ns = present_slop_ns;
|
||||
|
||||
COMP_SPEW(c,
|
||||
"WAIT_FRAME Finished at %8.3fms, predicted display "
|
||||
"time %8.3fms, period %8.3fms",
|
||||
ns_to_ms(now_ns), ns_to_ms(*predicted_display_time), ns_to_ms(*predicted_display_period));
|
||||
|
||||
return XRT_SUCCESS;
|
||||
uint64_t now_ns = os_monotonic_get_ns();
|
||||
if (now_ns < wake_up_time_ns) {
|
||||
os_nanosleep(wake_up_time_ns - now_ns);
|
||||
}
|
||||
|
||||
// First estimate of next display time.
|
||||
while (1) {
|
||||
now_ns = os_monotonic_get_ns();
|
||||
comp_target_mark_wake_up(c->target, frame_id, now_ns);
|
||||
|
||||
int64_t render_time_ns = c->expected_app_duration_ns + c->frame_overhead_ns;
|
||||
int64_t swap_interval = ceilf((float)render_time_ns / interval_ns);
|
||||
int64_t render_interval_ns = swap_interval * interval_ns;
|
||||
int64_t next_display_time = c->last_next_display_time + render_interval_ns;
|
||||
/*!
|
||||
* @todo adjust next_display_time to be a multiple of
|
||||
* interval_ns from c->last_frame_time_ns
|
||||
*/
|
||||
comp_target_update_timings(c->target);
|
||||
|
||||
while ((next_display_time - render_time_ns) < now_ns) {
|
||||
// we can't unblock in the past
|
||||
next_display_time += render_interval_ns;
|
||||
}
|
||||
if (compositor_wait_vsync_or_time(c, (next_display_time - render_time_ns))) {
|
||||
// True return val means we actually waited for the
|
||||
// deadline.
|
||||
*predicted_display_period = next_display_time - c->last_next_display_time;
|
||||
*predicted_display_time = next_display_time;
|
||||
*out_frame_id = c->last_next_display_time;
|
||||
*out_frame_id = frame_id;
|
||||
*out_predicted_display_time_ns = predicted_display_time_ns;
|
||||
*out_predicted_display_period_ns = interval_ns;
|
||||
|
||||
c->last_next_display_time = next_display_time;
|
||||
|
||||
COMP_SPEW(c,
|
||||
"WAIT_FRAME Finished at %8.3fms, predicted "
|
||||
"display time %8.3fms, period %8.3fms",
|
||||
ns_to_ms(now_ns), ns_to_ms(*predicted_display_time),
|
||||
ns_to_ms(*predicted_display_period));
|
||||
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
}
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
static xrt_result_t
|
||||
|
@ -1356,6 +1292,8 @@ xrt_gfx_provider_create_system(struct xrt_device *xdev, struct xrt_system_compos
|
|||
c->base.base.layer_commit = compositor_layer_commit;
|
||||
c->base.base.poll_events = compositor_poll_events;
|
||||
c->base.base.destroy = compositor_destroy;
|
||||
c->frame.waited.id = -1;
|
||||
c->frame.rendering.id = -1;
|
||||
c->system.create_native_compositor = system_compositor_create_native_compositor;
|
||||
c->system.destroy = system_compositor_destroy;
|
||||
c->xdev = xdev;
|
||||
|
|
|
@ -152,6 +152,16 @@ struct comp_shaders
|
|||
VkShaderModule layer_frag;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Tracking frame state.
|
||||
*/
|
||||
struct comp_frame
|
||||
{
|
||||
int64_t id;
|
||||
uint64_t desired_present_time_ns;
|
||||
uint64_t present_slop_ns;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Main compositor struct tying everything in the compositor together.
|
||||
*
|
||||
|
@ -221,6 +231,12 @@ struct comp_compositor
|
|||
struct u_var_timing *debug_var;
|
||||
} compositor_frame_times;
|
||||
|
||||
struct
|
||||
{
|
||||
struct comp_frame waited;
|
||||
struct comp_frame rendering;
|
||||
} frame;
|
||||
|
||||
/*!
|
||||
* @brief Estimated rendering time per frame of the application.
|
||||
*
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "xrt/xrt_compositor.h"
|
||||
|
||||
#include "os/os_time.h"
|
||||
|
||||
#include "math/m_space.h"
|
||||
|
||||
#include "util/u_misc.h"
|
||||
|
@ -93,7 +95,7 @@ static void
|
|||
renderer_acquire_swapchain_image(struct comp_renderer *r);
|
||||
|
||||
static void
|
||||
renderer_present_swapchain_image(struct comp_renderer *r);
|
||||
renderer_present_swapchain_image(struct comp_renderer *r, uint64_t desired_present_time_ns, uint64_t present_slop_ns);
|
||||
|
||||
static void
|
||||
renderer_destroy(struct comp_renderer *r);
|
||||
|
@ -622,14 +624,37 @@ comp_renderer_set_equirect2_layer(struct comp_renderer *r,
|
|||
void
|
||||
comp_renderer_draw(struct comp_renderer *r)
|
||||
{
|
||||
struct comp_target *ct = r->c->target;
|
||||
struct comp_compositor *c = r->c;
|
||||
|
||||
assert(c->frame.rendering.id == -1);
|
||||
|
||||
c->frame.rendering = c->frame.waited;
|
||||
c->frame.waited.id = -1;
|
||||
|
||||
comp_target_mark_begin(ct, c->frame.rendering.id, os_monotonic_get_ns());
|
||||
|
||||
comp_target_flush(ct);
|
||||
|
||||
comp_target_update_timings(ct);
|
||||
|
||||
renderer_acquire_swapchain_image(r);
|
||||
|
||||
comp_target_update_timings(ct);
|
||||
|
||||
comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns());
|
||||
|
||||
renderer_get_view_projection(r);
|
||||
comp_layer_renderer_draw(r->lr);
|
||||
|
||||
comp_target_flush(r->c->target);
|
||||
comp_target_update_timings(ct);
|
||||
|
||||
renderer_acquire_swapchain_image(r);
|
||||
renderer_submit_queue(r);
|
||||
renderer_present_swapchain_image(r);
|
||||
renderer_present_swapchain_image(r, c->frame.rendering.desired_present_time_ns,
|
||||
c->frame.rendering.present_slop_ns);
|
||||
|
||||
// Clear the frame.
|
||||
c->frame.rendering.id = -1;
|
||||
|
||||
/*
|
||||
* This fixes a lot of validation issues as it makes sure that the
|
||||
|
@ -639,6 +664,8 @@ comp_renderer_draw(struct comp_renderer *r)
|
|||
* This is done after a swap so isn't time critical.
|
||||
*/
|
||||
renderer_wait_gpu_idle(r);
|
||||
|
||||
comp_target_update_timings(ct);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -743,11 +770,12 @@ renderer_acquire_swapchain_image(struct comp_renderer *r)
|
|||
}
|
||||
|
||||
static void
|
||||
renderer_present_swapchain_image(struct comp_renderer *r)
|
||||
renderer_present_swapchain_image(struct comp_renderer *r, uint64_t desired_present_time_ns, uint64_t present_slop_ns)
|
||||
{
|
||||
VkResult ret;
|
||||
|
||||
ret = comp_target_present(r->c->target, r->queue, r->current_buffer, r->semaphores.render_complete);
|
||||
ret = comp_target_present(r->c->target, r->queue, r->current_buffer, r->semaphores.render_complete,
|
||||
desired_present_time_ns, present_slop_ns);
|
||||
if (ret == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
renderer_resize(r);
|
||||
return;
|
||||
|
|
|
@ -20,6 +20,16 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
* For marking timepoints on a frame's lifetime, not a async event.
|
||||
*/
|
||||
enum comp_target_timing_point
|
||||
{
|
||||
COMP_TARGET_TIMING_POINT_WAKE_UP, //<! Woke up after sleeping in wait frame.
|
||||
COMP_TARGET_TIMING_POINT_BEGIN, //<! Began CPU side work for GPU.
|
||||
COMP_TARGET_TIMING_POINT_SUBMIT, //<! Submitted work to the GPU.
|
||||
};
|
||||
|
||||
/*!
|
||||
* Image and view pair for @ref comp_target.
|
||||
*
|
||||
|
@ -60,6 +70,13 @@ struct comp_target
|
|||
//! Transformation of the current surface, required for pre-rotation
|
||||
VkSurfaceTransformFlagBitsKHR surface_transform;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Vulkan functions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Do any initialization that is required to happen before Vulkan has
|
||||
* been loaded.
|
||||
|
@ -91,13 +108,59 @@ struct comp_target
|
|||
/*!
|
||||
* Present the image at index to the screen.
|
||||
*/
|
||||
VkResult (*present)(struct comp_target *ct, VkQueue queue, uint32_t index, VkSemaphore semaphore);
|
||||
VkResult (*present)(struct comp_target *ct,
|
||||
VkQueue queue,
|
||||
uint32_t index,
|
||||
VkSemaphore semaphore,
|
||||
uint64_t desired_present_time_ns,
|
||||
uint64_t present_slop_ns);
|
||||
|
||||
/*!
|
||||
* Flush any WSI state before rendering.
|
||||
*/
|
||||
void (*flush)(struct comp_target *ct);
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Timing functions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Predict when the next frame should be started and when it will be
|
||||
* turned into photons by the hardware.
|
||||
*/
|
||||
void (*calc_frame_timings)(struct comp_target *ct,
|
||||
int64_t *out_frame_id,
|
||||
uint64_t *out_wake_up_time_ns,
|
||||
uint64_t *out_desired_present_time_ns,
|
||||
uint64_t *out_present_slop_ns,
|
||||
uint64_t *out_predicted_display_time_ns);
|
||||
|
||||
/*!
|
||||
* The compositor tells the target a timing information about a single
|
||||
* timing point on the frames lifecycle.
|
||||
*/
|
||||
void (*mark_timing_point)(struct comp_target *ct,
|
||||
enum comp_target_timing_point point,
|
||||
int64_t frame_id,
|
||||
uint64_t when_ns);
|
||||
|
||||
/*!
|
||||
* Update timing information for this target, this function should be
|
||||
* lightweight and is called multiple times during a frame to make sure
|
||||
* that we get the timing data as soon as possible.
|
||||
*/
|
||||
VkResult (*update_timings)(struct comp_target *ct);
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Misc functions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* If the target can show a title (like a window) set the title.
|
||||
*/
|
||||
|
@ -170,10 +233,21 @@ comp_target_acquire(struct comp_target *ct, VkSemaphore semaphore, uint32_t *out
|
|||
* @ingroup comp_main
|
||||
*/
|
||||
static inline VkResult
|
||||
comp_target_present(struct comp_target *ct, VkQueue queue, uint32_t index, VkSemaphore semaphore)
|
||||
comp_target_present(struct comp_target *ct,
|
||||
VkQueue queue,
|
||||
uint32_t index,
|
||||
VkSemaphore semaphore,
|
||||
uint64_t desired_present_time_ns,
|
||||
uint64_t present_slop_ns)
|
||||
|
||||
{
|
||||
return ct->present(ct, queue, index, semaphore);
|
||||
return ct->present( //
|
||||
ct, //
|
||||
queue, //
|
||||
index, //
|
||||
semaphore, //
|
||||
desired_present_time_ns, //
|
||||
present_slop_ns); //
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -188,6 +262,80 @@ comp_target_flush(struct comp_target *ct)
|
|||
ct->flush(ct);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @copydoc comp_target::calc_frame_timings
|
||||
*
|
||||
* @public @memberof comp_target
|
||||
* @ingroup comp_main
|
||||
*/
|
||||
static inline void
|
||||
comp_target_calc_frame_timings(struct comp_target *ct,
|
||||
int64_t *out_frame_id,
|
||||
uint64_t *out_wake_up_time_ns,
|
||||
uint64_t *out_desired_present_time_ns,
|
||||
uint64_t *out_present_slop_ns,
|
||||
uint64_t *out_predicted_display_time_ns)
|
||||
{
|
||||
ct->calc_frame_timings( //
|
||||
ct, //
|
||||
out_frame_id, //
|
||||
out_wake_up_time_ns, //
|
||||
out_desired_present_time_ns, //
|
||||
out_present_slop_ns, //
|
||||
out_predicted_display_time_ns); //
|
||||
}
|
||||
|
||||
/*!
|
||||
* Quick helper for marking wake up.
|
||||
* @copydoc comp_target::mark_timing_point
|
||||
*
|
||||
* @public @memberof comp_target
|
||||
* @ingroup comp_main
|
||||
*/
|
||||
static inline void
|
||||
comp_target_mark_wake_up(struct comp_target *ct, int64_t frame_id, uint64_t when_woke_ns)
|
||||
{
|
||||
ct->mark_timing_point(ct, COMP_TARGET_TIMING_POINT_WAKE_UP, frame_id, when_woke_ns);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Quick helper for marking begin.
|
||||
* @copydoc comp_target::mark_timing_point
|
||||
*
|
||||
* @public @memberof comp_target
|
||||
* @ingroup comp_main
|
||||
*/
|
||||
static inline void
|
||||
comp_target_mark_begin(struct comp_target *ct, int64_t frame_id, uint64_t when_began_ns)
|
||||
{
|
||||
ct->mark_timing_point(ct, COMP_TARGET_TIMING_POINT_BEGIN, frame_id, when_began_ns);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Quick helper for marking submit.
|
||||
* @copydoc comp_target::mark_timing_point
|
||||
*
|
||||
* @public @memberof comp_target
|
||||
* @ingroup comp_main
|
||||
*/
|
||||
static inline void
|
||||
comp_target_mark_submit(struct comp_target *ct, int64_t frame_id, uint64_t when_submitted_ns)
|
||||
{
|
||||
ct->mark_timing_point(ct, COMP_TARGET_TIMING_POINT_SUBMIT, frame_id, when_submitted_ns);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @copydoc comp_target::update_timings
|
||||
*
|
||||
* @public @memberof comp_target
|
||||
* @ingroup comp_main
|
||||
*/
|
||||
static inline VkResult
|
||||
comp_target_update_timings(struct comp_target *ct)
|
||||
{
|
||||
return ct->update_timings(ct);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @copydoc comp_target::set_title
|
||||
*
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
* @ingroup comp_main
|
||||
*/
|
||||
|
||||
#include "xrt/xrt_config_os.h"
|
||||
|
||||
#include "util/u_misc.h"
|
||||
#include "util/u_timing.h"
|
||||
|
||||
#include "main/comp_compositor.h"
|
||||
#include "main/comp_target_swapchain.h"
|
||||
|
@ -17,6 +20,7 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
/*
|
||||
|
@ -91,6 +95,17 @@ comp_target_swapchain_create_images(struct comp_target *ct,
|
|||
VkBool32 supported;
|
||||
VkResult ret;
|
||||
|
||||
#ifndef XRT_OS_ANDROID
|
||||
if (cts->uft == NULL && vk->has_GOOGLE_display_timing) {
|
||||
u_frame_timing_display_timing_create(ct->c->settings.nominal_frame_interval_ns, &cts->uft);
|
||||
} else if (cts->uft == NULL) {
|
||||
u_frame_timing_fake_create(ct->c->settings.nominal_frame_interval_ns, &cts->uft);
|
||||
}
|
||||
#else
|
||||
COMP_INFO(ct->c, "Always using the fake timing code on Android.");
|
||||
u_frame_timing_fake_create(ct->c->settings.nominal_frame_interval_ns, &cts->uft);
|
||||
#endif
|
||||
|
||||
// Free old image views.
|
||||
comp_target_swapchain_destroy_image_views(cts);
|
||||
|
||||
|
@ -250,13 +265,33 @@ comp_target_swapchain_acquire_next_image(struct comp_target *ct, VkSemaphore sem
|
|||
}
|
||||
|
||||
static VkResult
|
||||
comp_target_swapchain_present(struct comp_target *ct, VkQueue queue, uint32_t index, VkSemaphore semaphore)
|
||||
comp_target_swapchain_present(struct comp_target *ct,
|
||||
VkQueue queue,
|
||||
uint32_t index,
|
||||
VkSemaphore semaphore,
|
||||
uint64_t desired_present_time_ns,
|
||||
uint64_t present_slop_ns)
|
||||
{
|
||||
struct comp_target_swapchain *cts = (struct comp_target_swapchain *)ct;
|
||||
struct vk_bundle *vk = get_vk(cts);
|
||||
|
||||
assert(cts->current_frame_id >= 0);
|
||||
assert(cts->current_frame_id <= UINT32_MAX);
|
||||
|
||||
VkPresentTimeGOOGLE times = {
|
||||
.presentID = (uint32_t)cts->current_frame_id,
|
||||
.desiredPresentTime = desired_present_time_ns - present_slop_ns,
|
||||
};
|
||||
|
||||
VkPresentTimesInfoGOOGLE timings = {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
|
||||
.swapchainCount = 1,
|
||||
.pTimes = ×,
|
||||
};
|
||||
|
||||
VkPresentInfoKHR presentInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.pNext = vk->has_GOOGLE_display_timing ? &timings : NULL,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &semaphore,
|
||||
.swapchainCount = 1,
|
||||
|
@ -467,6 +502,116 @@ comp_target_swapchain_create_image_views(struct comp_target_swapchain *cts)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Timing functions.
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
comp_target_swapchain_calc_frame_timings(struct comp_target *ct,
|
||||
int64_t *out_frame_id,
|
||||
uint64_t *out_wake_up_time_ns,
|
||||
uint64_t *out_desired_present_time_ns,
|
||||
uint64_t *out_present_slop_ns,
|
||||
uint64_t *out_predicted_display_time_ns)
|
||||
{
|
||||
struct comp_target_swapchain *cts = (struct comp_target_swapchain *)ct;
|
||||
|
||||
int64_t frame_id = -1;
|
||||
uint64_t wake_up_time_ns = 0;
|
||||
uint64_t desired_present_time_ns = 0;
|
||||
uint64_t present_slop_ns = 0;
|
||||
uint64_t predicted_display_time_ns = 0;
|
||||
uint64_t predicted_display_period_ns = 0;
|
||||
uint64_t min_display_period_ns = 0;
|
||||
|
||||
u_frame_timing_predict(cts->uft, //
|
||||
&frame_id, //
|
||||
&wake_up_time_ns, //
|
||||
&desired_present_time_ns, //
|
||||
&present_slop_ns, //
|
||||
&predicted_display_time_ns, //
|
||||
&predicted_display_period_ns, //
|
||||
&min_display_period_ns); //
|
||||
|
||||
cts->current_frame_id = frame_id;
|
||||
|
||||
*out_frame_id = frame_id;
|
||||
*out_wake_up_time_ns = wake_up_time_ns;
|
||||
*out_desired_present_time_ns = desired_present_time_ns;
|
||||
*out_predicted_display_time_ns = predicted_display_time_ns;
|
||||
*out_present_slop_ns = present_slop_ns;
|
||||
}
|
||||
|
||||
static void
|
||||
comp_target_swapchain_mark_timing_point(struct comp_target *ct,
|
||||
enum comp_target_timing_point point,
|
||||
int64_t frame_id,
|
||||
uint64_t when_ns)
|
||||
{
|
||||
struct comp_target_swapchain *cts = (struct comp_target_swapchain *)ct;
|
||||
assert(frame_id == cts->current_frame_id);
|
||||
|
||||
switch (point) {
|
||||
case COMP_TARGET_TIMING_POINT_WAKE_UP:
|
||||
u_frame_timing_mark_point(cts->uft, U_TIMING_POINT_WAKE_UP, cts->current_frame_id, when_ns);
|
||||
break;
|
||||
case COMP_TARGET_TIMING_POINT_BEGIN:
|
||||
u_frame_timing_mark_point(cts->uft, U_TIMING_POINT_BEGIN, cts->current_frame_id, when_ns);
|
||||
break;
|
||||
case COMP_TARGET_TIMING_POINT_SUBMIT:
|
||||
u_frame_timing_mark_point(cts->uft, U_TIMING_POINT_SUBMIT, cts->current_frame_id, when_ns);
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static VkResult
|
||||
comp_target_swapchain_update_timings(struct comp_target *ct)
|
||||
{
|
||||
struct comp_target_swapchain *cts = (struct comp_target_swapchain *)ct;
|
||||
struct comp_compositor *c = ct->c;
|
||||
struct vk_bundle *vk = &c->vk;
|
||||
|
||||
if (!vk->has_GOOGLE_display_timing) {
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
if (cts->swapchain.handle == VK_NULL_HANDLE) {
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
c->vk.vkGetPastPresentationTimingGOOGLE( //
|
||||
vk->device, //
|
||||
cts->swapchain.handle, //
|
||||
&count, //
|
||||
NULL); //
|
||||
if (count <= 0) {
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VkPastPresentationTimingGOOGLE timings[count];
|
||||
c->vk.vkGetPastPresentationTimingGOOGLE( //
|
||||
vk->device, //
|
||||
cts->swapchain.handle, //
|
||||
&count, //
|
||||
timings); //
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
u_frame_timing_info(cts->uft, //
|
||||
timings[i].presentID, //
|
||||
timings[i].desiredPresentTime, //
|
||||
timings[i].actualPresentTime, //
|
||||
timings[i].earliestPresentTime, //
|
||||
timings[i].presentMargin); //
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* 'Exported' functions.
|
||||
|
@ -495,6 +640,8 @@ comp_target_swapchain_cleanup(struct comp_target_swapchain *cts)
|
|||
NULL); //
|
||||
cts->swapchain.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
u_frame_timing_destroy(&cts->uft);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -503,4 +650,7 @@ comp_target_swapchain_init_set_fnptrs(struct comp_target_swapchain *cts)
|
|||
cts->base.create_images = comp_target_swapchain_create_images;
|
||||
cts->base.acquire = comp_target_swapchain_acquire_next_image;
|
||||
cts->base.present = comp_target_swapchain_present;
|
||||
cts->base.calc_frame_timings = comp_target_swapchain_calc_frame_timings;
|
||||
cts->base.mark_timing_point = comp_target_swapchain_mark_timing_point;
|
||||
cts->base.update_timings = comp_target_swapchain_update_timings;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ extern "C" {
|
|||
*
|
||||
*/
|
||||
|
||||
struct u_frame_timing;
|
||||
|
||||
/*!
|
||||
* Wraps and manage VkSwapchainKHR and VkSurfaceKHR, used by @ref comp code.
|
||||
*
|
||||
|
@ -36,6 +38,12 @@ struct comp_target_swapchain
|
|||
//! Base target.
|
||||
struct comp_target base;
|
||||
|
||||
//! Frame timing tracker.
|
||||
struct u_frame_timing *uft;
|
||||
|
||||
//! Also works as a frame index.
|
||||
int64_t current_frame_id;
|
||||
|
||||
struct
|
||||
{
|
||||
VkSwapchainKHR handle;
|
||||
|
|
Loading…
Reference in a new issue