/** * Looking Glass * Copyright © 2017-2023 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 #include #include #include #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 + 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); 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); }