Files
grd/grd-rdp-dvc-graphics-pipeline.c
2026-02-13 13:06:50 +09:00

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, &region_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, &region_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;
}