/* * Copyright (C) 2024 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-frame.h" #include "grd-encode-context.h" #include "grd-rdp-render-context.h" #include "grd-rdp-renderer.h" struct _GrdRdpFrame { GrdRdpRenderer *renderer; GrdRdpRenderContext *render_context; GrdRdpFrameCallback frame_picked_up; GrdRdpFrameCallback view_finalized; GrdRdpFrameCallback frame_submitted; GrdRdpFrameCallback frame_finalized; gpointer callback_user_data; GDestroyNotify user_data_destroy; gboolean pending_view_finalization; GrdEncodeContext *encode_context; GList *acquired_image_views; GQueue *unused_image_views; GrdRdpBuffer *src_buffer_new; GrdRdpBuffer *src_buffer_old; GrdRdpFrameViewType view_type; cairo_region_t *damage_region; GList *bitstreams; }; GrdRdpRenderer * grd_rdp_frame_get_renderer (GrdRdpFrame *rdp_frame) { return rdp_frame->renderer; } GrdRdpRenderContext * grd_rdp_frame_get_render_context (GrdRdpFrame *rdp_frame) { return rdp_frame->render_context; } GrdEncodeContext * grd_rdp_frame_get_encode_context (GrdRdpFrame *rdp_frame) { return rdp_frame->encode_context; } GList * grd_rdp_frame_get_image_views (GrdRdpFrame *rdp_frame) { return rdp_frame->acquired_image_views; } GrdRdpBuffer * grd_rdp_frame_get_source_buffer (GrdRdpFrame *rdp_frame) { return rdp_frame->src_buffer_new; } GrdRdpBuffer * grd_rdp_frame_get_last_source_buffer (GrdRdpFrame *rdp_frame) { return rdp_frame->src_buffer_old; } GrdRdpFrameViewType grd_rdp_frame_get_avc_view_type (GrdRdpFrame *rdp_frame) { return rdp_frame->view_type; } cairo_region_t * grd_rdp_frame_get_damage_region (GrdRdpFrame *rdp_frame) { return rdp_frame->damage_region; } GList * grd_rdp_frame_get_bitstreams (GrdRdpFrame *rdp_frame) { return rdp_frame->bitstreams; } gboolean grd_rdp_frame_has_valid_view (GrdRdpFrame *rdp_frame) { return !!rdp_frame->damage_region; } gboolean grd_rdp_frame_is_surface_damaged (GrdRdpFrame *rdp_frame) { return cairo_region_num_rectangles (rdp_frame->damage_region) > 0; } void grd_rdp_frame_set_renderer (GrdRdpFrame *rdp_frame, GrdRdpRenderer *renderer) { rdp_frame->renderer = renderer; } void grd_rdp_frame_set_avc_view_type (GrdRdpFrame *rdp_frame, GrdRdpFrameViewType view_type) { g_assert (view_type != GRD_RDP_FRAME_VIEW_TYPE_DUAL); g_assert (view_type == GRD_RDP_FRAME_VIEW_TYPE_MAIN || view_type == GRD_RDP_FRAME_VIEW_TYPE_AUX); switch (rdp_frame->view_type) { case GRD_RDP_FRAME_VIEW_TYPE_DUAL: g_assert (g_queue_get_length (rdp_frame->unused_image_views) == 2); g_queue_pop_tail (rdp_frame->unused_image_views); break; case GRD_RDP_FRAME_VIEW_TYPE_MAIN: case GRD_RDP_FRAME_VIEW_TYPE_AUX: g_assert (g_queue_get_length (rdp_frame->unused_image_views) == 1); break; } rdp_frame->view_type = view_type; } static void finalize_view (GrdRdpFrame *rdp_frame) { g_assert (rdp_frame->pending_view_finalization); rdp_frame->view_finalized (rdp_frame, rdp_frame->callback_user_data); rdp_frame->pending_view_finalization = FALSE; } void grd_rdp_frame_set_damage_region (GrdRdpFrame *rdp_frame, cairo_region_t *damage_region) { GrdRdpRenderContext *render_context = rdp_frame->render_context; g_assert (!rdp_frame->damage_region); rdp_frame->damage_region = damage_region; grd_encode_context_set_damage_region (rdp_frame->encode_context, damage_region); if (!grd_rdp_render_context_must_delay_view_finalization (render_context)) finalize_view (rdp_frame); } void grd_rdp_frame_set_bitstreams (GrdRdpFrame *rdp_frame, GList *bitstreams) { rdp_frame->bitstreams = bitstreams; } void grd_rdp_frame_notify_picked_up (GrdRdpFrame *rdp_frame) { rdp_frame->frame_picked_up (rdp_frame, rdp_frame->callback_user_data); } void grd_rdp_frame_notify_frame_submission (GrdRdpFrame *rdp_frame) { rdp_frame->frame_submitted (rdp_frame, rdp_frame->callback_user_data); } GrdImageView * grd_rdp_frame_pop_image_view (GrdRdpFrame *rdp_frame) { return g_queue_pop_head (rdp_frame->unused_image_views); } static void set_view_type (GrdRdpFrame *rdp_frame, gboolean frame_upgrade) { GrdRdpRenderContext *render_context = rdp_frame->render_context; GrdRdpCodec codec = grd_rdp_render_context_get_codec (render_context); switch (codec) { case GRD_RDP_CODEC_CAPROGRESSIVE: case GRD_RDP_CODEC_AVC420: rdp_frame->view_type = GRD_RDP_FRAME_VIEW_TYPE_MAIN; break; case GRD_RDP_CODEC_AVC444v2: rdp_frame->view_type = frame_upgrade ? GRD_RDP_FRAME_VIEW_TYPE_AUX : GRD_RDP_FRAME_VIEW_TYPE_DUAL; break; } } static uint32_t get_n_required_image_views (GrdRdpFrame *rdp_frame) { GrdRdpRenderContext *render_context = rdp_frame->render_context; GrdRdpCodec codec = grd_rdp_render_context_get_codec (render_context); switch (codec) { case GRD_RDP_CODEC_CAPROGRESSIVE: return 1; case GRD_RDP_CODEC_AVC420: case GRD_RDP_CODEC_AVC444v2: return 2; } g_assert_not_reached (); } static uint32_t get_n_image_views_to_be_encoded (GrdRdpFrame *rdp_frame) { switch (grd_rdp_frame_get_avc_view_type (rdp_frame)) { case GRD_RDP_FRAME_VIEW_TYPE_DUAL: return 2; case GRD_RDP_FRAME_VIEW_TYPE_MAIN: case GRD_RDP_FRAME_VIEW_TYPE_AUX: return 1; } g_assert_not_reached (); } static void acquire_image_views (GrdRdpFrame *rdp_frame) { uint32_t n_image_views = get_n_required_image_views (rdp_frame); uint32_t n_image_views_to_be_encoded = get_n_image_views_to_be_encoded (rdp_frame); uint32_t i; g_assert (n_image_views_to_be_encoded <= n_image_views); for (i = 0; i < n_image_views; ++i) { GrdRdpRenderContext *render_context = rdp_frame->render_context; GrdImageView *image_view; image_view = grd_rdp_render_context_acquire_image_view (render_context); rdp_frame->acquired_image_views = g_list_append (rdp_frame->acquired_image_views, image_view); if (i < n_image_views_to_be_encoded) g_queue_push_tail (rdp_frame->unused_image_views, image_view); } } static void prepare_new_frame (GrdRdpFrame *rdp_frame) { rdp_frame->pending_view_finalization = TRUE; set_view_type (rdp_frame, FALSE); acquire_image_views (rdp_frame); } static void prepare_frame_upgrade (GrdRdpFrame *rdp_frame) { GrdRdpRenderContext *render_context = rdp_frame->render_context; cairo_region_t *damage_region = NULL; GrdImageView *image_view = NULL; set_view_type (rdp_frame, TRUE); grd_rdp_render_context_fetch_progressive_render_state (render_context, &image_view, &damage_region); g_assert (image_view); g_assert (damage_region); rdp_frame->acquired_image_views = g_list_append (rdp_frame->acquired_image_views, image_view); g_queue_push_tail (rdp_frame->unused_image_views, image_view); rdp_frame->damage_region = damage_region; } GrdRdpFrame * grd_rdp_frame_new (GrdRdpRenderContext *render_context, GrdRdpBuffer *src_buffer_new, GrdRdpBuffer *src_buffer_old, GrdRdpFrameCallback frame_picked_up, GrdRdpFrameCallback view_finalized, GrdRdpFrameCallback frame_submitted, GrdRdpFrameCallback frame_finalized, gpointer callback_user_data, GDestroyNotify user_data_destroy) { GrdRdpFrame *rdp_frame; rdp_frame = g_new0 (GrdRdpFrame, 1); rdp_frame->render_context = render_context; rdp_frame->src_buffer_new = src_buffer_new; rdp_frame->src_buffer_old = src_buffer_old; rdp_frame->frame_picked_up = frame_picked_up; rdp_frame->view_finalized = view_finalized; rdp_frame->frame_submitted = frame_submitted; rdp_frame->frame_finalized = frame_finalized; rdp_frame->callback_user_data = callback_user_data; rdp_frame->user_data_destroy = user_data_destroy; rdp_frame->encode_context = grd_encode_context_new (); rdp_frame->unused_image_views = g_queue_new (); if (src_buffer_new) prepare_new_frame (rdp_frame); else prepare_frame_upgrade (rdp_frame); return rdp_frame; } static void release_image_views (GrdRdpFrame *rdp_frame) { GList *l; for (l = rdp_frame->acquired_image_views; l; l = l->next) { GrdImageView *image_view = l->data; grd_rdp_render_context_release_image_view (rdp_frame->render_context, image_view); } g_clear_pointer (&rdp_frame->acquired_image_views, g_list_free); } void grd_rdp_frame_free (GrdRdpFrame *rdp_frame) { g_assert (!rdp_frame->bitstreams); g_assert (rdp_frame->renderer); if (rdp_frame->pending_view_finalization) finalize_view (rdp_frame); g_clear_pointer (&rdp_frame->encode_context, grd_encode_context_free); g_clear_pointer (&rdp_frame->damage_region, cairo_region_destroy); g_clear_pointer (&rdp_frame->unused_image_views, g_queue_free); release_image_views (rdp_frame); rdp_frame->frame_finalized (rdp_frame, rdp_frame->callback_user_data); g_clear_pointer (&rdp_frame->callback_user_data, rdp_frame->user_data_destroy); g_free (rdp_frame); }