mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
2288 lines
82 KiB
C
2288 lines
82 KiB
C
/*
|
|
* Copyright (C) 2021 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-dvc-graphics-pipeline.h"
|
|
|
|
#include <cairo/cairo.h>
|
|
#include <freerdp/server/rdpgfx.h>
|
|
#include <winpr/sysinfo.h>
|
|
|
|
#include "grd-avc-frame-info.h"
|
|
#include "grd-bitstream.h"
|
|
#include "grd-hwaccel-nvidia.h"
|
|
#include "grd-rdp-damage-detector.h"
|
|
#include "grd-rdp-frame.h"
|
|
#include "grd-rdp-frame-info.h"
|
|
#include "grd-rdp-gfx-frame-controller.h"
|
|
#include "grd-rdp-gfx-surface.h"
|
|
#include "grd-rdp-legacy-buffer.h"
|
|
#include "grd-rdp-network-autodetection.h"
|
|
#include "grd-rdp-render-context.h"
|
|
#include "grd-rdp-renderer.h"
|
|
#include "grd-rdp-surface.h"
|
|
#include "grd-rdp-surface-renderer.h"
|
|
#include "grd-session-rdp.h"
|
|
#include "grd-utils.h"
|
|
|
|
#define PROTOCOL_TIMEOUT_MS (10 * 1000)
|
|
|
|
#define ENC_TIMES_CHECK_INTERVAL_MS 1000
|
|
#define MAX_TRACKED_ENC_FRAMES 1000
|
|
#define MIN_BW_MEASURE_SIZE (10 * 1024)
|
|
|
|
typedef enum _HwAccelAPI
|
|
{
|
|
HW_ACCEL_API_NONE = 0,
|
|
HW_ACCEL_API_NVENC = 1 << 0,
|
|
} HwAccelAPI;
|
|
|
|
typedef struct _HWAccelContext
|
|
{
|
|
HwAccelAPI api;
|
|
uint32_t encode_session_id;
|
|
gboolean has_first_frame;
|
|
} HWAccelContext;
|
|
|
|
typedef struct _GfxSurfaceContext
|
|
{
|
|
GrdRdpGfxSurface *gfx_surface;
|
|
uint64_t ref_count;
|
|
} GfxSurfaceContext;
|
|
|
|
typedef struct _GfxFrameInfo
|
|
{
|
|
GrdRdpFrameInfo frame_info;
|
|
uint32_t surface_serial;
|
|
} GfxFrameInfo;
|
|
|
|
struct _GrdRdpDvcGraphicsPipeline
|
|
{
|
|
GrdRdpDvc parent;
|
|
|
|
RdpgfxServerContext *rdpgfx_context;
|
|
gboolean channel_opened;
|
|
gboolean channel_unavailable;
|
|
gboolean received_first_cap_sets;
|
|
gboolean initialized;
|
|
uint32_t initial_version;
|
|
|
|
GrdSessionRdp *session_rdp;
|
|
GrdRdpRenderer *renderer;
|
|
GrdRdpNetworkAutodetection *network_autodetection;
|
|
wStream *encode_stream;
|
|
RFX_CONTEXT *rfx_context;
|
|
|
|
GSource *protocol_timeout_source;
|
|
|
|
GSource *protocol_reset_source;
|
|
GMutex caps_mutex;
|
|
RDPGFX_CAPSET *cap_sets;
|
|
uint16_t n_cap_sets;
|
|
|
|
GMutex gfx_mutex;
|
|
GHashTable *surface_table;
|
|
GHashTable *codec_context_table;
|
|
|
|
/* Unacknowledged Frames ADM element ([MS-RDPEGFX] 3.2.1.2) */
|
|
GHashTable *frame_serial_table;
|
|
|
|
GHashTable *serial_surface_table;
|
|
gboolean frame_acks_suspended;
|
|
|
|
GQueue *encoded_frames;
|
|
uint32_t total_frames_encoded;
|
|
|
|
GSource *rtt_pause_source;
|
|
GQueue *enc_times;
|
|
|
|
GHashTable *surface_hwaccel_table;
|
|
GrdHwAccelNvidia *hwaccel_nvidia;
|
|
|
|
unsigned long gfx_initable_id;
|
|
|
|
uint32_t next_frame_id;
|
|
uint16_t next_surface_id;
|
|
uint32_t next_serial;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdRdpDvcGraphicsPipeline, grd_rdp_dvc_graphics_pipeline,
|
|
GRD_TYPE_RDP_DVC)
|
|
|
|
static gboolean
|
|
initiate_session_teardown (gpointer user_data)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = user_data;
|
|
|
|
g_warning ("[RDP.RDPGFX] Client did not respond to protocol initiation. "
|
|
"Terminating session");
|
|
|
|
g_clear_pointer (&graphics_pipeline->protocol_timeout_source, g_source_unref);
|
|
|
|
grd_session_rdp_notify_error (graphics_pipeline->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_BAD_CAPS);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_graphics_pipeline_maybe_init (GrdRdpDvc *dvc)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline =
|
|
GRD_RDP_DVC_GRAPHICS_PIPELINE (dvc);
|
|
GMainContext *graphics_context =
|
|
grd_rdp_renderer_get_graphics_context (graphics_pipeline->renderer);
|
|
RdpgfxServerContext *rdpgfx_context;
|
|
|
|
if (graphics_pipeline->channel_opened ||
|
|
graphics_pipeline->channel_unavailable)
|
|
return;
|
|
|
|
rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
if (!rdpgfx_context->Open (rdpgfx_context))
|
|
{
|
|
g_warning ("[RDP.RDPGFX] Failed to open channel. Terminating session");
|
|
graphics_pipeline->channel_unavailable = TRUE;
|
|
grd_session_rdp_notify_error (graphics_pipeline->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED);
|
|
return;
|
|
}
|
|
graphics_pipeline->channel_opened = TRUE;
|
|
|
|
g_assert (!graphics_pipeline->protocol_timeout_source);
|
|
|
|
graphics_pipeline->protocol_timeout_source =
|
|
g_timeout_source_new (PROTOCOL_TIMEOUT_MS);
|
|
g_source_set_callback (graphics_pipeline->protocol_timeout_source,
|
|
initiate_session_teardown, graphics_pipeline, NULL);
|
|
g_source_attach (graphics_pipeline->protocol_timeout_source,
|
|
graphics_context);
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_get_capabilities (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
gboolean *have_avc444,
|
|
gboolean *have_avc420)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
rdpSettings *rdp_settings = rdpgfx_context->rdpcontext->settings;
|
|
|
|
*have_avc444 = freerdp_settings_get_bool (rdp_settings, FreeRDP_GfxAVC444v2);
|
|
*have_avc420 = freerdp_settings_get_bool (rdp_settings, FreeRDP_GfxH264);
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_set_hwaccel_nvidia (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdHwAccelNvidia *hwaccel_nvidia)
|
|
{
|
|
graphics_pipeline->hwaccel_nvidia = hwaccel_nvidia;
|
|
}
|
|
|
|
static uint16_t
|
|
get_next_free_surface_id (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
uint16_t surface_id = graphics_pipeline->next_surface_id;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
while (g_hash_table_contains (graphics_pipeline->surface_table,
|
|
GUINT_TO_POINTER (surface_id)))
|
|
++surface_id;
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
graphics_pipeline->next_surface_id = surface_id + 1;
|
|
|
|
return surface_id;
|
|
}
|
|
|
|
static uint32_t
|
|
get_next_free_serial (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
uint32_t serial = graphics_pipeline->next_serial;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
while (g_hash_table_contains (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (serial)))
|
|
++serial;
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
graphics_pipeline->next_serial = serial + 1;
|
|
|
|
return serial;
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_create_surface (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpGfxSurface *gfx_surface)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
rdpSettings *rdp_settings = rdpgfx_context->rdpcontext->settings;
|
|
RDPGFX_CREATE_SURFACE_PDU create_surface = {0};
|
|
GrdRdpSurface *rdp_surface = grd_rdp_gfx_surface_get_rdp_surface (gfx_surface);
|
|
GrdRdpSurfaceRenderer *surface_renderer =
|
|
grd_rdp_surface_get_surface_renderer (rdp_surface);
|
|
uint32_t refresh_rate =
|
|
grd_rdp_surface_renderer_get_refresh_rate (surface_renderer);
|
|
uint16_t surface_id = grd_rdp_gfx_surface_get_surface_id (gfx_surface);
|
|
uint32_t surface_serial = grd_rdp_gfx_surface_get_serial (gfx_surface);
|
|
uint16_t surface_width = grd_rdp_gfx_surface_get_width (gfx_surface);
|
|
uint16_t surface_height = grd_rdp_gfx_surface_get_height (gfx_surface);
|
|
GfxSurfaceContext *surface_context;
|
|
gboolean needs_separate_render_surface = FALSE;
|
|
HWAccelContext *hwaccel_context;
|
|
uint32_t encode_session_id;
|
|
uint16_t aligned_width;
|
|
uint16_t aligned_height;
|
|
|
|
g_debug ("[RDP.RDPGFX] Creating surface with id %u", surface_id);
|
|
|
|
surface_context = g_malloc0 (sizeof (GfxSurfaceContext));
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
g_hash_table_insert (graphics_pipeline->surface_table,
|
|
GUINT_TO_POINTER (surface_id), gfx_surface);
|
|
|
|
surface_context->gfx_surface = gfx_surface;
|
|
g_hash_table_insert (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial), surface_context);
|
|
|
|
if (!grd_rdp_gfx_surface_disallows_hwaccel_sessions (gfx_surface) &&
|
|
(freerdp_settings_get_bool (rdp_settings, FreeRDP_GfxAVC444v2) ||
|
|
freerdp_settings_get_bool (rdp_settings, FreeRDP_GfxAVC444) ||
|
|
freerdp_settings_get_bool (rdp_settings, FreeRDP_GfxH264)) &&
|
|
graphics_pipeline->hwaccel_nvidia &&
|
|
grd_hwaccel_nvidia_create_nvenc_session (graphics_pipeline->hwaccel_nvidia,
|
|
&encode_session_id,
|
|
surface_width, surface_height,
|
|
&aligned_width, &aligned_height,
|
|
refresh_rate))
|
|
{
|
|
uint16_t aligned_width_16;
|
|
uint16_t aligned_height_16;
|
|
|
|
g_debug ("[RDP.RDPGFX] Created NVENC session for surface %u", surface_id);
|
|
|
|
aligned_width_16 = grd_get_aligned_size (surface_width, 16);
|
|
aligned_height_16 = grd_get_aligned_size (surface_height, 16);
|
|
if (aligned_width != aligned_width_16 || aligned_height != aligned_height_16)
|
|
needs_separate_render_surface = TRUE;
|
|
|
|
hwaccel_context = g_malloc0 (sizeof (HWAccelContext));
|
|
hwaccel_context->api = HW_ACCEL_API_NVENC;
|
|
hwaccel_context->encode_session_id = encode_session_id;
|
|
|
|
g_hash_table_insert (graphics_pipeline->surface_hwaccel_table,
|
|
GUINT_TO_POINTER (surface_id), hwaccel_context);
|
|
|
|
rdp_surface->needs_no_local_data = TRUE;
|
|
}
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
create_surface.surfaceId = surface_id;
|
|
create_surface.width = surface_width;
|
|
create_surface.height = surface_height;
|
|
create_surface.pixelFormat = GFX_PIXEL_FORMAT_XRGB_8888;
|
|
|
|
rdpgfx_context->CreateSurface (rdpgfx_context, &create_surface);
|
|
|
|
if (needs_separate_render_surface)
|
|
{
|
|
g_autoptr (GrdRdpGfxSurface) render_surface = NULL;
|
|
GrdRdpGfxSurfaceDescriptor surface_descriptor = {};
|
|
|
|
surface_descriptor.flags = GRD_RDP_GFX_SURFACE_FLAG_ALIGNED_SIZE |
|
|
GRD_RDP_GFX_SURFACE_FLAG_NO_HWACCEL_SESSIONS;
|
|
surface_descriptor.surface_id = get_next_free_surface_id (graphics_pipeline);
|
|
surface_descriptor.serial = get_next_free_serial (graphics_pipeline);
|
|
surface_descriptor.rdp_surface = rdp_surface;
|
|
|
|
surface_descriptor.aligned_width = aligned_width;
|
|
surface_descriptor.aligned_height = aligned_height;
|
|
|
|
g_debug ("[RDP.RDPGFX] Creating separate render surface (id %u) for "
|
|
"surface %u", surface_descriptor.surface_id, surface_id);
|
|
|
|
render_surface = grd_rdp_gfx_surface_new (graphics_pipeline,
|
|
&surface_descriptor);
|
|
grd_rdp_gfx_surface_override_render_surface (gfx_surface,
|
|
g_steal_pointer (&render_surface));
|
|
}
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_delete_surface (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpGfxSurface *gfx_surface)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
RDPGFX_DELETE_ENCODING_CONTEXT_PDU delete_encoding_context = {0};
|
|
RDPGFX_DELETE_SURFACE_PDU delete_surface = {0};
|
|
GrdRdpSurface *rdp_surface = grd_rdp_gfx_surface_get_rdp_surface (gfx_surface);
|
|
gboolean needs_encoding_context_deletion = FALSE;
|
|
GfxSurfaceContext *surface_context;
|
|
HWAccelContext *hwaccel_context;
|
|
uint16_t surface_id;
|
|
uint32_t codec_context_id;
|
|
uint32_t surface_serial;
|
|
|
|
if (!graphics_pipeline->channel_opened)
|
|
return;
|
|
|
|
surface_id = grd_rdp_gfx_surface_get_surface_id (gfx_surface);
|
|
codec_context_id = grd_rdp_gfx_surface_get_codec_context_id (gfx_surface);
|
|
surface_serial = grd_rdp_gfx_surface_get_serial (gfx_surface);
|
|
|
|
g_debug ("[RDP.RDPGFX] Deleting surface with id %u", surface_id);
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
g_assert (g_hash_table_contains (graphics_pipeline->surface_table,
|
|
GUINT_TO_POINTER (surface_id)));
|
|
|
|
if (!g_hash_table_lookup_extended (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial),
|
|
NULL, (gpointer *) &surface_context))
|
|
g_assert_not_reached ();
|
|
|
|
surface_context->gfx_surface = NULL;
|
|
if (surface_context->ref_count == 0)
|
|
{
|
|
g_hash_table_remove (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial));
|
|
}
|
|
|
|
if (g_hash_table_steal_extended (graphics_pipeline->surface_hwaccel_table,
|
|
GUINT_TO_POINTER (surface_id),
|
|
NULL, (gpointer *) &hwaccel_context))
|
|
{
|
|
g_debug ("[RDP.RDPGFX] Destroying NVENC session for surface %u", surface_id);
|
|
rdp_surface->needs_no_local_data = FALSE;
|
|
|
|
g_assert (hwaccel_context->api == HW_ACCEL_API_NVENC);
|
|
grd_hwaccel_nvidia_free_nvenc_session (graphics_pipeline->hwaccel_nvidia,
|
|
hwaccel_context->encode_session_id);
|
|
g_free (hwaccel_context);
|
|
}
|
|
|
|
if (g_hash_table_steal_extended (graphics_pipeline->codec_context_table,
|
|
GUINT_TO_POINTER (codec_context_id),
|
|
NULL, NULL))
|
|
needs_encoding_context_deletion = TRUE;
|
|
|
|
g_hash_table_remove (graphics_pipeline->surface_table,
|
|
GUINT_TO_POINTER (surface_id));
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
if (needs_encoding_context_deletion)
|
|
{
|
|
delete_encoding_context.surfaceId = surface_id;
|
|
delete_encoding_context.codecContextId = codec_context_id;
|
|
|
|
rdpgfx_context->DeleteEncodingContext (rdpgfx_context,
|
|
&delete_encoding_context);
|
|
}
|
|
|
|
delete_surface.surfaceId = surface_id;
|
|
|
|
rdpgfx_context->DeleteSurface (rdpgfx_context, &delete_surface);
|
|
}
|
|
|
|
static void
|
|
map_surface_to_output (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpGfxSurface *gfx_surface,
|
|
GrdRdpSurfaceMapping *surface_mapping)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
GrdRdpSurfaceMappingType mapping_type = surface_mapping->mapping_type;
|
|
uint16_t surface_id = grd_rdp_gfx_surface_get_surface_id (gfx_surface);
|
|
RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU map_surface_to_output = {};
|
|
|
|
g_assert (mapping_type == GRD_RDP_SURFACE_MAPPING_TYPE_MAP_TO_OUTPUT);
|
|
|
|
map_surface_to_output.surfaceId = surface_id;
|
|
map_surface_to_output.outputOriginX = surface_mapping->output_origin_x;
|
|
map_surface_to_output.outputOriginY = surface_mapping->output_origin_y;
|
|
|
|
rdpgfx_context->MapSurfaceToOutput (rdpgfx_context, &map_surface_to_output);
|
|
}
|
|
|
|
static void
|
|
map_surface (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpGfxSurface *gfx_surface)
|
|
{
|
|
GrdRdpSurface *rdp_surface = grd_rdp_gfx_surface_get_rdp_surface (gfx_surface);
|
|
GrdRdpSurfaceMapping *surface_mapping;
|
|
|
|
surface_mapping = grd_rdp_surface_get_mapping (rdp_surface);
|
|
switch (surface_mapping->mapping_type)
|
|
{
|
|
case GRD_RDP_SURFACE_MAPPING_TYPE_MAP_TO_OUTPUT:
|
|
map_surface_to_output (graphics_pipeline, gfx_surface, surface_mapping);
|
|
break;
|
|
}
|
|
}
|
|
|
|
GrdRdpGfxSurface *
|
|
grd_rdp_dvc_graphics_pipeline_acquire_gfx_surface (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpSurface *rdp_surface)
|
|
{
|
|
|
|
GrdRdpGfxSurfaceDescriptor surface_descriptor = {};
|
|
GrdRdpGfxSurface *gfx_surface;
|
|
GrdRdpGfxFrameController *frame_controller;
|
|
|
|
surface_descriptor.surface_id = get_next_free_surface_id (graphics_pipeline);
|
|
surface_descriptor.serial = get_next_free_serial (graphics_pipeline);
|
|
surface_descriptor.rdp_surface = rdp_surface;
|
|
|
|
gfx_surface = grd_rdp_gfx_surface_new (graphics_pipeline,
|
|
&surface_descriptor);
|
|
|
|
frame_controller = grd_rdp_gfx_frame_controller_new (rdp_surface);
|
|
grd_rdp_gfx_surface_attach_frame_controller (gfx_surface, frame_controller);
|
|
map_surface (graphics_pipeline, gfx_surface);
|
|
|
|
return gfx_surface;
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_reset_graphics (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint32_t width,
|
|
uint32_t height,
|
|
MONITOR_DEF *monitors,
|
|
uint32_t n_monitors)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
RDPGFX_RESET_GRAPHICS_PDU reset_graphics = {0};
|
|
|
|
g_debug ("[RDP.RDPGFX] Resetting graphics");
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
g_assert (g_hash_table_size (graphics_pipeline->surface_table) == 0);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
/*
|
|
* width and height refer here to the size of the Graphics Output Buffer
|
|
* ADM (Abstract Data Model) element
|
|
*/
|
|
reset_graphics.width = width;
|
|
reset_graphics.height = height;
|
|
reset_graphics.monitorCount = n_monitors;
|
|
reset_graphics.monitorDefArray = monitors;
|
|
|
|
rdpgfx_context->ResetGraphics (rdpgfx_context, &reset_graphics);
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_notify_new_round_trip_time (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint64_t round_trip_time_us)
|
|
{
|
|
GrdRdpGfxSurface *gfx_surface;
|
|
GrdRdpGfxFrameController *frame_controller;
|
|
GHashTableIter iter;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
g_hash_table_iter_init (&iter, graphics_pipeline->surface_table);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &gfx_surface))
|
|
{
|
|
frame_controller = grd_rdp_gfx_surface_get_frame_controller (gfx_surface);
|
|
if (!frame_controller)
|
|
continue;
|
|
|
|
grd_rdp_gfx_frame_controller_notify_new_round_trip_time (frame_controller,
|
|
round_trip_time_us);
|
|
}
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
}
|
|
|
|
static uint32_t
|
|
get_next_free_frame_id (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
uint32_t frame_id = graphics_pipeline->next_frame_id;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
while (g_hash_table_contains (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (frame_id)))
|
|
++frame_id;
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
graphics_pipeline->next_frame_id = frame_id + 1;
|
|
|
|
return frame_id;
|
|
}
|
|
|
|
static uint16_t
|
|
get_rdpgfx_codec_id (GrdRdpCodec codec)
|
|
{
|
|
switch (codec)
|
|
{
|
|
case GRD_RDP_CODEC_CAPROGRESSIVE:
|
|
return RDPGFX_CODECID_CAPROGRESSIVE;
|
|
case GRD_RDP_CODEC_AVC420:
|
|
return RDPGFX_CODECID_AVC420;
|
|
case GRD_RDP_CODEC_AVC444v2:
|
|
return RDPGFX_CODECID_AVC444v2;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
set_region_rects (RDPGFX_H264_METABLOCK *avc_meta,
|
|
cairo_region_t *region)
|
|
{
|
|
int n_rects;
|
|
int i;
|
|
|
|
avc_meta->numRegionRects = n_rects = cairo_region_num_rectangles (region);
|
|
avc_meta->regionRects = g_new0 (RECTANGLE_16, n_rects);
|
|
for (i = 0; i < n_rects; ++i)
|
|
{
|
|
cairo_rectangle_int_t cairo_rect;
|
|
|
|
cairo_region_get_rectangle (region, i, &cairo_rect);
|
|
|
|
avc_meta->regionRects[i].left = cairo_rect.x;
|
|
avc_meta->regionRects[i].top = cairo_rect.y;
|
|
avc_meta->regionRects[i].right = cairo_rect.x + cairo_rect.width;
|
|
avc_meta->regionRects[i].bottom = cairo_rect.y + cairo_rect.height;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_avc_info (RDPGFX_H264_METABLOCK *avc_meta,
|
|
GrdBitstream *bitstream,
|
|
int n_blocks)
|
|
{
|
|
GrdAVCFrameInfo *avc_frame_info =
|
|
grd_bitstream_get_avc_frame_info (bitstream);
|
|
int i = 0;
|
|
|
|
avc_meta->quantQualityVals = g_new0 (RDPGFX_H264_QUANT_QUALITY, n_blocks);
|
|
for (i = 0; i < n_blocks; ++i)
|
|
{
|
|
avc_meta->quantQualityVals[i].qp =
|
|
grd_avc_frame_info_get_qp (avc_frame_info);
|
|
avc_meta->quantQualityVals[i].p =
|
|
grd_avc_frame_info_get_frame_type (avc_frame_info) == GRD_AVC_FRAME_TYPE_P;
|
|
avc_meta->quantQualityVals[i].qualityVal =
|
|
grd_avc_frame_info_get_quality_value (avc_frame_info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
prepare_avc420_bitstream (RDPGFX_AVC420_BITMAP_STREAM *avc420,
|
|
cairo_region_t *region,
|
|
GrdBitstream *bitstream)
|
|
{
|
|
RDPGFX_H264_METABLOCK *avc_meta = &avc420->meta;
|
|
int n_rects;
|
|
|
|
n_rects = cairo_region_num_rectangles (region);
|
|
|
|
avc420->data = grd_bitstream_get_data (bitstream);
|
|
avc420->length = grd_bitstream_get_data_size (bitstream);
|
|
|
|
set_region_rects (avc_meta, region);
|
|
set_avc_info (avc_meta, bitstream, n_rects);
|
|
}
|
|
|
|
static void
|
|
prepare_avc444_bitstream (RDPGFX_AVC444_BITMAP_STREAM *avc444,
|
|
GrdRdpFrame *rdp_frame)
|
|
{
|
|
GList *bitstreams = grd_rdp_frame_get_bitstreams (rdp_frame);
|
|
cairo_region_t *region = grd_rdp_frame_get_damage_region (rdp_frame);
|
|
GrdRdpFrameViewType view_type = grd_rdp_frame_get_avc_view_type (rdp_frame);
|
|
GrdBitstream *bitstream0 = NULL;
|
|
GrdBitstream *bitstream1 = NULL;
|
|
int n_rects;
|
|
|
|
g_assert (bitstreams);
|
|
g_assert (g_list_length (bitstreams) >= 1);
|
|
|
|
bitstream0 = bitstreams->data;
|
|
if (bitstreams->next)
|
|
bitstream1 = bitstreams->next->data;
|
|
|
|
switch (view_type)
|
|
{
|
|
case GRD_RDP_FRAME_VIEW_TYPE_DUAL:
|
|
g_assert (g_list_length (bitstreams) == 2);
|
|
avc444->LC = 0;
|
|
break;
|
|
case GRD_RDP_FRAME_VIEW_TYPE_MAIN:
|
|
g_assert (g_list_length (bitstreams) == 1);
|
|
avc444->LC = 1;
|
|
break;
|
|
case GRD_RDP_FRAME_VIEW_TYPE_AUX:
|
|
g_assert (g_list_length (bitstreams) == 1);
|
|
avc444->LC = 2;
|
|
break;
|
|
}
|
|
|
|
prepare_avc420_bitstream (&avc444->bitstream[0], region, bitstream0);
|
|
if (bitstream1)
|
|
prepare_avc420_bitstream (&avc444->bitstream[1], region, bitstream1);
|
|
|
|
switch (view_type)
|
|
{
|
|
case GRD_RDP_FRAME_VIEW_TYPE_DUAL:
|
|
case GRD_RDP_FRAME_VIEW_TYPE_MAIN:
|
|
n_rects = cairo_region_num_rectangles (region);
|
|
|
|
/* RFX_AVC420_METABLOCK size + bitstream0 size */
|
|
avc444->cbAvc420EncodedBitstream1 = sizeof (uint32_t) + n_rects * 10 +
|
|
avc444->bitstream[0].length;
|
|
break;
|
|
case GRD_RDP_FRAME_VIEW_TYPE_AUX:
|
|
/*
|
|
* If no YUV420 frame is present, then this field MUST be set to zero.
|
|
* ([MS-RDPEGFX] 2.2.4.6)
|
|
*
|
|
* The auxiliary view is referred to as Chroma420 frame.
|
|
*/
|
|
avc444->cbAvc420EncodedBitstream1 = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
prepare_avc_update (RDPGFX_SURFACE_COMMAND *cmd,
|
|
RDPGFX_AVC444_BITMAP_STREAM *avc444,
|
|
GrdRdpFrame *rdp_frame)
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdRdpCodec codec = grd_rdp_render_context_get_codec (render_context);
|
|
cairo_region_t *region = grd_rdp_frame_get_damage_region (rdp_frame);
|
|
GList *bitstreams = grd_rdp_frame_get_bitstreams (rdp_frame);
|
|
cairo_rectangle_int_t region_extents = {};
|
|
GrdBitstream *bitstream0;
|
|
|
|
g_assert (bitstreams);
|
|
g_assert (g_list_length (bitstreams) >= 1);
|
|
|
|
bitstream0 = bitstreams->data;
|
|
|
|
cairo_region_get_extents (region, ®ion_extents);
|
|
|
|
cmd->left = 0;
|
|
cmd->top = 0;
|
|
cmd->right = region_extents.x + region_extents.width;
|
|
cmd->bottom = region_extents.y + region_extents.height;
|
|
|
|
switch (codec)
|
|
{
|
|
case GRD_RDP_CODEC_CAPROGRESSIVE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case GRD_RDP_CODEC_AVC420:
|
|
prepare_avc420_bitstream (&avc444->bitstream[0], region, bitstream0);
|
|
cmd->extra = &avc444->bitstream[0];
|
|
break;
|
|
case GRD_RDP_CODEC_AVC444v2:
|
|
prepare_avc444_bitstream (avc444, rdp_frame);
|
|
cmd->extra = avc444;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
get_subframe_count (GrdRdpFrame *rdp_frame)
|
|
{
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdRdpCodec codec = grd_rdp_render_context_get_codec (render_context);
|
|
GrdRdpFrameViewType view_type;
|
|
|
|
if (codec != GRD_RDP_CODEC_AVC444v2)
|
|
return 1;
|
|
|
|
view_type = grd_rdp_frame_get_avc_view_type (rdp_frame);
|
|
|
|
return view_type == GRD_RDP_FRAME_VIEW_TYPE_DUAL ? 2 : 1;
|
|
}
|
|
|
|
static void
|
|
surface_serial_ref (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint32_t surface_serial)
|
|
{
|
|
GfxSurfaceContext *surface_context;
|
|
|
|
if (!g_hash_table_lookup_extended (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial),
|
|
NULL, (gpointer *) &surface_context))
|
|
g_assert_not_reached ();
|
|
|
|
++surface_context->ref_count;
|
|
}
|
|
|
|
static void
|
|
surface_serial_unref (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint32_t surface_serial)
|
|
{
|
|
GfxSurfaceContext *surface_context;
|
|
|
|
if (!g_hash_table_lookup_extended (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial),
|
|
NULL, (gpointer *) &surface_context))
|
|
g_assert_not_reached ();
|
|
|
|
g_assert (surface_context->ref_count > 0);
|
|
--surface_context->ref_count;
|
|
|
|
if (!surface_context->gfx_surface && surface_context->ref_count == 0)
|
|
{
|
|
g_hash_table_remove (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gfx_frame_info_free (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GfxFrameInfo *gfx_frame_info)
|
|
{
|
|
uint32_t surface_serial = gfx_frame_info->surface_serial;
|
|
|
|
g_hash_table_remove (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (gfx_frame_info->frame_info.frame_id));
|
|
surface_serial_unref (graphics_pipeline, surface_serial);
|
|
|
|
g_free (gfx_frame_info);
|
|
}
|
|
|
|
static void
|
|
reduce_tracked_frame_infos (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint32_t max_tracked_frames)
|
|
{
|
|
while (g_queue_peek_head (graphics_pipeline->encoded_frames) &&
|
|
g_queue_get_length (graphics_pipeline->encoded_frames) > max_tracked_frames)
|
|
{
|
|
gfx_frame_info_free (graphics_pipeline,
|
|
g_queue_pop_head (graphics_pipeline->encoded_frames));
|
|
}
|
|
}
|
|
|
|
static void
|
|
enqueue_tracked_frame_info (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint32_t surface_serial,
|
|
uint32_t frame_id,
|
|
uint32_t n_subframes,
|
|
int64_t enc_time_us)
|
|
{
|
|
GfxFrameInfo *gfx_frame_info;
|
|
|
|
g_assert (MAX_TRACKED_ENC_FRAMES > 1);
|
|
reduce_tracked_frame_infos (graphics_pipeline, MAX_TRACKED_ENC_FRAMES - 1);
|
|
|
|
gfx_frame_info = g_malloc0 (sizeof (GfxFrameInfo));
|
|
gfx_frame_info->frame_info.frame_id = frame_id;
|
|
gfx_frame_info->frame_info.n_subframes = n_subframes;
|
|
gfx_frame_info->frame_info.enc_time_us = enc_time_us;
|
|
gfx_frame_info->surface_serial = surface_serial;
|
|
|
|
g_queue_push_tail (graphics_pipeline->encoded_frames, gfx_frame_info);
|
|
}
|
|
|
|
static uint32_t
|
|
get_frame_size (GrdRdpFrame *rdp_frame)
|
|
{
|
|
GList *bitstreams = grd_rdp_frame_get_bitstreams (rdp_frame);
|
|
uint32_t frame_size = 0;
|
|
|
|
while (bitstreams)
|
|
{
|
|
GrdBitstream *bitstream = bitstreams->data;
|
|
|
|
frame_size += grd_bitstream_get_data_size (bitstream);
|
|
bitstreams = bitstreams->next;
|
|
}
|
|
|
|
return frame_size;
|
|
}
|
|
|
|
static void
|
|
blit_surface_to_surface (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpGfxSurface *dst_surface,
|
|
GrdRdpGfxSurface *src_surface,
|
|
RECTANGLE_16 *region_rects,
|
|
int n_rects)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
RDPGFX_SURFACE_TO_SURFACE_PDU surface_to_surface = {};
|
|
RDPGFX_POINT16 dst_point = {};
|
|
int i;
|
|
|
|
surface_to_surface.surfaceIdSrc =
|
|
grd_rdp_gfx_surface_get_surface_id (src_surface);
|
|
surface_to_surface.surfaceIdDest =
|
|
grd_rdp_gfx_surface_get_surface_id (dst_surface);
|
|
|
|
for (i = 0; i < n_rects; ++i)
|
|
{
|
|
dst_point.x = region_rects[i].left;
|
|
dst_point.y = region_rects[i].top;
|
|
|
|
surface_to_surface.rectSrc = region_rects[i];
|
|
surface_to_surface.destPts = &dst_point;
|
|
surface_to_surface.destPtsCount = 1;
|
|
|
|
rdpgfx_context->SurfaceToSurface (rdpgfx_context, &surface_to_surface);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear_old_enc_times (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
int64_t current_time_us)
|
|
{
|
|
int64_t *tracked_enc_time_us;
|
|
|
|
while ((tracked_enc_time_us = g_queue_peek_head (graphics_pipeline->enc_times)) &&
|
|
current_time_us - *tracked_enc_time_us >= 1 * G_USEC_PER_SEC)
|
|
g_free (g_queue_pop_head (graphics_pipeline->enc_times));
|
|
}
|
|
|
|
static void
|
|
track_enc_time (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
int64_t enc_time_us)
|
|
{
|
|
int64_t *tracked_enc_time_us;
|
|
|
|
tracked_enc_time_us = g_malloc0 (sizeof (int64_t));
|
|
*tracked_enc_time_us = enc_time_us;
|
|
|
|
g_queue_push_tail (graphics_pipeline->enc_times, tracked_enc_time_us);
|
|
}
|
|
|
|
static gboolean
|
|
maybe_slow_down_rtts (gpointer user_data)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = user_data;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
clear_old_enc_times (graphics_pipeline, g_get_monotonic_time ());
|
|
|
|
if (g_queue_get_length (graphics_pipeline->enc_times) == 0)
|
|
{
|
|
grd_rdp_network_autodetection_set_rtt_consumer_necessity (
|
|
graphics_pipeline->network_autodetection,
|
|
GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX,
|
|
GRD_RDP_NW_AUTODETECT_RTT_NEC_LOW);
|
|
|
|
g_clear_pointer (&graphics_pipeline->rtt_pause_source, g_source_unref);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
ensure_rtt_receivement (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
GMainContext *graphics_context =
|
|
grd_rdp_renderer_get_graphics_context (graphics_pipeline->renderer);
|
|
|
|
g_assert (!graphics_pipeline->rtt_pause_source);
|
|
|
|
grd_rdp_network_autodetection_set_rtt_consumer_necessity (
|
|
graphics_pipeline->network_autodetection,
|
|
GRD_RDP_NW_AUTODETECT_RTT_CONSUMER_RDPGFX,
|
|
GRD_RDP_NW_AUTODETECT_RTT_NEC_HIGH);
|
|
|
|
graphics_pipeline->rtt_pause_source =
|
|
g_timeout_source_new (ENC_TIMES_CHECK_INTERVAL_MS);
|
|
g_source_set_callback (graphics_pipeline->rtt_pause_source, maybe_slow_down_rtts,
|
|
graphics_pipeline, NULL);
|
|
g_source_attach (graphics_pipeline->rtt_pause_source, graphics_context);
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_graphics_pipeline_submit_frame (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpFrame *rdp_frame)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
rdpSettings *rdp_settings = rdpgfx_context->rdpcontext->settings;
|
|
GrdRdpNetworkAutodetection *network_autodetection =
|
|
graphics_pipeline->network_autodetection;
|
|
GrdRdpRenderContext *render_context =
|
|
grd_rdp_frame_get_render_context (rdp_frame);
|
|
GrdRdpGfxSurface *gfx_surface =
|
|
grd_rdp_render_context_get_gfx_surface (render_context);
|
|
GrdRdpGfxSurface *render_surface =
|
|
grd_rdp_gfx_surface_get_render_surface (gfx_surface);
|
|
GrdRdpGfxFrameController *frame_controller =
|
|
grd_rdp_gfx_surface_get_frame_controller (gfx_surface);
|
|
GrdRdpCodec codec = grd_rdp_render_context_get_codec (render_context);
|
|
uint32_t codec_context_id =
|
|
grd_rdp_gfx_surface_get_codec_context_id (gfx_surface);
|
|
GList *bitstreams = grd_rdp_frame_get_bitstreams (rdp_frame);
|
|
RDPGFX_START_FRAME_PDU cmd_start = {};
|
|
RDPGFX_END_FRAME_PDU cmd_end = {};
|
|
SYSTEMTIME system_time = {};
|
|
RDPGFX_SURFACE_COMMAND cmd = {};
|
|
RDPGFX_AVC444_BITMAP_STREAM avc444 = {};
|
|
gboolean pending_bw_measure_stop = FALSE;
|
|
RECTANGLE_16 *region_rects = NULL;
|
|
int n_rects = 0;
|
|
GrdBitstream *bitstream0;
|
|
uint32_t surface_serial;
|
|
uint32_t n_subframes;
|
|
int64_t enc_ack_time_us;
|
|
|
|
g_assert (bitstreams);
|
|
g_assert (g_list_length (bitstreams) >= 1);
|
|
|
|
bitstream0 = bitstreams->data;
|
|
|
|
GetSystemTime (&system_time);
|
|
cmd_start.timestamp = system_time.wHour << 22 |
|
|
system_time.wMinute << 16 |
|
|
system_time.wSecond << 10 |
|
|
system_time.wMilliseconds;
|
|
cmd_start.frameId = get_next_free_frame_id (graphics_pipeline);
|
|
cmd_end.frameId = cmd_start.frameId;
|
|
|
|
cmd.surfaceId = grd_rdp_gfx_surface_get_surface_id (render_surface);
|
|
cmd.codecId = get_rdpgfx_codec_id (codec);
|
|
cmd.format = PIXEL_FORMAT_BGRX32;
|
|
|
|
switch (codec)
|
|
{
|
|
case GRD_RDP_CODEC_CAPROGRESSIVE:
|
|
g_assert (g_list_length (bitstreams) == 1);
|
|
|
|
cmd.contextId = codec_context_id;
|
|
cmd.length = grd_bitstream_get_data_size (bitstream0);
|
|
cmd.data = grd_bitstream_get_data (bitstream0);
|
|
break;
|
|
case GRD_RDP_CODEC_AVC420:
|
|
case GRD_RDP_CODEC_AVC444v2:
|
|
prepare_avc_update (&cmd, &avc444, rdp_frame);
|
|
region_rects = avc444.bitstream[0].meta.regionRects;
|
|
n_rects = avc444.bitstream[0].meta.numRegionRects;
|
|
break;
|
|
}
|
|
n_subframes = get_subframe_count (rdp_frame);
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
if (codec == GRD_RDP_CODEC_CAPROGRESSIVE &&
|
|
!g_hash_table_contains (graphics_pipeline->codec_context_table,
|
|
GUINT_TO_POINTER (codec_context_id)))
|
|
{
|
|
g_hash_table_insert (graphics_pipeline->codec_context_table,
|
|
GUINT_TO_POINTER (codec_context_id), gfx_surface);
|
|
}
|
|
|
|
enc_ack_time_us = g_get_monotonic_time ();
|
|
grd_rdp_gfx_frame_controller_unack_frame (frame_controller, cmd_start.frameId,
|
|
n_subframes, enc_ack_time_us);
|
|
|
|
surface_serial = grd_rdp_gfx_surface_get_serial (gfx_surface);
|
|
g_hash_table_insert (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (cmd_start.frameId),
|
|
GUINT_TO_POINTER (surface_serial));
|
|
surface_serial_ref (graphics_pipeline, surface_serial);
|
|
++graphics_pipeline->total_frames_encoded;
|
|
|
|
if (graphics_pipeline->frame_acks_suspended)
|
|
{
|
|
grd_rdp_gfx_frame_controller_ack_frame (frame_controller, cmd_start.frameId,
|
|
enc_ack_time_us);
|
|
enqueue_tracked_frame_info (graphics_pipeline, surface_serial,
|
|
cmd_start.frameId, n_subframes,
|
|
enc_ack_time_us);
|
|
}
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
if (network_autodetection &&
|
|
get_frame_size (rdp_frame) >= MIN_BW_MEASURE_SIZE)
|
|
{
|
|
pending_bw_measure_stop =
|
|
grd_rdp_network_autodetection_try_bw_measure_start (network_autodetection);
|
|
}
|
|
|
|
rdpgfx_context->StartFrame (rdpgfx_context, &cmd_start);
|
|
rdpgfx_context->SurfaceCommand (rdpgfx_context, &cmd);
|
|
|
|
if (render_surface != gfx_surface)
|
|
{
|
|
blit_surface_to_surface (graphics_pipeline, gfx_surface, render_surface,
|
|
region_rects, n_rects);
|
|
}
|
|
rdpgfx_context->EndFrame (rdpgfx_context, &cmd_end);
|
|
|
|
if (pending_bw_measure_stop)
|
|
grd_rdp_network_autodetection_queue_bw_measure_stop (network_autodetection);
|
|
|
|
g_free (avc444.bitstream[1].meta.quantQualityVals);
|
|
g_free (avc444.bitstream[1].meta.regionRects);
|
|
g_free (avc444.bitstream[0].meta.quantQualityVals);
|
|
g_free (avc444.bitstream[0].meta.regionRects);
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
clear_old_enc_times (graphics_pipeline, g_get_monotonic_time ());
|
|
track_enc_time (graphics_pipeline, enc_ack_time_us);
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect) &&
|
|
!graphics_pipeline->rtt_pause_source)
|
|
ensure_rtt_receivement (graphics_pipeline);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
}
|
|
|
|
static gboolean
|
|
refresh_gfx_surface_avc420 (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
HWAccelContext *hwaccel_context,
|
|
GrdRdpSurface *rdp_surface,
|
|
GrdRdpGfxSurface *gfx_surface,
|
|
GrdRdpLegacyBuffer *buffer,
|
|
int64_t *enc_time_us)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
GrdRdpNetworkAutodetection *network_autodetection =
|
|
graphics_pipeline->network_autodetection;
|
|
GrdRdpGfxSurface *render_surface =
|
|
grd_rdp_gfx_surface_get_render_surface (gfx_surface);
|
|
GrdRdpGfxFrameController *frame_controller =
|
|
grd_rdp_gfx_surface_get_frame_controller (gfx_surface);
|
|
CUdeviceptr src_data = grd_rdp_legacy_buffer_get_mapped_cuda_pointer (buffer);
|
|
RDPGFX_SURFACE_COMMAND cmd = {0};
|
|
RDPGFX_START_FRAME_PDU cmd_start = {0};
|
|
RDPGFX_END_FRAME_PDU cmd_end = {0};
|
|
RDPGFX_AVC420_BITMAP_STREAM avc420 = {0};
|
|
RECTANGLE_16 *region_rects;
|
|
SYSTEMTIME system_time;
|
|
cairo_rectangle_int_t cairo_rect, region_extents;
|
|
int n_rects;
|
|
uint16_t surface_width = grd_rdp_gfx_surface_get_width (gfx_surface);
|
|
uint16_t surface_height = grd_rdp_gfx_surface_get_height (gfx_surface);
|
|
uint16_t aligned_width;
|
|
uint16_t aligned_height;
|
|
cairo_region_t *region;
|
|
uint32_t surface_serial;
|
|
int64_t enc_ack_time_us;
|
|
gboolean pending_bw_measure_stop = FALSE;
|
|
int i;
|
|
|
|
aligned_width = surface_width + (surface_width % 16 ? 16 - surface_width % 16 : 0);
|
|
aligned_height = surface_height + (surface_height % 64 ? 64 - surface_height % 64 : 0);
|
|
|
|
if (!grd_hwaccel_nvidia_avc420_encode_bgrx_frame (graphics_pipeline->hwaccel_nvidia,
|
|
hwaccel_context->encode_session_id,
|
|
src_data,
|
|
&rdp_surface->avc.main_view,
|
|
surface_width, surface_height,
|
|
aligned_width, aligned_height,
|
|
rdp_surface->cuda_stream))
|
|
{
|
|
g_warning ("[RDP.RDPGFX] Failed to encode YUV420 frame");
|
|
return FALSE;
|
|
}
|
|
|
|
region = grd_rdp_damage_detector_get_damage_region (rdp_surface->detector);
|
|
if (!region)
|
|
{
|
|
grd_hwaccel_nvidia_avc420_retrieve_bitstream (graphics_pipeline->hwaccel_nvidia,
|
|
hwaccel_context->encode_session_id,
|
|
NULL, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!grd_hwaccel_nvidia_avc420_retrieve_bitstream (graphics_pipeline->hwaccel_nvidia,
|
|
hwaccel_context->encode_session_id,
|
|
&avc420.data, &avc420.length))
|
|
{
|
|
g_warning ("[RDP.RDPGFX] Failed to retrieve AVC420 bitstream");
|
|
cairo_region_destroy (region);
|
|
return FALSE;
|
|
}
|
|
|
|
GetSystemTime (&system_time);
|
|
cmd_start.timestamp = system_time.wHour << 22 |
|
|
system_time.wMinute << 16 |
|
|
system_time.wSecond << 10 |
|
|
system_time.wMilliseconds;
|
|
cmd_start.frameId = get_next_free_frame_id (graphics_pipeline);
|
|
cmd_end.frameId = cmd_start.frameId;
|
|
|
|
cairo_region_get_extents (region, ®ion_extents);
|
|
|
|
cmd.surfaceId = grd_rdp_gfx_surface_get_surface_id (render_surface);
|
|
cmd.codecId = RDPGFX_CODECID_AVC420;
|
|
cmd.format = PIXEL_FORMAT_BGRX32;
|
|
cmd.left = 0;
|
|
cmd.top = 0;
|
|
cmd.right = region_extents.x + region_extents.width;
|
|
cmd.bottom = region_extents.y + region_extents.height;
|
|
cmd.length = 0;
|
|
cmd.data = NULL;
|
|
cmd.extra = &avc420;
|
|
|
|
avc420.meta.numRegionRects = n_rects = cairo_region_num_rectangles (region);
|
|
avc420.meta.regionRects = region_rects = g_new0 (RECTANGLE_16, n_rects);
|
|
avc420.meta.quantQualityVals = g_malloc0 (n_rects * sizeof (RDPGFX_H264_QUANT_QUALITY));
|
|
for (i = 0; i < n_rects; ++i)
|
|
{
|
|
cairo_region_get_rectangle (region, i, &cairo_rect);
|
|
|
|
region_rects[i].left = cairo_rect.x;
|
|
region_rects[i].top = cairo_rect.y;
|
|
region_rects[i].right = cairo_rect.x + cairo_rect.width;
|
|
region_rects[i].bottom = cairo_rect.y + cairo_rect.height;
|
|
|
|
avc420.meta.quantQualityVals[i].qp = 22;
|
|
avc420.meta.quantQualityVals[i].p = hwaccel_context->has_first_frame ? 1 : 0;
|
|
avc420.meta.quantQualityVals[i].qualityVal = 100;
|
|
}
|
|
hwaccel_context->has_first_frame = TRUE;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
enc_ack_time_us = g_get_monotonic_time ();
|
|
grd_rdp_gfx_frame_controller_unack_frame (frame_controller, cmd_start.frameId,
|
|
1, enc_ack_time_us);
|
|
|
|
surface_serial = grd_rdp_gfx_surface_get_serial (gfx_surface);
|
|
g_hash_table_insert (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (cmd_start.frameId),
|
|
GUINT_TO_POINTER (surface_serial));
|
|
surface_serial_ref (graphics_pipeline, surface_serial);
|
|
++graphics_pipeline->total_frames_encoded;
|
|
|
|
if (graphics_pipeline->frame_acks_suspended)
|
|
{
|
|
grd_rdp_gfx_frame_controller_ack_frame (frame_controller, cmd_start.frameId,
|
|
enc_ack_time_us);
|
|
enqueue_tracked_frame_info (graphics_pipeline, surface_serial,
|
|
cmd_start.frameId, 1, enc_ack_time_us);
|
|
}
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
if (network_autodetection &&
|
|
(4 + avc420.meta.numRegionRects * 10 + avc420.length) >= MIN_BW_MEASURE_SIZE)
|
|
{
|
|
pending_bw_measure_stop =
|
|
grd_rdp_network_autodetection_try_bw_measure_start (network_autodetection);
|
|
}
|
|
|
|
rdpgfx_context->StartFrame (rdpgfx_context, &cmd_start);
|
|
rdpgfx_context->SurfaceCommand (rdpgfx_context, &cmd);
|
|
|
|
if (render_surface != gfx_surface)
|
|
{
|
|
blit_surface_to_surface (graphics_pipeline, gfx_surface, render_surface,
|
|
region_rects, n_rects);
|
|
}
|
|
rdpgfx_context->EndFrame (rdpgfx_context, &cmd_end);
|
|
|
|
if (pending_bw_measure_stop)
|
|
grd_rdp_network_autodetection_queue_bw_measure_stop (network_autodetection);
|
|
|
|
*enc_time_us = enc_ack_time_us;
|
|
|
|
g_free (avc420.data);
|
|
g_free (avc420.meta.quantQualityVals);
|
|
g_free (region_rects);
|
|
cairo_region_destroy (region);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
rfx_progressive_write_message (RFX_MESSAGE *rfx_message,
|
|
wStream *s,
|
|
gboolean needs_progressive_header)
|
|
{
|
|
const RFX_RECT *rfx_rects;
|
|
uint16_t n_rfx_rects = 0;
|
|
const UINT32 *quant_vals;
|
|
uint16_t n_quant_vals = 0;
|
|
const RFX_TILE **rfx_tiles;
|
|
uint16_t n_rfx_tiles = 0;
|
|
uint32_t block_len;
|
|
const uint32_t *qv;
|
|
const RFX_TILE *rfx_tile;
|
|
uint32_t tiles_data_size;
|
|
uint16_t i;
|
|
|
|
rfx_rects = rfx_message_get_rects (rfx_message, &n_rfx_rects);
|
|
quant_vals = rfx_message_get_quants (rfx_message, &n_quant_vals);
|
|
rfx_tiles = rfx_message_get_tiles (rfx_message, &n_rfx_tiles);
|
|
|
|
if (needs_progressive_header)
|
|
{
|
|
/* RFX_PROGRESSIVE_SYNC */
|
|
block_len = 12;
|
|
if (!Stream_EnsureRemainingCapacity (s, block_len))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write_UINT16 (s, 0xCCC0); /* blockType */
|
|
Stream_Write_UINT32 (s, block_len); /* blockLen */
|
|
Stream_Write_UINT32 (s, 0xCACCACCA); /* magic */
|
|
Stream_Write_UINT16 (s, 0x0100); /* version */
|
|
|
|
/* RFX_PROGRESSIVE_CONTEXT */
|
|
block_len = 10;
|
|
if (!Stream_EnsureRemainingCapacity (s, block_len))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write_UINT16 (s, 0xCCC3); /* blockType */
|
|
Stream_Write_UINT32 (s, block_len); /* blockLen */
|
|
Stream_Write_UINT8 (s, 0); /* ctxId */
|
|
Stream_Write_UINT16 (s, 0x0040); /* tileSize */
|
|
Stream_Write_UINT8 (s, 0); /* flags */
|
|
}
|
|
|
|
/* RFX_PROGRESSIVE_FRAME_BEGIN */
|
|
block_len = 12;
|
|
if (!Stream_EnsureRemainingCapacity (s, block_len))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write_UINT16 (s, 0xCCC1); /* blockType */
|
|
Stream_Write_UINT32 (s, block_len); /* blockLen */
|
|
Stream_Write_UINT32 (s, rfx_message_get_frame_idx (rfx_message)); /* frameIndex */
|
|
Stream_Write_UINT16 (s, 1); /* regionCount */
|
|
|
|
/* RFX_PROGRESSIVE_REGION */
|
|
block_len = 18;
|
|
block_len += n_rfx_rects * 8;
|
|
block_len += n_quant_vals * 5;
|
|
tiles_data_size = n_rfx_tiles * 22;
|
|
|
|
for (i = 0; i < n_rfx_tiles; i++)
|
|
{
|
|
rfx_tile = rfx_tiles[i];
|
|
tiles_data_size += rfx_tile->YLen + rfx_tile->CbLen + rfx_tile->CrLen;
|
|
}
|
|
|
|
block_len += tiles_data_size;
|
|
if (!Stream_EnsureRemainingCapacity (s, block_len))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write_UINT16 (s, 0xCCC4); /* blockType */
|
|
Stream_Write_UINT32 (s, block_len); /* blockLen */
|
|
Stream_Write_UINT8 (s, 0x40); /* tileSize */
|
|
Stream_Write_UINT16 (s, n_rfx_rects); /* numRects */
|
|
Stream_Write_UINT8 (s, n_quant_vals); /* numQuant */
|
|
Stream_Write_UINT8 (s, 0); /* numProgQuant */
|
|
Stream_Write_UINT8 (s, 0); /* flags */
|
|
Stream_Write_UINT16 (s, n_rfx_tiles); /* numTiles */
|
|
Stream_Write_UINT32 (s, tiles_data_size); /* tilesDataSize */
|
|
|
|
for (i = 0; i < n_rfx_rects; i++)
|
|
{
|
|
/* TS_RFX_RECT */
|
|
Stream_Write_UINT16 (s, rfx_rects[i].x); /* x */
|
|
Stream_Write_UINT16 (s, rfx_rects[i].y); /* y */
|
|
Stream_Write_UINT16 (s, rfx_rects[i].width); /* width */
|
|
Stream_Write_UINT16 (s, rfx_rects[i].height); /* height */
|
|
}
|
|
|
|
/*
|
|
* The RFX_COMPONENT_CODEC_QUANT structure differs from the
|
|
* TS_RFX_CODEC_QUANT ([MS-RDPRFX] section 2.2.2.1.5) structure with respect
|
|
* to the order of the bands.
|
|
* 0 1 2 3 4 5 6 7 8 9
|
|
* RDPRFX: LL3, LH3, HL3, HH3, LH2, HL2, HH2, LH1, HL1, HH1
|
|
* RDPEGFX: LL3, HL3, LH3, HH3, HL2, LH2, HH2, HL1, LH1, HH1
|
|
*/
|
|
for (i = 0, qv = quant_vals; i < n_quant_vals; ++i, qv += 10)
|
|
{
|
|
/* RFX_COMPONENT_CODEC_QUANT */
|
|
Stream_Write_UINT8 (s, qv[0] + (qv[2] << 4)); /* LL3, HL3 */
|
|
Stream_Write_UINT8 (s, qv[1] + (qv[3] << 4)); /* LH3, HH3 */
|
|
Stream_Write_UINT8 (s, qv[5] + (qv[4] << 4)); /* HL2, LH2 */
|
|
Stream_Write_UINT8 (s, qv[6] + (qv[8] << 4)); /* HH2, HL1 */
|
|
Stream_Write_UINT8 (s, qv[7] + (qv[9] << 4)); /* LH1, HH1 */
|
|
}
|
|
|
|
for (i = 0; i < n_rfx_tiles; ++i)
|
|
{
|
|
/* RFX_PROGRESSIVE_TILE_SIMPLE */
|
|
rfx_tile = rfx_tiles[i];
|
|
block_len = 22 + rfx_tile->YLen + rfx_tile->CbLen + rfx_tile->CrLen;
|
|
Stream_Write_UINT16 (s, 0xCCC5); /* blockType */
|
|
Stream_Write_UINT32 (s, block_len); /* blockLen */
|
|
Stream_Write_UINT8 (s, rfx_tile->quantIdxY); /* quantIdxY */
|
|
Stream_Write_UINT8 (s, rfx_tile->quantIdxCb); /* quantIdxCb */
|
|
Stream_Write_UINT8 (s, rfx_tile->quantIdxCr); /* quantIdxCr */
|
|
Stream_Write_UINT16 (s, rfx_tile->xIdx); /* xIdx */
|
|
Stream_Write_UINT16 (s, rfx_tile->yIdx); /* yIdx */
|
|
Stream_Write_UINT8 (s, 0); /* flags */
|
|
Stream_Write_UINT16 (s, rfx_tile->YLen); /* YLen */
|
|
Stream_Write_UINT16 (s, rfx_tile->CbLen); /* CbLen */
|
|
Stream_Write_UINT16 (s, rfx_tile->CrLen); /* CrLen */
|
|
Stream_Write_UINT16 (s, 0); /* tailLen */
|
|
Stream_Write (s, rfx_tile->YData, rfx_tile->YLen); /* YData */
|
|
Stream_Write (s, rfx_tile->CbData, rfx_tile->CbLen); /* CbData */
|
|
Stream_Write (s, rfx_tile->CrData, rfx_tile->CrLen); /* CrData */
|
|
}
|
|
|
|
/* RFX_PROGRESSIVE_FRAME_END */
|
|
block_len = 6;
|
|
if (!Stream_EnsureRemainingCapacity (s, block_len))
|
|
g_assert_not_reached ();
|
|
|
|
Stream_Write_UINT16 (s, 0xCCC2); /* blockType */
|
|
Stream_Write_UINT32 (s, block_len); /* blockLen */
|
|
}
|
|
|
|
static gboolean
|
|
refresh_gfx_surface_rfx_progressive (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpSurface *rdp_surface,
|
|
GrdRdpGfxSurface *gfx_surface,
|
|
GrdRdpLegacyBuffer *buffer,
|
|
int64_t *enc_time_us)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
GrdRdpNetworkAutodetection *network_autodetection =
|
|
graphics_pipeline->network_autodetection;
|
|
GrdRdpGfxFrameController *frame_controller =
|
|
grd_rdp_gfx_surface_get_frame_controller (gfx_surface);
|
|
uint16_t surface_width = grd_rdp_gfx_surface_get_width (gfx_surface);
|
|
uint16_t surface_height = grd_rdp_gfx_surface_get_height (gfx_surface);
|
|
uint32_t src_stride = grd_rdp_legacy_buffer_get_stride (buffer);
|
|
RDPGFX_SURFACE_COMMAND cmd = {0};
|
|
RDPGFX_START_FRAME_PDU cmd_start = {0};
|
|
RDPGFX_END_FRAME_PDU cmd_end = {0};
|
|
gboolean needs_progressive_header = FALSE;
|
|
cairo_region_t *region;
|
|
cairo_rectangle_int_t cairo_rect;
|
|
RFX_RECT *rfx_rects, *rfx_rect;
|
|
int n_rects;
|
|
RFX_MESSAGE *rfx_message;
|
|
SYSTEMTIME system_time;
|
|
uint32_t codec_context_id;
|
|
uint32_t surface_serial;
|
|
int64_t enc_ack_time_us;
|
|
gboolean pending_bw_measure_stop = FALSE;
|
|
int i;
|
|
|
|
region = grd_rdp_damage_detector_get_damage_region (rdp_surface->detector);
|
|
if (!region)
|
|
return FALSE;
|
|
|
|
rfx_context_set_mode (graphics_pipeline->rfx_context, RLGR1);
|
|
rfx_context_reset (graphics_pipeline->rfx_context,
|
|
surface_width, surface_height);
|
|
|
|
codec_context_id = grd_rdp_gfx_surface_get_codec_context_id (gfx_surface);
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
if (!g_hash_table_contains (graphics_pipeline->codec_context_table,
|
|
GUINT_TO_POINTER (codec_context_id)))
|
|
needs_progressive_header = TRUE;
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
n_rects = cairo_region_num_rectangles (region);
|
|
rfx_rects = g_malloc0 (n_rects * sizeof (RFX_RECT));
|
|
for (i = 0; i < n_rects; ++i)
|
|
{
|
|
cairo_region_get_rectangle (region, i, &cairo_rect);
|
|
|
|
rfx_rect = &rfx_rects[i];
|
|
rfx_rect->x = cairo_rect.x;
|
|
rfx_rect->y = cairo_rect.y;
|
|
rfx_rect->width = cairo_rect.width;
|
|
rfx_rect->height = cairo_rect.height;
|
|
}
|
|
|
|
rfx_message = rfx_encode_message (graphics_pipeline->rfx_context,
|
|
rfx_rects,
|
|
n_rects,
|
|
grd_rdp_legacy_buffer_get_local_data (buffer),
|
|
surface_width,
|
|
surface_height,
|
|
src_stride);
|
|
g_free (rfx_rects);
|
|
|
|
GetSystemTime (&system_time);
|
|
cmd_start.timestamp = system_time.wHour << 22 |
|
|
system_time.wMinute << 16 |
|
|
system_time.wSecond << 10 |
|
|
system_time.wMilliseconds;
|
|
cmd_start.frameId = get_next_free_frame_id (graphics_pipeline);
|
|
cmd_end.frameId = cmd_start.frameId;
|
|
|
|
cmd.surfaceId = grd_rdp_gfx_surface_get_surface_id (gfx_surface);
|
|
cmd.codecId = RDPGFX_CODECID_CAPROGRESSIVE;
|
|
cmd.contextId = codec_context_id;
|
|
cmd.format = PIXEL_FORMAT_BGRX32;
|
|
|
|
Stream_SetPosition (graphics_pipeline->encode_stream, 0);
|
|
rfx_progressive_write_message (rfx_message,
|
|
graphics_pipeline->encode_stream,
|
|
needs_progressive_header);
|
|
rfx_message_free (graphics_pipeline->rfx_context, rfx_message);
|
|
|
|
cmd.length = Stream_GetPosition (graphics_pipeline->encode_stream);
|
|
cmd.data = Stream_Buffer (graphics_pipeline->encode_stream);
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
if (needs_progressive_header)
|
|
{
|
|
g_hash_table_insert (graphics_pipeline->codec_context_table,
|
|
GUINT_TO_POINTER (codec_context_id), gfx_surface);
|
|
}
|
|
|
|
enc_ack_time_us = g_get_monotonic_time ();
|
|
grd_rdp_gfx_frame_controller_unack_frame (frame_controller, cmd_start.frameId,
|
|
1, enc_ack_time_us);
|
|
|
|
surface_serial = grd_rdp_gfx_surface_get_serial (gfx_surface);
|
|
g_hash_table_insert (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (cmd_start.frameId),
|
|
GUINT_TO_POINTER (surface_serial));
|
|
surface_serial_ref (graphics_pipeline, surface_serial);
|
|
++graphics_pipeline->total_frames_encoded;
|
|
|
|
if (graphics_pipeline->frame_acks_suspended)
|
|
{
|
|
grd_rdp_gfx_frame_controller_ack_frame (frame_controller, cmd_start.frameId,
|
|
enc_ack_time_us);
|
|
enqueue_tracked_frame_info (graphics_pipeline, surface_serial,
|
|
cmd_start.frameId, 1, enc_ack_time_us);
|
|
}
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
if (network_autodetection && cmd.length >= MIN_BW_MEASURE_SIZE)
|
|
{
|
|
pending_bw_measure_stop =
|
|
grd_rdp_network_autodetection_try_bw_measure_start (network_autodetection);
|
|
}
|
|
|
|
rdpgfx_context->SurfaceFrameCommand (rdpgfx_context, &cmd,
|
|
&cmd_start, &cmd_end);
|
|
|
|
if (pending_bw_measure_stop)
|
|
grd_rdp_network_autodetection_queue_bw_measure_stop (network_autodetection);
|
|
|
|
*enc_time_us = enc_ack_time_us;
|
|
|
|
cairo_region_destroy (region);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
grd_rdp_dvc_graphics_pipeline_refresh_gfx (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
GrdRdpSurface *rdp_surface,
|
|
GrdRdpRenderContext *render_context,
|
|
GrdRdpLegacyBuffer *buffer)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
rdpSettings *rdp_settings = rdpgfx_context->rdpcontext->settings;
|
|
GrdRdpGfxSurface *gfx_surface =
|
|
grd_rdp_render_context_get_gfx_surface (render_context);
|
|
HWAccelContext *hwaccel_context;
|
|
uint16_t surface_id;
|
|
int64_t enc_time_us;
|
|
gboolean success;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect) &&
|
|
!graphics_pipeline->rtt_pause_source)
|
|
ensure_rtt_receivement (graphics_pipeline);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
surface_id = grd_rdp_gfx_surface_get_surface_id (gfx_surface);
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_GfxH264) &&
|
|
g_hash_table_lookup_extended (graphics_pipeline->surface_hwaccel_table,
|
|
GUINT_TO_POINTER (surface_id),
|
|
NULL, (gpointer *) &hwaccel_context))
|
|
{
|
|
g_assert (hwaccel_context->api == HW_ACCEL_API_NVENC);
|
|
success = refresh_gfx_surface_avc420 (graphics_pipeline, hwaccel_context,
|
|
rdp_surface, gfx_surface, buffer,
|
|
&enc_time_us);
|
|
}
|
|
else
|
|
{
|
|
success = refresh_gfx_surface_rfx_progressive (graphics_pipeline,
|
|
rdp_surface, gfx_surface,
|
|
buffer, &enc_time_us);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
clear_old_enc_times (graphics_pipeline, g_get_monotonic_time ());
|
|
track_enc_time (graphics_pipeline, enc_time_us);
|
|
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect) &&
|
|
!graphics_pipeline->rtt_pause_source)
|
|
ensure_rtt_receivement (graphics_pipeline);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static void
|
|
dvc_creation_status (gpointer user_data,
|
|
int32_t creation_status)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = user_data;
|
|
|
|
if (creation_status < 0)
|
|
{
|
|
g_warning ("[RDP.RDPGFX] Failed to open channel (CreationStatus %i). "
|
|
"Terminating session", creation_status);
|
|
grd_session_rdp_notify_error (graphics_pipeline->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_BAD_CAPS);
|
|
}
|
|
}
|
|
|
|
static BOOL
|
|
rdpgfx_channel_id_assigned (RdpgfxServerContext *rdpgfx_context,
|
|
uint32_t channel_id)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = rdpgfx_context->custom;
|
|
GrdRdpDvc *dvc = GRD_RDP_DVC (graphics_pipeline);
|
|
|
|
g_debug ("[RDP.RDPGFX] DVC channel id assigned to id %u", channel_id);
|
|
|
|
grd_rdp_dvc_subscribe_creation_status (dvc, channel_id,
|
|
dvc_creation_status,
|
|
graphics_pipeline);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static uint32_t cap_list[] =
|
|
{
|
|
RDPGFX_CAPVERSION_107, /* [MS-RDPEGFX] 2.2.3.10 */
|
|
RDPGFX_CAPVERSION_106, /* [MS-RDPEGFX] 2.2.3.9 */
|
|
RDPGFX_CAPVERSION_105, /* [MS-RDPEGFX] 2.2.3.8 */
|
|
RDPGFX_CAPVERSION_104, /* [MS-RDPEGFX] 2.2.3.7 */
|
|
RDPGFX_CAPVERSION_103, /* [MS-RDPEGFX] 2.2.3.6 */
|
|
RDPGFX_CAPVERSION_102, /* [MS-RDPEGFX] 2.2.3.5 */
|
|
RDPGFX_CAPVERSION_101, /* [MS-RDPEGFX] 2.2.3.4 */
|
|
RDPGFX_CAPVERSION_10, /* [MS-RDPEGFX] 2.2.3.3 */
|
|
RDPGFX_CAPVERSION_81, /* [MS-RDPEGFX] 2.2.3.2 */
|
|
RDPGFX_CAPVERSION_8, /* [MS-RDPEGFX] 2.2.3.1 */
|
|
};
|
|
|
|
static const char *
|
|
rdpgfx_caps_version_to_string (uint32_t caps_version)
|
|
{
|
|
switch (caps_version)
|
|
{
|
|
case RDPGFX_CAPVERSION_107:
|
|
return "RDPGFX_CAPVERSION_107";
|
|
case RDPGFX_CAPVERSION_106:
|
|
return "RDPGFX_CAPVERSION_106";
|
|
case RDPGFX_CAPVERSION_105:
|
|
return "RDPGFX_CAPVERSION_105";
|
|
case RDPGFX_CAPVERSION_104:
|
|
return "RDPGFX_CAPVERSION_104";
|
|
case RDPGFX_CAPVERSION_103:
|
|
return "RDPGFX_CAPVERSION_103";
|
|
case RDPGFX_CAPVERSION_102:
|
|
return "RDPGFX_CAPVERSION_102";
|
|
case RDPGFX_CAPVERSION_101:
|
|
return "RDPGFX_CAPVERSION_101";
|
|
case RDPGFX_CAPVERSION_10:
|
|
return "RDPGFX_CAPVERSION_10";
|
|
case RDPGFX_CAPVERSION_81:
|
|
return "RDPGFX_CAPVERSION_81";
|
|
case RDPGFX_CAPVERSION_8:
|
|
return "RDPGFX_CAPVERSION_8";
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
search_and_list_unknown_cap_sets_versions_and_flags (RDPGFX_CAPSET *cap_sets,
|
|
uint16_t n_cap_sets)
|
|
{
|
|
uint16_t i;
|
|
size_t j;
|
|
|
|
for (i = 0; i < n_cap_sets; ++i)
|
|
{
|
|
gboolean cap_found = FALSE;
|
|
|
|
for (j = 0; j < G_N_ELEMENTS (cap_list) && !cap_found; ++j)
|
|
{
|
|
const char *version_string;
|
|
gboolean has_flags_field;
|
|
|
|
if (cap_sets[i].version != cap_list[j])
|
|
continue;
|
|
|
|
cap_found = TRUE;
|
|
has_flags_field = cap_sets[i].version != RDPGFX_CAPVERSION_101;
|
|
|
|
version_string = rdpgfx_caps_version_to_string (cap_sets[i].version);
|
|
g_debug ("[RDP.RDPGFX] Client caps set %s flags: 0x%08X%s",
|
|
version_string, cap_sets[i].flags,
|
|
has_flags_field ? "" : " (invalid flags field)");
|
|
}
|
|
if (!cap_found)
|
|
{
|
|
g_debug ("[RDP.RDPGFX] Received unknown capability set with "
|
|
"version 0x%08X, length: %u Bytes",
|
|
cap_sets[i].version, cap_sets[i].length);
|
|
if (cap_sets[i].length >= 4)
|
|
{
|
|
g_debug ("[RDP.RDPGFX] Possible flags of unknown capability set: "
|
|
"0x%08X", cap_sets[i].flags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
cap_sets_contains_supported_version (RDPGFX_CAPSET *cap_sets,
|
|
uint16_t n_cap_sets)
|
|
{
|
|
size_t i;
|
|
uint16_t j;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (cap_list); ++i)
|
|
{
|
|
for (j = 0; j < n_cap_sets; ++j)
|
|
{
|
|
if (cap_sets[j].version == cap_list[i])
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
g_warning ("[RDP.RDPGFX] Client did not advertise any supported "
|
|
"capability set");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
cap_sets_would_disable_avc (RDPGFX_CAPSET *cap_sets,
|
|
uint16_t n_cap_sets)
|
|
{
|
|
size_t i;
|
|
uint16_t j;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (cap_list); ++i)
|
|
{
|
|
for (j = 0; j < n_cap_sets; ++j)
|
|
{
|
|
if (cap_sets[j].version == cap_list[i])
|
|
{
|
|
uint32_t flags = cap_sets[i].flags;
|
|
|
|
switch (cap_sets[j].version)
|
|
{
|
|
case RDPGFX_CAPVERSION_107:
|
|
case RDPGFX_CAPVERSION_106:
|
|
case RDPGFX_CAPVERSION_105:
|
|
case RDPGFX_CAPVERSION_104:
|
|
case RDPGFX_CAPVERSION_103:
|
|
case RDPGFX_CAPVERSION_102:
|
|
case RDPGFX_CAPVERSION_101:
|
|
case RDPGFX_CAPVERSION_10:
|
|
if (flags & RDPGFX_CAPS_FLAG_AVC_DISABLED)
|
|
return TRUE;
|
|
return FALSE;
|
|
case RDPGFX_CAPVERSION_81:
|
|
if (!(flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED))
|
|
return TRUE;
|
|
return FALSE;
|
|
case RDPGFX_CAPVERSION_8:
|
|
return TRUE;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static uint32_t
|
|
rdpgfx_caps_advertise (RdpgfxServerContext *rdpgfx_context,
|
|
const RDPGFX_CAPS_ADVERTISE_PDU *caps_advertise)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = rdpgfx_context->custom;
|
|
GrdRdpRenderer *renderer = graphics_pipeline->renderer;
|
|
|
|
g_debug ("[RDP.RDPGFX] Received a CapsAdvertise PDU");
|
|
search_and_list_unknown_cap_sets_versions_and_flags (caps_advertise->capsSets,
|
|
caps_advertise->capsSetCount);
|
|
|
|
if (graphics_pipeline->initialized &&
|
|
graphics_pipeline->initial_version < RDPGFX_CAPVERSION_103)
|
|
{
|
|
g_warning ("[RDP.RDPGFX] Protocol violation: Received an illegal "
|
|
"CapsAdvertise PDU (RDPGFX: initialized, initial "
|
|
"version < 103)");
|
|
grd_session_rdp_notify_error (graphics_pipeline->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_BAD_CAPS);
|
|
|
|
return CHANNEL_RC_ALREADY_INITIALIZED;
|
|
}
|
|
|
|
if (!cap_sets_contains_supported_version (caps_advertise->capsSets,
|
|
caps_advertise->capsSetCount))
|
|
{
|
|
g_warning ("[RDP.RDPGFX] CapsAdvertise PDU does NOT contain any supported "
|
|
"capability sets");
|
|
grd_session_rdp_notify_error (graphics_pipeline->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_BAD_CAPS);
|
|
|
|
return CHANNEL_RC_UNSUPPORTED_VERSION;
|
|
}
|
|
|
|
if (graphics_pipeline->received_first_cap_sets &&
|
|
cap_sets_would_disable_avc (caps_advertise->capsSets,
|
|
caps_advertise->capsSetCount))
|
|
{
|
|
g_warning ("[RDP.RDPGFX] CapsAdvertise PDU would reset protocol with "
|
|
"unsupported capability sets (disabling AVC)");
|
|
grd_session_rdp_notify_error (graphics_pipeline->session_rdp,
|
|
GRD_SESSION_RDP_ERROR_BAD_CAPS);
|
|
|
|
return CHANNEL_RC_ALREADY_INITIALIZED;
|
|
}
|
|
graphics_pipeline->received_first_cap_sets = TRUE;
|
|
|
|
g_mutex_lock (&graphics_pipeline->caps_mutex);
|
|
g_clear_pointer (&graphics_pipeline->cap_sets, g_free);
|
|
|
|
graphics_pipeline->n_cap_sets = caps_advertise->capsSetCount;
|
|
graphics_pipeline->cap_sets = g_memdup2 (caps_advertise->capsSets,
|
|
graphics_pipeline->n_cap_sets *
|
|
sizeof (RDPGFX_CAPSET));
|
|
|
|
grd_rdp_renderer_notify_graphics_pipeline_reset (renderer);
|
|
g_mutex_unlock (&graphics_pipeline->caps_mutex);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static uint32_t
|
|
rdpgfx_cache_import_offer (RdpgfxServerContext *rdpgfx_context,
|
|
const RDPGFX_CACHE_IMPORT_OFFER_PDU *cache_import_offer)
|
|
{
|
|
RDPGFX_CACHE_IMPORT_REPLY_PDU cache_import_reply = {0};
|
|
|
|
return rdpgfx_context->CacheImportReply (rdpgfx_context, &cache_import_reply);
|
|
}
|
|
|
|
static void
|
|
notify_updated_frame_controllers (GHashTable *updated_frame_controllers)
|
|
{
|
|
GrdRdpGfxFrameController *frame_controller = NULL;
|
|
GHashTableIter iter;
|
|
|
|
g_hash_table_iter_init (&iter, updated_frame_controllers);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &frame_controller))
|
|
grd_rdp_gfx_frame_controller_notify_history_changed (frame_controller);
|
|
}
|
|
|
|
static void
|
|
maybe_rewrite_frame_history (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
uint32_t pending_frame_acks)
|
|
{
|
|
GfxFrameInfo *gfx_frame_info;
|
|
GHashTable *updated_frame_controllers;
|
|
|
|
if (g_queue_get_length (graphics_pipeline->encoded_frames) == 0)
|
|
return;
|
|
|
|
reduce_tracked_frame_infos (graphics_pipeline, pending_frame_acks + 1);
|
|
updated_frame_controllers = g_hash_table_new (NULL, NULL);
|
|
|
|
while ((gfx_frame_info = g_queue_pop_tail (graphics_pipeline->encoded_frames)))
|
|
{
|
|
GrdRdpFrameInfo *frame_info = &gfx_frame_info->frame_info;
|
|
uint32_t surface_serial = gfx_frame_info->surface_serial;
|
|
GfxSurfaceContext *surface_context;
|
|
|
|
if (!g_hash_table_lookup_extended (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial),
|
|
NULL, (gpointer *) &surface_context))
|
|
g_assert_not_reached ();
|
|
|
|
if (surface_context->gfx_surface)
|
|
{
|
|
GrdRdpGfxFrameController *frame_controller =
|
|
grd_rdp_gfx_surface_get_frame_controller (surface_context->gfx_surface);
|
|
|
|
grd_rdp_gfx_frame_controller_unack_last_acked_frame (frame_controller,
|
|
frame_info->frame_id,
|
|
frame_info->n_subframes,
|
|
frame_info->enc_time_us);
|
|
g_hash_table_add (updated_frame_controllers, frame_controller);
|
|
}
|
|
|
|
g_free (gfx_frame_info);
|
|
}
|
|
|
|
notify_updated_frame_controllers (updated_frame_controllers);
|
|
g_hash_table_unref (updated_frame_controllers);
|
|
}
|
|
|
|
static void
|
|
clear_all_unacked_frames_in_gfx_surface (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
GrdRdpGfxSurface *gfx_surface = value;
|
|
GrdRdpGfxFrameController *frame_controller =
|
|
grd_rdp_gfx_surface_get_frame_controller (gfx_surface);
|
|
|
|
if (!frame_controller)
|
|
return;
|
|
|
|
grd_rdp_gfx_frame_controller_clear_all_unacked_frames (frame_controller);
|
|
}
|
|
|
|
static gboolean
|
|
frame_serial_free (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = user_data;
|
|
uint32_t surface_serial = GPOINTER_TO_UINT (value);
|
|
|
|
surface_serial_unref (graphics_pipeline, surface_serial);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
suspend_frame_acknowledgement (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
graphics_pipeline->frame_acks_suspended = TRUE;
|
|
|
|
g_hash_table_foreach (graphics_pipeline->surface_table,
|
|
clear_all_unacked_frames_in_gfx_surface, NULL);
|
|
|
|
reduce_tracked_frame_infos (graphics_pipeline, 0);
|
|
g_hash_table_foreach_remove (graphics_pipeline->frame_serial_table,
|
|
frame_serial_free, graphics_pipeline);
|
|
}
|
|
|
|
static void
|
|
handle_frame_ack_event (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
const RDPGFX_FRAME_ACKNOWLEDGE_PDU *frame_acknowledge)
|
|
{
|
|
uint32_t pending_frame_acks;
|
|
gpointer value = NULL;
|
|
|
|
pending_frame_acks = graphics_pipeline->total_frames_encoded -
|
|
frame_acknowledge->totalFramesDecoded;
|
|
if (pending_frame_acks <= MAX_TRACKED_ENC_FRAMES &&
|
|
!g_hash_table_contains (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (frame_acknowledge->frameId)))
|
|
return;
|
|
|
|
maybe_rewrite_frame_history (graphics_pipeline, pending_frame_acks);
|
|
if (frame_acknowledge->queueDepth != SUSPEND_FRAME_ACKNOWLEDGEMENT)
|
|
graphics_pipeline->frame_acks_suspended = FALSE;
|
|
|
|
if (g_hash_table_steal_extended (graphics_pipeline->frame_serial_table,
|
|
GUINT_TO_POINTER (frame_acknowledge->frameId),
|
|
NULL, &value))
|
|
{
|
|
GfxSurfaceContext *surface_context;
|
|
uint32_t surface_serial;
|
|
|
|
surface_serial = GPOINTER_TO_UINT (value);
|
|
if (!g_hash_table_lookup_extended (graphics_pipeline->serial_surface_table,
|
|
GUINT_TO_POINTER (surface_serial),
|
|
NULL, (gpointer *) &surface_context))
|
|
g_assert_not_reached ();
|
|
|
|
if (surface_context->gfx_surface)
|
|
{
|
|
GrdRdpGfxFrameController *frame_controller =
|
|
grd_rdp_gfx_surface_get_frame_controller (surface_context->gfx_surface);
|
|
|
|
grd_rdp_gfx_frame_controller_ack_frame (frame_controller,
|
|
frame_acknowledge->frameId,
|
|
g_get_monotonic_time ());
|
|
}
|
|
|
|
surface_serial_unref (graphics_pipeline, surface_serial);
|
|
}
|
|
|
|
if (frame_acknowledge->queueDepth == SUSPEND_FRAME_ACKNOWLEDGEMENT)
|
|
suspend_frame_acknowledgement (graphics_pipeline);
|
|
}
|
|
|
|
static uint32_t
|
|
rdpgfx_frame_acknowledge (RdpgfxServerContext *rdpgfx_context,
|
|
const RDPGFX_FRAME_ACKNOWLEDGE_PDU *frame_acknowledge)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = rdpgfx_context->custom;
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
handle_frame_ack_event (graphics_pipeline, frame_acknowledge);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static uint32_t
|
|
rdpgfx_qoe_frame_acknowledge (RdpgfxServerContext *rdpgfx_context,
|
|
const RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU *qoe_frame_acknowledge)
|
|
{
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static void
|
|
reset_graphics_pipeline (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
g_hash_table_foreach (graphics_pipeline->surface_table,
|
|
clear_all_unacked_frames_in_gfx_surface, NULL);
|
|
|
|
reduce_tracked_frame_infos (graphics_pipeline, 0);
|
|
g_hash_table_foreach_remove (graphics_pipeline->frame_serial_table,
|
|
frame_serial_free, graphics_pipeline);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
grd_rdp_renderer_clear_render_contexts (graphics_pipeline->renderer);
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
graphics_pipeline->frame_acks_suspended = FALSE;
|
|
graphics_pipeline->total_frames_encoded = 0;
|
|
|
|
g_assert (g_hash_table_size (graphics_pipeline->surface_table) == 0);
|
|
g_assert (g_hash_table_size (graphics_pipeline->codec_context_table) == 0);
|
|
g_assert (g_hash_table_size (graphics_pipeline->frame_serial_table) == 0);
|
|
g_assert (g_hash_table_size (graphics_pipeline->serial_surface_table) == 0);
|
|
g_assert (g_queue_get_length (graphics_pipeline->encoded_frames) == 0);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
}
|
|
|
|
static gboolean
|
|
test_caps_version (GrdRdpDvcGraphicsPipeline *graphics_pipeline,
|
|
RDPGFX_CAPSET *cap_sets,
|
|
uint16_t n_cap_sets,
|
|
uint32_t caps_version)
|
|
{
|
|
RdpgfxServerContext *rdpgfx_context = graphics_pipeline->rdpgfx_context;
|
|
rdpSettings *rdp_settings = rdpgfx_context->rdpcontext->settings;
|
|
RDPGFX_CAPS_CONFIRM_PDU caps_confirm = {0};
|
|
uint16_t i;
|
|
|
|
for (i = 0; i < n_cap_sets; ++i)
|
|
{
|
|
if (cap_sets[i].version == caps_version)
|
|
{
|
|
uint32_t flags = cap_sets[i].flags;
|
|
gboolean have_avc444 = FALSE;
|
|
gboolean have_avc420 = FALSE;
|
|
|
|
switch (caps_version)
|
|
{
|
|
case RDPGFX_CAPVERSION_107:
|
|
case RDPGFX_CAPVERSION_106:
|
|
case RDPGFX_CAPVERSION_105:
|
|
case RDPGFX_CAPVERSION_104:
|
|
case RDPGFX_CAPVERSION_103:
|
|
case RDPGFX_CAPVERSION_102:
|
|
case RDPGFX_CAPVERSION_101:
|
|
case RDPGFX_CAPVERSION_10:
|
|
have_avc444 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED);
|
|
have_avc420 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED);
|
|
break;
|
|
case RDPGFX_CAPVERSION_81:
|
|
have_avc420 = !!(flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED);
|
|
break;
|
|
case RDPGFX_CAPVERSION_8:
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxAVC444v2, have_avc444);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxAVC444, have_avc444);
|
|
freerdp_settings_set_bool (rdp_settings, FreeRDP_GfxH264, have_avc420);
|
|
|
|
g_message ("[RDP.RDPGFX] CapsAdvertise: Accepting capability set with version "
|
|
"%s, Client cap flags: H264 (AVC444): %s, H264 (AVC420): %s",
|
|
rdpgfx_caps_version_to_string (caps_version),
|
|
have_avc444 ? "true" : "false",
|
|
have_avc420 ? "true" : "false");
|
|
if (!graphics_pipeline->initialized)
|
|
graphics_pipeline->initial_version = caps_version;
|
|
graphics_pipeline->initialized = TRUE;
|
|
|
|
reset_graphics_pipeline (graphics_pipeline);
|
|
|
|
caps_confirm.capsSet = &cap_sets[i];
|
|
|
|
rdpgfx_context->CapsConfirm (rdpgfx_context, &caps_confirm);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
reset_protocol (gpointer user_data)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline = user_data;
|
|
GrdRdpRenderer *renderer = graphics_pipeline->renderer;
|
|
g_autofree RDPGFX_CAPSET *cap_sets = NULL;
|
|
uint16_t n_cap_sets;
|
|
size_t i;
|
|
|
|
g_mutex_lock (&graphics_pipeline->caps_mutex);
|
|
cap_sets = g_steal_pointer (&graphics_pipeline->cap_sets);
|
|
n_cap_sets = graphics_pipeline->n_cap_sets;
|
|
g_mutex_unlock (&graphics_pipeline->caps_mutex);
|
|
|
|
if (!cap_sets || !n_cap_sets)
|
|
{
|
|
g_assert (graphics_pipeline->initialized);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
if (graphics_pipeline->protocol_timeout_source)
|
|
{
|
|
g_source_destroy (graphics_pipeline->protocol_timeout_source);
|
|
g_clear_pointer (&graphics_pipeline->protocol_timeout_source, g_source_unref);
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (cap_list); ++i)
|
|
{
|
|
if (test_caps_version (graphics_pipeline,
|
|
cap_sets, n_cap_sets,
|
|
cap_list[i]))
|
|
{
|
|
grd_rdp_renderer_notify_graphics_pipeline_ready (renderer);
|
|
|
|
g_mutex_lock (&graphics_pipeline->caps_mutex);
|
|
if (graphics_pipeline->cap_sets)
|
|
grd_rdp_renderer_notify_graphics_pipeline_reset (renderer);
|
|
g_mutex_unlock (&graphics_pipeline->caps_mutex);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CapsAdvertise already checked the capability sets to have at least one
|
|
* supported version.
|
|
* It is therefore impossible to hit this path.
|
|
*/
|
|
g_assert_not_reached ();
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static gboolean
|
|
protocol_reset_source_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
g_source_set_ready_time (source, -1);
|
|
|
|
return callback (user_data);
|
|
}
|
|
|
|
static GSourceFuncs protocol_reset_source_funcs =
|
|
{
|
|
.dispatch = protocol_reset_source_dispatch,
|
|
};
|
|
|
|
static void
|
|
on_gfx_initable (GrdRdpRenderer *renderer,
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
g_source_set_ready_time (graphics_pipeline->protocol_reset_source, 0);
|
|
}
|
|
|
|
GrdRdpDvcGraphicsPipeline *
|
|
grd_rdp_dvc_graphics_pipeline_new (GrdSessionRdp *session_rdp,
|
|
GrdRdpRenderer *renderer,
|
|
GrdRdpDvcHandler *dvc_handler,
|
|
HANDLE vcm,
|
|
rdpContext *rdp_context,
|
|
GrdRdpNetworkAutodetection *network_autodetection,
|
|
wStream *encode_stream,
|
|
RFX_CONTEXT *rfx_context)
|
|
{
|
|
GMainContext *graphics_context =
|
|
grd_rdp_renderer_get_graphics_context (renderer);
|
|
rdpSettings *rdp_settings = rdp_context->settings;
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline;
|
|
RdpgfxServerContext *rdpgfx_context;
|
|
GSource *protocol_reset_source;
|
|
|
|
graphics_pipeline = g_object_new (GRD_TYPE_RDP_DVC_GRAPHICS_PIPELINE, NULL);
|
|
rdpgfx_context = rdpgfx_server_context_new (vcm);
|
|
if (!rdpgfx_context)
|
|
g_error ("[RDP.RDPGFX] Failed to create server context");
|
|
|
|
graphics_pipeline->rdpgfx_context = rdpgfx_context;
|
|
graphics_pipeline->session_rdp = session_rdp;
|
|
graphics_pipeline->renderer = renderer;
|
|
graphics_pipeline->network_autodetection = network_autodetection;
|
|
graphics_pipeline->encode_stream = encode_stream;
|
|
graphics_pipeline->rfx_context = rfx_context;
|
|
|
|
grd_rdp_dvc_initialize_base (GRD_RDP_DVC (graphics_pipeline),
|
|
dvc_handler, session_rdp,
|
|
GRD_RDP_CHANNEL_GRAPHICS_PIPELINE);
|
|
|
|
rdpgfx_context->ChannelIdAssigned = rdpgfx_channel_id_assigned;
|
|
rdpgfx_context->CapsAdvertise = rdpgfx_caps_advertise;
|
|
rdpgfx_context->CacheImportOffer = rdpgfx_cache_import_offer;
|
|
rdpgfx_context->FrameAcknowledge = rdpgfx_frame_acknowledge;
|
|
rdpgfx_context->QoeFrameAcknowledge = rdpgfx_qoe_frame_acknowledge;
|
|
rdpgfx_context->rdpcontext = rdp_context;
|
|
rdpgfx_context->custom = graphics_pipeline;
|
|
|
|
protocol_reset_source = g_source_new (&protocol_reset_source_funcs,
|
|
sizeof (GSource));
|
|
g_source_set_callback (protocol_reset_source, reset_protocol,
|
|
graphics_pipeline, NULL);
|
|
g_source_set_ready_time (protocol_reset_source, -1);
|
|
g_source_attach (protocol_reset_source, graphics_context);
|
|
graphics_pipeline->protocol_reset_source = protocol_reset_source;
|
|
|
|
graphics_pipeline->gfx_initable_id =
|
|
g_signal_connect (renderer, "gfx-initable",
|
|
G_CALLBACK (on_gfx_initable),
|
|
graphics_pipeline);
|
|
|
|
g_mutex_lock (&graphics_pipeline->gfx_mutex);
|
|
if (freerdp_settings_get_bool (rdp_settings, FreeRDP_NetworkAutoDetect) &&
|
|
!graphics_pipeline->rtt_pause_source)
|
|
ensure_rtt_receivement (graphics_pipeline);
|
|
g_mutex_unlock (&graphics_pipeline->gfx_mutex);
|
|
|
|
return graphics_pipeline;
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_graphics_pipeline_dispose (GObject *object)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline =
|
|
GRD_RDP_DVC_GRAPHICS_PIPELINE (object);
|
|
GrdRdpDvc *dvc = GRD_RDP_DVC (graphics_pipeline);
|
|
|
|
if (graphics_pipeline->channel_opened)
|
|
{
|
|
reset_graphics_pipeline (graphics_pipeline);
|
|
graphics_pipeline->rdpgfx_context->Close (graphics_pipeline->rdpgfx_context);
|
|
graphics_pipeline->channel_opened = FALSE;
|
|
}
|
|
grd_rdp_dvc_maybe_unsubscribe_creation_status (dvc);
|
|
|
|
g_clear_signal_handler (&graphics_pipeline->gfx_initable_id,
|
|
graphics_pipeline->renderer);
|
|
|
|
if (graphics_pipeline->protocol_timeout_source)
|
|
{
|
|
g_source_destroy (graphics_pipeline->protocol_timeout_source);
|
|
g_clear_pointer (&graphics_pipeline->protocol_timeout_source, g_source_unref);
|
|
}
|
|
if (graphics_pipeline->rtt_pause_source)
|
|
{
|
|
g_source_destroy (graphics_pipeline->rtt_pause_source);
|
|
g_clear_pointer (&graphics_pipeline->rtt_pause_source, g_source_unref);
|
|
}
|
|
if (graphics_pipeline->protocol_reset_source)
|
|
{
|
|
g_source_destroy (graphics_pipeline->protocol_reset_source);
|
|
g_clear_pointer (&graphics_pipeline->protocol_reset_source, g_source_unref);
|
|
}
|
|
|
|
if (graphics_pipeline->enc_times)
|
|
{
|
|
g_queue_free_full (graphics_pipeline->enc_times, g_free);
|
|
graphics_pipeline->enc_times = NULL;
|
|
}
|
|
|
|
if (graphics_pipeline->encoded_frames)
|
|
{
|
|
g_assert (g_queue_get_length (graphics_pipeline->encoded_frames) == 0);
|
|
g_clear_pointer (&graphics_pipeline->encoded_frames, g_queue_free);
|
|
}
|
|
|
|
g_assert (g_hash_table_size (graphics_pipeline->surface_hwaccel_table) == 0);
|
|
g_clear_pointer (&graphics_pipeline->surface_hwaccel_table, g_hash_table_destroy);
|
|
|
|
g_clear_pointer (&graphics_pipeline->cap_sets, g_free);
|
|
|
|
g_assert (g_hash_table_size (graphics_pipeline->serial_surface_table) == 0);
|
|
g_clear_pointer (&graphics_pipeline->serial_surface_table, g_hash_table_destroy);
|
|
g_clear_pointer (&graphics_pipeline->frame_serial_table, g_hash_table_destroy);
|
|
g_clear_pointer (&graphics_pipeline->codec_context_table, g_hash_table_destroy);
|
|
g_clear_pointer (&graphics_pipeline->surface_table, g_hash_table_destroy);
|
|
g_clear_pointer (&graphics_pipeline->rdpgfx_context, rdpgfx_server_context_free);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_dvc_graphics_pipeline_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_graphics_pipeline_finalize (GObject *object)
|
|
{
|
|
GrdRdpDvcGraphicsPipeline *graphics_pipeline =
|
|
GRD_RDP_DVC_GRAPHICS_PIPELINE (object);
|
|
|
|
g_mutex_clear (&graphics_pipeline->gfx_mutex);
|
|
g_mutex_clear (&graphics_pipeline->caps_mutex);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_dvc_graphics_pipeline_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_graphics_pipeline_init (GrdRdpDvcGraphicsPipeline *graphics_pipeline)
|
|
{
|
|
graphics_pipeline->surface_table = g_hash_table_new (NULL, NULL);
|
|
graphics_pipeline->codec_context_table = g_hash_table_new (NULL, NULL);
|
|
|
|
graphics_pipeline->frame_serial_table = g_hash_table_new (NULL, NULL);
|
|
graphics_pipeline->serial_surface_table = g_hash_table_new_full (NULL, NULL,
|
|
NULL, g_free);
|
|
graphics_pipeline->surface_hwaccel_table = g_hash_table_new (NULL, NULL);
|
|
graphics_pipeline->encoded_frames = g_queue_new ();
|
|
graphics_pipeline->enc_times = g_queue_new ();
|
|
|
|
g_mutex_init (&graphics_pipeline->caps_mutex);
|
|
g_mutex_init (&graphics_pipeline->gfx_mutex);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_graphics_pipeline_class_init (GrdRdpDvcGraphicsPipelineClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GrdRdpDvcClass *dvc_class = GRD_RDP_DVC_CLASS (klass);
|
|
|
|
object_class->dispose = grd_rdp_dvc_graphics_pipeline_dispose;
|
|
object_class->finalize = grd_rdp_dvc_graphics_pipeline_finalize;
|
|
|
|
dvc_class->maybe_init = grd_rdp_dvc_graphics_pipeline_maybe_init;
|
|
}
|