mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
292 lines
9.4 KiB
C
292 lines
9.4 KiB
C
/*
|
|
* Copyright (C) 2023 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-session-metrics.h"
|
|
|
|
#include "grd-rdp-surface.h"
|
|
|
|
typedef struct
|
|
{
|
|
gboolean pending_frame_reception;
|
|
gboolean pending_frame_transmission;
|
|
int64_t first_frame_reception;
|
|
int64_t first_frame_transmission;
|
|
uint32_t skipped_frames;
|
|
} SurfaceMetrics;
|
|
|
|
struct _GrdRdpSessionMetrics
|
|
{
|
|
GObject parent;
|
|
|
|
GrdRdpPhase phase;
|
|
|
|
int64_t rd_session_init_us;
|
|
int64_t rd_session_post_connect_us;
|
|
int64_t rd_session_ready_us;
|
|
int64_t rd_session_started_us;
|
|
|
|
gboolean pending_layout_change;
|
|
int64_t layout_change_notification;
|
|
|
|
GMutex metrics_mutex;
|
|
GHashTable *surface_metrics_table;
|
|
uint32_t n_pending_surface_metrics;
|
|
|
|
gboolean pending_output;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdRdpSessionMetrics, grd_rdp_session_metrics, G_TYPE_OBJECT)
|
|
|
|
void
|
|
grd_rdp_session_metrics_notify_phase_completion (GrdRdpSessionMetrics *session_metrics,
|
|
GrdRdpPhase phase)
|
|
{
|
|
g_assert (session_metrics->pending_output);
|
|
|
|
g_assert (phase > session_metrics->phase);
|
|
session_metrics->phase = phase;
|
|
|
|
switch (phase)
|
|
{
|
|
case GRD_RDP_PHASE_SESSION_INITIALIZATION:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case GRD_RDP_PHASE_POST_CONNECT:
|
|
session_metrics->rd_session_post_connect_us = g_get_monotonic_time ();
|
|
break;
|
|
case GRD_RDP_PHASE_SESSION_READY:
|
|
session_metrics->rd_session_ready_us = g_get_monotonic_time ();
|
|
break;
|
|
case GRD_RDP_PHASE_SESSION_STARTED:
|
|
session_metrics->rd_session_started_us = g_get_monotonic_time ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
grd_rdp_session_metrics_notify_frame_reception (GrdRdpSessionMetrics *session_metrics,
|
|
GrdRdpSurface *rdp_surface)
|
|
{
|
|
SurfaceMetrics *surface_metrics = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
|
|
|
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
|
if (session_metrics->pending_layout_change)
|
|
return;
|
|
|
|
if (!session_metrics->pending_output)
|
|
return;
|
|
|
|
if (!g_hash_table_lookup_extended (session_metrics->surface_metrics_table,
|
|
rdp_surface,
|
|
NULL, (gpointer *) &surface_metrics))
|
|
g_assert_not_reached ();
|
|
|
|
if (surface_metrics->pending_frame_reception)
|
|
{
|
|
g_assert (surface_metrics->pending_frame_transmission);
|
|
|
|
surface_metrics->first_frame_reception = g_get_monotonic_time ();
|
|
surface_metrics->pending_frame_reception = FALSE;
|
|
}
|
|
else if (surface_metrics->pending_frame_transmission)
|
|
{
|
|
++surface_metrics->skipped_frames;
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_session_metrics (GrdRdpSessionMetrics *session_metrics)
|
|
{
|
|
GrdRdpSurface *rdp_surface = NULL;
|
|
SurfaceMetrics *surface_metrics = NULL;
|
|
GHashTableIter iter;
|
|
|
|
g_debug ("[RDP] Remote Desktop session metrics: _post_connect: %lims, "
|
|
"_ready: %lims, _started: %lims, whole startup: %lims",
|
|
(session_metrics->rd_session_post_connect_us -
|
|
session_metrics->rd_session_init_us) / 1000,
|
|
(session_metrics->rd_session_ready_us -
|
|
session_metrics->rd_session_post_connect_us) / 1000,
|
|
(session_metrics->rd_session_started_us -
|
|
session_metrics->rd_session_ready_us) / 1000,
|
|
(session_metrics->rd_session_started_us -
|
|
session_metrics->rd_session_init_us) / 1000);
|
|
|
|
g_hash_table_iter_init (&iter, session_metrics->surface_metrics_table);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &rdp_surface,
|
|
(gpointer *) &surface_metrics))
|
|
{
|
|
GrdRdpSurfaceMapping *surface_mapping =
|
|
grd_rdp_surface_get_mapping (rdp_surface);
|
|
GrdRdpSurfaceMappingType mapping_type = surface_mapping->mapping_type;
|
|
|
|
g_assert (!surface_metrics->pending_frame_reception);
|
|
g_assert (!surface_metrics->pending_frame_transmission);
|
|
|
|
g_assert (mapping_type == GRD_RDP_SURFACE_MAPPING_TYPE_MAP_TO_OUTPUT);
|
|
|
|
g_debug ("[RDP] Surface %ux%u (%i, %i) Metrics: First frame: "
|
|
"Reception: %lims, Transmission: %lims, Skipped frames: %u",
|
|
grd_rdp_surface_get_width (rdp_surface),
|
|
grd_rdp_surface_get_height (rdp_surface),
|
|
surface_mapping->output_origin_x,
|
|
surface_mapping->output_origin_y,
|
|
(surface_metrics->first_frame_reception -
|
|
session_metrics->layout_change_notification) / 1000,
|
|
(surface_metrics->first_frame_transmission -
|
|
surface_metrics->first_frame_reception) / 1000,
|
|
surface_metrics->skipped_frames);
|
|
}
|
|
}
|
|
|
|
void
|
|
grd_rdp_session_metrics_notify_frame_transmission (GrdRdpSessionMetrics *session_metrics,
|
|
GrdRdpSurface *rdp_surface)
|
|
{
|
|
SurfaceMetrics *surface_metrics = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
|
|
|
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
|
g_assert (!session_metrics->pending_layout_change);
|
|
|
|
if (!session_metrics->pending_output)
|
|
return;
|
|
|
|
if (!g_hash_table_lookup_extended (session_metrics->surface_metrics_table,
|
|
rdp_surface,
|
|
NULL, (gpointer *) &surface_metrics))
|
|
g_assert_not_reached ();
|
|
|
|
g_assert (!surface_metrics->pending_frame_reception);
|
|
|
|
if (surface_metrics->pending_frame_transmission)
|
|
{
|
|
surface_metrics->first_frame_transmission = g_get_monotonic_time ();
|
|
surface_metrics->pending_frame_transmission = FALSE;
|
|
|
|
g_assert (session_metrics->n_pending_surface_metrics > 0);
|
|
--session_metrics->n_pending_surface_metrics;
|
|
}
|
|
|
|
if (session_metrics->n_pending_surface_metrics == 0)
|
|
{
|
|
print_session_metrics (session_metrics);
|
|
session_metrics->pending_output = FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
grd_rdp_session_metrics_notify_layout_change (GrdRdpSessionMetrics *session_metrics)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
|
|
|
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
|
session_metrics->layout_change_notification = g_get_monotonic_time ();
|
|
session_metrics->pending_layout_change = TRUE;
|
|
}
|
|
|
|
void
|
|
grd_rdp_session_metrics_prepare_surface_metrics (GrdRdpSessionMetrics *session_metrics,
|
|
GList *surfaces)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GList *l;
|
|
|
|
g_assert (session_metrics->phase == GRD_RDP_PHASE_SESSION_STARTED);
|
|
|
|
locker = g_mutex_locker_new (&session_metrics->metrics_mutex);
|
|
g_hash_table_remove_all (session_metrics->surface_metrics_table);
|
|
session_metrics->n_pending_surface_metrics = 0;
|
|
|
|
for (l = surfaces; l; l = l->next)
|
|
{
|
|
GrdRdpSurface *rdp_surface = l->data;
|
|
SurfaceMetrics *surface_metrics;
|
|
|
|
surface_metrics = g_new0 (SurfaceMetrics, 1);
|
|
surface_metrics->pending_frame_reception = TRUE;
|
|
surface_metrics->pending_frame_transmission = TRUE;
|
|
|
|
++session_metrics->n_pending_surface_metrics;
|
|
g_hash_table_insert (session_metrics->surface_metrics_table,
|
|
rdp_surface, surface_metrics);
|
|
}
|
|
|
|
session_metrics->pending_layout_change = FALSE;
|
|
}
|
|
|
|
GrdRdpSessionMetrics *
|
|
grd_rdp_session_metrics_new (void)
|
|
{
|
|
return g_object_new (GRD_TYPE_RDP_SESSION_METRICS, NULL);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_session_metrics_dispose (GObject *object)
|
|
{
|
|
GrdRdpSessionMetrics *session_metrics = GRD_RDP_SESSION_METRICS (object);
|
|
|
|
g_clear_pointer (&session_metrics->surface_metrics_table, g_hash_table_unref);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_session_metrics_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_session_metrics_finalize (GObject *object)
|
|
{
|
|
GrdRdpSessionMetrics *session_metrics = GRD_RDP_SESSION_METRICS (object);
|
|
|
|
g_mutex_clear (&session_metrics->metrics_mutex);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_session_metrics_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_session_metrics_init (GrdRdpSessionMetrics *session_metrics)
|
|
{
|
|
session_metrics->pending_layout_change = TRUE;
|
|
session_metrics->pending_output = TRUE;
|
|
|
|
session_metrics->phase = GRD_RDP_PHASE_SESSION_INITIALIZATION;
|
|
session_metrics->rd_session_init_us = g_get_monotonic_time ();
|
|
|
|
session_metrics->surface_metrics_table = g_hash_table_new_full (NULL, NULL,
|
|
NULL, g_free);
|
|
|
|
g_mutex_init (&session_metrics->metrics_mutex);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_session_metrics_class_init (GrdRdpSessionMetricsClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = grd_rdp_session_metrics_dispose;
|
|
object_class->finalize = grd_rdp_session_metrics_finalize;
|
|
}
|