Files
LookingGlass/client/displayservers/Wayland/presentation.c
Geoffrey McRae fbe08d00ce [client] expose EGL stages in frame latency
Correlate received frames with the texture update actually consumed by
the renderer and retain damage until its token becomes renderable.

Split client latency into dispatch, import, queue, preparation, setup,
effects, desktop, composition, and swap stages in FRAME LATENCY.
Exclude diagnostic overlay work from composition and preserve client
wait time when producer timing is unavailable.

Publish joined samples through a bounded token queue, avoid sleeping
while producer timing is finalized, and correct Wayland photon units.
2026-08-03 23:53:33 +10:00

130 lines
3.5 KiB
C

/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "wayland.h"
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <wayland-client.h>
#include "common/debug.h"
#include "common/time.h"
struct FrameData
{
struct timespec sent;
};
static void presentationClockId(void * data,
struct wp_presentation * presentation, uint32_t clkId)
{
wlWm.clkId = clkId;
}
static const struct wp_presentation_listener presentationListener = {
.clock_id = presentationClockId,
};
static void presentationFeedbackSyncOutput(void * data,
struct wp_presentation_feedback * feedback, struct wl_output * output)
{
// Do nothing.
}
static void presentationFeedbackPresented(void * opaque,
struct wp_presentation_feedback * feedback, uint32_t tvSecHi, uint32_t tvSecLo,
uint32_t tvNsec, uint32_t refresh, uint32_t seqHi, uint32_t seqLo, uint32_t flags)
{
struct FrameData * data = opaque;
struct timespec present = {
.tv_sec = (uint64_t) tvSecHi << 32 | tvSecLo,
.tv_nsec = tvNsec,
};
struct timespec delta;
tsDiff(&delta, &present, &data->sent);
ringbuffer_push(wlWm.photonTimings,
&(float){ delta.tv_sec * 1e3f + delta.tv_nsec * 1e-6f });
free(data);
wp_presentation_feedback_destroy(feedback);
}
static void presentationFeedbackDiscarded(void * data,
struct wp_presentation_feedback * feedback)
{
free(data);
wp_presentation_feedback_destroy(feedback);
}
static const struct wp_presentation_feedback_listener presentationFeedbackListener = {
.sync_output = presentationFeedbackSyncOutput,
.presented = presentationFeedbackPresented,
.discarded = presentationFeedbackDiscarded,
};
bool waylandPresentationInit(void)
{
if (wlWm.presentation)
{
wlWm.photonTimings = ringbuffer_new(256, sizeof(float));
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings,
0.0f, 30.0f, NULL);
app_setGraphCompact(wlWm.photonGraph, true);
wp_presentation_add_listener(wlWm.presentation, &presentationListener, NULL);
}
return true;
}
void waylandPresentationFree(void)
{
if (!wlWm.presentation)
return;
wp_presentation_destroy(wlWm.presentation);
app_unregisterGraph(wlWm.photonGraph);
ringbuffer_free(&wlWm.photonTimings);
}
void waylandPresentationFrame(void)
{
if (!wlWm.presentation)
return;
struct FrameData * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("out of memory");
return;
}
if (clock_gettime(wlWm.clkId, &data->sent))
{
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
free(data);
return;
}
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);
wp_presentation_feedback_add_listener(feedback, &presentationFeedbackListener, data);
}