Skip to content

Commit

Permalink
Control how many frames are captured per second
Browse files Browse the repository at this point in the history
The goal is to rate-control the capturing, as it can represent a
performance issue and can cause a laggy mouse; see emersion#66.

The delay for the current frame is based on the time it took to capture
the previous frame. The targeted frame rate is a constant.
  • Loading branch information
zsolt-donca committed Dec 25, 2020
1 parent 727f13f commit 4d22af0
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/fps_limit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef FPS_LIMIT_H
#define FPS_LIMIT_H

#include <stdint.h>

struct fps_limit_state {
uint64_t last_time_us;

uint64_t fps_measurement_last_time_us;
uint32_t fps_measurement_frame_count;
};

void fps_limit_apply(struct fps_limit_state* state);

#endif
5 changes: 5 additions & 0 deletions include/screencast_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <spa/param/video/format-utils.h>
#include <wayland-client-protocol.h>

#include "fps_limit.h"

// this seems to be right based on
// https://github.com/flatpak/xdg-desktop-portal/blob/309a1fc0cf2fb32cceb91dbc666d20cf0a3202c2/src/screen-cast.c#L955
#define XDP_CAST_PROTO_VER 2
Expand Down Expand Up @@ -91,6 +93,9 @@ struct xdpw_screencast_instance {
bool with_cursor;
int err;
bool quit;

// fps limit
struct fps_limit_state fps_limit;
};

struct xdpw_wlr_output {
Expand Down
46 changes: 46 additions & 0 deletions include/time-util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2019 - 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* 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.
*/

#pragma once

#include <time.h>
#include <stdint.h>

static inline uint64_t timespec_to_us(const struct timespec* ts)
{
return (uint64_t)ts->tv_sec * UINT64_C(1000000) +
(uint64_t)ts->tv_nsec / UINT64_C(1000);
}

static inline uint64_t timespec_to_ms(const struct timespec* ts)
{
return (uint64_t)ts->tv_sec * UINT64_C(1000) +
(uint64_t)ts->tv_nsec / UINT64_C(1000000);
}

static inline uint64_t gettime_us(void)
{
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
return timespec_to_us(&ts);
}

static inline uint64_t gettime_ms(void)
{
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
return timespec_to_ms(&ts);
}
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_project_arguments(cc.get_supported_arguments([
'-Wno-missing-field-initializers',
'-Wno-unused-parameter',
'-D_POSIX_C_SOURCE=200809L',
'-D_GNU_SOURCE',
]), language: 'c')

inc = include_directories('include')
Expand Down Expand Up @@ -49,6 +50,7 @@ executable(
'src/screencast/screencast_common.c',
'src/screencast/wlr_screencast.c',
'src/screencast/pipewire_screencast.c',
'src/screencast/fps_limit.c'
]),
dependencies: [
wayland_client,
Expand Down
47 changes: 47 additions & 0 deletions src/screencast/fps_limit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "fps_limit.h"
#include "logger.h"
#include "time-util.h"
#include <stdint.h>
#include <unistd.h>

#define FPS_MEASURE_PERIOD_SEC 2.0
#define TARGET_FPS 10.0

void measure_fps(struct fps_limit_state* state, uint64_t now_us);

void fps_limit_apply(struct fps_limit_state* state) {
uint64_t now_us = gettime_us();

if (state->last_time_us == 0) {
state->last_time_us = now_us;
state->fps_measurement_last_time_us = now_us;
} else {
uint64_t elapsed_us = now_us - state->last_time_us;

measure_fps(state, now_us);

uint64_t target_us = (1.0 / TARGET_FPS) * 1e6;
int64_t delay_us = target_us - elapsed_us;
if (delay_us > 0) {
logprint(TRACE, "fps_limit: elapsed time since the end of last sleep: %u, sleeping for %u (microseconds)", elapsed_us, delay_us);
usleep(delay_us);
}

state->last_time_us = gettime_us();
}
}

void measure_fps(struct fps_limit_state* state, uint64_t now_us) {
state->fps_measurement_frame_count++;

double elapsed_since_last_measure_sec = (now_us - state->fps_measurement_last_time_us) / 1e6;
if (elapsed_since_last_measure_sec >= FPS_MEASURE_PERIOD_SEC) {
double average_delay_sec = elapsed_since_last_measure_sec / state->fps_measurement_frame_count;
double average_frames_per_sec = 1.0 / average_delay_sec;

logprint(DEBUG, "fps_limit: measured FPS: %f", average_frames_per_sec);

state->fps_measurement_last_time_us = now_us;
state->fps_measurement_frame_count = 0;
}
}
4 changes: 4 additions & 0 deletions src/screencast/wlr_screencast.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "pipewire_screencast.h"
#include "xdpw.h"
#include "logger.h"
#include "fps_limit.h"

static void wlr_frame_buffer_destroy(struct xdpw_screencast_instance *cast) {
// Even though this check may be deemed unnecessary,
Expand Down Expand Up @@ -135,6 +136,9 @@ static void wlr_frame_buffer_done(void *data,
struct xdpw_screencast_instance *cast = data;

logprint(TRACE, "wlroots: buffer_done event handler");

fps_limit_apply(&cast->fps_limit);

zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->simple_frame.buffer);
logprint(TRACE, "wlroots: frame copied");
}
Expand Down

0 comments on commit 4d22af0

Please sign in to comment.