mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-25 03:58:19 +02:00
Silence warning about result of asprintf() calls not being used. Seen with gcc 11.4 on Ubuntu 22.04 Signed-off-by: Brian Paul <brian.paul@broadcom.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/38775>
437 lines
13 KiB
C
437 lines
13 KiB
C
/*
|
|
* Copyright © 2022 Red Hat, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
* that the name of the copyright holders not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no representations
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
* OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <poll.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "presentation-time-client-protocol.h"
|
|
|
|
#include "util/list.h"
|
|
#include <util/os_time.h>
|
|
#include "util/perf/cpu_trace.h"
|
|
|
|
#include "loader_wayland_helper.h"
|
|
|
|
struct loader_wayland_presentation_feedback_data {
|
|
struct loader_wayland_presentation *presentation;
|
|
bool tracing;
|
|
struct mesa_trace_flow flow;
|
|
/* We store copies of name and id, since buffers can be
|
|
* destroyed before feedback is serviced */
|
|
char *buffer_name;
|
|
uint32_t buffer_id;
|
|
void *callback_data;
|
|
struct wp_presentation_feedback *feedback;
|
|
struct list_head link;
|
|
};
|
|
|
|
#ifndef HAVE_WL_DISPATCH_QUEUE_TIMEOUT
|
|
static int
|
|
wl_display_poll(struct wl_display *display,
|
|
short int events,
|
|
const struct timespec *timeout)
|
|
{
|
|
int ret;
|
|
struct pollfd pfd[1];
|
|
struct timespec now;
|
|
struct timespec deadline = {0};
|
|
struct timespec result;
|
|
struct timespec *remaining_timeout = NULL;
|
|
|
|
if (timeout) {
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
timespec_add(&deadline, &now, timeout);
|
|
}
|
|
|
|
pfd[0].fd = wl_display_get_fd(display);
|
|
pfd[0].events = events;
|
|
do {
|
|
if (timeout) {
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
timespec_sub_saturate(&result, &deadline, &now);
|
|
remaining_timeout = &result;
|
|
}
|
|
ret = ppoll(pfd, 1, remaining_timeout, NULL);
|
|
} while (ret == -1 && errno == EINTR);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
wl_display_dispatch_queue_timeout(struct wl_display *display,
|
|
struct wl_event_queue *queue,
|
|
const struct timespec *timeout)
|
|
{
|
|
int ret;
|
|
struct timespec now;
|
|
struct timespec deadline = {0};
|
|
struct timespec result;
|
|
struct timespec *remaining_timeout = NULL;
|
|
|
|
if (timeout) {
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
timespec_add(&deadline, &now, timeout);
|
|
}
|
|
|
|
if (wl_display_prepare_read_queue(display, queue) == -1)
|
|
return wl_display_dispatch_queue_pending(display, queue);
|
|
|
|
while (true) {
|
|
ret = wl_display_flush(display);
|
|
|
|
if (ret != -1 || errno != EAGAIN)
|
|
break;
|
|
|
|
if (timeout) {
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
timespec_sub_saturate(&result, &deadline, &now);
|
|
remaining_timeout = &result;
|
|
}
|
|
ret = wl_display_poll(display, POLLOUT, remaining_timeout);
|
|
|
|
if (ret <= 0) {
|
|
wl_display_cancel_read(display);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Don't stop if flushing hits an EPIPE; continue so we can read any
|
|
* protocol error that may have triggered it. */
|
|
if (ret < 0 && errno != EPIPE) {
|
|
wl_display_cancel_read(display);
|
|
return -1;
|
|
}
|
|
|
|
while (true) {
|
|
if (timeout) {
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
timespec_sub_saturate(&result, &deadline, &now);
|
|
remaining_timeout = &result;
|
|
}
|
|
|
|
ret = wl_display_poll(display, POLLIN, remaining_timeout);
|
|
if (ret <= 0) {
|
|
wl_display_cancel_read(display);
|
|
break;
|
|
}
|
|
|
|
ret = wl_display_read_events(display);
|
|
if (ret == -1)
|
|
break;
|
|
|
|
ret = wl_display_dispatch_queue_pending(display, queue);
|
|
if (ret != 0)
|
|
break;
|
|
|
|
/* wl_display_dispatch_queue_pending can return 0 if we ended up reading
|
|
* from WL fd, but there was no complete event to dispatch yet.
|
|
* Try reading again. */
|
|
if (wl_display_prepare_read_queue(display, queue) == -1)
|
|
return wl_display_dispatch_queue_pending(display, queue);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_WL_CREATE_QUEUE_WITH_NAME
|
|
struct wl_event_queue *
|
|
wl_display_create_queue_with_name(struct wl_display *display, const char *name)
|
|
{
|
|
return wl_display_create_queue(display);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
loader_wayland_dispatch(struct wl_display *wl_display,
|
|
struct wl_event_queue *queue,
|
|
struct timespec *end_time)
|
|
{
|
|
struct timespec current_time;
|
|
struct timespec remaining_timeout;
|
|
|
|
MESA_TRACE_FUNC();
|
|
|
|
if (!end_time)
|
|
return wl_display_dispatch_queue(wl_display, queue);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
|
timespec_sub_saturate(&remaining_timeout, end_time, ¤t_time);
|
|
return wl_display_dispatch_queue_timeout(wl_display,
|
|
queue,
|
|
&remaining_timeout);
|
|
}
|
|
|
|
static char *
|
|
stringify_wayland_id(uint32_t id)
|
|
{
|
|
char *out;
|
|
|
|
if (asprintf(&out, "wl%d", id) < 0)
|
|
return strdup("Wayland buffer");
|
|
|
|
return out;
|
|
}
|
|
|
|
void
|
|
loader_wayland_wrap_buffer(struct loader_wayland_buffer *lwb,
|
|
struct wl_buffer *wl_buffer)
|
|
{
|
|
lwb->buffer = wl_buffer;
|
|
lwb->id = wl_proxy_get_id((struct wl_proxy *)wl_buffer);
|
|
lwb->flow.id = 0;
|
|
lwb->name = stringify_wayland_id(lwb->id);
|
|
}
|
|
|
|
void
|
|
loader_wayland_buffer_destroy(struct loader_wayland_buffer *lwb)
|
|
{
|
|
wl_buffer_destroy(lwb->buffer);
|
|
lwb->buffer = NULL;
|
|
lwb->id = 0;
|
|
lwb->flow.id = 0;
|
|
free(lwb->name);
|
|
lwb->name = NULL;
|
|
}
|
|
|
|
void
|
|
loader_wayland_buffer_set_flow(struct loader_wayland_buffer *lwb,
|
|
struct mesa_trace_flow *flow)
|
|
{
|
|
lwb->flow = *flow;
|
|
}
|
|
|
|
bool
|
|
loader_wayland_wrap_surface(struct loader_wayland_surface *lws,
|
|
struct wl_surface *wl_surface,
|
|
struct wl_event_queue *queue)
|
|
{
|
|
char *track_name;
|
|
|
|
lws->surface = wl_surface;
|
|
lws->wrapper = wl_proxy_create_wrapper(wl_surface);
|
|
if (!lws->wrapper)
|
|
return false;
|
|
|
|
lws->id = wl_proxy_get_id((struct wl_proxy *)wl_surface);
|
|
wl_proxy_set_queue((struct wl_proxy *)lws->wrapper, queue);
|
|
|
|
if (asprintf(&track_name, "wl%d presentation", lws->id) < 0) {
|
|
return false;
|
|
}
|
|
|
|
lws->analytics.presentation_track_id = util_perfetto_new_track(track_name);
|
|
free(track_name);
|
|
|
|
if (asprintf(&lws->analytics.latency_str, "wl%d latency", lws->id) < 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
loader_wayland_surface_destroy(struct loader_wayland_surface *lws)
|
|
{
|
|
if (!lws->wrapper)
|
|
return;
|
|
|
|
wl_proxy_wrapper_destroy(lws->wrapper);
|
|
lws->surface = NULL;
|
|
lws->id = 0;
|
|
free(lws->analytics.latency_str);
|
|
lws->analytics.latency_str = NULL;
|
|
}
|
|
|
|
static void
|
|
loader_wayland_trace_present(struct loader_wayland_presentation_feedback_data *fd,
|
|
uint64_t presentation_time)
|
|
{
|
|
struct loader_wayland_surface *lws;
|
|
UNUSED clockid_t clock;
|
|
|
|
if (!fd->tracing || !util_perfetto_is_tracing_enabled())
|
|
return;
|
|
|
|
lws = fd->presentation->wayland_surface;
|
|
clock = fd->presentation->clock_id;
|
|
|
|
MESA_TRACE_SET_COUNTER(lws->analytics.latency_str,
|
|
(presentation_time - fd->flow.start_time) / 1000000.0);
|
|
|
|
/* Close the previous image display interval first, if there is one. */
|
|
if (lws->analytics.presenting) {
|
|
MESA_TRACE_TIMESTAMP_END(fd->buffer_name,
|
|
lws->analytics.presentation_track_id,
|
|
clock, presentation_time);
|
|
}
|
|
|
|
lws->analytics.presenting = fd->buffer_id;
|
|
|
|
MESA_TRACE_TIMESTAMP_BEGIN(fd->buffer_name,
|
|
lws->analytics.presentation_track_id,
|
|
fd->flow.id,
|
|
clock, presentation_time);
|
|
}
|
|
|
|
static void
|
|
presentation_handle_sync_output(void *data,
|
|
struct wp_presentation_feedback *feedback,
|
|
struct wl_output *output)
|
|
{
|
|
}
|
|
|
|
static void
|
|
feedback_fini(struct loader_wayland_presentation_feedback_data *fd)
|
|
{
|
|
if (fd->tracing)
|
|
free(fd->buffer_name);
|
|
|
|
wp_presentation_feedback_destroy(fd->feedback);
|
|
list_del(&fd->link);
|
|
free(fd);
|
|
}
|
|
|
|
static void
|
|
presentation_handle_presented(void *data,
|
|
struct wp_presentation_feedback *feedback,
|
|
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
|
uint32_t tv_nsec, uint32_t refresh,
|
|
uint32_t seq_hi, uint32_t seq_lo,
|
|
uint32_t flags)
|
|
{
|
|
struct loader_wayland_presentation_feedback_data *fd = data;
|
|
struct loader_wayland_presentation *pres = fd->presentation;
|
|
struct timespec presentation_ts;
|
|
uint64_t presentation_time;
|
|
|
|
MESA_TRACE_FUNC_FLOW(&fd->flow);
|
|
|
|
presentation_ts.tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
|
|
presentation_ts.tv_nsec = tv_nsec;
|
|
presentation_time = timespec_to_nsec(&presentation_ts);
|
|
|
|
loader_wayland_trace_present(fd, presentation_time);
|
|
if (pres->presented_callback) {
|
|
pres->presented_callback(fd->callback_data, presentation_time, refresh);
|
|
}
|
|
|
|
feedback_fini(fd);
|
|
}
|
|
|
|
static void
|
|
presentation_handle_discarded(void *data,
|
|
struct wp_presentation_feedback *feedback)
|
|
{
|
|
struct loader_wayland_presentation_feedback_data *fd = data;
|
|
struct loader_wayland_presentation *pres = fd->presentation;
|
|
|
|
MESA_TRACE_FUNC_FLOW(&fd->flow);
|
|
|
|
if (pres->discarded_callback)
|
|
pres->discarded_callback(fd->callback_data);
|
|
|
|
feedback_fini(fd);
|
|
}
|
|
|
|
static const struct wp_presentation_feedback_listener
|
|
pres_feedback_listener = {
|
|
presentation_handle_sync_output,
|
|
presentation_handle_presented,
|
|
presentation_handle_discarded,
|
|
};
|
|
|
|
void
|
|
loader_wayland_wrap_presentation(struct loader_wayland_presentation *lpf,
|
|
struct wp_presentation *wp_presentation,
|
|
struct wl_event_queue *queue,
|
|
clockid_t presentation_clock_id,
|
|
struct loader_wayland_surface *lws,
|
|
void (*presented_callback)(void *data, uint64_t pres_time, uint32_t refresh),
|
|
void (*discarded_callback)(void *data),
|
|
void (*teardown_callback)(void *data))
|
|
|
|
{
|
|
lpf->presentation = wl_proxy_create_wrapper(wp_presentation);
|
|
wl_proxy_set_queue((void *)lpf->presentation, queue);
|
|
lpf->clock_id = presentation_clock_id;
|
|
lpf->wayland_surface = lws;
|
|
lpf->presented_callback = presented_callback;
|
|
lpf->discarded_callback = discarded_callback;
|
|
lpf->teardown_callback = teardown_callback;
|
|
list_inithead(&lpf->outstanding_list);
|
|
}
|
|
|
|
void
|
|
loader_wayland_presentation_destroy(struct loader_wayland_presentation *pres)
|
|
{
|
|
struct loader_wayland_presentation_feedback_data *fb, *tmp;
|
|
|
|
if (pres->presentation == NULL)
|
|
return;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(fb, tmp, &pres->outstanding_list, link) {
|
|
if (pres->teardown_callback)
|
|
pres->teardown_callback(fb->callback_data);
|
|
|
|
feedback_fini(fb);
|
|
}
|
|
wl_proxy_wrapper_destroy(pres->presentation);
|
|
pres->presentation = NULL;
|
|
}
|
|
|
|
void
|
|
loader_wayland_presentation_feedback(struct loader_wayland_presentation *pres,
|
|
struct loader_wayland_buffer *lwb,
|
|
void *callback_data)
|
|
{
|
|
struct loader_wayland_presentation_feedback_data *fd;
|
|
bool tracing = false;
|
|
|
|
if (!pres->presentation)
|
|
return;
|
|
|
|
tracing = util_perfetto_is_tracing_enabled();
|
|
if (!pres->presented_callback &&
|
|
!pres->discarded_callback &&
|
|
!tracing)
|
|
return;
|
|
|
|
fd = calloc(1, sizeof *fd);
|
|
|
|
fd->presentation = pres;
|
|
fd->tracing = tracing;
|
|
if (tracing) {
|
|
fd->buffer_name = strdup(lwb->name);
|
|
fd->buffer_id = lwb->id;
|
|
fd->flow = lwb->flow;
|
|
}
|
|
fd->callback_data = callback_data;
|
|
fd->feedback = wp_presentation_feedback(pres->presentation,
|
|
pres->wayland_surface->wrapper);
|
|
wp_presentation_feedback_add_listener(fd->feedback,
|
|
&pres_feedback_listener,
|
|
fd);
|
|
list_add(&fd->link, &pres->outstanding_list);
|
|
}
|