mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
.
This commit is contained in:
395
grd-rdp-gfx-frame-controller.c
Normal file
395
grd-rdp-gfx-frame-controller.c
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Pascal Nowack
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "grd-rdp-gfx-frame-controller.h"
|
||||
|
||||
#include "grd-rdp-frame-stats.h"
|
||||
#include "grd-rdp-gfx-frame-log.h"
|
||||
#include "grd-rdp-gfx-framerate-log.h"
|
||||
#include "grd-rdp-surface.h"
|
||||
#include "grd-rdp-surface-renderer.h"
|
||||
|
||||
#define ACTIVATE_THROTTLING_TH_DEFAULT 2
|
||||
#define DEACTIVATE_THROTTLING_TH_DEFAULT 1
|
||||
|
||||
#define UNLIMITED_FRAME_SLOTS (UINT32_MAX)
|
||||
|
||||
typedef enum _ThrottlingState
|
||||
{
|
||||
THROTTLING_STATE_INACTIVE,
|
||||
THROTTLING_STATE_ACTIVE,
|
||||
THROTTLING_STATE_ACTIVE_LOWERING_LATENCY,
|
||||
} ThrottlingState;
|
||||
|
||||
struct _GrdRdpGfxFrameController
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GrdRdpSurface *rdp_surface;
|
||||
|
||||
GrdRdpGfxFrameLog *frame_log;
|
||||
GrdRdpGfxFramerateLog *framerate_log;
|
||||
|
||||
int64_t nw_auto_last_rtt_us;
|
||||
|
||||
ThrottlingState throttling_state;
|
||||
/* Throttling triggers on >= activate_throttling_th */
|
||||
uint32_t activate_throttling_th;
|
||||
/* Throttling triggers on <= deactivate_throttling_th */
|
||||
uint32_t deactivate_throttling_th;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GrdRdpGfxFrameController,
|
||||
grd_rdp_gfx_frame_controller,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
GrdRdpGfxFramerateLog *
|
||||
grd_rdp_gfx_frame_controller_get_framerate_log (GrdRdpGfxFrameController *frame_controller)
|
||||
{
|
||||
return frame_controller->framerate_log;
|
||||
}
|
||||
|
||||
static void
|
||||
notify_frame_stats (GrdRdpGfxFrameController *frame_controller,
|
||||
uint32_t enc_rate,
|
||||
uint32_t ack_rate)
|
||||
{
|
||||
GrdRdpGfxFrameLog *frame_log = frame_controller->frame_log;
|
||||
uint32_t missing_dual_frame_acks =
|
||||
grd_rdp_gfx_frame_log_get_unacked_dual_frames_count (frame_log);
|
||||
g_autoptr (GrdRdpFrameStats) frame_stats = NULL;
|
||||
|
||||
frame_stats = grd_rdp_frame_stats_new (missing_dual_frame_acks,
|
||||
enc_rate, ack_rate);
|
||||
|
||||
grd_rdp_gfx_framerate_log_notify_frame_stats (frame_controller->framerate_log,
|
||||
frame_stats);
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_frame_controller_notify_history_changed (GrdRdpGfxFrameController *frame_controller)
|
||||
{
|
||||
GrdRdpGfxFrameLog *frame_log = frame_controller->frame_log;
|
||||
uint32_t enc_rate = 0;
|
||||
uint32_t ack_rate = 0;
|
||||
|
||||
grd_rdp_gfx_frame_log_update_rates (frame_log, &enc_rate, &ack_rate);
|
||||
notify_frame_stats (frame_controller, enc_rate, ack_rate);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_rendering_suspended (GrdRdpGfxFrameController *frame_controller)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = frame_controller->rdp_surface;
|
||||
GrdRdpSurfaceRenderer *surface_renderer =
|
||||
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
||||
uint32_t total_frame_slots =
|
||||
grd_rdp_surface_renderer_get_total_frame_slots (surface_renderer);
|
||||
|
||||
return total_frame_slots == 0;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_activate_throttling_th_from_rtt (GrdRdpGfxFrameController *frame_controller,
|
||||
int64_t rtt_us)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = frame_controller->rdp_surface;
|
||||
GrdRdpSurfaceRenderer *surface_renderer =
|
||||
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
||||
int64_t refresh_rate =
|
||||
grd_rdp_surface_renderer_get_refresh_rate (surface_renderer);
|
||||
uint32_t activate_throttling_th;
|
||||
uint32_t delayed_frames;
|
||||
|
||||
delayed_frames = rtt_us * refresh_rate / G_USEC_PER_SEC;
|
||||
|
||||
activate_throttling_th = MAX (2, MIN (delayed_frames + 2, refresh_rate));
|
||||
g_assert (activate_throttling_th > frame_controller->deactivate_throttling_th);
|
||||
|
||||
return activate_throttling_th;
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_frame_controller_unack_frame (GrdRdpGfxFrameController *frame_controller,
|
||||
uint32_t frame_id,
|
||||
uint32_t n_subframes,
|
||||
int64_t enc_time_us)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = frame_controller->rdp_surface;
|
||||
GrdRdpGfxFrameLog *frame_log = frame_controller->frame_log;
|
||||
GrdRdpSurfaceRenderer *surface_renderer =
|
||||
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
||||
uint32_t current_activate_throttling_th;
|
||||
uint32_t n_unacked_frames;
|
||||
uint32_t enc_rate = 0;
|
||||
uint32_t ack_rate = 0;
|
||||
|
||||
grd_rdp_gfx_frame_log_track_frame (frame_log, frame_id, n_subframes,
|
||||
enc_time_us);
|
||||
|
||||
n_unacked_frames = grd_rdp_gfx_frame_log_get_unacked_frames_count (frame_log);
|
||||
grd_rdp_gfx_frame_log_update_rates (frame_log, &enc_rate, &ack_rate);
|
||||
notify_frame_stats (frame_controller, enc_rate, ack_rate);
|
||||
|
||||
switch (frame_controller->throttling_state)
|
||||
{
|
||||
case THROTTLING_STATE_INACTIVE:
|
||||
frame_controller->activate_throttling_th =
|
||||
get_activate_throttling_th_from_rtt (frame_controller,
|
||||
frame_controller->nw_auto_last_rtt_us);
|
||||
|
||||
if (n_unacked_frames >= frame_controller->activate_throttling_th)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_ACTIVE;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer, 0);
|
||||
}
|
||||
break;
|
||||
case THROTTLING_STATE_ACTIVE:
|
||||
current_activate_throttling_th =
|
||||
get_activate_throttling_th_from_rtt (frame_controller,
|
||||
frame_controller->nw_auto_last_rtt_us);
|
||||
|
||||
if (current_activate_throttling_th < frame_controller->activate_throttling_th)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_ACTIVE_LOWERING_LATENCY;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t total_frame_slots;
|
||||
|
||||
frame_controller->activate_throttling_th = current_activate_throttling_th;
|
||||
|
||||
total_frame_slots = enc_rate > ack_rate + 1 ? 0 : ack_rate + 2 - enc_rate;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer,
|
||||
total_frame_slots);
|
||||
}
|
||||
break;
|
||||
case THROTTLING_STATE_ACTIVE_LOWERING_LATENCY:
|
||||
g_assert (is_rendering_suspended (frame_controller));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_frame_controller_ack_frame (GrdRdpGfxFrameController *frame_controller,
|
||||
uint32_t frame_id,
|
||||
int64_t ack_time_us)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = frame_controller->rdp_surface;
|
||||
GrdRdpGfxFrameLog *frame_log = frame_controller->frame_log;
|
||||
GrdRdpSurfaceRenderer *surface_renderer =
|
||||
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
||||
uint32_t current_activate_throttling_th;
|
||||
uint32_t n_unacked_frames;
|
||||
uint32_t enc_rate = 0;
|
||||
uint32_t ack_rate = 0;
|
||||
|
||||
grd_rdp_gfx_frame_log_ack_tracked_frame (frame_log, frame_id, ack_time_us);
|
||||
|
||||
n_unacked_frames = grd_rdp_gfx_frame_log_get_unacked_frames_count (frame_log);
|
||||
grd_rdp_gfx_frame_log_update_rates (frame_log, &enc_rate, &ack_rate);
|
||||
notify_frame_stats (frame_controller, enc_rate, ack_rate);
|
||||
|
||||
switch (frame_controller->throttling_state)
|
||||
{
|
||||
case THROTTLING_STATE_INACTIVE:
|
||||
break;
|
||||
case THROTTLING_STATE_ACTIVE:
|
||||
if (n_unacked_frames <= frame_controller->deactivate_throttling_th)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_INACTIVE;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer,
|
||||
UNLIMITED_FRAME_SLOTS);
|
||||
break;
|
||||
}
|
||||
|
||||
current_activate_throttling_th =
|
||||
get_activate_throttling_th_from_rtt (frame_controller,
|
||||
frame_controller->nw_auto_last_rtt_us);
|
||||
if (current_activate_throttling_th < frame_controller->activate_throttling_th)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_ACTIVE_LOWERING_LATENCY;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t total_frame_slots;
|
||||
|
||||
frame_controller->activate_throttling_th = current_activate_throttling_th;
|
||||
|
||||
total_frame_slots = enc_rate > ack_rate ? 0 : ack_rate + 1 - enc_rate;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer,
|
||||
total_frame_slots);
|
||||
}
|
||||
break;
|
||||
case THROTTLING_STATE_ACTIVE_LOWERING_LATENCY:
|
||||
current_activate_throttling_th =
|
||||
get_activate_throttling_th_from_rtt (frame_controller,
|
||||
frame_controller->nw_auto_last_rtt_us);
|
||||
|
||||
if (n_unacked_frames < current_activate_throttling_th)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_INACTIVE;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer,
|
||||
UNLIMITED_FRAME_SLOTS);
|
||||
}
|
||||
else if (n_unacked_frames == current_activate_throttling_th)
|
||||
{
|
||||
uint32_t total_frame_slots;
|
||||
|
||||
frame_controller->throttling_state = THROTTLING_STATE_ACTIVE;
|
||||
|
||||
total_frame_slots = enc_rate > ack_rate ? 0 : ack_rate + 1 - enc_rate;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer,
|
||||
total_frame_slots);
|
||||
}
|
||||
else if (n_unacked_frames > current_activate_throttling_th)
|
||||
{
|
||||
g_assert (is_rendering_suspended (frame_controller));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reevaluate_encoding_suspension_state (GrdRdpGfxFrameController *frame_controller)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = frame_controller->rdp_surface;
|
||||
GrdRdpGfxFrameLog *frame_log = frame_controller->frame_log;
|
||||
GrdRdpSurfaceRenderer *surface_renderer =
|
||||
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
||||
uint32_t n_unacked_frames;
|
||||
uint32_t enc_rate = 0;
|
||||
uint32_t ack_rate = 0;
|
||||
|
||||
n_unacked_frames = grd_rdp_gfx_frame_log_get_unacked_frames_count (frame_log);
|
||||
grd_rdp_gfx_frame_log_update_rates (frame_log, &enc_rate, &ack_rate);
|
||||
|
||||
switch (frame_controller->throttling_state)
|
||||
{
|
||||
case THROTTLING_STATE_INACTIVE:
|
||||
frame_controller->activate_throttling_th =
|
||||
get_activate_throttling_th_from_rtt (frame_controller,
|
||||
frame_controller->nw_auto_last_rtt_us);
|
||||
|
||||
if (n_unacked_frames >= frame_controller->activate_throttling_th)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_ACTIVE;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer, 0);
|
||||
}
|
||||
break;
|
||||
case THROTTLING_STATE_ACTIVE:
|
||||
g_assert (frame_controller->activate_throttling_th >
|
||||
frame_controller->deactivate_throttling_th);
|
||||
g_assert (n_unacked_frames > frame_controller->deactivate_throttling_th);
|
||||
g_assert (is_rendering_suspended (frame_controller));
|
||||
break;
|
||||
case THROTTLING_STATE_ACTIVE_LOWERING_LATENCY:
|
||||
/*
|
||||
* While the graphics pipeline rewrites the frame history, the RTT
|
||||
* detection mechanism cannot submit a new round trip time.
|
||||
*/
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_frame_controller_unack_last_acked_frame (GrdRdpGfxFrameController *frame_controller,
|
||||
uint32_t frame_id,
|
||||
uint32_t n_subframes,
|
||||
int64_t enc_ack_time_us)
|
||||
{
|
||||
grd_rdp_gfx_frame_log_unack_last_acked_frame (frame_controller->frame_log,
|
||||
frame_id, n_subframes,
|
||||
enc_ack_time_us);
|
||||
reevaluate_encoding_suspension_state (frame_controller);
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_frame_controller_clear_all_unacked_frames (GrdRdpGfxFrameController *frame_controller)
|
||||
{
|
||||
GrdRdpSurface *rdp_surface = frame_controller->rdp_surface;
|
||||
GrdRdpSurfaceRenderer *surface_renderer =
|
||||
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
||||
|
||||
grd_rdp_gfx_frame_log_clear (frame_controller->frame_log);
|
||||
|
||||
frame_controller->throttling_state = THROTTLING_STATE_INACTIVE;
|
||||
grd_rdp_surface_renderer_update_total_frame_slots (surface_renderer,
|
||||
UNLIMITED_FRAME_SLOTS);
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_frame_controller_notify_new_round_trip_time (GrdRdpGfxFrameController *frame_controller,
|
||||
int64_t round_trip_time_us)
|
||||
{
|
||||
frame_controller->nw_auto_last_rtt_us = round_trip_time_us;
|
||||
}
|
||||
|
||||
GrdRdpGfxFrameController *
|
||||
grd_rdp_gfx_frame_controller_new (GrdRdpSurface *rdp_surface)
|
||||
{
|
||||
GrdRdpGfxFrameController *frame_controller;
|
||||
|
||||
frame_controller = g_object_new (GRD_TYPE_RDP_GFX_FRAME_CONTROLLER, NULL);
|
||||
frame_controller->rdp_surface = rdp_surface;
|
||||
|
||||
return frame_controller;
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_frame_controller_dispose (GObject *object)
|
||||
{
|
||||
GrdRdpGfxFrameController *frame_controller = GRD_RDP_GFX_FRAME_CONTROLLER (object);
|
||||
|
||||
g_clear_object (&frame_controller->framerate_log);
|
||||
g_clear_object (&frame_controller->frame_log);
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_gfx_frame_controller_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_frame_controller_init (GrdRdpGfxFrameController *frame_controller)
|
||||
{
|
||||
frame_controller->throttling_state = THROTTLING_STATE_INACTIVE;
|
||||
frame_controller->activate_throttling_th = ACTIVATE_THROTTLING_TH_DEFAULT;
|
||||
frame_controller->deactivate_throttling_th = DEACTIVATE_THROTTLING_TH_DEFAULT;
|
||||
|
||||
g_assert (frame_controller->activate_throttling_th >
|
||||
frame_controller->deactivate_throttling_th);
|
||||
|
||||
frame_controller->frame_log = grd_rdp_gfx_frame_log_new ();
|
||||
frame_controller->framerate_log = grd_rdp_gfx_framerate_log_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_frame_controller_class_init (GrdRdpGfxFrameControllerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = grd_rdp_gfx_frame_controller_dispose;
|
||||
}
|
||||
Reference in New Issue
Block a user