Files
grd/grd-rdp-surface-renderer.c
2026-02-13 13:06:50 +09:00

1021 lines
33 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-surface-renderer.h"
#include <drm_fourcc.h>
#include "grd-rdp-buffer.h"
#include "grd-rdp-damage-detector.h"
#include "grd-rdp-frame.h"
#include "grd-rdp-legacy-buffer.h"
#include "grd-rdp-pw-buffer.h"
#include "grd-rdp-render-context.h"
#include "grd-rdp-renderer.h"
#include "grd-rdp-session-metrics.h"
#include "grd-rdp-surface.h"
#include "grd-session-rdp.h"
#define FRAME_UPGRADE_DELAY_US (60 * 1000)
#define UNLIMITED_FRAME_SLOTS (UINT32_MAX)
/*
* The value of the transition time is based upon testing. During times where
* a lot of frames are submitted, the frame-controller could quickly alternate
* between throttling and no-throttling. In these situations, the client is
* ready for a frame containing only a main view, but not for a dual view.
*/
#define TRANSITION_TIME_US (200 * 1000)
typedef enum
{
OBJECT_TYPE_BUFFER,
OBJECT_TYPE_RENDER_CONTEXT,
} ObjectType;
typedef struct
{
ObjectType object_type;
gpointer object;
} UnrefContext;
typedef struct
{
GrdRdpSurfaceRenderer *surface_renderer;
GrdRdpBuffer *current_buffer;
GrdRdpBuffer *last_buffer;
} GrdRdpFrameContext;
struct _GrdRdpSurfaceRenderer
{
GObject parent;
GrdRdpSurface *rdp_surface;
GrdRdpRenderer *renderer;
uint32_t refresh_rate;
GSource *object_unref_source;
GAsyncQueue *unref_queue;
GSource *render_source;
gboolean pending_render_context_reset;
GSource *frame_upgrade_source;
gboolean pending_frame_upgrade;
GSource *trigger_frame_upgrade_source;
gboolean graphics_subsystem_failed;
uint32_t total_frame_slots;
GHashTable *assigned_frame_slots;
int64_t unthrottled_since_us;
GMutex render_mutex;
GrdRdpBuffer *pending_buffer;
GrdRdpBuffer *last_buffer;
GHashTable *acquired_buffers;
GHashTable *registered_buffers;
GrdRdpBufferInfo *rdp_buffer_info;
};
G_DEFINE_TYPE (GrdRdpSurfaceRenderer, grd_rdp_surface_renderer, G_TYPE_OBJECT)
static UnrefContext *
unref_context_new (ObjectType object_type,
gpointer object)
{
UnrefContext *unref_context;
unref_context = g_new0 (UnrefContext, 1);
unref_context->object_type = object_type;
unref_context->object = object;
return unref_context;
}
uint32_t
grd_rdp_surface_renderer_get_refresh_rate (GrdRdpSurfaceRenderer *surface_renderer)
{
return surface_renderer->refresh_rate;
}
GrdRdpBufferInfo *
grd_rdp_surface_renderer_get_buffer_info (GrdRdpSurfaceRenderer *surface_renderer)
{
return surface_renderer->rdp_buffer_info;
}
uint32_t
grd_rdp_surface_renderer_get_total_frame_slots (GrdRdpSurfaceRenderer *surface_renderer)
{
return surface_renderer->total_frame_slots;
}
static void
trigger_frame_upgrade_schedule (GrdRdpSurfaceRenderer *surface_renderer)
{
g_source_set_ready_time (surface_renderer->trigger_frame_upgrade_source, 0);
}
void
grd_rdp_surface_renderer_update_total_frame_slots (GrdRdpSurfaceRenderer *surface_renderer,
uint32_t total_frame_slots)
{
uint32_t old_slot_count = surface_renderer->total_frame_slots;
if (old_slot_count < UNLIMITED_FRAME_SLOTS &&
total_frame_slots == UNLIMITED_FRAME_SLOTS)
surface_renderer->unthrottled_since_us = g_get_monotonic_time ();
surface_renderer->total_frame_slots = total_frame_slots;
if (old_slot_count < surface_renderer->total_frame_slots)
grd_rdp_surface_renderer_trigger_render_source (surface_renderer);
if (old_slot_count < UNLIMITED_FRAME_SLOTS &&
surface_renderer->total_frame_slots == UNLIMITED_FRAME_SLOTS)
trigger_frame_upgrade_schedule (surface_renderer);
}
void
grd_rdp_surface_renderer_notify_frame_upgrade_state (GrdRdpSurfaceRenderer *surface_renderer,
gboolean can_upgrade_frame)
{
surface_renderer->pending_frame_upgrade = can_upgrade_frame;
}
static gboolean
is_buffer_combination_valid (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpPwBuffer *rdp_pw_buffer,
GError **error)
{
GrdRdpBufferInfo *rdp_buffer_info = surface_renderer->rdp_buffer_info;
GrdRdpBufferType buffer_type;
if (g_hash_table_size (surface_renderer->registered_buffers) == 0)
return TRUE;
g_assert (rdp_buffer_info);
buffer_type = grd_rdp_pw_buffer_get_buffer_type (rdp_pw_buffer);
if (buffer_type != rdp_buffer_info->buffer_type)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid buffer combination: Mixed buffer types");
return FALSE;
}
/*
* No need to check whether an implicit or explicit DRM format modifier is
* used per buffer, since per PipeWire stream there is only one DRM format
* modifier and that one is used by all PW buffers of that PW stream.
*/
return TRUE;
}
static gboolean
is_render_context_reset_required (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpPwBuffer *rdp_pw_buffer,
uint64_t new_drm_format_modifier)
{
GrdRdpBufferInfo *rdp_buffer_info;
GrdRdpBufferType buffer_type;
uint64_t current_drm_format_modifier;
if (g_hash_table_size (surface_renderer->registered_buffers) > 0)
return FALSE;
rdp_buffer_info = surface_renderer->rdp_buffer_info;
if (!rdp_buffer_info)
return FALSE;
buffer_type = rdp_buffer_info->buffer_type;
if (buffer_type != grd_rdp_pw_buffer_get_buffer_type (rdp_pw_buffer))
return TRUE;
current_drm_format_modifier = rdp_buffer_info->drm_format_modifier;
if (current_drm_format_modifier != DRM_FORMAT_MOD_INVALID &&
new_drm_format_modifier == DRM_FORMAT_MOD_INVALID)
return TRUE;
if (current_drm_format_modifier == DRM_FORMAT_MOD_INVALID &&
new_drm_format_modifier != DRM_FORMAT_MOD_INVALID)
return TRUE;
return FALSE;
}
static GrdRdpBufferInfo *
rdp_buffer_info_new (GrdRdpPwBuffer *rdp_pw_buffer,
uint32_t drm_format,
uint64_t drm_format_modifier)
{
GrdRdpBufferType buffer_type =
grd_rdp_pw_buffer_get_buffer_type (rdp_pw_buffer);
GrdRdpBufferInfo *rdp_buffer_info;
rdp_buffer_info = g_new0 (GrdRdpBufferInfo, 1);
rdp_buffer_info->buffer_type = buffer_type;
rdp_buffer_info->drm_format = drm_format;
rdp_buffer_info->drm_format_modifier = drm_format_modifier;
return rdp_buffer_info;
}
gboolean
grd_rdp_surface_renderer_register_pw_buffer (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpPwBuffer *rdp_pw_buffer,
uint32_t drm_format,
uint64_t drm_format_modifier,
GError **error)
{
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
g_autofree GrdRdpBufferInfo *rdp_buffer_info = NULL;
GrdVkDevice *vk_device =
grd_rdp_renderer_get_vk_device (surface_renderer->renderer);
GrdRdpBuffer *rdp_buffer;
if (!is_buffer_combination_valid (surface_renderer, rdp_pw_buffer, error))
return FALSE;
rdp_buffer_info = rdp_buffer_info_new (rdp_pw_buffer, drm_format,
drm_format_modifier);
rdp_buffer = grd_rdp_buffer_new (rdp_pw_buffer, rdp_buffer_info, rdp_surface,
vk_device, error);
if (!rdp_buffer)
return FALSE;
if (is_render_context_reset_required (surface_renderer, rdp_pw_buffer,
drm_format_modifier))
surface_renderer->pending_render_context_reset = TRUE;
if (surface_renderer->rdp_buffer_info &&
surface_renderer->rdp_buffer_info->buffer_type == GRD_RDP_BUFFER_TYPE_DMA_BUF &&
surface_renderer->rdp_buffer_info->drm_format_modifier != drm_format_modifier)
g_assert (g_hash_table_size (surface_renderer->registered_buffers) == 0);
if (g_hash_table_size (surface_renderer->registered_buffers) == 0)
g_clear_pointer (&surface_renderer->rdp_buffer_info, g_free);
if (surface_renderer->pending_render_context_reset)
g_assert (!surface_renderer->rdp_buffer_info);
if (!surface_renderer->rdp_buffer_info)
surface_renderer->rdp_buffer_info = g_steal_pointer (&rdp_buffer_info);
g_hash_table_insert (surface_renderer->registered_buffers,
rdp_pw_buffer, rdp_buffer);
return TRUE;
}
static void
release_pw_buffer (GrdRdpBuffer *rdp_buffer)
{
GrdRdpPwBuffer *rdp_pw_buffer = grd_rdp_buffer_get_rdp_pw_buffer (rdp_buffer);
grd_rdp_pw_buffer_queue_pw_buffer (rdp_pw_buffer);
}
void
grd_rdp_surface_renderer_unregister_pw_buffer (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpPwBuffer *rdp_pw_buffer)
{
GrdRdpBuffer *rdp_buffer = NULL;
GrdRdpBuffer *buffer_to_release = NULL;
if (!g_hash_table_lookup_extended (surface_renderer->registered_buffers,
rdp_pw_buffer,
NULL, (gpointer *) &rdp_buffer))
g_assert_not_reached ();
/* Ensure buffer cannot be acquired anymore */
g_mutex_lock (&surface_renderer->render_mutex);
if (surface_renderer->pending_buffer || surface_renderer->last_buffer)
g_assert (surface_renderer->pending_buffer != surface_renderer->last_buffer);
if (surface_renderer->pending_buffer == rdp_buffer)
buffer_to_release = g_steal_pointer (&surface_renderer->pending_buffer);
if (surface_renderer->last_buffer == rdp_buffer)
buffer_to_release = g_steal_pointer (&surface_renderer->last_buffer);
grd_rdp_buffer_mark_for_removal (rdp_buffer);
g_mutex_unlock (&surface_renderer->render_mutex);
/* Ensure buffer lock is released and PipeWire buffer queued again */
grd_rdp_pw_buffer_ensure_unlocked (rdp_pw_buffer);
g_clear_pointer (&buffer_to_release, release_pw_buffer);
g_hash_table_remove (surface_renderer->registered_buffers, rdp_pw_buffer);
}
void
grd_rdp_surface_renderer_submit_buffer (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpPwBuffer *rdp_pw_buffer)
{
GrdRdpBuffer *rdp_buffer = NULL;
if (!g_hash_table_lookup_extended (surface_renderer->registered_buffers,
rdp_pw_buffer,
NULL, (gpointer *) &rdp_buffer))
g_assert_not_reached ();
g_mutex_lock (&surface_renderer->render_mutex);
g_clear_pointer (&surface_renderer->pending_buffer, release_pw_buffer);
surface_renderer->pending_buffer = rdp_buffer;
g_mutex_unlock (&surface_renderer->render_mutex);
grd_rdp_surface_renderer_trigger_render_source (surface_renderer);
}
void
grd_rdp_surface_renderer_submit_legacy_buffer (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpLegacyBuffer *buffer)
{
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
g_mutex_lock (&surface_renderer->render_mutex);
g_clear_pointer (&rdp_surface->pending_framebuffer, grd_rdp_legacy_buffer_release);
rdp_surface->pending_framebuffer = buffer;
g_mutex_unlock (&surface_renderer->render_mutex);
grd_rdp_surface_renderer_trigger_render_source (surface_renderer);
}
void
grd_rdp_surface_renderer_invalidate_surface (GrdRdpSurfaceRenderer *surface_renderer)
{
g_autoptr (GMutexLocker) locker = NULL;
locker = g_mutex_locker_new (&surface_renderer->render_mutex);
grd_rdp_surface_renderer_invalidate_surface_unlocked (surface_renderer);
}
void
grd_rdp_surface_renderer_invalidate_surface_unlocked (GrdRdpSurfaceRenderer *surface_renderer)
{
g_assert (g_hash_table_size (surface_renderer->acquired_buffers) == 0);
if (!surface_renderer->pending_buffer)
{
surface_renderer->pending_buffer =
g_steal_pointer (&surface_renderer->last_buffer);
}
g_clear_pointer (&surface_renderer->last_buffer, release_pw_buffer);
}
void
grd_rdp_surface_renderer_trigger_render_source (GrdRdpSurfaceRenderer *surface_renderer)
{
g_source_set_ready_time (surface_renderer->render_source, 0);
trigger_frame_upgrade_schedule (surface_renderer);
}
void
grd_rdp_surface_renderer_reset (GrdRdpSurfaceRenderer *surface_renderer)
{
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
g_mutex_lock (&surface_renderer->render_mutex);
g_clear_pointer (&rdp_surface->pending_framebuffer, grd_rdp_legacy_buffer_release);
g_mutex_unlock (&surface_renderer->render_mutex);
}
static GrdRdpBuffer *
rdp_buffer_ref (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpBuffer *rdp_buffer)
{
GrdRdpPwBuffer *rdp_pw_buffer = grd_rdp_buffer_get_rdp_pw_buffer (rdp_buffer);
uint32_t *ref_count = NULL;
if (g_hash_table_lookup_extended (surface_renderer->acquired_buffers,
rdp_buffer,
NULL, (gpointer *) &ref_count))
{
g_assert (*ref_count > 0);
++(*ref_count);
return rdp_buffer;
}
ref_count = g_new0 (uint32_t, 1);
*ref_count = 1;
grd_rdp_pw_buffer_acquire_lock (rdp_pw_buffer);
g_hash_table_insert (surface_renderer->acquired_buffers,
rdp_buffer, ref_count);
return rdp_buffer;
}
static void
rdp_buffer_unref (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpBuffer *rdp_buffer)
{
GrdRdpPwBuffer *rdp_pw_buffer = grd_rdp_buffer_get_rdp_pw_buffer (rdp_buffer);
uint32_t *ref_count = NULL;
if (!g_hash_table_lookup_extended (surface_renderer->acquired_buffers,
rdp_buffer,
NULL, (gpointer *) &ref_count))
g_assert_not_reached ();
g_assert (*ref_count > 0);
--(*ref_count);
if (*ref_count == 0)
{
g_assert (surface_renderer->pending_buffer != rdp_buffer);
/*
* unregister_pw_buffer () already takes care of the PW buffer release
* here. In such case, the second condition
* (surface_renderer->last_buffer != rdp_buffer) is always true.
*/
if (!grd_rdp_buffer_is_marked_for_removal (rdp_buffer) &&
surface_renderer->last_buffer != rdp_buffer)
release_pw_buffer (rdp_buffer);
g_hash_table_remove (surface_renderer->acquired_buffers, rdp_buffer);
grd_rdp_pw_buffer_maybe_release_lock (rdp_pw_buffer);
}
}
static void
unref_buffer (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpBuffer *rdp_buffer)
{
g_mutex_lock (&surface_renderer->render_mutex);
g_assert (surface_renderer->pending_buffer != rdp_buffer);
rdp_buffer_unref (surface_renderer, rdp_buffer);
g_mutex_unlock (&surface_renderer->render_mutex);
}
static void
unref_render_context (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpRenderContext *render_context)
{
grd_rdp_renderer_release_render_context (surface_renderer->renderer,
render_context);
}
static gboolean
unref_objects (gpointer user_data)
{
GrdRdpSurfaceRenderer *surface_renderer = user_data;
UnrefContext *unref_context;
while ((unref_context = g_async_queue_try_pop (surface_renderer->unref_queue)))
{
switch (unref_context->object_type)
{
case OBJECT_TYPE_BUFFER:
unref_buffer (surface_renderer, unref_context->object);
break;
case OBJECT_TYPE_RENDER_CONTEXT:
unref_render_context (surface_renderer, unref_context->object);
break;
}
g_free (unref_context);
}
return G_SOURCE_CONTINUE;
}
static gboolean
can_prepare_new_frame (GrdRdpSurfaceRenderer *surface_renderer)
{
uint32_t total_frame_slots = surface_renderer->total_frame_slots;
uint32_t used_frame_slots;
used_frame_slots = g_hash_table_size (surface_renderer->assigned_frame_slots);
return total_frame_slots > used_frame_slots;
}
static void
on_frame_picked_up (GrdRdpFrame *rdp_frame,
gpointer user_data)
{
GrdRdpFrameContext *frame_context = user_data;
GrdRdpSurfaceRenderer *surface_renderer = frame_context->surface_renderer;
GrdRdpBuffer *current_buffer = frame_context->current_buffer;
g_hash_table_add (surface_renderer->assigned_frame_slots, rdp_frame);
if (!current_buffer)
return;
g_mutex_lock (&surface_renderer->render_mutex);
if (!grd_rdp_buffer_is_marked_for_removal (current_buffer))
surface_renderer->last_buffer = current_buffer;
else
surface_renderer->last_buffer = NULL;
g_mutex_unlock (&surface_renderer->render_mutex);
}
static void
queue_buffer_unref (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpBuffer *rdp_buffer)
{
UnrefContext *unref_context;
unref_context = unref_context_new (OBJECT_TYPE_BUFFER, rdp_buffer);
g_async_queue_push (surface_renderer->unref_queue, unref_context);
}
static void
on_view_finalization (GrdRdpFrame *rdp_frame,
gpointer user_data)
{
GrdRdpFrameContext *frame_context = user_data;
GrdRdpSurfaceRenderer *surface_renderer = frame_context->surface_renderer;
GrdRdpBuffer *current_buffer = frame_context->current_buffer;
GrdRdpBuffer *last_buffer = frame_context->last_buffer;
queue_buffer_unref (surface_renderer, current_buffer);
if (last_buffer)
queue_buffer_unref (surface_renderer, last_buffer);
g_source_set_ready_time (surface_renderer->object_unref_source, 0);
}
static void
on_frame_submission (GrdRdpFrame *rdp_frame,
gpointer user_data)
{
GrdRdpFrameContext *frame_context = user_data;
GrdRdpSurfaceRenderer *surface_renderer = frame_context->surface_renderer;
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
GrdSessionRdp *session_rdp =
grd_rdp_renderer_get_session (surface_renderer->renderer);
GrdRdpSessionMetrics *session_metrics =
grd_session_rdp_get_session_metrics (session_rdp);
grd_rdp_session_metrics_notify_frame_transmission (session_metrics,
rdp_surface);
}
static void
reset_frame_upgrade_schedule (GrdRdpSurfaceRenderer *surface_renderer)
{
g_source_set_ready_time (surface_renderer->frame_upgrade_source, -1);
trigger_frame_upgrade_schedule (surface_renderer);
}
static void
queue_render_context_unref (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpFrame *rdp_frame)
{
GrdRdpRenderContext *render_context =
grd_rdp_frame_get_render_context (rdp_frame);
UnrefContext *unref_context;
unref_context = unref_context_new (OBJECT_TYPE_RENDER_CONTEXT,
render_context);
g_async_queue_push (surface_renderer->unref_queue, unref_context);
g_source_set_ready_time (surface_renderer->object_unref_source, 0);
}
static void
on_frame_finalization (GrdRdpFrame *rdp_frame,
gpointer user_data)
{
GrdRdpFrameContext *frame_context = user_data;
GrdRdpSurfaceRenderer *surface_renderer = frame_context->surface_renderer;
g_hash_table_remove (surface_renderer->assigned_frame_slots, rdp_frame);
grd_rdp_surface_renderer_trigger_render_source (surface_renderer);
reset_frame_upgrade_schedule (surface_renderer);
queue_render_context_unref (surface_renderer, rdp_frame);
}
static gboolean
should_avoid_auxiliary_frame (GrdRdpSurfaceRenderer *surface_renderer)
{
int64_t current_time_us;
current_time_us = g_get_monotonic_time ();
return g_hash_table_size (surface_renderer->assigned_frame_slots) > 0 ||
surface_renderer->total_frame_slots < UNLIMITED_FRAME_SLOTS ||
current_time_us - surface_renderer->unthrottled_since_us < TRANSITION_TIME_US;
}
static void
maybe_downgrade_view_type (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpRenderContext *render_context,
GrdRdpFrame *rdp_frame)
{
GrdRdpFrameViewType view_type;
view_type = grd_rdp_frame_get_avc_view_type (rdp_frame);
if (view_type != GRD_RDP_FRAME_VIEW_TYPE_DUAL)
return;
if (should_avoid_auxiliary_frame (surface_renderer) ||
grd_rdp_render_context_should_avoid_dual_frame (render_context))
grd_rdp_frame_set_avc_view_type (rdp_frame, GRD_RDP_FRAME_VIEW_TYPE_MAIN);
}
static void
handle_pending_buffer (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpRenderContext *render_context)
{
GrdRdpRenderer *renderer = surface_renderer->renderer;
GrdRdpFrameContext *frame_context;
GrdRdpBuffer *current_buffer = NULL;
GrdRdpBuffer *last_buffer = NULL;
GrdRdpFrame *rdp_frame;
frame_context = g_new0 (GrdRdpFrameContext, 1);
frame_context->surface_renderer = surface_renderer;
current_buffer = g_steal_pointer (&surface_renderer->pending_buffer);
frame_context->current_buffer = rdp_buffer_ref (surface_renderer,
current_buffer);
if (surface_renderer->last_buffer)
{
last_buffer = surface_renderer->last_buffer;
frame_context->last_buffer = rdp_buffer_ref (surface_renderer,
last_buffer);
}
rdp_frame = grd_rdp_frame_new (render_context,
current_buffer, last_buffer,
on_frame_picked_up, on_view_finalization,
on_frame_submission, on_frame_finalization,
frame_context, g_free);
maybe_downgrade_view_type (surface_renderer, render_context, rdp_frame);
grd_rdp_renderer_submit_frame (renderer, render_context, rdp_frame);
}
static void
handle_graphics_subsystem_failure (GrdRdpSurfaceRenderer *surface_renderer)
{
GrdSessionRdp *session_rdp =
grd_rdp_renderer_get_session (surface_renderer->renderer);
surface_renderer->graphics_subsystem_failed = TRUE;
grd_session_rdp_notify_error (session_rdp,
GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED);
}
static void
maybe_encode_pending_frame (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpRenderContext *render_context)
{
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
GrdSessionRdp *session_rdp =
grd_rdp_renderer_get_session (surface_renderer->renderer);
GrdRdpSessionMetrics *session_metrics =
grd_session_rdp_get_session_metrics (session_rdp);
GrdRdpLegacyBuffer *buffer;
buffer = g_steal_pointer (&rdp_surface->pending_framebuffer);
if (!grd_rdp_damage_detector_submit_new_framebuffer (rdp_surface->detector,
buffer))
{
grd_rdp_legacy_buffer_release (buffer);
handle_graphics_subsystem_failure (surface_renderer);
return;
}
if (!grd_rdp_damage_detector_is_region_damaged (rdp_surface->detector))
return;
if (!grd_rdp_renderer_render_frame (surface_renderer->renderer,
rdp_surface, render_context, buffer))
{
handle_graphics_subsystem_failure (surface_renderer);
return;
}
grd_rdp_session_metrics_notify_frame_transmission (session_metrics,
rdp_surface);
}
static gboolean
maybe_render_frame (gpointer user_data)
{
GrdRdpSurfaceRenderer *surface_renderer = user_data;
GrdRdpRenderer *renderer = surface_renderer->renderer;
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
GrdRdpAcquireContextFlags acquire_flags;
GrdRdpRenderContext *render_context;
g_autoptr (GMutexLocker) locker = NULL;
if (surface_renderer->graphics_subsystem_failed)
return G_SOURCE_CONTINUE;
locker = g_mutex_locker_new (&surface_renderer->render_mutex);
if (!surface_renderer->pending_buffer &&
!rdp_surface->pending_framebuffer)
return G_SOURCE_CONTINUE;
g_assert (!surface_renderer->pending_buffer ||
!rdp_surface->pending_framebuffer);
g_source_set_ready_time (surface_renderer->frame_upgrade_source, -1);
if (!can_prepare_new_frame (surface_renderer))
return G_SOURCE_CONTINUE;
acquire_flags = GRD_RDP_ACQUIRE_CONTEXT_FLAG_NONE;
if (surface_renderer->pending_render_context_reset)
acquire_flags |= GRD_RDP_ACQUIRE_CONTEXT_FLAG_FORCE_RESET;
render_context =
grd_rdp_renderer_try_acquire_render_context (renderer, rdp_surface,
acquire_flags);
if (!render_context)
return G_SOURCE_CONTINUE;
surface_renderer->pending_render_context_reset = FALSE;
if (surface_renderer->pending_buffer)
{
handle_pending_buffer (surface_renderer, render_context);
}
else if (rdp_surface->pending_framebuffer) /* Legacy render handling */
{
maybe_encode_pending_frame (surface_renderer, render_context);
grd_rdp_renderer_release_render_context (renderer, render_context);
}
else
{
g_assert_not_reached ();
}
return G_SOURCE_CONTINUE;
}
static gboolean
should_retry_frame_upgrade (GrdRdpSurfaceRenderer *surface_renderer)
{
/*
* should_avoid_auxiliary_frame() returned true. If the following conditions
* are true too, then the transition period from throttled to unthrottled is
* not over yet.
*/
return g_hash_table_size (surface_renderer->assigned_frame_slots) == 0 &&
surface_renderer->total_frame_slots == UNLIMITED_FRAME_SLOTS;
}
static void
upgrade_frame (GrdRdpSurfaceRenderer *surface_renderer,
GrdRdpRenderContext *render_context)
{
GrdRdpRenderer *renderer = surface_renderer->renderer;
GrdRdpFrameContext *frame_context;
GrdRdpFrame *rdp_frame;
frame_context = g_new0 (GrdRdpFrameContext, 1);
frame_context->surface_renderer = surface_renderer;
rdp_frame = grd_rdp_frame_new (render_context, NULL, NULL,
on_frame_picked_up, NULL,
on_frame_submission, on_frame_finalization,
frame_context, g_free);
grd_rdp_renderer_submit_frame (renderer, render_context, rdp_frame);
}
static gboolean
maybe_upgrade_frame (gpointer user_data)
{
GrdRdpSurfaceRenderer *surface_renderer = user_data;
GrdRdpRenderer *renderer = surface_renderer->renderer;
GrdRdpSurface *rdp_surface = surface_renderer->rdp_surface;
GrdRdpAcquireContextFlags acquire_flags;
GrdRdpRenderContext *render_context;
g_autoptr (GMutexLocker) locker = NULL;
locker = g_mutex_locker_new (&surface_renderer->render_mutex);
if (surface_renderer->pending_buffer)
return G_SOURCE_CONTINUE;
g_clear_pointer (&locker, g_mutex_locker_free);
if (!surface_renderer->pending_frame_upgrade)
return G_SOURCE_CONTINUE;
if (!can_prepare_new_frame (surface_renderer))
return G_SOURCE_CONTINUE;
if (should_avoid_auxiliary_frame (surface_renderer))
{
if (should_retry_frame_upgrade (surface_renderer))
trigger_frame_upgrade_schedule (surface_renderer);
return G_SOURCE_CONTINUE;
}
acquire_flags = GRD_RDP_ACQUIRE_CONTEXT_FLAG_RETAIN_OR_NULL;
render_context =
grd_rdp_renderer_try_acquire_render_context (renderer, rdp_surface,
acquire_flags);
if (!render_context)
return G_SOURCE_CONTINUE;
upgrade_frame (surface_renderer, render_context);
return G_SOURCE_CONTINUE;
}
static gboolean
trigger_frame_upgrade (gpointer user_data)
{
GrdRdpSurfaceRenderer *surface_renderer = user_data;
int64_t current_time_us;
if (g_source_get_ready_time (surface_renderer->frame_upgrade_source) != -1)
return G_SOURCE_CONTINUE;
current_time_us = g_source_get_time (surface_renderer->frame_upgrade_source);
g_source_set_ready_time (surface_renderer->frame_upgrade_source,
current_time_us + FRAME_UPGRADE_DELAY_US);
return G_SOURCE_CONTINUE;
}
static gboolean
source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
g_source_set_ready_time (source, -1);
return callback (user_data);
}
static GSourceFuncs source_funcs =
{
.dispatch = source_dispatch,
};
GrdRdpSurfaceRenderer *
grd_rdp_surface_renderer_new (GrdRdpSurface *rdp_surface,
GrdRdpRenderer *renderer,
uint32_t refresh_rate)
{
GMainContext *graphics_context =
grd_rdp_renderer_get_graphics_context (renderer);
GrdRdpSurfaceRenderer *surface_renderer;
GSource *object_unref_source;
GSource *render_source;
GSource *frame_upgrade_source;
GSource *trigger_frame_upgrade_source;
surface_renderer = g_object_new (GRD_TYPE_RDP_SURFACE_RENDERER, NULL);
surface_renderer->rdp_surface = rdp_surface;
surface_renderer->renderer = renderer;
surface_renderer->refresh_rate = refresh_rate;
object_unref_source = g_source_new (&source_funcs, sizeof (GSource));
g_source_set_callback (object_unref_source, unref_objects,
surface_renderer, NULL);
g_source_set_ready_time (object_unref_source, -1);
g_source_attach (object_unref_source, graphics_context);
surface_renderer->object_unref_source = object_unref_source;
render_source = g_source_new (&source_funcs, sizeof (GSource));
g_source_set_callback (render_source, maybe_render_frame,
surface_renderer, NULL);
g_source_set_ready_time (render_source, -1);
g_source_attach (render_source, graphics_context);
surface_renderer->render_source = render_source;
frame_upgrade_source = g_source_new (&source_funcs, sizeof (GSource));
g_source_set_callback (frame_upgrade_source, maybe_upgrade_frame,
surface_renderer, NULL);
g_source_set_ready_time (frame_upgrade_source, -1);
g_source_attach (frame_upgrade_source, graphics_context);
surface_renderer->frame_upgrade_source = frame_upgrade_source;
trigger_frame_upgrade_source = g_source_new (&source_funcs, sizeof (GSource));
g_source_set_callback (trigger_frame_upgrade_source, trigger_frame_upgrade,
surface_renderer, NULL);
g_source_set_ready_time (trigger_frame_upgrade_source, -1);
g_source_attach (trigger_frame_upgrade_source, graphics_context);
surface_renderer->trigger_frame_upgrade_source = trigger_frame_upgrade_source;
return surface_renderer;
}
static void
grd_rdp_surface_renderer_dispose (GObject *object)
{
GrdRdpSurfaceRenderer *surface_renderer = GRD_RDP_SURFACE_RENDERER (object);
/*
* No need to lock the render mutex here, the surface renderer outlives the
* PipeWire stream.
*/
if (surface_renderer->acquired_buffers)
g_assert (g_hash_table_size (surface_renderer->acquired_buffers) == 0);
if (surface_renderer->assigned_frame_slots)
g_assert (g_hash_table_size (surface_renderer->assigned_frame_slots) == 0);
g_assert (g_async_queue_try_pop (surface_renderer->unref_queue) == NULL);
if (surface_renderer->trigger_frame_upgrade_source)
{
g_source_destroy (surface_renderer->trigger_frame_upgrade_source);
g_clear_pointer (&surface_renderer->trigger_frame_upgrade_source, g_source_unref);
}
if (surface_renderer->frame_upgrade_source)
{
g_source_destroy (surface_renderer->frame_upgrade_source);
g_clear_pointer (&surface_renderer->frame_upgrade_source, g_source_unref);
}
if (surface_renderer->render_source)
{
g_source_destroy (surface_renderer->render_source);
g_clear_pointer (&surface_renderer->render_source, g_source_unref);
}
if (surface_renderer->object_unref_source)
{
g_source_destroy (surface_renderer->object_unref_source);
g_clear_pointer (&surface_renderer->object_unref_source, g_source_unref);
}
g_clear_pointer (&surface_renderer->assigned_frame_slots, g_hash_table_unref);
g_clear_pointer (&surface_renderer->acquired_buffers, g_hash_table_unref);
g_clear_pointer (&surface_renderer->registered_buffers, g_hash_table_unref);
g_clear_pointer (&surface_renderer->rdp_buffer_info, g_free);
G_OBJECT_CLASS (grd_rdp_surface_renderer_parent_class)->dispose (object);
}
static void
grd_rdp_surface_renderer_finalize (GObject *object)
{
GrdRdpSurfaceRenderer *surface_renderer = GRD_RDP_SURFACE_RENDERER (object);
g_clear_pointer (&surface_renderer->unref_queue, g_async_queue_unref);
g_mutex_clear (&surface_renderer->render_mutex);
G_OBJECT_CLASS (grd_rdp_surface_renderer_parent_class)->finalize (object);
}
static void
grd_rdp_surface_renderer_init (GrdRdpSurfaceRenderer *surface_renderer)
{
surface_renderer->total_frame_slots = UNLIMITED_FRAME_SLOTS;
surface_renderer->registered_buffers =
g_hash_table_new_full (NULL, NULL,
NULL, g_object_unref);
surface_renderer->acquired_buffers =
g_hash_table_new_full (NULL, NULL,
NULL, g_free);
surface_renderer->assigned_frame_slots =
g_hash_table_new (NULL, NULL);
surface_renderer->unref_queue = g_async_queue_new ();
g_mutex_init (&surface_renderer->render_mutex);
}
static void
grd_rdp_surface_renderer_class_init (GrdRdpSurfaceRendererClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = grd_rdp_surface_renderer_dispose;
object_class->finalize = grd_rdp_surface_renderer_finalize;
}