mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
1202 lines
38 KiB
C
1202 lines
38 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-renderer.h"
|
|
|
|
#include "grd-context.h"
|
|
#include "grd-encode-session.h"
|
|
#include "grd-hwaccel-nvidia.h"
|
|
#include "grd-hwaccel-vaapi.h"
|
|
#include "grd-hwaccel-vulkan.h"
|
|
#include "grd-rdp-dvc-graphics-pipeline.h"
|
|
#include "grd-rdp-frame.h"
|
|
#include "grd-rdp-private.h"
|
|
#include "grd-rdp-render-context.h"
|
|
#include "grd-rdp-server.h"
|
|
#include "grd-rdp-surface.h"
|
|
#include "grd-rdp-surface-renderer.h"
|
|
#include "grd-rdp-sw-encoder-ca.h"
|
|
#include "grd-rdp-view-creator.h"
|
|
#include "grd-session-rdp.h"
|
|
|
|
enum
|
|
{
|
|
INHIBITION_DONE,
|
|
GFX_INITABLE,
|
|
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint signals[N_SIGNALS];
|
|
|
|
struct _GrdRdpRenderer
|
|
{
|
|
GObject parent;
|
|
|
|
gboolean in_shutdown;
|
|
|
|
GrdSessionRdp *session_rdp;
|
|
GrdVkPhysicalDevice *vk_physical_device;
|
|
GrdVkDevice *vk_device;
|
|
GrdHwAccelVaapi *hwaccel_vaapi;
|
|
GrdRdpSwEncoderCa *encoder_ca;
|
|
|
|
GThread *graphics_thread;
|
|
GMainContext *graphics_context;
|
|
|
|
gboolean graphics_subsystem_failed;
|
|
|
|
gboolean stop_rendering;
|
|
GCond stop_rendering_cond;
|
|
|
|
gboolean rendering_inhibited;
|
|
gboolean output_suppressed;
|
|
|
|
gboolean pending_gfx_init;
|
|
gboolean pending_gfx_graphics_reset;
|
|
|
|
GMutex surface_renderers_mutex;
|
|
GHashTable *surface_renderer_table;
|
|
|
|
GMutex inhibition_mutex;
|
|
GHashTable *render_context_table;
|
|
GHashTable *acquired_render_contexts;
|
|
GHashTable *render_resource_mappings;
|
|
|
|
GSource *surface_disposal_source;
|
|
GAsyncQueue *disposal_queue;
|
|
|
|
GSource *surface_render_source;
|
|
GHashTable *queued_frames;
|
|
|
|
GMutex view_creations_mutex;
|
|
GHashTable *finished_view_creations;
|
|
|
|
GMutex frame_encodings_mutex;
|
|
GHashTable *finished_frame_encodings;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdRdpRenderer, grd_rdp_renderer, G_TYPE_OBJECT)
|
|
|
|
GMainContext *
|
|
grd_rdp_renderer_get_graphics_context (GrdRdpRenderer *renderer)
|
|
{
|
|
return renderer->graphics_context;
|
|
}
|
|
|
|
GrdSessionRdp *
|
|
grd_rdp_renderer_get_session (GrdRdpRenderer *renderer)
|
|
{
|
|
return renderer->session_rdp;
|
|
}
|
|
|
|
GrdVkDevice *
|
|
grd_rdp_renderer_get_vk_device (GrdRdpRenderer *renderer)
|
|
{
|
|
return renderer->vk_device;
|
|
}
|
|
|
|
GrdHwAccelVaapi *
|
|
grd_rdp_renderer_get_hwaccel_vaapi (GrdRdpRenderer *renderer)
|
|
{
|
|
return renderer->hwaccel_vaapi;
|
|
}
|
|
|
|
GrdRdpSwEncoderCa *
|
|
grd_rdp_renderer_get_encoder_ca (GrdRdpRenderer *renderer)
|
|
{
|
|
return renderer->encoder_ca;
|
|
}
|
|
|
|
static void
|
|
trigger_render_sources (GrdRdpRenderer *renderer)
|
|
{
|
|
GrdRdpSurfaceRenderer *surface_renderer = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GHashTableIter iter;
|
|
|
|
locker = g_mutex_locker_new (&renderer->surface_renderers_mutex);
|
|
g_hash_table_iter_init (&iter, renderer->surface_renderer_table);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &surface_renderer))
|
|
grd_rdp_surface_renderer_trigger_render_source (surface_renderer);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_update_output_suppression_state (GrdRdpRenderer *renderer,
|
|
gboolean suppress_output)
|
|
{
|
|
renderer->output_suppressed = suppress_output;
|
|
|
|
if (!renderer->output_suppressed)
|
|
trigger_render_sources (renderer);
|
|
}
|
|
|
|
static void
|
|
stop_rendering (GrdRdpRenderer *renderer)
|
|
{
|
|
g_mutex_lock (&renderer->inhibition_mutex);
|
|
renderer->stop_rendering = TRUE;
|
|
|
|
while (g_hash_table_size (renderer->acquired_render_contexts) > 0)
|
|
g_cond_wait (&renderer->stop_rendering_cond, &renderer->inhibition_mutex);
|
|
g_mutex_unlock (&renderer->inhibition_mutex);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_invoke_shutdown (GrdRdpRenderer *renderer)
|
|
{
|
|
g_assert (renderer->graphics_context);
|
|
|
|
stop_rendering (renderer);
|
|
|
|
renderer->in_shutdown = TRUE;
|
|
|
|
g_main_context_wakeup (renderer->graphics_context);
|
|
g_clear_pointer (&renderer->graphics_thread, g_thread_join);
|
|
}
|
|
|
|
static gpointer
|
|
graphics_thread_func (gpointer data)
|
|
{
|
|
GrdRdpRenderer *renderer = data;
|
|
GrdRdpServer *rdp_server = grd_session_rdp_get_server (renderer->session_rdp);
|
|
GrdHwAccelNvidia *hwaccel_nvidia =
|
|
grd_rdp_server_get_hwaccel_nvidia (rdp_server);
|
|
|
|
if (hwaccel_nvidia)
|
|
grd_hwaccel_nvidia_push_cuda_context (hwaccel_nvidia);
|
|
|
|
while (!renderer->in_shutdown)
|
|
g_main_context_iteration (renderer->graphics_context, TRUE);
|
|
|
|
if (hwaccel_nvidia)
|
|
grd_hwaccel_nvidia_pop_cuda_context (hwaccel_nvidia);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
maybe_initialize_hardware_acceleration (GrdRdpRenderer *renderer,
|
|
GrdHwAccelVulkan *hwaccel_vulkan)
|
|
{
|
|
GrdVkPhysicalDevice *vk_physical_device;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
vk_physical_device =
|
|
grd_hwaccel_vulkan_acquire_physical_device (hwaccel_vulkan, &error);
|
|
if (!vk_physical_device)
|
|
{
|
|
g_message ("[HWAccel.Vulkan] Could not acquire Vulkan physical "
|
|
"device: %s", error->message);
|
|
return TRUE;
|
|
}
|
|
renderer->vk_physical_device = vk_physical_device;
|
|
|
|
renderer->vk_device = grd_hwaccel_vulkan_acquire_device (hwaccel_vulkan,
|
|
vk_physical_device,
|
|
&error);
|
|
if (!renderer->vk_device)
|
|
{
|
|
g_warning ("[RDP] Failed to acquire Vulkan device: %s",
|
|
error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
renderer->hwaccel_vaapi = grd_hwaccel_vaapi_new (renderer->vk_device,
|
|
&error);
|
|
if (!renderer->hwaccel_vaapi)
|
|
{
|
|
g_message ("[RDP] Did not initialize VAAPI: %s", error->message);
|
|
g_clear_object (&renderer->vk_device);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
grd_rdp_renderer_start (GrdRdpRenderer *renderer)
|
|
{
|
|
GrdRdpServer *rdp_server = grd_session_rdp_get_server (renderer->session_rdp);
|
|
GrdHwAccelVulkan *hwaccel_vulkan =
|
|
grd_rdp_server_get_hwaccel_vulkan (rdp_server);
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (hwaccel_vulkan &&
|
|
!maybe_initialize_hardware_acceleration (renderer, hwaccel_vulkan))
|
|
return FALSE;
|
|
|
|
renderer->encoder_ca = grd_rdp_sw_encoder_ca_new (&error);
|
|
if (!renderer->encoder_ca)
|
|
{
|
|
g_warning ("[RDP] Failed to create fallback software renderer: %s",
|
|
error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
renderer->graphics_thread = g_thread_new ("RDP graphics thread",
|
|
graphics_thread_func,
|
|
renderer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GrdRdpDvcGraphicsPipeline *
|
|
graphics_pipeline_from_renderer (GrdRdpRenderer *renderer)
|
|
{
|
|
return grd_session_rdp_get_graphics_pipeline (renderer->session_rdp);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_notify_new_desktop_layout (GrdRdpRenderer *renderer,
|
|
uint32_t desktop_width,
|
|
uint32_t desktop_height)
|
|
{
|
|
rdpContext *rdp_context =
|
|
grd_session_rdp_get_rdp_context (renderer->session_rdp);
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
|
|
g_assert (graphics_pipeline_from_renderer (renderer));
|
|
renderer->pending_gfx_graphics_reset = TRUE;
|
|
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_DesktopWidth,
|
|
desktop_width);
|
|
freerdp_settings_set_uint32 (rdp_settings, FreeRDP_DesktopHeight,
|
|
desktop_height);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_notify_graphics_pipeline_ready (GrdRdpRenderer *renderer)
|
|
{
|
|
g_debug ("[RDP] Renderer: Received Graphics Pipeline ready notification");
|
|
|
|
renderer->pending_gfx_graphics_reset = TRUE;
|
|
renderer->pending_gfx_init = FALSE;
|
|
|
|
trigger_render_sources (renderer);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_notify_graphics_pipeline_reset (GrdRdpRenderer *renderer)
|
|
{
|
|
gboolean gfx_initable = FALSE;
|
|
|
|
g_debug ("[RDP] Renderer: Received Graphics Pipeline reset notification");
|
|
|
|
g_mutex_lock (&renderer->inhibition_mutex);
|
|
renderer->pending_gfx_init = TRUE;
|
|
|
|
if (g_hash_table_size (renderer->acquired_render_contexts) == 0)
|
|
gfx_initable = TRUE;
|
|
g_mutex_unlock (&renderer->inhibition_mutex);
|
|
|
|
if (gfx_initable)
|
|
g_signal_emit (renderer, signals[GFX_INITABLE], 0);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_inhibit_rendering (GrdRdpRenderer *renderer)
|
|
{
|
|
gboolean inhibition_done = FALSE;
|
|
|
|
g_mutex_lock (&renderer->inhibition_mutex);
|
|
renderer->rendering_inhibited = TRUE;
|
|
|
|
if (g_hash_table_size (renderer->acquired_render_contexts) == 0)
|
|
inhibition_done = TRUE;
|
|
g_mutex_unlock (&renderer->inhibition_mutex);
|
|
|
|
if (inhibition_done)
|
|
g_signal_emit (renderer, signals[INHIBITION_DONE], 0);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_uninhibit_rendering (GrdRdpRenderer *renderer)
|
|
{
|
|
renderer->rendering_inhibited = FALSE;
|
|
|
|
trigger_render_sources (renderer);
|
|
}
|
|
|
|
GrdRdpSurface *
|
|
grd_rdp_renderer_try_acquire_surface (GrdRdpRenderer *renderer,
|
|
uint32_t refresh_rate)
|
|
{
|
|
GrdRdpSurface *rdp_surface;
|
|
GrdRdpSurfaceRenderer *surface_renderer;
|
|
|
|
rdp_surface = grd_rdp_surface_new (renderer);
|
|
if (!rdp_surface)
|
|
return NULL;
|
|
|
|
surface_renderer = grd_rdp_surface_renderer_new (rdp_surface, renderer,
|
|
refresh_rate);
|
|
grd_rdp_surface_attach_surface_renderer (rdp_surface, surface_renderer);
|
|
|
|
g_mutex_lock (&renderer->surface_renderers_mutex);
|
|
g_hash_table_insert (renderer->surface_renderer_table,
|
|
rdp_surface, surface_renderer);
|
|
g_mutex_unlock (&renderer->surface_renderers_mutex);
|
|
|
|
return rdp_surface;
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_release_surface (GrdRdpRenderer *renderer,
|
|
GrdRdpSurface *rdp_surface)
|
|
{
|
|
g_assert (rdp_surface);
|
|
|
|
g_async_queue_push (renderer->disposal_queue, rdp_surface);
|
|
g_source_set_ready_time (renderer->surface_disposal_source, 0);
|
|
}
|
|
|
|
static void
|
|
invalidate_surfaces (GrdRdpRenderer *renderer,
|
|
gboolean locked)
|
|
{
|
|
GrdRdpSurfaceRenderer *surface_renderer = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GHashTableIter iter;
|
|
|
|
locker = g_mutex_locker_new (&renderer->surface_renderers_mutex);
|
|
g_hash_table_iter_init (&iter, renderer->surface_renderer_table);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &surface_renderer))
|
|
{
|
|
if (!locked)
|
|
grd_rdp_surface_renderer_invalidate_surface_unlocked (surface_renderer);
|
|
else
|
|
grd_rdp_surface_renderer_invalidate_surface (surface_renderer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear_render_contexts (GrdRdpRenderer *renderer,
|
|
gboolean locked)
|
|
{
|
|
g_assert (g_hash_table_size (renderer->acquired_render_contexts) == 0);
|
|
g_assert (g_hash_table_size (renderer->queued_frames) == 0);
|
|
|
|
invalidate_surfaces (renderer, locked);
|
|
|
|
g_hash_table_remove_all (renderer->render_resource_mappings);
|
|
g_hash_table_remove_all (renderer->render_context_table);
|
|
}
|
|
|
|
static void
|
|
maybe_reset_graphics (GrdRdpRenderer *renderer)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline =
|
|
graphics_pipeline_from_renderer (renderer);
|
|
rdpContext *rdp_context =
|
|
grd_session_rdp_get_rdp_context (renderer->session_rdp);
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
uint32_t desktop_width =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopWidth);
|
|
uint32_t desktop_height =
|
|
freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopHeight);
|
|
g_autofree MONITOR_DEF *monitor_defs = NULL;
|
|
uint32_t n_monitors;
|
|
uint32_t i;
|
|
|
|
if (!renderer->pending_gfx_graphics_reset)
|
|
return;
|
|
|
|
clear_render_contexts (renderer, FALSE);
|
|
|
|
n_monitors = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_MonitorCount);
|
|
g_assert (n_monitors > 0);
|
|
|
|
monitor_defs = g_new0 (MONITOR_DEF, n_monitors);
|
|
|
|
for (i = 0; i < n_monitors; ++i)
|
|
{
|
|
const rdpMonitor *monitor =
|
|
freerdp_settings_get_pointer_array (rdp_settings,
|
|
FreeRDP_MonitorDefArray, i);
|
|
MONITOR_DEF *monitor_def = &monitor_defs[i];
|
|
|
|
monitor_def->left = monitor->x;
|
|
monitor_def->top = monitor->y;
|
|
monitor_def->right = monitor_def->left + monitor->width - 1;
|
|
monitor_def->bottom = monitor_def->top + monitor->height - 1;
|
|
|
|
if (monitor->is_primary)
|
|
monitor_def->flags = MONITOR_PRIMARY;
|
|
}
|
|
|
|
grd_rdp_dvc_graphics_pipeline_reset_graphics (graphics_pipeline,
|
|
desktop_width, desktop_height,
|
|
monitor_defs, n_monitors);
|
|
renderer->pending_gfx_graphics_reset = FALSE;
|
|
}
|
|
|
|
static void
|
|
destroy_render_context_locked (GrdRdpRenderer *renderer,
|
|
GrdRdpSurface *rdp_surface)
|
|
{
|
|
GrdRdpRenderContext *render_context = NULL;
|
|
|
|
if (!g_hash_table_lookup_extended (renderer->render_context_table,
|
|
rdp_surface,
|
|
NULL, (gpointer *) &render_context))
|
|
return;
|
|
|
|
g_assert (render_context);
|
|
g_assert (!g_hash_table_contains (renderer->acquired_render_contexts,
|
|
render_context));
|
|
g_assert (!g_hash_table_contains (renderer->queued_frames,
|
|
render_context));
|
|
|
|
g_hash_table_remove (renderer->render_resource_mappings, render_context);
|
|
g_hash_table_remove (renderer->render_context_table, rdp_surface);
|
|
}
|
|
|
|
static void
|
|
destroy_render_context (GrdRdpRenderer *renderer,
|
|
GrdRdpSurface *rdp_surface)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&renderer->inhibition_mutex);
|
|
destroy_render_context_locked (renderer, rdp_surface);
|
|
}
|
|
|
|
static GrdRdpRenderContext *
|
|
render_context_ref (GrdRdpRenderer *renderer,
|
|
GrdRdpRenderContext *render_context)
|
|
{
|
|
uint32_t *ref_count = NULL;
|
|
|
|
if (g_hash_table_lookup_extended (renderer->acquired_render_contexts,
|
|
render_context,
|
|
NULL, (gpointer *) &ref_count))
|
|
{
|
|
g_assert (*ref_count > 0);
|
|
++(*ref_count);
|
|
|
|
return render_context;
|
|
}
|
|
|
|
ref_count = g_new0 (uint32_t, 1);
|
|
*ref_count = 1;
|
|
|
|
g_hash_table_insert (renderer->acquired_render_contexts,
|
|
render_context, ref_count);
|
|
|
|
return render_context;
|
|
}
|
|
|
|
static void
|
|
render_context_unref (GrdRdpRenderer *renderer,
|
|
GrdRdpRenderContext *render_context)
|
|
{
|
|
uint32_t *ref_count = NULL;
|
|
|
|
if (!g_hash_table_lookup_extended (renderer->acquired_render_contexts,
|
|
render_context,
|
|
NULL, (gpointer *) &ref_count))
|
|
g_assert_not_reached ();
|
|
|
|
g_assert (*ref_count > 0);
|
|
--(*ref_count);
|
|
|
|
if (*ref_count == 0)
|
|
g_hash_table_remove (renderer->acquired_render_contexts, render_context);
|
|
}
|
|
|
|
static void
|
|
handle_graphics_subsystem_failure (GrdRdpRenderer *renderer)
|
|
{
|
|
renderer->graphics_subsystem_failed = TRUE;
|
|
renderer->stop_rendering = TRUE;
|
|
|
|
grd_session_rdp_notify_error (renderer->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED);
|
|
}
|
|
|
|
GrdRdpRenderContext *
|
|
grd_rdp_renderer_try_acquire_render_context (GrdRdpRenderer *renderer,
|
|
GrdRdpSurface *rdp_surface,
|
|
GrdRdpAcquireContextFlags flags)
|
|
{
|
|
GrdRdpRenderContext *render_context = NULL;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
g_assert (!((flags & GRD_RDP_ACQUIRE_CONTEXT_FLAG_FORCE_RESET) &&
|
|
(flags & GRD_RDP_ACQUIRE_CONTEXT_FLAG_RETAIN_OR_NULL)));
|
|
|
|
locker = g_mutex_locker_new (&renderer->inhibition_mutex);
|
|
if (renderer->stop_rendering ||
|
|
renderer->rendering_inhibited ||
|
|
renderer->pending_gfx_init ||
|
|
renderer->output_suppressed)
|
|
return NULL;
|
|
|
|
maybe_reset_graphics (renderer);
|
|
|
|
if (flags & GRD_RDP_ACQUIRE_CONTEXT_FLAG_FORCE_RESET)
|
|
destroy_render_context_locked (renderer, rdp_surface);
|
|
|
|
if (g_hash_table_lookup_extended (renderer->render_context_table, rdp_surface,
|
|
NULL, (gpointer *) &render_context))
|
|
return render_context_ref (renderer, render_context);
|
|
|
|
if (flags & GRD_RDP_ACQUIRE_CONTEXT_FLAG_RETAIN_OR_NULL)
|
|
return NULL;
|
|
|
|
render_context = grd_rdp_render_context_new (renderer, rdp_surface);
|
|
if (!render_context)
|
|
{
|
|
handle_graphics_subsystem_failure (renderer);
|
|
return NULL;
|
|
}
|
|
|
|
g_hash_table_insert (renderer->render_context_table,
|
|
rdp_surface, render_context);
|
|
g_hash_table_insert (renderer->render_resource_mappings,
|
|
render_context, g_hash_table_new (NULL, NULL));
|
|
|
|
return render_context_ref (renderer, render_context);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_release_render_context (GrdRdpRenderer *renderer,
|
|
GrdRdpRenderContext *render_context)
|
|
{
|
|
gboolean inhibition_done = FALSE;
|
|
gboolean gfx_initable = FALSE;
|
|
|
|
g_mutex_lock (&renderer->inhibition_mutex);
|
|
render_context_unref (renderer, render_context);
|
|
|
|
if (renderer->stop_rendering &&
|
|
g_hash_table_size (renderer->acquired_render_contexts) == 0)
|
|
g_cond_signal (&renderer->stop_rendering_cond);
|
|
|
|
if (renderer->rendering_inhibited &&
|
|
g_hash_table_size (renderer->acquired_render_contexts) == 0)
|
|
inhibition_done = TRUE;
|
|
|
|
if (renderer->pending_gfx_init &&
|
|
g_hash_table_size (renderer->acquired_render_contexts) == 0)
|
|
gfx_initable = TRUE;
|
|
g_mutex_unlock (&renderer->inhibition_mutex);
|
|
|
|
if (inhibition_done)
|
|
g_signal_emit (renderer, signals[INHIBITION_DONE], 0);
|
|
if (gfx_initable)
|
|
g_signal_emit (renderer, signals[GFX_INITABLE], 0);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_clear_render_contexts (GrdRdpRenderer *renderer)
|
|
{
|
|
clear_render_contexts (renderer, TRUE);
|
|
}
|
|
|
|
static void
|
|
queue_prepared_frame (GrdRdpRenderer *renderer,
|
|
GrdRdpRenderContext *render_context,
|
|
GrdRdpFrame *rdp_frame)
|
|
{
|
|
GHashTable *acquired_resources = NULL;
|
|
GrdRdpViewCreator *view_creator;
|
|
|
|
if (!g_hash_table_lookup_extended (renderer->render_resource_mappings,
|
|
render_context,
|
|
NULL, (gpointer *) &acquired_resources))
|
|
g_assert_not_reached ();
|
|
|
|
view_creator = grd_rdp_render_context_get_view_creator (render_context);
|
|
g_assert (!g_hash_table_contains (acquired_resources, view_creator));
|
|
|
|
grd_rdp_frame_notify_picked_up (rdp_frame);
|
|
g_hash_table_insert (acquired_resources, view_creator, rdp_frame);
|
|
|
|
g_mutex_lock (&renderer->view_creations_mutex);
|
|
g_hash_table_add (renderer->finished_view_creations, rdp_frame);
|
|
g_mutex_unlock (&renderer->view_creations_mutex);
|
|
}
|
|
|
|
void
|
|
grd_rdp_renderer_submit_frame (GrdRdpRenderer *renderer,
|
|
GrdRdpRenderContext *render_context,
|
|
GrdRdpFrame *rdp_frame)
|
|
{
|
|
grd_rdp_frame_set_renderer (rdp_frame, renderer);
|
|
|
|
if (grd_rdp_frame_has_valid_view (rdp_frame))
|
|
queue_prepared_frame (renderer, render_context, rdp_frame);
|
|
else
|
|
g_hash_table_insert (renderer->queued_frames, render_context, rdp_frame);
|
|
|
|
g_source_set_ready_time (renderer->surface_render_source, 0);
|
|
}
|
|
|
|
gboolean
|
|
grd_rdp_renderer_render_frame (GrdRdpRenderer *renderer,
|
|
GrdRdpSurface *rdp_surface,
|
|
GrdRdpRenderContext *render_context,
|
|
GrdRdpLegacyBuffer *buffer)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline =
|
|
graphics_pipeline_from_renderer (renderer);
|
|
|
|
return grd_rdp_dvc_graphics_pipeline_refresh_gfx (graphics_pipeline,
|
|
rdp_surface, render_context,
|
|
buffer);
|
|
}
|
|
|
|
GrdRdpRenderer *
|
|
grd_rdp_renderer_new (GrdSessionRdp *session_rdp)
|
|
{
|
|
GrdRdpRenderer *renderer;
|
|
|
|
renderer = g_object_new (GRD_TYPE_RDP_RENDERER, NULL);
|
|
renderer->session_rdp = session_rdp;
|
|
|
|
return renderer;
|
|
}
|
|
|
|
static gboolean
|
|
dispose_surfaces (gpointer user_data)
|
|
{
|
|
GrdRdpRenderer *renderer = user_data;
|
|
GrdRdpSurface *rdp_surface;
|
|
|
|
while ((rdp_surface = g_async_queue_try_pop (renderer->disposal_queue)))
|
|
{
|
|
destroy_render_context (renderer, rdp_surface);
|
|
|
|
g_mutex_lock (&renderer->surface_renderers_mutex);
|
|
g_hash_table_remove (renderer->surface_renderer_table, rdp_surface);
|
|
g_mutex_unlock (&renderer->surface_renderers_mutex);
|
|
|
|
grd_rdp_surface_free (rdp_surface);
|
|
}
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
grd_rdp_renderer_dispose (GObject *object)
|
|
{
|
|
GrdRdpRenderer *renderer = GRD_RDP_RENDERER (object);
|
|
|
|
grd_rdp_renderer_invoke_shutdown (renderer);
|
|
|
|
if (renderer->acquired_render_contexts)
|
|
g_assert (g_hash_table_size (renderer->acquired_render_contexts) == 0);
|
|
if (renderer->render_resource_mappings)
|
|
g_assert (g_hash_table_size (renderer->render_resource_mappings) == 0);
|
|
if (renderer->queued_frames)
|
|
g_assert (g_hash_table_size (renderer->queued_frames) == 0);
|
|
if (renderer->finished_view_creations)
|
|
g_assert (g_hash_table_size (renderer->finished_view_creations) == 0);
|
|
if (renderer->finished_frame_encodings)
|
|
g_assert (g_hash_table_size (renderer->finished_frame_encodings) == 0);
|
|
|
|
if (renderer->surface_render_source)
|
|
{
|
|
g_source_destroy (renderer->surface_render_source);
|
|
g_clear_pointer (&renderer->surface_render_source, g_source_unref);
|
|
}
|
|
if (renderer->surface_disposal_source)
|
|
{
|
|
g_source_destroy (renderer->surface_disposal_source);
|
|
g_clear_pointer (&renderer->surface_disposal_source, g_source_unref);
|
|
}
|
|
|
|
g_clear_pointer (&renderer->graphics_context, g_main_context_unref);
|
|
dispose_surfaces (renderer);
|
|
|
|
g_clear_pointer (&renderer->finished_frame_encodings, g_hash_table_unref);
|
|
g_clear_pointer (&renderer->finished_view_creations, g_hash_table_unref);
|
|
g_clear_pointer (&renderer->queued_frames, g_hash_table_unref);
|
|
g_clear_pointer (&renderer->render_resource_mappings, g_hash_table_unref);
|
|
g_clear_pointer (&renderer->acquired_render_contexts, g_hash_table_unref);
|
|
g_clear_pointer (&renderer->render_context_table, g_hash_table_unref);
|
|
|
|
g_clear_pointer (&renderer->disposal_queue, g_async_queue_unref);
|
|
|
|
g_assert (g_hash_table_size (renderer->surface_renderer_table) == 0);
|
|
|
|
g_clear_object (&renderer->encoder_ca);
|
|
g_clear_object (&renderer->hwaccel_vaapi);
|
|
g_clear_object (&renderer->vk_device);
|
|
g_clear_object (&renderer->vk_physical_device);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_renderer_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_renderer_finalize (GObject *object)
|
|
{
|
|
GrdRdpRenderer *renderer = GRD_RDP_RENDERER (object);
|
|
|
|
g_mutex_clear (&renderer->frame_encodings_mutex);
|
|
g_mutex_clear (&renderer->view_creations_mutex);
|
|
|
|
g_cond_clear (&renderer->stop_rendering_cond);
|
|
g_mutex_clear (&renderer->inhibition_mutex);
|
|
g_mutex_clear (&renderer->surface_renderers_mutex);
|
|
|
|
g_clear_pointer (&renderer->surface_renderer_table, g_hash_table_unref);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_renderer_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
release_acquired_resource (GrdRdpRenderer *renderer,
|
|
GrdRdpRenderContext *render_context,
|
|
gpointer resource)
|
|
{
|
|
GHashTable *acquired_resources = NULL;
|
|
|
|
if (!g_hash_table_lookup_extended (renderer->render_resource_mappings,
|
|
render_context,
|
|
NULL, (gpointer *) &acquired_resources))
|
|
g_assert_not_reached ();
|
|
|
|
if (!g_hash_table_remove (acquired_resources, resource))
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static GList *
|
|
fetch_rendered_frames (GrdRdpRenderer *renderer)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GrdRdpFrame *rdp_frame = NULL;
|
|
GHashTableIter iter;
|
|
GList *rendered_frames;
|
|
|
|
locker = g_mutex_locker_new (&renderer->frame_encodings_mutex);
|
|
g_hash_table_iter_init (&iter, renderer->finished_frame_encodings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &rdp_frame))
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdEncodeSession *encode_session =
|
|
grd_rdp_render_context_get_encode_session (render_context);
|
|
|
|
release_acquired_resource (renderer, render_context, encode_session);
|
|
|
|
if (!grd_rdp_frame_get_bitstreams (rdp_frame))
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
|
|
rendered_frames =
|
|
g_hash_table_get_values (renderer->finished_frame_encodings);
|
|
g_hash_table_steal_all (renderer->finished_frame_encodings);
|
|
|
|
return rendered_frames;
|
|
}
|
|
|
|
static void
|
|
on_bitstream_locked (GrdEncodeSession *encode_session,
|
|
GrdBitstream *bitstream,
|
|
gpointer user_data,
|
|
GError *error)
|
|
{
|
|
GrdRdpFrame *rdp_frame = user_data;
|
|
GrdRdpRenderer *renderer = grd_rdp_frame_get_renderer (rdp_frame);
|
|
|
|
if (bitstream)
|
|
{
|
|
GList *bitstreams = grd_rdp_frame_get_bitstreams (rdp_frame);
|
|
|
|
bitstreams = g_list_append (bitstreams, bitstream);
|
|
grd_rdp_frame_set_bitstreams (rdp_frame, bitstreams);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("[RDP] Failed to lock bitstream: %s", error->message);
|
|
}
|
|
|
|
g_mutex_lock (&renderer->frame_encodings_mutex);
|
|
if (!grd_encode_session_has_pending_frames (encode_session))
|
|
g_hash_table_add (renderer->finished_frame_encodings, rdp_frame);
|
|
|
|
if (!bitstream)
|
|
renderer->graphics_subsystem_failed = TRUE;
|
|
g_mutex_unlock (&renderer->frame_encodings_mutex);
|
|
|
|
g_source_set_ready_time (renderer->surface_render_source, 0);
|
|
}
|
|
|
|
static gboolean
|
|
encode_image_views (GrdRdpRenderer *renderer,
|
|
GrdRdpFrame *rdp_frame)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
gboolean submitted_frame = FALSE;
|
|
g_autoptr (GError) error = NULL;
|
|
GrdImageView *image_view;
|
|
|
|
locker = g_mutex_locker_new (&renderer->frame_encodings_mutex);
|
|
while ((image_view = grd_rdp_frame_pop_image_view (rdp_frame)))
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdEncodeSession *encode_session =
|
|
grd_rdp_render_context_get_encode_session (render_context);
|
|
GrdEncodeContext *encode_context =
|
|
grd_rdp_frame_get_encode_context (rdp_frame);
|
|
|
|
if (!grd_encode_session_encode_frame (encode_session, encode_context,
|
|
image_view, &error))
|
|
{
|
|
g_warning ("[RDP] Failed to encode frame: %s", error->message);
|
|
|
|
if (!submitted_frame)
|
|
{
|
|
release_acquired_resource (renderer, render_context,
|
|
encode_session);
|
|
grd_rdp_frame_free (rdp_frame);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
submitted_frame = TRUE;
|
|
|
|
grd_encode_session_lock_bitstream (encode_session, image_view,
|
|
on_bitstream_locked, rdp_frame);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
maybe_start_encodings (GrdRdpRenderer *renderer)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GrdRdpFrame *rdp_frame = NULL;
|
|
GHashTableIter iter;
|
|
|
|
if (renderer->stop_rendering)
|
|
return TRUE;
|
|
|
|
locker = g_mutex_locker_new (&renderer->view_creations_mutex);
|
|
if (renderer->graphics_subsystem_failed)
|
|
return FALSE;
|
|
|
|
g_hash_table_iter_init (&iter, renderer->finished_view_creations);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &rdp_frame))
|
|
{
|
|
GHashTable *acquired_resources = NULL;
|
|
GrdRdpRenderContext *render_context;
|
|
GrdRdpViewCreator *view_creator;
|
|
GrdEncodeSession *encode_session;
|
|
|
|
g_assert (grd_rdp_frame_has_valid_view (rdp_frame));
|
|
|
|
render_context = grd_rdp_frame_get_render_context (rdp_frame);
|
|
view_creator = grd_rdp_render_context_get_view_creator (render_context);
|
|
|
|
if (!grd_rdp_frame_is_surface_damaged (rdp_frame))
|
|
{
|
|
release_acquired_resource (renderer, render_context, view_creator);
|
|
g_hash_table_iter_remove (&iter);
|
|
continue;
|
|
}
|
|
|
|
if (!g_hash_table_lookup_extended (renderer->render_resource_mappings,
|
|
render_context,
|
|
NULL, (gpointer *) &acquired_resources))
|
|
g_assert_not_reached ();
|
|
|
|
encode_session =
|
|
grd_rdp_render_context_get_encode_session (render_context);
|
|
if (g_hash_table_contains (acquired_resources, encode_session))
|
|
continue;
|
|
|
|
release_acquired_resource (renderer, render_context, view_creator);
|
|
g_hash_table_insert (acquired_resources, encode_session, rdp_frame);
|
|
g_hash_table_iter_steal (&iter);
|
|
|
|
if (!encode_image_views (renderer, rdp_frame))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
on_view_created (GrdRdpFrame *rdp_frame,
|
|
GError *error)
|
|
{
|
|
GrdRdpRenderer *renderer = grd_rdp_frame_get_renderer (rdp_frame);
|
|
|
|
if (!grd_rdp_frame_has_valid_view (rdp_frame))
|
|
g_warning ("[RDP] Failed to create image view: %s", error->message);
|
|
|
|
g_mutex_lock (&renderer->view_creations_mutex);
|
|
g_hash_table_add (renderer->finished_view_creations, rdp_frame);
|
|
|
|
if (!grd_rdp_frame_has_valid_view (rdp_frame))
|
|
renderer->graphics_subsystem_failed = TRUE;
|
|
g_mutex_unlock (&renderer->view_creations_mutex);
|
|
|
|
g_source_set_ready_time (renderer->surface_render_source, 0);
|
|
}
|
|
|
|
static gboolean
|
|
maybe_start_view_creations (GrdRdpRenderer *renderer)
|
|
{
|
|
GrdRdpFrame *rdp_frame = NULL;
|
|
GHashTableIter iter;
|
|
|
|
if (renderer->stop_rendering)
|
|
return TRUE;
|
|
|
|
g_hash_table_iter_init (&iter, renderer->queued_frames);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &rdp_frame))
|
|
{
|
|
GHashTable *acquired_resources = NULL;
|
|
GrdRdpRenderContext *render_context;
|
|
GrdRdpViewCreator *view_creator;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
render_context = grd_rdp_frame_get_render_context (rdp_frame);
|
|
if (!g_hash_table_lookup_extended (renderer->render_resource_mappings,
|
|
render_context,
|
|
NULL, (gpointer *) &acquired_resources))
|
|
g_assert_not_reached ();
|
|
|
|
view_creator = grd_rdp_render_context_get_view_creator (render_context);
|
|
if (g_hash_table_contains (acquired_resources, view_creator))
|
|
continue;
|
|
|
|
grd_rdp_frame_notify_picked_up (rdp_frame);
|
|
|
|
/* There is no resource to release here (no predecessor) */
|
|
g_hash_table_insert (acquired_resources, view_creator, rdp_frame);
|
|
g_hash_table_iter_steal (&iter);
|
|
|
|
if (!grd_rdp_view_creator_create_view (view_creator, rdp_frame,
|
|
on_view_created, &error))
|
|
{
|
|
g_warning ("[RDP] Failed to create view: %s", error->message);
|
|
grd_rdp_frame_free (rdp_frame);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
submit_rendered_frames (GrdRdpRenderer *renderer,
|
|
GList *frames)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline =
|
|
graphics_pipeline_from_renderer (renderer);
|
|
GList *l;
|
|
|
|
for (l = frames; l; l = l->next)
|
|
{
|
|
GrdRdpFrame *rdp_frame = l->data;
|
|
|
|
grd_rdp_dvc_graphics_pipeline_submit_frame (graphics_pipeline,
|
|
rdp_frame);
|
|
grd_rdp_frame_notify_frame_submission (rdp_frame);
|
|
}
|
|
}
|
|
|
|
static void
|
|
release_bitstreams (gpointer data,
|
|
gpointer user_data)
|
|
{
|
|
GrdRdpRenderer *renderer = user_data;
|
|
GrdRdpFrame *rdp_frame = data;
|
|
GList *bitstreams = grd_rdp_frame_get_bitstreams (rdp_frame);
|
|
GList *l;
|
|
|
|
for (l = bitstreams; l; l = l->next)
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdEncodeSession *encode_session =
|
|
grd_rdp_render_context_get_encode_session (render_context);
|
|
GrdBitstream *bitstream = l->data;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!grd_encode_session_unlock_bitstream (encode_session, bitstream,
|
|
&error))
|
|
{
|
|
g_warning ("[RDP] Renderer: Failed to unlock bitstream %s",
|
|
error->message);
|
|
handle_graphics_subsystem_failure (renderer);
|
|
}
|
|
}
|
|
g_list_free (bitstreams);
|
|
|
|
grd_rdp_frame_set_bitstreams (rdp_frame, NULL);
|
|
}
|
|
|
|
static void
|
|
clear_pending_frames (GrdRdpRenderer *renderer)
|
|
{
|
|
GrdRdpFrame *rdp_frame = NULL;
|
|
GHashTableIter iter;
|
|
|
|
g_hash_table_remove_all (renderer->queued_frames);
|
|
|
|
g_mutex_lock (&renderer->view_creations_mutex);
|
|
g_hash_table_iter_init (&iter, renderer->finished_view_creations);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &rdp_frame))
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdRdpViewCreator *view_creator =
|
|
grd_rdp_render_context_get_view_creator (render_context);
|
|
|
|
release_acquired_resource (renderer, render_context, view_creator);
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
g_mutex_unlock (&renderer->view_creations_mutex);
|
|
|
|
g_mutex_lock (&renderer->frame_encodings_mutex);
|
|
g_hash_table_iter_init (&iter, renderer->finished_frame_encodings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &rdp_frame))
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdEncodeSession *encode_session =
|
|
grd_rdp_render_context_get_encode_session (render_context);
|
|
|
|
release_acquired_resource (renderer, render_context, encode_session);
|
|
release_bitstreams (rdp_frame, renderer);
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
g_mutex_unlock (&renderer->frame_encodings_mutex);
|
|
}
|
|
|
|
static gboolean
|
|
render_surfaces (gpointer user_data)
|
|
{
|
|
GrdRdpRenderer *renderer = user_data;
|
|
GList *finished_frames;
|
|
|
|
finished_frames = fetch_rendered_frames (renderer);
|
|
|
|
if (!maybe_start_encodings (renderer) ||
|
|
!maybe_start_view_creations (renderer))
|
|
handle_graphics_subsystem_failure (renderer);
|
|
|
|
if (!renderer->stop_rendering)
|
|
submit_rendered_frames (renderer, finished_frames);
|
|
else
|
|
clear_pending_frames (renderer);
|
|
|
|
g_list_foreach (finished_frames, release_bitstreams, renderer);
|
|
g_clear_list (&finished_frames, (GDestroyNotify) grd_rdp_frame_free);
|
|
|
|
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,
|
|
};
|
|
|
|
static void
|
|
grd_rdp_renderer_init (GrdRdpRenderer *renderer)
|
|
{
|
|
GSource *surface_disposal_source;
|
|
GSource *surface_render_source;
|
|
|
|
renderer->surface_renderer_table = g_hash_table_new (NULL, NULL);
|
|
renderer->render_context_table = g_hash_table_new_full (NULL, NULL,
|
|
NULL, g_object_unref);
|
|
renderer->acquired_render_contexts = g_hash_table_new_full (NULL, NULL,
|
|
NULL, g_free);
|
|
renderer->render_resource_mappings =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) g_hash_table_unref);
|
|
renderer->disposal_queue = g_async_queue_new ();
|
|
|
|
renderer->queued_frames =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) grd_rdp_frame_free);
|
|
renderer->finished_view_creations =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) grd_rdp_frame_free);
|
|
renderer->finished_frame_encodings =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) grd_rdp_frame_free);
|
|
|
|
g_mutex_init (&renderer->surface_renderers_mutex);
|
|
g_mutex_init (&renderer->inhibition_mutex);
|
|
g_cond_init (&renderer->stop_rendering_cond);
|
|
|
|
g_mutex_init (&renderer->view_creations_mutex);
|
|
g_mutex_init (&renderer->frame_encodings_mutex);
|
|
|
|
renderer->graphics_context = g_main_context_new ();
|
|
|
|
surface_disposal_source = g_source_new (&source_funcs, sizeof (GSource));
|
|
g_source_set_callback (surface_disposal_source, dispose_surfaces,
|
|
renderer, NULL);
|
|
g_source_set_ready_time (surface_disposal_source, -1);
|
|
g_source_attach (surface_disposal_source, renderer->graphics_context);
|
|
renderer->surface_disposal_source = surface_disposal_source;
|
|
|
|
surface_render_source = g_source_new (&source_funcs, sizeof (GSource));
|
|
g_source_set_callback (surface_render_source, render_surfaces,
|
|
renderer, NULL);
|
|
g_source_set_ready_time (surface_render_source, -1);
|
|
g_source_attach (surface_render_source, renderer->graphics_context);
|
|
renderer->surface_render_source = surface_render_source;
|
|
}
|
|
|
|
static void
|
|
grd_rdp_renderer_class_init (GrdRdpRendererClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = grd_rdp_renderer_dispose;
|
|
object_class->finalize = grd_rdp_renderer_finalize;
|
|
|
|
signals[INHIBITION_DONE] = g_signal_new ("inhibition-done",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
signals[GFX_INITABLE] = g_signal_new ("gfx-initable",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|