stage: Clear update_scheduled
field when update discarded
clutter_stage_schedule_update() sets the field `update_scheduled` to `TRUE` as an optimization to make redundant updates a no-op. This failed if there was a pending event and if the stage was not yet mapped. What happened is: * clutter_stage_schedule_update() is called - ClutterStage::update_scheduled is set to TRUE - frame clock scheduled * frame clock dispatches - frame is discarded early, no actual stage update happens * device is created (e.g. virtual device from remote desktop session) - `device-added` event reaches ClutterStage::event_queue * stage is shown - clutter_stage_schedule_update() is called - ClutterStage::update_scheduled is TRUE - ClutterStage::event_queue has events in it - These two conditions means clutter_schedule_update() becomes a no-op At this point, no more updates will happen from clutter_stage_schedule_update(). Fix this by resetting `ClutterStage::update_scheduled` to `FALSE` even if the frame was discarded due to the stage not yet being mapped. A test case is added that replicates the above descibed events. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3804 Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4152>
This commit is contained in:
parent
904c39116e
commit
24083e1e58
@ -62,6 +62,9 @@ void clutter_stage_emit_after_paint (ClutterStage
|
||||
void clutter_stage_after_update (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterFrame *frame);
|
||||
void clutter_stage_frame_discarded (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterFrame *frame);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void _clutter_stage_set_window (ClutterStage *stage,
|
||||
|
@ -1050,11 +1050,12 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock,
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
||||
return CLUTTER_FRAME_RESULT_IDLE;
|
||||
|
||||
if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage)))
|
||||
return CLUTTER_FRAME_RESULT_IDLE;
|
||||
|
||||
if (!clutter_actor_is_mapped (CLUTTER_ACTOR (stage)))
|
||||
return CLUTTER_FRAME_RESULT_IDLE;
|
||||
if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage)) ||
|
||||
!clutter_actor_is_mapped (CLUTTER_ACTOR (stage)))
|
||||
{
|
||||
clutter_stage_frame_discarded (stage, view, frame);
|
||||
return CLUTTER_FRAME_RESULT_IDLE;
|
||||
}
|
||||
|
||||
if (clutter_context_get_show_fps (context))
|
||||
begin_frame_timing_measurement (view);
|
||||
|
@ -535,6 +535,16 @@ clutter_stage_after_update (ClutterStage *stage,
|
||||
priv->update_scheduled = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_frame_discarded (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterFrame *frame)
|
||||
{
|
||||
ClutterStagePrivate *priv = clutter_stage_get_instance_private (stage);
|
||||
|
||||
priv->update_scheduled = FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_stage_get_paint_volume (ClutterActor *self,
|
||||
ClutterPaintVolume *volume)
|
||||
|
@ -313,6 +313,11 @@ test_cases += [
|
||||
'--compile-schemas',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'stage',
|
||||
'suite': 'backend',
|
||||
'sources': [ 'stage-tests.c', ],
|
||||
},
|
||||
]
|
||||
|
||||
screen_cast_client = executable('mutter-screen-cast-client',
|
||||
|
102
src/tests/stage-tests.c
Normal file
102
src/tests/stage-tests.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat Inc.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/meta-backend-private.h"
|
||||
#include "core/meta-context-private.h"
|
||||
#include "tests/meta-test-utils.h"
|
||||
#include "tests/meta-test/meta-context-test.h"
|
||||
|
||||
static MetaContext *test_context;
|
||||
|
||||
static gboolean
|
||||
event_filter_cb (const ClutterEvent *event,
|
||||
ClutterActor *event_actor,
|
||||
gpointer user_data)
|
||||
{
|
||||
gboolean *saw_event = user_data;
|
||||
|
||||
if (clutter_event_type (event) == CLUTTER_DEVICE_ADDED)
|
||||
*saw_event = TRUE;
|
||||
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_test_stage_scheduling_delayed_show (void)
|
||||
{
|
||||
MetaBackend *backend = meta_context_get_backend (test_context);
|
||||
ClutterActor *stage = meta_backend_get_stage (backend);
|
||||
ClutterSeat *seat = meta_backend_get_default_seat (backend);
|
||||
g_autoptr (MetaVirtualMonitor) virtual_monitor = NULL;
|
||||
g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL;
|
||||
guint filter_id;
|
||||
gboolean saw_event = FALSE;
|
||||
|
||||
virtual_monitor = meta_create_test_monitor (test_context, 800, 600, 60.0f);
|
||||
g_debug ("Wait for initial dummy dispatch");
|
||||
while (TRUE)
|
||||
{
|
||||
if (!g_main_context_iteration (NULL, FALSE))
|
||||
break;
|
||||
}
|
||||
|
||||
filter_id = clutter_event_add_filter (NULL, event_filter_cb, NULL, &saw_event);
|
||||
g_debug ("Creating virtual pointer");
|
||||
virtual_pointer =
|
||||
clutter_seat_create_virtual_device (seat, CLUTTER_KEYBOARD_DEVICE);
|
||||
while (!saw_event)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
g_debug ("Scheduling update with DEVICE_ADDED in stage queue");
|
||||
clutter_stage_schedule_update (CLUTTER_STAGE (stage));
|
||||
g_debug ("Showing stage");
|
||||
clutter_actor_show (stage);
|
||||
g_debug ("Waiting for paint");
|
||||
clutter_actor_queue_redraw (stage);
|
||||
meta_wait_for_paint (test_context);
|
||||
clutter_event_remove_filter (filter_id);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
{
|
||||
g_autoptr (MetaContext) context = NULL;
|
||||
g_auto (GVariantBuilder) plugin_options_builder =
|
||||
G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
|
||||
g_autoptr (GVariant) plugin_options = NULL;
|
||||
|
||||
context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS,
|
||||
META_CONTEXT_TEST_FLAG_NO_X11);
|
||||
g_assert (meta_context_configure (context, &argc, &argv, NULL));
|
||||
|
||||
g_variant_builder_add (&plugin_options_builder, "{sv}",
|
||||
"show-stage", g_variant_new_boolean (FALSE));
|
||||
plugin_options =
|
||||
g_variant_ref_sink (g_variant_builder_end (&plugin_options_builder));
|
||||
meta_context_set_plugin_options (context, plugin_options);
|
||||
|
||||
test_context = context;
|
||||
|
||||
g_test_add_func ("/stage/scheduling/delayed-show",
|
||||
meta_test_stage_scheduling_delayed_show);
|
||||
|
||||
return meta_context_test_run_tests (META_CONTEXT_TEST (context),
|
||||
META_TEST_RUN_FLAG_NONE);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user