mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-05-09 05:58:13 +02:00
ext-image-copy-capture: emit capture_duration_us event
The dmabuf path defers ready emission via wlr_drm_syncobj_timeline_waiter so the wlr_render_timer query is available without blocking, emitting HW_TIMER. When the renderer lacks timeline support, the dmabuf path falls back to a CPU bracket around submit emitted as SUBMISSION_ONLY. The SHM path brackets the synchronous copy and emits SW_TIMER. IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION bumped to 2. Signed-off-by: Greg Lamberson <greg@lamco.io>
This commit is contained in:
parent
57441ded02
commit
70f04717ba
2 changed files with 168 additions and 16 deletions
|
|
@ -10,11 +10,14 @@
|
|||
#define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H
|
||||
|
||||
#include <pixman.h>
|
||||
#include <stdint.h>
|
||||
#include <wayland-server-protocol.h>
|
||||
#include <wayland-protocols/ext-image-copy-capture-v1-enum.h>
|
||||
#include <wlr/render/drm_syncobj.h>
|
||||
#include <time.h>
|
||||
|
||||
struct wlr_renderer;
|
||||
struct wlr_render_timer;
|
||||
|
||||
struct wlr_ext_image_copy_capture_manager_v1 {
|
||||
struct wl_global *global;
|
||||
|
|
@ -58,6 +61,18 @@ struct wlr_ext_image_copy_capture_frame_v1 {
|
|||
|
||||
struct {
|
||||
struct wlr_ext_image_copy_capture_session_v1 *session;
|
||||
struct wlr_render_timer *timer;
|
||||
|
||||
struct wlr_drm_syncobj_timeline *signal_timeline;
|
||||
uint64_t signal_point;
|
||||
struct wlr_drm_syncobj_timeline_waiter waiter;
|
||||
bool waiter_active;
|
||||
|
||||
int64_t cpu_duration_ns;
|
||||
uint32_t cpu_duration_flags;
|
||||
|
||||
enum wl_output_transform deferred_transform;
|
||||
struct timespec deferred_presentation_time;
|
||||
} WLR_PRIVATE;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include "ext-image-copy-capture-v1-protocol.h"
|
||||
#include "render/pixel_format.h"
|
||||
|
||||
#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1
|
||||
#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 2
|
||||
|
||||
struct wlr_ext_image_copy_capture_cursor_session_v1 {
|
||||
struct wl_resource *resource;
|
||||
|
|
@ -71,6 +71,16 @@ static void frame_destroy(struct wlr_ext_image_copy_capture_frame_v1 *frame) {
|
|||
wl_resource_set_user_data(frame->resource, NULL);
|
||||
wlr_buffer_unlock(frame->buffer);
|
||||
pixman_region32_fini(&frame->buffer_damage);
|
||||
if (frame->waiter_active) {
|
||||
wlr_drm_syncobj_timeline_waiter_finish(&frame->waiter);
|
||||
frame->waiter_active = false;
|
||||
}
|
||||
if (frame->signal_timeline != NULL) {
|
||||
wlr_drm_syncobj_timeline_unref(frame->signal_timeline);
|
||||
}
|
||||
if (frame->timer != NULL) {
|
||||
wlr_render_timer_destroy(frame->timer);
|
||||
}
|
||||
if (frame->session->frame == frame) {
|
||||
frame->session->frame = NULL;
|
||||
}
|
||||
|
|
@ -81,11 +91,7 @@ static void frame_handle_resource_destroy(struct wl_resource *resource) {
|
|||
frame_destroy(wl_resource_get_user_data(resource));
|
||||
}
|
||||
|
||||
void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame,
|
||||
enum wl_output_transform transform,
|
||||
const struct timespec *presentation_time) {
|
||||
assert(frame->capturing);
|
||||
|
||||
static void frame_emit_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame) {
|
||||
int rects_len = 0;
|
||||
const pixman_box32_t *rects =
|
||||
pixman_region32_rectangles(&frame->session->damage, &rects_len);
|
||||
|
|
@ -97,24 +103,112 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture
|
|||
|
||||
pixman_region32_clear(&frame->session->damage);
|
||||
|
||||
uint64_t pres_time_sec = (uint64_t)presentation_time->tv_sec;
|
||||
ext_image_copy_capture_frame_v1_send_transform(frame->resource, transform);
|
||||
uint64_t pres_time_sec = (uint64_t)frame->deferred_presentation_time.tv_sec;
|
||||
ext_image_copy_capture_frame_v1_send_transform(frame->resource,
|
||||
frame->deferred_transform);
|
||||
ext_image_copy_capture_frame_v1_send_presentation_time(frame->resource,
|
||||
pres_time_sec >> 32, (uint32_t)pres_time_sec, presentation_time->tv_nsec);
|
||||
pres_time_sec >> 32, (uint32_t)pres_time_sec,
|
||||
frame->deferred_presentation_time.tv_nsec);
|
||||
|
||||
if (wl_resource_get_version(frame->resource) >=
|
||||
EXT_IMAGE_COPY_CAPTURE_FRAME_V1_CAPTURE_DURATION_US_SINCE_VERSION) {
|
||||
if (frame->timer != NULL) {
|
||||
int duration_ns = wlr_render_timer_get_duration_ns(frame->timer);
|
||||
if (duration_ns >= 0) {
|
||||
uint32_t duration_us = (uint32_t)(duration_ns / 1000);
|
||||
ext_image_copy_capture_frame_v1_send_capture_duration_us(frame->resource,
|
||||
duration_us,
|
||||
EXT_IMAGE_COPY_CAPTURE_FRAME_V1_CAPTURE_DURATION_FLAGS_HW_TIMER);
|
||||
}
|
||||
} else if (frame->cpu_duration_ns >= 0 && frame->cpu_duration_flags != 0) {
|
||||
uint32_t duration_us = (uint32_t)(frame->cpu_duration_ns / 1000);
|
||||
ext_image_copy_capture_frame_v1_send_capture_duration_us(frame->resource,
|
||||
duration_us, frame->cpu_duration_flags);
|
||||
}
|
||||
}
|
||||
|
||||
ext_image_copy_capture_frame_v1_send_ready(frame->resource);
|
||||
frame_destroy(frame);
|
||||
}
|
||||
|
||||
static bool copy_dmabuf(struct wlr_buffer *dst,
|
||||
struct wlr_buffer *src, struct wlr_renderer *renderer,
|
||||
const pixman_region32_t *clip) {
|
||||
static void frame_handle_capture_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) {
|
||||
struct wlr_ext_image_copy_capture_frame_v1 *frame =
|
||||
wl_container_of(waiter, frame, waiter);
|
||||
wlr_drm_syncobj_timeline_waiter_finish(&frame->waiter);
|
||||
frame->waiter_active = false;
|
||||
frame_emit_ready(frame);
|
||||
}
|
||||
|
||||
void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame,
|
||||
enum wl_output_transform transform,
|
||||
const struct timespec *presentation_time) {
|
||||
assert(frame->capturing);
|
||||
|
||||
frame->deferred_transform = transform;
|
||||
frame->deferred_presentation_time = *presentation_time;
|
||||
|
||||
if (frame->signal_timeline != NULL) {
|
||||
struct wl_client *client = wl_resource_get_client(frame->resource);
|
||||
struct wl_display *display = wl_client_get_display(client);
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop(display);
|
||||
|
||||
if (wlr_drm_syncobj_timeline_waiter_init(&frame->waiter,
|
||||
frame->signal_timeline, frame->signal_point,
|
||||
0, loop, frame_handle_capture_ready)) {
|
||||
frame->waiter_active = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Waiter init failed. The GPU work was submitted with signal_timeline
|
||||
// but we can't async-wait. Drop the timer to avoid a per-frame error
|
||||
// log on Vulkan or a synchronous stall on GLES2; emit without duration.
|
||||
if (frame->timer != NULL) {
|
||||
wlr_render_timer_destroy(frame->timer);
|
||||
frame->timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
frame_emit_ready(frame);
|
||||
}
|
||||
|
||||
static bool copy_dmabuf(struct wlr_ext_image_copy_capture_frame_v1 *frame,
|
||||
struct wlr_buffer *dst, struct wlr_buffer *src,
|
||||
struct wlr_renderer *renderer, const pixman_region32_t *clip) {
|
||||
struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src);
|
||||
if (texture == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct wlr_render_timer *timer = NULL;
|
||||
struct wlr_drm_syncobj_timeline *signal_timeline = NULL;
|
||||
struct timespec cpu_start;
|
||||
bool measure_cpu = false;
|
||||
|
||||
if (renderer->features.timeline) {
|
||||
int drm_fd = wlr_renderer_get_drm_fd(renderer);
|
||||
if (drm_fd >= 0) {
|
||||
signal_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
|
||||
}
|
||||
if (signal_timeline != NULL) {
|
||||
timer = wlr_render_timer_create(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_timeline == NULL) {
|
||||
// Bracket submit with a CPU monotonic clock and emit later with
|
||||
// SUBMISSION_ONLY. Avoids the Vulkan VK_NOT_READY error log and the
|
||||
// GLES2 stall in glGetQueryObjectui64vEXT(GL_QUERY_RESULT_EXT).
|
||||
measure_cpu = true;
|
||||
clock_gettime(CLOCK_MONOTONIC, &cpu_start);
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL);
|
||||
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst,
|
||||
&(struct wlr_buffer_pass_options){
|
||||
.timer = timer,
|
||||
.signal_timeline = signal_timeline,
|
||||
.signal_point = 1,
|
||||
});
|
||||
if (!pass) {
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -127,13 +221,43 @@ static bool copy_dmabuf(struct wlr_buffer *dst,
|
|||
|
||||
ok = wlr_render_pass_submit(pass);
|
||||
|
||||
if (ok && measure_cpu) {
|
||||
struct timespec cpu_end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &cpu_end);
|
||||
int64_t ns = (int64_t)(cpu_end.tv_sec - cpu_start.tv_sec) * 1000000000LL +
|
||||
((int64_t)cpu_end.tv_nsec - (int64_t)cpu_start.tv_nsec);
|
||||
if (ns >= 0) {
|
||||
frame->cpu_duration_ns = ns;
|
||||
frame->cpu_duration_flags =
|
||||
EXT_IMAGE_COPY_CAPTURE_FRAME_V1_CAPTURE_DURATION_FLAGS_SUBMISSION_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
wlr_texture_destroy(texture);
|
||||
if (ok) {
|
||||
frame->timer = timer;
|
||||
if (signal_timeline != NULL) {
|
||||
frame->signal_timeline = signal_timeline;
|
||||
frame->signal_point = 1;
|
||||
}
|
||||
} else {
|
||||
if (timer != NULL) {
|
||||
wlr_render_timer_destroy(timer);
|
||||
}
|
||||
if (signal_timeline != NULL) {
|
||||
wlr_drm_syncobj_timeline_unref(signal_timeline);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool copy_shm(void *data, uint32_t format, size_t stride,
|
||||
static bool copy_shm(struct wlr_ext_image_copy_capture_frame_v1 *frame,
|
||||
void *data, uint32_t format, size_t stride,
|
||||
struct wlr_buffer *src, struct wlr_renderer *renderer) {
|
||||
struct timespec cpu_start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &cpu_start);
|
||||
|
||||
// TODO: bypass renderer if source buffer supports data ptr access
|
||||
struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src);
|
||||
if (!texture) {
|
||||
|
|
@ -149,6 +273,18 @@ static bool copy_shm(void *data, uint32_t format, size_t stride,
|
|||
|
||||
wlr_texture_destroy(texture);
|
||||
|
||||
if (ok) {
|
||||
struct timespec cpu_end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &cpu_end);
|
||||
int64_t ns = (int64_t)(cpu_end.tv_sec - cpu_start.tv_sec) * 1000000000LL +
|
||||
((int64_t)cpu_end.tv_nsec - (int64_t)cpu_start.tv_nsec);
|
||||
if (ns >= 0) {
|
||||
frame->cpu_duration_ns = ns;
|
||||
frame->cpu_duration_flags =
|
||||
EXT_IMAGE_COPY_CAPTURE_FRAME_V1_CAPTURE_DURATION_FLAGS_SW_TIMER;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +310,7 @@ bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_c
|
|||
ok = false;
|
||||
failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS;
|
||||
} else {
|
||||
ok = copy_dmabuf(dst, src, renderer, &frame->buffer_damage);
|
||||
ok = copy_dmabuf(frame, dst, src, renderer, &frame->buffer_damage);
|
||||
}
|
||||
} else if (wlr_buffer_begin_data_ptr_access(dst,
|
||||
WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) {
|
||||
|
|
@ -182,7 +318,7 @@ bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_c
|
|||
ok = false;
|
||||
failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS;
|
||||
} else {
|
||||
ok = copy_shm(data, format, stride, src, renderer);
|
||||
ok = copy_shm(frame, data, format, stride, src, renderer);
|
||||
}
|
||||
wlr_buffer_end_data_ptr_access(dst);
|
||||
}
|
||||
|
|
@ -332,6 +468,7 @@ static void session_handle_create_frame(struct wl_client *client,
|
|||
|
||||
frame->resource = frame_resource;
|
||||
frame->session = session;
|
||||
frame->cpu_duration_ns = -1;
|
||||
pixman_region32_init(&frame->buffer_damage);
|
||||
wl_signal_init(&frame->events.destroy);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue