mirror of
https://github.com/brl/mutter.git
synced 2024-12-22 11:02:05 +00:00
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each driving its own stage view. As each stage view represents one CRTC, this means we draw each CRTC with its own designated frame clock, disconnected from all the others. For example this means we when using the native backend will never need to wait for one monitor to vsync before painting another, so e.g. having a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland and X11 applications and shell UI will be able to render at the corresponding monitor refresh rate. This also changes a warning about missed frames when sending _NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected that we'll start missing frames e.g. when a X11 window (via Xwayland) is exclusively within a stage view that was not painted, while another one was, still increasing the global frame clock. Addititonally, this also requires the X11 window actor to schedule timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting, if the actor wasn't on any stage views, as now we'll only get the frame callbacks on actors when they actually were painted, while in the past, we'd invoke that vfunc when anything was painted. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903 Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3 https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
This commit is contained in:
parent
57a2f7b4a3
commit
a9a9a0d1c5
@ -61,7 +61,6 @@
|
||||
#include "clutter-input-pointer-a11y-private.h"
|
||||
#include "clutter-graphene.h"
|
||||
#include "clutter-main.h"
|
||||
#include "clutter-master-clock.h"
|
||||
#include "clutter-mutter.h"
|
||||
#include "clutter-paint-node-private.h"
|
||||
#include "clutter-private.h"
|
||||
|
@ -1,609 +0,0 @@
|
||||
/*
|
||||
* Clutter.
|
||||
*
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Authored By: Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SECTION:clutter-master-clock-default
|
||||
* @short_description: The default master clock for all animations
|
||||
*
|
||||
* The #ClutterMasterClockDefault class is the default implementation
|
||||
* of #ClutterMasterClock.
|
||||
*/
|
||||
|
||||
#include "clutter-build-config.h"
|
||||
|
||||
#include <cogl/cogl.h>
|
||||
|
||||
#include "clutter-master-clock.h"
|
||||
#include "clutter-master-clock-default.h"
|
||||
#include "clutter-debug.h"
|
||||
#include "clutter-private.h"
|
||||
#include "clutter-stage-manager-private.h"
|
||||
#include "clutter-stage-private.h"
|
||||
#include "clutter-timeline-private.h"
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
#define clutter_warn_if_over_budget(master_clock,start_time,section) G_STMT_START { \
|
||||
gint64 __delta = g_get_monotonic_time () - start_time; \
|
||||
gint64 __budget = master_clock->remaining_budget; \
|
||||
if (__budget > 0 && __delta >= __budget) { \
|
||||
_clutter_diagnostic_message ("%s took %" G_GINT64_FORMAT " microseconds " \
|
||||
"more than the remaining budget of %" G_GINT64_FORMAT \
|
||||
" microseconds", \
|
||||
section, __delta - __budget, __budget); \
|
||||
} } G_STMT_END
|
||||
#else
|
||||
#define clutter_warn_if_over_budget(master_clock,start_time,section)
|
||||
#endif
|
||||
|
||||
typedef struct _ClutterClockSource ClutterClockSource;
|
||||
|
||||
struct _ClutterMasterClockDefault
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/* the list of timelines handled by the clock */
|
||||
GSList *timelines;
|
||||
|
||||
/* the current state of the clock, in usecs */
|
||||
gint64 cur_tick;
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
gint64 frame_budget;
|
||||
gint64 remaining_budget;
|
||||
#endif
|
||||
|
||||
/* an idle source, used by the Master Clock to queue
|
||||
* a redraw on the stage and drive the animations
|
||||
*/
|
||||
GSource *source;
|
||||
|
||||
guint ensure_next_iteration : 1;
|
||||
|
||||
guint paused : 1;
|
||||
};
|
||||
|
||||
struct _ClutterClockSource
|
||||
{
|
||||
GSource source;
|
||||
|
||||
ClutterMasterClockDefault *master_clock;
|
||||
};
|
||||
|
||||
static gboolean clutter_clock_prepare (GSource *source,
|
||||
gint *timeout);
|
||||
static gboolean clutter_clock_check (GSource *source);
|
||||
static gboolean clutter_clock_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data);
|
||||
|
||||
static GSourceFuncs clock_funcs = {
|
||||
clutter_clock_prepare,
|
||||
clutter_clock_check,
|
||||
clutter_clock_dispatch,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void
|
||||
clutter_master_clock_iface_init (ClutterMasterClockInterface *iface);
|
||||
|
||||
#define clutter_master_clock_default_get_type _clutter_master_clock_default_get_type
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (ClutterMasterClockDefault,
|
||||
clutter_master_clock_default,
|
||||
G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_MASTER_CLOCK,
|
||||
clutter_master_clock_iface_init));
|
||||
|
||||
/*
|
||||
* master_clock_is_running:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Checks if we should currently be advancing timelines or redrawing
|
||||
* stages.
|
||||
*
|
||||
* Return value: %TRUE if the #ClutterMasterClock has at least
|
||||
* one running timeline
|
||||
*/
|
||||
static gboolean
|
||||
master_clock_is_running (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
|
||||
if (master_clock->paused)
|
||||
return FALSE;
|
||||
|
||||
if (master_clock->timelines)
|
||||
return TRUE;
|
||||
|
||||
for (l = stages; l; l = l->next)
|
||||
{
|
||||
if (clutter_actor_is_mapped (l->data) &&
|
||||
(_clutter_stage_has_queued_events (l->data) ||
|
||||
_clutter_stage_needs_update (l->data)))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (master_clock->ensure_next_iteration)
|
||||
{
|
||||
master_clock->ensure_next_iteration = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint
|
||||
master_clock_get_swap_wait_time (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
gint64 min_update_time = -1;
|
||||
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
{
|
||||
gint64 update_time = _clutter_stage_get_update_time (l->data);
|
||||
if (min_update_time == -1 ||
|
||||
(update_time != -1 && update_time < min_update_time))
|
||||
min_update_time = update_time;
|
||||
}
|
||||
|
||||
if (min_update_time == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gint64 now = g_source_get_time (master_clock->source);
|
||||
if (min_update_time < now)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gint64 delay_us = min_update_time - now;
|
||||
return (delay_us + 999) / 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
clutter_stage_schedule_update (l->data);
|
||||
}
|
||||
|
||||
static GSList *
|
||||
master_clock_list_ready_stages (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
GSList *result;
|
||||
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
|
||||
result = NULL;
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
{
|
||||
gint64 update_time = _clutter_stage_get_update_time (l->data);
|
||||
/* We carefully avoid to update stages that aren't mapped, because
|
||||
* they have nothing to render and this could cause a deadlock with
|
||||
* some of the SwapBuffers implementations (in particular
|
||||
* GLX_INTEL_swap_event is not emitted if nothing was rendered).
|
||||
*
|
||||
* Also, if a stage has a swap-buffers pending we don't want to draw
|
||||
* to it in case the driver may block the CPU while it waits for the
|
||||
* next backbuffer to become available.
|
||||
*
|
||||
* TODO: We should be able to identify if we are running triple or N
|
||||
* buffered and in these cases we can still draw if there is 1 swap
|
||||
* pending so we can hopefully always be ready to swap for the next
|
||||
* vblank and really match the vsync frequency.
|
||||
*/
|
||||
if (clutter_actor_is_mapped (l->data) &&
|
||||
update_time != -1 && update_time <= master_clock->cur_tick)
|
||||
result = g_slist_prepend (result, g_object_ref (l->data));
|
||||
}
|
||||
|
||||
return g_slist_reverse (result);
|
||||
}
|
||||
|
||||
static void
|
||||
master_clock_reschedule_stage_updates (ClutterMasterClockDefault *master_clock,
|
||||
GSList *stages)
|
||||
{
|
||||
const GSList *l;
|
||||
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
{
|
||||
/* Clear the old update time */
|
||||
_clutter_stage_clear_update_time (l->data);
|
||||
|
||||
/* And if there is still work to be done, schedule a new one */
|
||||
if (master_clock->timelines ||
|
||||
_clutter_stage_has_queued_events (l->data) ||
|
||||
_clutter_stage_needs_update (l->data))
|
||||
clutter_stage_schedule_update (l->data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* master_clock_next_frame_delay:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Computes the number of delay before we need to draw the next frame.
|
||||
*
|
||||
* Return value: -1 if there is no next frame pending, otherwise the
|
||||
* number of millseconds before the we need to draw the next frame
|
||||
*/
|
||||
static gint
|
||||
master_clock_next_frame_delay (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
if (!master_clock_is_running (master_clock))
|
||||
return -1;
|
||||
|
||||
/* If all of the stages are busy waiting for a swap-buffers to complete
|
||||
* then we wait for one to be ready.. */
|
||||
return master_clock_get_swap_wait_time (master_clock);
|
||||
}
|
||||
|
||||
static void
|
||||
master_clock_process_events (ClutterMasterClockDefault *master_clock,
|
||||
GSList *stages)
|
||||
{
|
||||
GSList *l;
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
gint64 start = g_get_monotonic_time ();
|
||||
#endif
|
||||
|
||||
/* Process queued events */
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
_clutter_stage_process_queued_events (l->data);
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
if (_clutter_diagnostic_enabled ())
|
||||
clutter_warn_if_over_budget (master_clock, start, "Event processing");
|
||||
|
||||
master_clock->remaining_budget -= (g_get_monotonic_time () - start);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* master_clock_advance_timelines:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Advances all the timelines held by the master clock. This function
|
||||
* should be called before calling _clutter_stage_do_update() to
|
||||
* make sure that all the timelines are advanced and the scene is updated.
|
||||
*/
|
||||
static void
|
||||
master_clock_advance_timelines (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
GSList *timelines, *l;
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
gint64 start = g_get_monotonic_time ();
|
||||
#endif
|
||||
|
||||
/* we protect ourselves from timelines being removed during
|
||||
* the advancement by other timelines by copying the list of
|
||||
* timelines, taking a reference on them, iterating over the
|
||||
* copied list and then releasing the reference.
|
||||
*
|
||||
* we cannot simply take a reference on the timelines and still
|
||||
* use the list held by the master clock because the do_tick()
|
||||
* might result in the creation of a new timeline, which gets
|
||||
* added at the end of the list with no reference increase and
|
||||
* thus gets disposed at the end of the iteration.
|
||||
*
|
||||
* this implies that a newly added timeline will not be advanced
|
||||
* by this clock iteration, which is perfectly fine since we're
|
||||
* in its first cycle.
|
||||
*
|
||||
* we also cannot steal the master clock timelines list because
|
||||
* a timeline might be removed as the direct result of do_tick()
|
||||
* and remove_timeline() would not find the timeline, failing
|
||||
* and leaving a dangling pointer behind.
|
||||
*/
|
||||
timelines = g_slist_copy (master_clock->timelines);
|
||||
g_slist_foreach (timelines, (GFunc) g_object_ref, NULL);
|
||||
|
||||
for (l = timelines; l != NULL; l = l->next)
|
||||
_clutter_timeline_do_tick (l->data, master_clock->cur_tick / 1000);
|
||||
|
||||
g_slist_free_full (timelines, g_object_unref);
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
if (_clutter_diagnostic_enabled ())
|
||||
clutter_warn_if_over_budget (master_clock, start, "Animations");
|
||||
|
||||
master_clock->remaining_budget -= (g_get_monotonic_time () - start);
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
master_clock_update_stages (ClutterMasterClockDefault *master_clock,
|
||||
GSList *stages)
|
||||
{
|
||||
gboolean stages_updated = FALSE;
|
||||
GSList *l;
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
gint64 start = g_get_monotonic_time ();
|
||||
#endif
|
||||
|
||||
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT);
|
||||
|
||||
/* Update any stage that needs redraw/relayout after the clock
|
||||
* is advanced.
|
||||
*/
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
stages_updated |= _clutter_stage_do_update (l->data);
|
||||
|
||||
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
if (_clutter_diagnostic_enabled ())
|
||||
clutter_warn_if_over_budget (master_clock, start, "Updating the stage");
|
||||
|
||||
master_clock->remaining_budget -= (g_get_monotonic_time () - start);
|
||||
#endif
|
||||
|
||||
return stages_updated;
|
||||
}
|
||||
|
||||
/*
|
||||
* clutter_clock_source_new:
|
||||
* @master_clock: a #ClutterMasterClock for the source
|
||||
*
|
||||
* The #ClutterClockSource is an idle GSource that will queue a redraw
|
||||
* if @master_clock has at least a running #ClutterTimeline. The redraw
|
||||
* will cause @master_clock to advance all timelines, thus advancing all
|
||||
* animations as well.
|
||||
*
|
||||
* Return value: the newly created #GSource
|
||||
*/
|
||||
static GSource *
|
||||
clutter_clock_source_new (ClutterMasterClockDefault *master_clock)
|
||||
{
|
||||
GSource *source = g_source_new (&clock_funcs, sizeof (ClutterClockSource));
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
|
||||
g_source_set_name (source, "Clutter master clock");
|
||||
g_source_set_priority (source, CLUTTER_PRIORITY_REDRAW);
|
||||
g_source_set_can_recurse (source, FALSE);
|
||||
clock_source->master_clock = master_clock;
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_clock_prepare (GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
ClutterMasterClockDefault *master_clock = clock_source->master_clock;
|
||||
int delay;
|
||||
|
||||
if (G_UNLIKELY (clutter_paint_debug_flags &
|
||||
CLUTTER_DEBUG_CONTINUOUS_REDRAW))
|
||||
{
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
|
||||
/* Queue a full redraw on all of the stages */
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
clutter_actor_queue_redraw (l->data);
|
||||
}
|
||||
|
||||
delay = master_clock_next_frame_delay (master_clock);
|
||||
|
||||
*timeout = delay;
|
||||
|
||||
return delay == 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_clock_check (GSource *source)
|
||||
{
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
ClutterMasterClockDefault *master_clock = clock_source->master_clock;
|
||||
int delay;
|
||||
|
||||
delay = master_clock_next_frame_delay (master_clock);
|
||||
|
||||
return delay == 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_clock_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
ClutterMasterClockDefault *master_clock = clock_source->master_clock;
|
||||
GSList *stages;
|
||||
|
||||
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
|
||||
|
||||
COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)");
|
||||
|
||||
/* Get the time to use for this frame */
|
||||
master_clock->cur_tick = g_source_get_time (source);
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
master_clock->remaining_budget = master_clock->frame_budget;
|
||||
#endif
|
||||
|
||||
/* We need to protect ourselves against stages being destroyed during
|
||||
* event handling - master_clock_list_ready_stages() returns a
|
||||
* list of referenced that we'll unref afterwards.
|
||||
*/
|
||||
stages = master_clock_list_ready_stages (master_clock);
|
||||
|
||||
/* Each frame is split into three separate phases: */
|
||||
|
||||
/* 1. process all the events; each stage goes through its events queue
|
||||
* and processes each event according to its type, then emits the
|
||||
* various signals that are associated with the event
|
||||
*/
|
||||
master_clock_process_events (master_clock, stages);
|
||||
|
||||
/* 2. advance the timelines */
|
||||
master_clock_advance_timelines (master_clock);
|
||||
|
||||
/* 3. relayout and redraw the stages */
|
||||
master_clock_update_stages (master_clock, stages);
|
||||
|
||||
master_clock_reschedule_stage_updates (master_clock, stages);
|
||||
|
||||
g_slist_free_full (stages, g_object_unref);
|
||||
|
||||
COGL_TRACE_END (ClutterMasterClockTick);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_finalize (GObject *gobject)
|
||||
{
|
||||
ClutterMasterClockDefault *master_clock = CLUTTER_MASTER_CLOCK_DEFAULT (gobject);
|
||||
|
||||
g_slist_free (master_clock->timelines);
|
||||
|
||||
G_OBJECT_CLASS (clutter_master_clock_default_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_class_init (ClutterMasterClockDefaultClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = clutter_master_clock_default_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_init (ClutterMasterClockDefault *self)
|
||||
{
|
||||
GSource *source;
|
||||
|
||||
source = clutter_clock_source_new (self);
|
||||
self->source = source;
|
||||
|
||||
self->ensure_next_iteration = FALSE;
|
||||
self->paused = FALSE;
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
self->frame_budget = G_USEC_PER_SEC / 60;
|
||||
#endif
|
||||
|
||||
g_source_attach (source, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_add_timeline (ClutterMasterClock *clock,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock;
|
||||
gboolean is_first;
|
||||
|
||||
if (g_slist_find (master_clock->timelines, timeline))
|
||||
return;
|
||||
|
||||
is_first = master_clock->timelines == NULL;
|
||||
|
||||
master_clock->timelines = g_slist_prepend (master_clock->timelines,
|
||||
timeline);
|
||||
|
||||
if (is_first)
|
||||
{
|
||||
master_clock_schedule_stage_updates (master_clock);
|
||||
_clutter_master_clock_start_running (clock);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_remove_timeline (ClutterMasterClock *clock,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock;
|
||||
|
||||
master_clock->timelines = g_slist_remove (master_clock->timelines,
|
||||
timeline);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_start_running (ClutterMasterClock *master_clock)
|
||||
{
|
||||
/* If called from a different thread, we need to wake up the
|
||||
* main loop to start running the timelines
|
||||
*/
|
||||
g_main_context_wakeup (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_ensure_next_iteration (ClutterMasterClock *clock)
|
||||
{
|
||||
ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock;
|
||||
|
||||
master_clock->ensure_next_iteration = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_set_paused (ClutterMasterClock *clock,
|
||||
gboolean paused)
|
||||
{
|
||||
ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock;
|
||||
|
||||
if (paused && !master_clock->paused)
|
||||
{
|
||||
g_clear_pointer (&master_clock->source, g_source_destroy);
|
||||
}
|
||||
else if (!paused && master_clock->paused)
|
||||
{
|
||||
master_clock->source = clutter_clock_source_new (master_clock);
|
||||
g_source_attach (master_clock->source, NULL);
|
||||
}
|
||||
|
||||
master_clock->paused = !!paused;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_iface_init (ClutterMasterClockInterface *iface)
|
||||
{
|
||||
iface->add_timeline = clutter_master_clock_default_add_timeline;
|
||||
iface->remove_timeline = clutter_master_clock_default_remove_timeline;
|
||||
iface->start_running = clutter_master_clock_default_start_running;
|
||||
iface->ensure_next_iteration = clutter_master_clock_default_ensure_next_iteration;
|
||||
iface->set_paused = clutter_master_clock_default_set_paused;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Clutter.
|
||||
*
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Authored By: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CLUTTER_MASTER_CLOCK_DEFAULT_H__
|
||||
#define __CLUTTER_MASTER_CLOCK_DEFAULT_H__
|
||||
|
||||
#include <clutter/clutter-timeline.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLUTTER_TYPE_MASTER_CLOCK_DEFAULT (_clutter_master_clock_default_get_type ())
|
||||
#define CLUTTER_MASTER_CLOCK_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_MASTER_CLOCK_DEFAULT, ClutterMasterClockDefault))
|
||||
#define CLUTTER_IS_MASTER_CLOCK_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_MASTER_CLOCK_DEFAULT))
|
||||
#define CLUTTER_MASTER_CLOCK_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_MASTER_CLOCK_DEFAULT, ClutterMasterClockDefaultClass))
|
||||
|
||||
typedef struct _ClutterMasterClockDefault ClutterMasterClockDefault;
|
||||
typedef struct _ClutterMasterClockDefaultClass ClutterMasterClockDefaultClass;
|
||||
|
||||
struct _ClutterMasterClockDefaultClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType _clutter_master_clock_default_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_MASTER_CLOCK_DEFAULT_H__ */
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Clutter.
|
||||
*
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Authored By: Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SECTION:clutter-master-clock
|
||||
* @short_description: The master clock for all animations
|
||||
*
|
||||
* The #ClutterMasterClock class is responsible for advancing all
|
||||
* #ClutterTimelines when a stage is being redrawn. The master clock
|
||||
* makes sure that the scenegraph is always integrally updated before
|
||||
* painting it.
|
||||
*/
|
||||
|
||||
#include "clutter-build-config.h"
|
||||
|
||||
#include "clutter-master-clock.h"
|
||||
#include "clutter-master-clock-default.h"
|
||||
#include "clutter-private.h"
|
||||
|
||||
G_DEFINE_INTERFACE (ClutterMasterClock, clutter_master_clock, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
clutter_master_clock_default_init (ClutterMasterClockInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
ClutterMasterClock *
|
||||
_clutter_master_clock_get_default (void)
|
||||
{
|
||||
ClutterMainContext *context = _clutter_context_get_default ();
|
||||
|
||||
if (G_UNLIKELY (context->master_clock == NULL))
|
||||
context->master_clock = g_object_new (CLUTTER_TYPE_MASTER_CLOCK_DEFAULT, NULL);
|
||||
|
||||
return context->master_clock;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* _clutter_master_clock_add_timeline:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
* @timeline: a #ClutterTimeline
|
||||
*
|
||||
* Adds @timeline to the list of playing timelines held by the master
|
||||
* clock.
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_add_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
|
||||
|
||||
CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->add_timeline (master_clock,
|
||||
timeline);
|
||||
}
|
||||
|
||||
/*
|
||||
* _clutter_master_clock_remove_timeline:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
* @timeline: a #ClutterTimeline
|
||||
*
|
||||
* Removes @timeline from the list of playing timelines held by the
|
||||
* master clock.
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
|
||||
|
||||
CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->remove_timeline (master_clock,
|
||||
timeline);
|
||||
}
|
||||
|
||||
/*
|
||||
* _clutter_master_clock_start_running:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Called when we have events or redraws to process; if the clock
|
||||
* is stopped, does the processing necessary to wake it up again.
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_start_running (ClutterMasterClock *master_clock)
|
||||
{
|
||||
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
|
||||
|
||||
CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->start_running (master_clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_master_clock_ensure_next_iteration:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Ensures that the master clock will run at least one iteration
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_ensure_next_iteration (ClutterMasterClock *master_clock)
|
||||
{
|
||||
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
|
||||
|
||||
CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->ensure_next_iteration (master_clock);
|
||||
}
|
||||
|
||||
void
|
||||
_clutter_master_clock_set_paused (ClutterMasterClock *master_clock,
|
||||
gboolean paused)
|
||||
{
|
||||
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
|
||||
|
||||
CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->set_paused (master_clock,
|
||||
!!paused);
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Clutter.
|
||||
*
|
||||
* An OpenGL based 'interactive canvas' library.
|
||||
*
|
||||
* Authored By: Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CLUTTER_MASTER_CLOCK_H__
|
||||
#define __CLUTTER_MASTER_CLOCK_H__
|
||||
|
||||
#include <clutter/clutter-timeline.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLUTTER_TYPE_MASTER_CLOCK (clutter_master_clock_get_type ())
|
||||
G_DECLARE_INTERFACE (ClutterMasterClock, clutter_master_clock,
|
||||
CLUTTER, MASTER_CLOCK,
|
||||
GObject)
|
||||
|
||||
struct _ClutterMasterClockInterface
|
||||
{
|
||||
/*< private >*/
|
||||
GTypeInterface parent_iface;
|
||||
|
||||
void (* add_timeline) (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline);
|
||||
void (* remove_timeline) (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline);
|
||||
void (* start_running) (ClutterMasterClock *master_clock);
|
||||
void (* ensure_next_iteration) (ClutterMasterClock *master_clock);
|
||||
void (* set_paused) (ClutterMasterClock *master_clock,
|
||||
gboolean paused);
|
||||
};
|
||||
|
||||
ClutterMasterClock * _clutter_master_clock_get_default (void);
|
||||
void _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline);
|
||||
void _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline);
|
||||
void _clutter_master_clock_start_running (ClutterMasterClock *master_clock);
|
||||
void _clutter_master_clock_ensure_next_iteration (ClutterMasterClock *master_clock);
|
||||
void _clutter_master_clock_set_paused (ClutterMasterClock *master_clock,
|
||||
gboolean paused);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_MASTER_CLOCK_H__ */
|
@ -68,12 +68,6 @@ gboolean clutter_stage_paint_to_buffer (ClutterStage *stage,
|
||||
ClutterPaintFlag paint_flags,
|
||||
GError **error);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_stage_freeze_updates (ClutterStage *stage);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_stage_thaw_updates (ClutterStage *stage);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_stage_clear_stage_views (ClutterStage *stage);
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "clutter-gesture-action-private.h"
|
||||
#include "clutter-marshal.h"
|
||||
#include "clutter-private.h"
|
||||
#include "clutter-timeline.h"
|
||||
#include <math.h>
|
||||
|
||||
#define FLOAT_EPSILON (1e-15)
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "clutter-feature.h"
|
||||
#include "clutter-id-pool.h"
|
||||
#include "clutter-layout-manager.h"
|
||||
#include "clutter-master-clock.h"
|
||||
#include "clutter-settings.h"
|
||||
#include "clutter-stage-manager.h"
|
||||
#include "clutter-stage.h"
|
||||
@ -122,9 +121,6 @@ struct _ClutterMainContext
|
||||
/* the object holding all the stage instances */
|
||||
ClutterStageManager *stage_manager;
|
||||
|
||||
/* the clock driving all the frame operations */
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
/* the main event queue */
|
||||
GQueue *events_queue;
|
||||
|
||||
|
@ -40,10 +40,14 @@ void clutter_stage_paint_view (ClutterStage
|
||||
ClutterStageView *view,
|
||||
const cairo_region_t *redraw_clip);
|
||||
|
||||
void clutter_stage_emit_before_update (ClutterStage *stage);
|
||||
void clutter_stage_emit_before_paint (ClutterStage *stage);
|
||||
void clutter_stage_emit_after_paint (ClutterStage *stage);
|
||||
void clutter_stage_emit_after_update (ClutterStage *stage);
|
||||
void clutter_stage_emit_before_update (ClutterStage *stage,
|
||||
ClutterStageView *view);
|
||||
void clutter_stage_emit_before_paint (ClutterStage *stage,
|
||||
ClutterStageView *view);
|
||||
void clutter_stage_emit_after_paint (ClutterStage *stage,
|
||||
ClutterStageView *view);
|
||||
void clutter_stage_emit_after_update (ClutterStage *stage,
|
||||
ClutterStageView *view);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void _clutter_stage_set_window (ClutterStage *stage,
|
||||
@ -67,8 +71,6 @@ GSList * clutter_stage_find_updated_devices (ClutterStage
|
||||
void clutter_stage_update_devices (ClutterStage *stage,
|
||||
GSList *devices);
|
||||
void clutter_stage_update_actor_stage_views (ClutterStage *stage);
|
||||
gboolean _clutter_stage_needs_update (ClutterStage *stage);
|
||||
gboolean _clutter_stage_do_update (ClutterStage *stage);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void _clutter_stage_queue_event (ClutterStage *stage,
|
||||
@ -77,8 +79,6 @@ void _clutter_stage_queue_event (ClutterStage *stage,
|
||||
gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
|
||||
void _clutter_stage_process_queued_events (ClutterStage *stage);
|
||||
void _clutter_stage_update_input_devices (ClutterStage *stage);
|
||||
gint64 _clutter_stage_get_update_time (ClutterStage *stage);
|
||||
void _clutter_stage_clear_update_time (ClutterStage *stage);
|
||||
gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage);
|
||||
|
||||
void clutter_stage_log_pick (ClutterStage *stage,
|
||||
@ -135,6 +135,7 @@ void _clutter_stage_set_scale_factor (ClutterStage *stag
|
||||
int factor);
|
||||
|
||||
void clutter_stage_presented (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterFrameInfo *frame_info);
|
||||
|
||||
void clutter_stage_queue_actor_relayout (ClutterStage *stage,
|
||||
|
@ -63,6 +63,11 @@ void clutter_stage_view_transform_rect_to_onscreen (ClutterStageView
|
||||
int dst_height,
|
||||
cairo_rectangle_int_t *dst_rect);
|
||||
|
||||
void clutter_stage_view_schedule_update (ClutterStageView *view);
|
||||
|
||||
float clutter_stage_view_get_refresh_rate (ClutterStageView *view);
|
||||
|
||||
void clutter_stage_view_notify_presented (ClutterStageView *view,
|
||||
ClutterFrameInfo *frame_info);
|
||||
|
||||
#endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "clutter/clutter-frame-clock.h"
|
||||
#include "clutter/clutter-private.h"
|
||||
#include "clutter/clutter-mutter.h"
|
||||
#include "clutter/clutter-stage-private.h"
|
||||
#include "cogl/cogl.h"
|
||||
|
||||
enum
|
||||
@ -1001,6 +1002,15 @@ clutter_stage_view_take_scanout (ClutterStageView *view)
|
||||
return g_steal_pointer (&priv->next_scanout);
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_view_schedule_update (ClutterStageView *view)
|
||||
{
|
||||
ClutterStageViewPrivate *priv =
|
||||
clutter_stage_view_get_instance_private (view);
|
||||
|
||||
clutter_frame_clock_schedule_update (priv->frame_clock);
|
||||
}
|
||||
|
||||
float
|
||||
clutter_stage_view_get_refresh_rate (ClutterStageView *view)
|
||||
{
|
||||
@ -1040,7 +1050,57 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock,
|
||||
int64_t time_us,
|
||||
gpointer user_data)
|
||||
{
|
||||
return CLUTTER_FRAME_RESULT_IDLE;
|
||||
ClutterStageView *view = user_data;
|
||||
ClutterStageViewPrivate *priv =
|
||||
clutter_stage_view_get_instance_private (view);
|
||||
ClutterStage *stage = priv->stage;
|
||||
g_autoptr (GSList) devices = NULL;
|
||||
ClutterFrameResult result;
|
||||
|
||||
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;
|
||||
|
||||
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT);
|
||||
clutter_stage_emit_before_update (stage, view);
|
||||
|
||||
clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
|
||||
clutter_stage_update_actor_stage_views (stage);
|
||||
clutter_stage_maybe_finish_queue_redraws (stage);
|
||||
|
||||
devices = clutter_stage_find_updated_devices (stage);
|
||||
|
||||
if (clutter_stage_view_has_redraw_clip (view))
|
||||
{
|
||||
ClutterStageWindow *stage_window;
|
||||
|
||||
clutter_stage_emit_before_paint (stage, view);
|
||||
|
||||
stage_window = _clutter_stage_get_window (stage);
|
||||
_clutter_stage_window_redraw_view (stage_window, view);
|
||||
|
||||
clutter_stage_emit_after_paint (stage, view);
|
||||
|
||||
_clutter_stage_window_finish_frame (stage_window);
|
||||
|
||||
result = CLUTTER_FRAME_RESULT_PENDING_PRESENTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = CLUTTER_FRAME_RESULT_IDLE;
|
||||
}
|
||||
|
||||
clutter_stage_update_devices (stage, devices);
|
||||
|
||||
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
|
||||
clutter_stage_emit_after_update (stage, view);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const ClutterFrameListenerIface frame_clock_listener_iface = {
|
||||
@ -1048,6 +1108,17 @@ static const ClutterFrameListenerIface frame_clock_listener_iface = {
|
||||
.frame = handle_frame_clock_frame,
|
||||
};
|
||||
|
||||
void
|
||||
clutter_stage_view_notify_presented (ClutterStageView *view,
|
||||
ClutterFrameInfo *frame_info)
|
||||
{
|
||||
ClutterStageViewPrivate *priv =
|
||||
clutter_stage_view_get_instance_private (view);
|
||||
|
||||
clutter_stage_presented (priv->stage, view, frame_info);
|
||||
clutter_frame_clock_notify_presented (priv->frame_clock, frame_info);
|
||||
}
|
||||
|
||||
static void
|
||||
sanity_check_framebuffer (ClutterStageView *view)
|
||||
{
|
||||
@ -1071,10 +1142,12 @@ clutter_stage_view_set_framebuffer (ClutterStageView *view,
|
||||
ClutterStageViewPrivate *priv =
|
||||
clutter_stage_view_get_instance_private (view);
|
||||
|
||||
priv->framebuffer = cogl_object_ref (framebuffer);
|
||||
|
||||
if (priv->framebuffer)
|
||||
sanity_check_framebuffer (view);
|
||||
g_warn_if_fail (!priv->framebuffer);
|
||||
if (framebuffer)
|
||||
{
|
||||
priv->framebuffer = cogl_object_ref (framebuffer);
|
||||
sanity_check_framebuffer (view);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -103,81 +103,12 @@ _clutter_stage_window_get_geometry (ClutterStageWindow *window,
|
||||
}
|
||||
|
||||
void
|
||||
_clutter_stage_window_schedule_update (ClutterStageWindow *window,
|
||||
int sync_delay)
|
||||
_clutter_stage_window_redraw_view (ClutterStageWindow *window,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
ClutterStageWindowInterface *iface;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
|
||||
|
||||
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||
if (iface->schedule_update == NULL)
|
||||
{
|
||||
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
||||
return;
|
||||
}
|
||||
|
||||
iface->schedule_update (window, sync_delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_window_get_update_time:
|
||||
* @window: a #ClutterStageWindow object
|
||||
*
|
||||
* See _clutter_stage_get_update_time() for more info.
|
||||
*
|
||||
* Returns: The timestamp of the update time
|
||||
*/
|
||||
gint64
|
||||
_clutter_stage_window_get_update_time (ClutterStageWindow *window)
|
||||
{
|
||||
ClutterStageWindowInterface *iface;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0);
|
||||
|
||||
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||
if (iface->get_update_time == NULL)
|
||||
{
|
||||
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return iface->get_update_time (window);
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_window_clear_update_time:
|
||||
* @window: a #ClutterStageWindow object
|
||||
*
|
||||
* Clears the update time. See _clutter_stage_clear_update_time() for more info.
|
||||
*/
|
||||
void
|
||||
_clutter_stage_window_clear_update_time (ClutterStageWindow *window)
|
||||
{
|
||||
ClutterStageWindowInterface *iface;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
|
||||
|
||||
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||
if (iface->clear_update_time == NULL)
|
||||
{
|
||||
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
||||
return;
|
||||
}
|
||||
|
||||
iface->clear_update_time (window);
|
||||
}
|
||||
|
||||
void
|
||||
_clutter_stage_window_redraw (ClutterStageWindow *window)
|
||||
{
|
||||
ClutterStageWindowInterface *iface;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
|
||||
|
||||
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||
if (iface->redraw)
|
||||
iface->redraw (window);
|
||||
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->redraw_view (window, view);
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
@ -44,12 +44,8 @@ struct _ClutterStageWindowInterface
|
||||
void (* get_geometry) (ClutterStageWindow *stage_window,
|
||||
cairo_rectangle_int_t *geometry);
|
||||
|
||||
void (* schedule_update) (ClutterStageWindow *stage_window,
|
||||
int sync_delay);
|
||||
gint64 (* get_update_time) (ClutterStageWindow *stage_window);
|
||||
void (* clear_update_time) (ClutterStageWindow *stage_window);
|
||||
|
||||
void (* redraw) (ClutterStageWindow *stage_window);
|
||||
void (* redraw_view) (ClutterStageWindow *stage_window,
|
||||
ClutterStageView *view);
|
||||
|
||||
gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
|
||||
|
||||
@ -78,15 +74,12 @@ void _clutter_stage_window_resize (ClutterStageWin
|
||||
CLUTTER_EXPORT
|
||||
void _clutter_stage_window_get_geometry (ClutterStageWindow *window,
|
||||
cairo_rectangle_int_t *geometry);
|
||||
void _clutter_stage_window_schedule_update (ClutterStageWindow *window,
|
||||
int sync_delay);
|
||||
gint64 _clutter_stage_window_get_update_time (ClutterStageWindow *window);
|
||||
void _clutter_stage_window_clear_update_time (ClutterStageWindow *window);
|
||||
|
||||
void _clutter_stage_window_set_accept_focus (ClutterStageWindow *window,
|
||||
gboolean accept_focus);
|
||||
|
||||
void _clutter_stage_window_redraw (ClutterStageWindow *window);
|
||||
void _clutter_stage_window_redraw_view (ClutterStageWindow *window,
|
||||
ClutterStageView *view);
|
||||
|
||||
gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window);
|
||||
|
||||
|
@ -60,11 +60,11 @@
|
||||
#include "clutter-debug.h"
|
||||
#include "clutter-enum-types.h"
|
||||
#include "clutter-event-private.h"
|
||||
#include "clutter-frame-clock.h"
|
||||
#include "clutter-id-pool.h"
|
||||
#include "clutter-input-device-private.h"
|
||||
#include "clutter-main.h"
|
||||
#include "clutter-marshal.h"
|
||||
#include "clutter-master-clock.h"
|
||||
#include "clutter-mutter.h"
|
||||
#include "clutter-paint-context-private.h"
|
||||
#include "clutter-paint-volume-private.h"
|
||||
@ -139,7 +139,6 @@ struct _ClutterStagePrivate
|
||||
|
||||
int update_freeze_count;
|
||||
|
||||
gboolean needs_update;
|
||||
gboolean needs_update_devices;
|
||||
gboolean pending_finish_queue_redraws;
|
||||
|
||||
@ -893,27 +892,31 @@ clutter_stage_paint_view (ClutterStage *stage,
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_emit_before_update (ClutterStage *stage)
|
||||
clutter_stage_emit_before_update (ClutterStage *stage,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
g_signal_emit (stage, stage_signals[BEFORE_UPDATE], 0);
|
||||
g_signal_emit (stage, stage_signals[BEFORE_UPDATE], 0, view);
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_emit_before_paint (ClutterStage *stage)
|
||||
clutter_stage_emit_before_paint (ClutterStage *stage,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
g_signal_emit (stage, stage_signals[BEFORE_PAINT], 0);
|
||||
g_signal_emit (stage, stage_signals[BEFORE_PAINT], 0, view);
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_emit_after_paint (ClutterStage *stage)
|
||||
clutter_stage_emit_after_paint (ClutterStage *stage,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
g_signal_emit (stage, stage_signals[AFTER_PAINT], 0);
|
||||
g_signal_emit (stage, stage_signals[AFTER_PAINT], 0, view);
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_emit_after_update (ClutterStage *stage)
|
||||
clutter_stage_emit_after_update (ClutterStage *stage,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
g_signal_emit (stage, stage_signals[AFTER_UPDATE], 0);
|
||||
g_signal_emit (stage, stage_signals[AFTER_UPDATE], 0, view);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -1072,11 +1075,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
|
||||
g_queue_push_tail (priv->event_queue, event);
|
||||
|
||||
if (first_event)
|
||||
{
|
||||
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_start_running (master_clock);
|
||||
clutter_stage_schedule_update (stage);
|
||||
}
|
||||
clutter_stage_schedule_update (stage);
|
||||
}
|
||||
|
||||
gboolean
|
||||
@ -1190,28 +1189,6 @@ _clutter_stage_process_queued_events (ClutterStage *stage)
|
||||
g_object_unref (stage);
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_needs_update:
|
||||
* @stage: A #ClutterStage
|
||||
*
|
||||
* Determines if _clutter_stage_do_update() needs to be called.
|
||||
*
|
||||
* Return value: %TRUE if the stage need layout or painting
|
||||
*/
|
||||
gboolean
|
||||
_clutter_stage_needs_update (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
|
||||
|
||||
priv = stage->priv;
|
||||
|
||||
return (priv->redraw_pending ||
|
||||
priv->needs_update ||
|
||||
priv->pending_relayouts != NULL);
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_queue_actor_relayout (ClutterStage *stage,
|
||||
ClutterActor *actor)
|
||||
@ -1276,52 +1253,6 @@ clutter_stage_maybe_relayout (ClutterActor *actor)
|
||||
priv->needs_update_devices = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_do_redraw (ClutterStage *stage)
|
||||
{
|
||||
ClutterActor *actor = CLUTTER_ACTOR (stage);
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
||||
return;
|
||||
|
||||
if (priv->impl == NULL)
|
||||
return;
|
||||
|
||||
COGL_TRACE_BEGIN_SCOPED (ClutterStagePaint, "Paint");
|
||||
|
||||
CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]",
|
||||
_clutter_actor_get_debug_name (actor),
|
||||
stage);
|
||||
|
||||
if (_clutter_context_get_show_fps ())
|
||||
{
|
||||
if (priv->fps_timer == NULL)
|
||||
priv->fps_timer = g_timer_new ();
|
||||
}
|
||||
|
||||
_clutter_stage_window_redraw (priv->impl);
|
||||
|
||||
if (_clutter_context_get_show_fps ())
|
||||
{
|
||||
priv->timer_n_frames += 1;
|
||||
|
||||
if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0)
|
||||
{
|
||||
g_print ("*** FPS for %s: %i ***\n",
|
||||
_clutter_actor_get_debug_name (actor),
|
||||
priv->timer_n_frames);
|
||||
|
||||
priv->timer_n_frames = 0;
|
||||
g_timer_start (priv->fps_timer);
|
||||
}
|
||||
}
|
||||
|
||||
CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]",
|
||||
_clutter_actor_get_debug_name (actor),
|
||||
stage);
|
||||
}
|
||||
|
||||
GSList *
|
||||
clutter_stage_find_updated_devices (ClutterStage *stage)
|
||||
{
|
||||
@ -1431,75 +1362,6 @@ clutter_stage_update_devices (ClutterStage *stage,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_do_update:
|
||||
* @stage: A #ClutterStage
|
||||
*
|
||||
* Handles per-frame layout and repaint for the stage.
|
||||
*
|
||||
* Return value: %TRUE if the stage was updated
|
||||
*/
|
||||
gboolean
|
||||
_clutter_stage_do_update (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
g_autoptr (GSList) devices = NULL;
|
||||
|
||||
priv->needs_update = FALSE;
|
||||
|
||||
/* if the stage is being destroyed, or if the destruction already
|
||||
* happened and we don't have an StageWindow any more, then we
|
||||
* should bail out
|
||||
*/
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_REALIZED (stage))
|
||||
return FALSE;
|
||||
|
||||
COGL_TRACE_BEGIN_SCOPED (ClutterStageDoUpdate, "Update");
|
||||
|
||||
clutter_stage_emit_before_update (stage);
|
||||
|
||||
/* NB: We need to ensure we have an up to date layout *before* we
|
||||
* check or clear the pending redraws flag since a relayout may
|
||||
* queue a redraw.
|
||||
*/
|
||||
clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
|
||||
|
||||
if (!priv->redraw_pending)
|
||||
{
|
||||
clutter_stage_emit_after_update (stage);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
clutter_stage_update_actor_stage_views (stage);
|
||||
clutter_stage_maybe_finish_queue_redraws (stage);
|
||||
|
||||
devices = clutter_stage_find_updated_devices (stage);
|
||||
|
||||
clutter_stage_do_redraw (stage);
|
||||
|
||||
/* reset the guard, so that new redraws are possible */
|
||||
priv->redraw_pending = FALSE;
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
if (priv->redraw_count > 0)
|
||||
{
|
||||
CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle",
|
||||
priv->redraw_count);
|
||||
|
||||
priv->redraw_count = 0;
|
||||
}
|
||||
#endif /* CLUTTER_ENABLE_DEBUG */
|
||||
|
||||
clutter_stage_update_devices (stage, devices);
|
||||
|
||||
clutter_stage_emit_after_update (stage);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_real_queue_relayout (ClutterActor *self)
|
||||
{
|
||||
@ -2028,6 +1890,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
/**
|
||||
* ClutterStage::before-update:
|
||||
* @stage: the #ClutterStage
|
||||
* @view: a #ClutterStageView
|
||||
*/
|
||||
stage_signals[BEFORE_UPDATE] =
|
||||
g_signal_new (I_("before-update"),
|
||||
@ -2035,11 +1898,13 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
G_TYPE_NONE, 1,
|
||||
CLUTTER_TYPE_STAGE_VIEW);
|
||||
|
||||
/**
|
||||
* ClutterStage::before-paint:
|
||||
* @stage: the stage that received the event
|
||||
* @view: a #ClutterStageView
|
||||
*
|
||||
* The ::before-paint signal is emitted before the stage is painted.
|
||||
*/
|
||||
@ -2049,11 +1914,12 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
G_TYPE_NONE, 1,
|
||||
CLUTTER_TYPE_STAGE_VIEW);
|
||||
/**
|
||||
* ClutterStage::after-paint:
|
||||
* @stage: the stage that received the event
|
||||
* @paint_Context: the paint context
|
||||
* @view: a #ClutterStageView
|
||||
*
|
||||
* The ::after-paint signal is emitted after the stage is painted,
|
||||
* but before the results are displayed on the screen.
|
||||
@ -2066,11 +1932,13 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, /* no corresponding vfunc */
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
G_TYPE_NONE, 1,
|
||||
CLUTTER_TYPE_STAGE_VIEW);
|
||||
|
||||
/**
|
||||
* ClutterStage::after-update:
|
||||
* @stage: the #ClutterStage
|
||||
* @view: a #ClutterStageView
|
||||
*/
|
||||
stage_signals[AFTER_UPDATE] =
|
||||
g_signal_new (I_("after-update"),
|
||||
@ -2078,7 +1946,8 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
G_TYPE_NONE, 1,
|
||||
CLUTTER_TYPE_STAGE_VIEW);
|
||||
|
||||
/**
|
||||
* ClutterStage::paint-view:
|
||||
@ -2106,6 +1975,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
/**
|
||||
* ClutterStage::presented: (skip)
|
||||
* @stage: the stage that received the event
|
||||
* @view: the #ClutterStageView presented
|
||||
* @frame_info: a #ClutterFrameInfo
|
||||
*
|
||||
* Signals that the #ClutterStage was presented on the screen to the user.
|
||||
@ -2116,7 +1986,8 @@ clutter_stage_class_init (ClutterStageClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_NONE, 2,
|
||||
CLUTTER_TYPE_STAGE_VIEW,
|
||||
G_TYPE_POINTER);
|
||||
|
||||
klass->activate = clutter_stage_real_activate;
|
||||
@ -3204,6 +3075,7 @@ void
|
||||
clutter_stage_schedule_update (ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *stage_window;
|
||||
GList *l;
|
||||
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
||||
return;
|
||||
@ -3212,54 +3084,12 @@ clutter_stage_schedule_update (ClutterStage *stage)
|
||||
if (stage_window == NULL)
|
||||
return;
|
||||
|
||||
stage->priv->needs_update = TRUE;
|
||||
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *view = l->data;
|
||||
|
||||
return _clutter_stage_window_schedule_update (stage_window,
|
||||
stage->priv->sync_delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_get_update_time:
|
||||
* @stage: a #ClutterStage actor
|
||||
*
|
||||
* Returns the earliest time in which the stage is ready to update. The update
|
||||
* time is set when clutter_stage_schedule_update() is called. This can then
|
||||
* be used by e.g. the #ClutterMasterClock to know when the stage needs to be
|
||||
* redrawn.
|
||||
*
|
||||
* Returns: -1 if no redraw is needed; 0 if the backend doesn't know, or the
|
||||
* timestamp (in microseconds) otherwise.
|
||||
*/
|
||||
gint64
|
||||
_clutter_stage_get_update_time (ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *stage_window;
|
||||
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
||||
return 0;
|
||||
|
||||
stage_window = _clutter_stage_get_window (stage);
|
||||
if (stage_window == NULL)
|
||||
return 0;
|
||||
|
||||
return _clutter_stage_window_get_update_time (stage_window);
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_clear_update_time:
|
||||
* @stage: a #ClutterStage actor
|
||||
*
|
||||
* Resets the update time. Call this after a redraw, so that the update time
|
||||
* can again be updated.
|
||||
*/
|
||||
void
|
||||
_clutter_stage_clear_update_time (ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *stage_window;
|
||||
|
||||
stage_window = _clutter_stage_get_window (stage);
|
||||
if (stage_window)
|
||||
_clutter_stage_window_clear_update_time (stage_window);
|
||||
clutter_stage_view_schedule_update (view);
|
||||
}
|
||||
}
|
||||
|
||||
ClutterPaintVolume *
|
||||
@ -3327,19 +3157,26 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
|
||||
*/
|
||||
priv->cached_pick_mode = CLUTTER_PICK_NONE;
|
||||
|
||||
priv->pending_finish_queue_redraws = TRUE;
|
||||
if (!priv->pending_finish_queue_redraws)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *view = l->data;
|
||||
|
||||
clutter_stage_view_schedule_update (view);
|
||||
}
|
||||
|
||||
priv->pending_finish_queue_redraws = TRUE;
|
||||
}
|
||||
|
||||
if (!priv->redraw_pending)
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
CLUTTER_NOTE (PAINT, "First redraw request");
|
||||
|
||||
clutter_stage_schedule_update (stage);
|
||||
priv->redraw_pending = TRUE;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_start_running (master_clock);
|
||||
}
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
else
|
||||
@ -3749,27 +3586,6 @@ clutter_stage_set_sync_delay (ClutterStage *stage,
|
||||
stage->priv->sync_delay = sync_delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_skip_sync_delay:
|
||||
* @stage: a #ClutterStage
|
||||
*
|
||||
* Causes the next frame for the stage to be drawn as quickly as
|
||||
* possible, ignoring any delay that clutter_stage_set_sync_delay()
|
||||
* would normally cause.
|
||||
*
|
||||
* Since: 1.14
|
||||
* Stability: unstable
|
||||
*/
|
||||
void
|
||||
clutter_stage_skip_sync_delay (ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *stage_window;
|
||||
|
||||
stage_window = _clutter_stage_get_window (stage);
|
||||
if (stage_window)
|
||||
_clutter_stage_window_schedule_update (stage_window, -1);
|
||||
}
|
||||
|
||||
int64_t
|
||||
clutter_stage_get_frame_counter (ClutterStage *stage)
|
||||
{
|
||||
@ -3781,9 +3597,10 @@ clutter_stage_get_frame_counter (ClutterStage *stage)
|
||||
|
||||
void
|
||||
clutter_stage_presented (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterFrameInfo *frame_info)
|
||||
{
|
||||
g_signal_emit (stage, stage_signals[PRESENTED], 0, frame_info);
|
||||
g_signal_emit (stage, stage_signals[PRESENTED], 0, view, frame_info);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -4123,61 +3940,6 @@ clutter_stage_capture_into (ClutterStage *stage,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_freeze_updates:
|
||||
*
|
||||
* Freezing updates makes Clutter stop processing events,
|
||||
* redrawing, and advancing timelines, by pausing the master clock. This is
|
||||
* necessary when implementing a display server, to ensure that Clutter doesn't
|
||||
* keep trying to page flip when DRM master has been dropped, e.g. when VT
|
||||
* switched away.
|
||||
*
|
||||
* The master clock starts out running, so if you are VT switched away on
|
||||
* startup, you need to call this immediately.
|
||||
*
|
||||
* To thaw updates, use clutter_stage_thaw_updates().
|
||||
*/
|
||||
void
|
||||
clutter_stage_freeze_updates (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
|
||||
priv->update_freeze_count++;
|
||||
if (priv->update_freeze_count == 1)
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_set_paused (master_clock, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_thaw_updates:
|
||||
*
|
||||
* Resumes a master clock that has previously been frozen with
|
||||
* clutter_stage_freeze_updates(), and start pumping the master clock
|
||||
* again at the next iteration. Note that if you're switching back to your
|
||||
* own VT, you should probably also queue a stage redraw with
|
||||
* clutter_stage_ensure_redraw().
|
||||
*/
|
||||
void
|
||||
clutter_stage_thaw_updates (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
|
||||
g_assert (priv->update_freeze_count > 0);
|
||||
|
||||
priv->update_freeze_count--;
|
||||
if (priv->update_freeze_count == 0)
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_set_paused (master_clock, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_peek_stage_views: (skip)
|
||||
*/
|
||||
|
@ -103,7 +103,6 @@
|
||||
#include "clutter-frame-clock.h"
|
||||
#include "clutter-main.h"
|
||||
#include "clutter-marshal.h"
|
||||
#include "clutter-master-clock.h"
|
||||
#include "clutter-private.h"
|
||||
#include "clutter-scriptable.h"
|
||||
#include "clutter-timeline-private.h"
|
||||
@ -112,10 +111,12 @@ struct _ClutterTimelinePrivate
|
||||
{
|
||||
ClutterTimelineDirection direction;
|
||||
|
||||
ClutterFrameClock *custom_frame_clock;
|
||||
ClutterFrameClock *frame_clock;
|
||||
|
||||
ClutterActor *actor;
|
||||
gulong actor_destroy_handler_id;
|
||||
gulong actor_stage_views_handler_id;
|
||||
|
||||
guint delay_id;
|
||||
|
||||
@ -322,6 +323,70 @@ clutter_timeline_get_actor (ClutterTimeline *timeline)
|
||||
return priv->actor;
|
||||
}
|
||||
|
||||
static void
|
||||
maybe_add_timeline (ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (!priv->frame_clock)
|
||||
return;
|
||||
|
||||
clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
|
||||
}
|
||||
|
||||
static void
|
||||
maybe_remove_timeline (ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (!priv->frame_clock)
|
||||
return;
|
||||
|
||||
clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
|
||||
}
|
||||
|
||||
static void
|
||||
set_frame_clock_internal (ClutterTimeline *timeline,
|
||||
ClutterFrameClock *frame_clock)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (priv->frame_clock == frame_clock)
|
||||
return;
|
||||
|
||||
if (priv->frame_clock && priv->is_playing)
|
||||
maybe_remove_timeline (timeline);
|
||||
|
||||
g_set_object (&priv->frame_clock, frame_clock);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (timeline),
|
||||
obj_props[PROP_FRAME_CLOCK]);
|
||||
|
||||
if (priv->is_playing)
|
||||
maybe_add_timeline (timeline);
|
||||
}
|
||||
|
||||
static void
|
||||
update_frame_clock (ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
ClutterFrameClock *frame_clock;
|
||||
|
||||
if (priv->actor)
|
||||
frame_clock = clutter_actor_pick_frame_clock (priv->actor);
|
||||
else
|
||||
frame_clock = NULL;
|
||||
|
||||
set_frame_clock_internal (timeline, frame_clock);
|
||||
}
|
||||
|
||||
static void
|
||||
on_actor_stage_views_changed (ClutterActor *actor,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
update_frame_clock (timeline);
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_timeline_set_actor:
|
||||
* @timeline: a #ClutterTimeline
|
||||
@ -335,9 +400,18 @@ clutter_timeline_set_actor (ClutterTimeline *timeline,
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
g_return_if_fail (!actor || (actor && !priv->custom_frame_clock));
|
||||
|
||||
if (priv->actor)
|
||||
{
|
||||
g_clear_signal_handler (&priv->actor_destroy_handler_id, priv->actor);
|
||||
g_clear_signal_handler (&priv->actor_stage_views_handler_id, priv->actor);
|
||||
priv->actor = NULL;
|
||||
|
||||
if (priv->is_playing)
|
||||
maybe_remove_timeline (timeline);
|
||||
|
||||
priv->frame_clock = NULL;
|
||||
}
|
||||
|
||||
priv->actor = actor;
|
||||
@ -348,7 +422,13 @@ clutter_timeline_set_actor (ClutterTimeline *timeline,
|
||||
g_signal_connect (priv->actor, "destroy",
|
||||
G_CALLBACK (on_actor_destroyed),
|
||||
timeline);
|
||||
priv->actor_stage_views_handler_id =
|
||||
g_signal_connect (priv->actor, "stage-views-changed",
|
||||
G_CALLBACK (on_actor_stage_views_changed),
|
||||
timeline);
|
||||
}
|
||||
|
||||
update_frame_clock (timeline);
|
||||
}
|
||||
|
||||
/* Scriptable */
|
||||
@ -579,42 +659,6 @@ clutter_timeline_get_property (GObject *object,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_timeline (ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (priv->frame_clock)
|
||||
{
|
||||
clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_add_timeline (master_clock, timeline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remove_timeline (ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (priv->frame_clock)
|
||||
{
|
||||
clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_remove_timeline (master_clock, timeline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_timeline_constructed (GObject *object)
|
||||
{
|
||||
@ -636,7 +680,7 @@ clutter_timeline_finalize (GObject *object)
|
||||
g_hash_table_destroy (priv->markers_by_name);
|
||||
|
||||
if (priv->is_playing)
|
||||
remove_timeline (self);
|
||||
maybe_remove_timeline (self);
|
||||
|
||||
g_clear_object (&priv->frame_clock);
|
||||
|
||||
@ -656,6 +700,7 @@ clutter_timeline_dispose (GObject *object)
|
||||
if (priv->actor)
|
||||
{
|
||||
g_clear_signal_handler (&priv->actor_destroy_handler_id, priv->actor);
|
||||
g_clear_signal_handler (&priv->actor_stage_views_handler_id, priv->actor);
|
||||
priv->actor = NULL;
|
||||
}
|
||||
|
||||
@ -1093,11 +1138,11 @@ set_is_playing (ClutterTimeline *timeline,
|
||||
priv->waiting_first_tick = TRUE;
|
||||
priv->current_repeat = 0;
|
||||
|
||||
add_timeline (timeline);
|
||||
maybe_add_timeline (timeline);
|
||||
}
|
||||
else
|
||||
{
|
||||
remove_timeline (timeline);
|
||||
maybe_remove_timeline (timeline);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1797,7 +1842,7 @@ _clutter_timeline_do_tick (ClutterTimeline *timeline,
|
||||
|
||||
/* Check the is_playing variable before performing the timeline tick.
|
||||
* This is necessary, as if a timeline is stopped in response to a
|
||||
* master-clock generated signal of a different timeline, this code can
|
||||
* frame clock generated signal of a different timeline, this code can
|
||||
* still be reached.
|
||||
*/
|
||||
if (!priv->is_playing)
|
||||
@ -2637,17 +2682,10 @@ clutter_timeline_set_frame_clock (ClutterTimeline *timeline,
|
||||
|
||||
priv = timeline->priv;
|
||||
|
||||
if (priv->frame_clock == frame_clock)
|
||||
return;
|
||||
g_assert (!frame_clock || (frame_clock && !priv->actor));
|
||||
g_return_if_fail (!frame_clock || (frame_clock && !priv->actor));
|
||||
|
||||
if (priv->frame_clock && priv->is_playing)
|
||||
remove_timeline (timeline);
|
||||
|
||||
g_set_object (&priv->frame_clock, frame_clock);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (timeline),
|
||||
obj_props[PROP_FRAME_CLOCK]);
|
||||
|
||||
if (priv->is_playing)
|
||||
add_timeline (timeline);
|
||||
priv->custom_frame_clock = frame_clock;
|
||||
if (!priv->actor)
|
||||
set_frame_clock_internal (timeline, frame_clock);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "clutter-private.h"
|
||||
#include "clutter-stage-private.h"
|
||||
#include "clutter-stage-view-private.h"
|
||||
#include "cogl.h"
|
||||
|
||||
#define MAX_STACK_RECTS 256
|
||||
|
||||
@ -55,6 +56,8 @@ typedef struct _ClutterStageViewCoglPrivate
|
||||
/* Damage history, in stage view render target framebuffer coordinate space.
|
||||
*/
|
||||
ClutterDamageHistory *damage_history;
|
||||
|
||||
guint notify_presented_handle_id;
|
||||
} ClutterStageViewCoglPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl,
|
||||
@ -83,67 +86,12 @@ enum
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static void
|
||||
clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
|
||||
gint sync_delay);
|
||||
|
||||
static void
|
||||
clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
|
||||
{
|
||||
CLUTTER_NOTE (BACKEND, "Unrealizing Cogl stage [%p]", stage_window);
|
||||
}
|
||||
|
||||
void
|
||||
_clutter_stage_cogl_presented (ClutterStageCogl *stage_cogl,
|
||||
CoglFrameEvent frame_event,
|
||||
ClutterFrameInfo *frame_info)
|
||||
{
|
||||
|
||||
if (frame_event == COGL_FRAME_EVENT_SYNC)
|
||||
{
|
||||
/* Early versions of the swap_event implementation in Mesa
|
||||
* deliver BufferSwapComplete event when not selected for,
|
||||
* so if we get a swap event we aren't expecting, just ignore it.
|
||||
*
|
||||
* https://bugs.freedesktop.org/show_bug.cgi?id=27962
|
||||
*
|
||||
* FIXME: This issue can be hidden inside Cogl so we shouldn't
|
||||
* need to care about this bug here.
|
||||
*/
|
||||
if (stage_cogl->pending_swaps > 0)
|
||||
stage_cogl->pending_swaps--;
|
||||
}
|
||||
else if (frame_event == COGL_FRAME_EVENT_COMPLETE)
|
||||
{
|
||||
gint64 presentation_time_cogl = frame_info->presentation_time;
|
||||
|
||||
if (presentation_time_cogl != 0)
|
||||
{
|
||||
ClutterBackend *backend = stage_cogl->backend;
|
||||
CoglContext *context = clutter_backend_get_cogl_context (backend);
|
||||
gint64 current_time_cogl = cogl_get_clock_time (context);
|
||||
gint64 now = g_get_monotonic_time ();
|
||||
|
||||
stage_cogl->last_presentation_time =
|
||||
now + (presentation_time_cogl - current_time_cogl) / 1000;
|
||||
}
|
||||
|
||||
stage_cogl->refresh_rate = frame_info->refresh_rate;
|
||||
}
|
||||
|
||||
clutter_stage_presented (stage_cogl->wrapper, frame_info);
|
||||
|
||||
if (frame_event == COGL_FRAME_EVENT_COMPLETE &&
|
||||
stage_cogl->update_time != -1)
|
||||
{
|
||||
ClutterStageWindow *stage_window = CLUTTER_STAGE_WINDOW (stage_cogl);
|
||||
|
||||
stage_cogl->update_time = -1;
|
||||
clutter_stage_cogl_schedule_update (stage_window,
|
||||
stage_cogl->last_sync_delay);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
|
||||
{
|
||||
@ -174,103 +122,6 @@ clutter_stage_cogl_get_frame_counter (ClutterStageWindow *stage_window)
|
||||
return priv->global_frame_counter;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
|
||||
gint sync_delay)
|
||||
{
|
||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||
gint64 now;
|
||||
float refresh_rate;
|
||||
gint64 refresh_interval;
|
||||
int64_t min_render_time_allowed;
|
||||
int64_t max_render_time_allowed;
|
||||
int64_t next_presentation_time;
|
||||
|
||||
if (stage_cogl->update_time != -1)
|
||||
return;
|
||||
|
||||
stage_cogl->last_sync_delay = sync_delay;
|
||||
|
||||
now = g_get_monotonic_time ();
|
||||
|
||||
if (sync_delay < 0)
|
||||
{
|
||||
stage_cogl->update_time = now;
|
||||
return;
|
||||
}
|
||||
|
||||
refresh_rate = stage_cogl->refresh_rate;
|
||||
if (refresh_rate <= 0.0)
|
||||
refresh_rate = clutter_get_default_frame_rate ();
|
||||
|
||||
refresh_interval = (gint64) (0.5 + G_USEC_PER_SEC / refresh_rate);
|
||||
if (refresh_interval == 0)
|
||||
{
|
||||
stage_cogl->update_time = now;
|
||||
return;
|
||||
}
|
||||
|
||||
min_render_time_allowed = refresh_interval / 2;
|
||||
max_render_time_allowed = refresh_interval - 1000 * sync_delay;
|
||||
|
||||
/* Be robust in the case of incredibly bogus refresh rate */
|
||||
if (max_render_time_allowed <= 0)
|
||||
{
|
||||
g_warning ("Unsupported monitor refresh rate detected. "
|
||||
"(Refresh rate: %.3f, refresh interval: %" G_GINT64_FORMAT ")",
|
||||
refresh_rate,
|
||||
refresh_interval);
|
||||
stage_cogl->update_time = now;
|
||||
return;
|
||||
}
|
||||
|
||||
if (min_render_time_allowed > max_render_time_allowed)
|
||||
min_render_time_allowed = max_render_time_allowed;
|
||||
|
||||
next_presentation_time = stage_cogl->last_presentation_time + refresh_interval;
|
||||
|
||||
/* Get next_presentation_time closer to its final value, to reduce
|
||||
* the number of while iterations below.
|
||||
*/
|
||||
if (next_presentation_time < now)
|
||||
{
|
||||
int64_t last_virtual_presentation_time = now - now % refresh_interval;
|
||||
int64_t hardware_clock_phase =
|
||||
stage_cogl->last_presentation_time % refresh_interval;
|
||||
|
||||
next_presentation_time =
|
||||
last_virtual_presentation_time + hardware_clock_phase;
|
||||
}
|
||||
|
||||
while (next_presentation_time < now + min_render_time_allowed)
|
||||
next_presentation_time += refresh_interval;
|
||||
|
||||
stage_cogl->update_time = next_presentation_time - max_render_time_allowed;
|
||||
|
||||
if (stage_cogl->update_time == stage_cogl->last_update_time)
|
||||
stage_cogl->update_time = stage_cogl->last_update_time + refresh_interval;
|
||||
}
|
||||
|
||||
static gint64
|
||||
clutter_stage_cogl_get_update_time (ClutterStageWindow *stage_window)
|
||||
{
|
||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||
|
||||
if (stage_cogl->pending_swaps)
|
||||
return -1; /* in the future, indefinite */
|
||||
|
||||
return stage_cogl->update_time;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window)
|
||||
{
|
||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||
|
||||
stage_cogl->last_update_time = stage_cogl->update_time;
|
||||
stage_cogl->update_time = -1;
|
||||
}
|
||||
|
||||
static ClutterActor *
|
||||
clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window)
|
||||
{
|
||||
@ -371,7 +222,27 @@ paint_damage_region (ClutterStageWindow *stage_window,
|
||||
cogl_framebuffer_pop_matrix (framebuffer);
|
||||
}
|
||||
|
||||
typedef struct _NotifyPresentedClosure
|
||||
{
|
||||
ClutterStageView *view;
|
||||
ClutterFrameInfo frame_info;
|
||||
} NotifyPresentedClosure;
|
||||
|
||||
static gboolean
|
||||
notify_presented_idle (gpointer user_data)
|
||||
{
|
||||
NotifyPresentedClosure *closure = user_data;
|
||||
ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (closure->view);
|
||||
ClutterStageViewCoglPrivate *view_priv =
|
||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||
|
||||
view_priv->notify_presented_handle_id = 0;
|
||||
clutter_stage_view_notify_presented (closure->view, &closure->frame_info);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
swap_framebuffer (ClutterStageWindow *stage_window,
|
||||
ClutterStageView *view,
|
||||
cairo_region_t *swap_region,
|
||||
@ -416,8 +287,6 @@ swap_framebuffer (ClutterStageWindow *stage_window,
|
||||
cogl_onscreen_swap_region (onscreen,
|
||||
damage, n_rects,
|
||||
frame_info);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -427,17 +296,33 @@ swap_framebuffer (ClutterStageWindow *stage_window,
|
||||
cogl_onscreen_swap_buffers_with_damage (onscreen,
|
||||
damage, n_rects,
|
||||
frame_info);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
|
||||
ClutterStageViewCoglPrivate *view_priv =
|
||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||
NotifyPresentedClosure *closure;
|
||||
|
||||
CLUTTER_NOTE (BACKEND, "cogl_framebuffer_finish (framebuffer: %p)",
|
||||
framebuffer);
|
||||
cogl_framebuffer_finish (framebuffer);
|
||||
|
||||
return FALSE;
|
||||
closure = g_new0 (NotifyPresentedClosure, 1);
|
||||
closure->view = view;
|
||||
closure->frame_info = (ClutterFrameInfo) {
|
||||
.frame_counter = priv->global_frame_counter,
|
||||
.refresh_rate = clutter_stage_view_get_refresh_rate (view),
|
||||
.presentation_time = g_get_monotonic_time (),
|
||||
};
|
||||
priv->global_frame_counter++;
|
||||
|
||||
g_warn_if_fail (view_priv->notify_presented_handle_id == 0);
|
||||
view_priv->notify_presented_handle_id =
|
||||
g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||
notify_presented_idle,
|
||||
closure, g_free);
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,11 +449,11 @@ is_buffer_age_enabled (void)
|
||||
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
ClutterStageView *view)
|
||||
static void
|
||||
clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||
ClutterStageWindow *stage_window = CLUTTER_STAGE_WINDOW (stage_cogl);
|
||||
ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
|
||||
ClutterStageViewCoglPrivate *view_priv =
|
||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||
@ -587,7 +472,6 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
float fb_scale;
|
||||
int fb_width, fb_height;
|
||||
int buffer_age = 0;
|
||||
gboolean res;
|
||||
|
||||
clutter_stage_view_get_layout (view, &view_rect);
|
||||
fb_scale = clutter_stage_view_get_scale (view);
|
||||
@ -654,7 +538,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
redraw_clip = cairo_region_create_rectangle (&view_rect);
|
||||
}
|
||||
|
||||
g_return_val_if_fail (!cairo_region_is_empty (fb_clip_region), FALSE);
|
||||
g_return_if_fail (!cairo_region_is_empty (fb_clip_region));
|
||||
|
||||
swap_with_damage = FALSE;
|
||||
if (has_buffer_age)
|
||||
@ -755,14 +639,12 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
cairo_region_destroy (queued_redraw_clip);
|
||||
}
|
||||
|
||||
res = swap_framebuffer (stage_window,
|
||||
view,
|
||||
swap_region,
|
||||
swap_with_damage);
|
||||
swap_framebuffer (stage_window,
|
||||
view,
|
||||
swap_region,
|
||||
swap_with_damage);
|
||||
|
||||
cairo_region_destroy (swap_region);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -787,68 +669,17 @@ clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl,
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
|
||||
clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
ClutterStageView *view)
|
||||
{
|
||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||
gboolean has_redraw_clip = FALSE;
|
||||
gboolean swap_event = FALSE;
|
||||
GList *l;
|
||||
g_autoptr (CoglScanout) scanout = NULL;
|
||||
|
||||
COGL_TRACE_BEGIN (ClutterStageCoglRedraw, "Paint (Cogl Redraw)");
|
||||
|
||||
for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *view = l->data;
|
||||
|
||||
if (!clutter_stage_view_has_redraw_clip (view))
|
||||
continue;
|
||||
|
||||
has_redraw_clip = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (has_redraw_clip)
|
||||
clutter_stage_emit_before_paint (stage_cogl->wrapper);
|
||||
|
||||
for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *view = l->data;
|
||||
g_autoptr (CoglScanout) scanout = NULL;
|
||||
|
||||
if (!clutter_stage_view_has_redraw_clip (view))
|
||||
continue;
|
||||
|
||||
scanout = clutter_stage_view_take_scanout (view);
|
||||
if (scanout)
|
||||
{
|
||||
clutter_stage_cogl_scanout_view (stage_cogl,
|
||||
view,
|
||||
scanout);
|
||||
swap_event = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
swap_event |= clutter_stage_cogl_redraw_view (stage_window, view);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_redraw_clip)
|
||||
clutter_stage_emit_after_paint (stage_cogl->wrapper);
|
||||
|
||||
_clutter_stage_window_finish_frame (stage_window);
|
||||
|
||||
if (swap_event)
|
||||
{
|
||||
/* If we have swap buffer events then cogl_onscreen_swap_buffers
|
||||
* will return immediately and we need to track that there is a
|
||||
* swap in progress... */
|
||||
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
|
||||
stage_cogl->pending_swaps++;
|
||||
}
|
||||
|
||||
stage_cogl->frame_count++;
|
||||
|
||||
COGL_TRACE_END (ClutterStageCoglRedraw);
|
||||
scanout = clutter_stage_view_take_scanout (view);
|
||||
if (scanout)
|
||||
clutter_stage_cogl_scanout_view (stage_cogl, view, scanout);
|
||||
else
|
||||
clutter_stage_cogl_redraw_view_primary (stage_cogl, view);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -861,10 +692,7 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
|
||||
iface->show = clutter_stage_cogl_show;
|
||||
iface->hide = clutter_stage_cogl_hide;
|
||||
iface->get_frame_counter = clutter_stage_cogl_get_frame_counter;
|
||||
iface->schedule_update = clutter_stage_cogl_schedule_update;
|
||||
iface->get_update_time = clutter_stage_cogl_get_update_time;
|
||||
iface->clear_update_time = clutter_stage_cogl_clear_update_time;
|
||||
iface->redraw = clutter_stage_cogl_redraw;
|
||||
iface->redraw_view = clutter_stage_cogl_redraw_view;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -905,10 +733,43 @@ _clutter_stage_cogl_class_init (ClutterStageCoglClass *klass)
|
||||
static void
|
||||
_clutter_stage_cogl_init (ClutterStageCogl *stage)
|
||||
{
|
||||
stage->last_presentation_time = 0;
|
||||
stage->refresh_rate = 0.0;
|
||||
}
|
||||
|
||||
stage->update_time = -1;
|
||||
static void
|
||||
frame_cb (CoglOnscreen *onscreen,
|
||||
CoglFrameEvent frame_event,
|
||||
CoglFrameInfo *frame_info,
|
||||
void *user_data)
|
||||
{
|
||||
ClutterStageView *view = user_data;
|
||||
ClutterFrameInfo clutter_frame_info;
|
||||
|
||||
if (frame_event == COGL_FRAME_EVENT_SYNC)
|
||||
return;
|
||||
|
||||
clutter_frame_info = (ClutterFrameInfo) {
|
||||
.frame_counter = cogl_frame_info_get_global_frame_counter (frame_info),
|
||||
.refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
|
||||
.presentation_time = ns2us (cogl_frame_info_get_presentation_time (frame_info)),
|
||||
};
|
||||
|
||||
clutter_stage_view_notify_presented (view, &clutter_frame_info);
|
||||
}
|
||||
|
||||
static void
|
||||
on_framebuffer_set (ClutterStageView *view)
|
||||
{
|
||||
CoglFramebuffer *framebuffer;
|
||||
|
||||
framebuffer = clutter_stage_view_get_onscreen (view);
|
||||
|
||||
if (framebuffer && cogl_is_onscreen (framebuffer))
|
||||
{
|
||||
cogl_onscreen_add_frame_callback (COGL_ONSCREEN (framebuffer),
|
||||
frame_cb,
|
||||
view,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -918,6 +779,7 @@ clutter_stage_view_cogl_finalize (GObject *object)
|
||||
ClutterStageViewCoglPrivate *view_priv =
|
||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||
|
||||
g_clear_handle_id (&view_priv->notify_presented_handle_id, g_source_remove);
|
||||
clutter_damage_history_free (view_priv->damage_history);
|
||||
|
||||
G_OBJECT_CLASS (clutter_stage_view_cogl_parent_class)->finalize (object);
|
||||
@ -930,6 +792,9 @@ clutter_stage_view_cogl_init (ClutterStageViewCogl *view_cogl)
|
||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||
|
||||
view_priv->damage_history = clutter_damage_history_new ();
|
||||
|
||||
g_signal_connect (view_cogl, "notify::framebuffer",
|
||||
G_CALLBACK (on_framebuffer_set), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -41,20 +41,6 @@ struct _ClutterStageCogl
|
||||
|
||||
/* back pointer to the backend */
|
||||
ClutterBackend *backend;
|
||||
|
||||
float refresh_rate;
|
||||
int pending_swaps;
|
||||
|
||||
gint64 last_presentation_time;
|
||||
gint64 update_time;
|
||||
int64_t last_update_time;
|
||||
|
||||
/* We only enable clipped redraws after 2 frames, since we've seen
|
||||
* a lot of drivers can struggle to get going and may output some
|
||||
* junk frames to start with. */
|
||||
unsigned int frame_count;
|
||||
|
||||
gint last_sync_delay;
|
||||
};
|
||||
|
||||
struct _ClutterStageCoglClass
|
||||
|
@ -141,8 +141,6 @@ clutter_sources = [
|
||||
'clutter-layout-manager.c',
|
||||
'clutter-layout-meta.c',
|
||||
'clutter-main.c',
|
||||
'clutter-master-clock.c',
|
||||
'clutter-master-clock-default.c',
|
||||
'clutter-offscreen-effect.c',
|
||||
'clutter-page-turn-effect.c',
|
||||
'clutter-paint-context.c',
|
||||
@ -200,8 +198,6 @@ clutter_private_headers = [
|
||||
'clutter-input-focus-private.h',
|
||||
'clutter-input-method-private.h',
|
||||
'clutter-input-pointer-a11y-private.h',
|
||||
'clutter-master-clock.h',
|
||||
'clutter-master-clock-default.h',
|
||||
'clutter-offscreen-effect-private.h',
|
||||
'clutter-paint-context-private.h',
|
||||
'clutter-paint-node-private.h',
|
||||
|
@ -170,8 +170,6 @@ struct _MetaBackendPrivate
|
||||
guint sleep_signal_id;
|
||||
GCancellable *cancellable;
|
||||
GDBusConnection *system_bus;
|
||||
|
||||
gboolean was_headless;
|
||||
};
|
||||
typedef struct _MetaBackendPrivate MetaBackendPrivate;
|
||||
|
||||
@ -292,19 +290,6 @@ meta_backend_monitors_changed (MetaBackend *backend)
|
||||
}
|
||||
|
||||
meta_cursor_renderer_force_update (priv->cursor_renderer);
|
||||
|
||||
if (meta_monitor_manager_is_headless (priv->monitor_manager) &&
|
||||
!priv->was_headless)
|
||||
{
|
||||
clutter_stage_freeze_updates (CLUTTER_STAGE (priv->stage));
|
||||
priv->was_headless = TRUE;
|
||||
}
|
||||
else if (!meta_monitor_manager_is_headless (priv->monitor_manager) &&
|
||||
priv->was_headless)
|
||||
{
|
||||
clutter_stage_thaw_updates (CLUTTER_STAGE (priv->stage));
|
||||
priv->was_headless = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "clutter/clutter.h"
|
||||
#include "clutter/clutter-mutter.h"
|
||||
#include "cogl/cogl.h"
|
||||
#include "core/boxes-private.h"
|
||||
#include "meta/meta-backend.h"
|
||||
#include "meta/util.h"
|
||||
|
||||
@ -155,13 +156,25 @@ queue_redraw (MetaCursorRenderer *renderer,
|
||||
|
||||
static void
|
||||
meta_cursor_renderer_after_paint (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
MetaCursorRenderer *renderer)
|
||||
{
|
||||
MetaCursorRendererPrivate *priv =
|
||||
meta_cursor_renderer_get_instance_private (renderer);
|
||||
|
||||
if (priv->displayed_cursor && !priv->handled_by_backend)
|
||||
meta_cursor_renderer_emit_painted (renderer, priv->displayed_cursor);
|
||||
{
|
||||
graphene_rect_t rect;
|
||||
MetaRectangle view_layout;
|
||||
graphene_rect_t view_rect;
|
||||
|
||||
rect = meta_cursor_renderer_calculate_rect (renderer,
|
||||
priv->displayed_cursor);
|
||||
clutter_stage_view_get_layout (stage_view, &view_layout);
|
||||
view_rect = meta_rectangle_to_graphene_rect (&view_layout);
|
||||
if (graphene_rect_intersection (&rect, &view_rect, NULL))
|
||||
meta_cursor_renderer_emit_painted (renderer, priv->displayed_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -64,6 +64,7 @@ typedef struct _MetaRendererPrivate
|
||||
{
|
||||
MetaBackend *backend;
|
||||
GList *views;
|
||||
gboolean is_paused;
|
||||
} MetaRendererPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (MetaRenderer, meta_renderer, G_TYPE_OBJECT)
|
||||
@ -247,6 +248,14 @@ meta_renderer_add_view (MetaRenderer *renderer,
|
||||
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
|
||||
|
||||
priv->views = g_list_append (priv->views, view);
|
||||
|
||||
if (priv->is_paused)
|
||||
{
|
||||
ClutterFrameClock *frame_clock =
|
||||
clutter_stage_view_get_frame_clock (CLUTTER_STAGE_VIEW (view));
|
||||
|
||||
clutter_frame_clock_inhibit (frame_clock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,6 +276,44 @@ meta_renderer_get_views (MetaRenderer *renderer)
|
||||
return priv->views;
|
||||
}
|
||||
|
||||
void
|
||||
meta_renderer_pause (MetaRenderer *renderer)
|
||||
{
|
||||
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
|
||||
GList *l;
|
||||
|
||||
g_return_if_fail (!priv->is_paused);
|
||||
priv->is_paused = TRUE;
|
||||
|
||||
for (l = priv->views; l; l = l->next)
|
||||
{
|
||||
ClutterStageView *stage_view = l->data;
|
||||
ClutterFrameClock *frame_clock =
|
||||
clutter_stage_view_get_frame_clock (stage_view);
|
||||
|
||||
clutter_frame_clock_inhibit (frame_clock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_renderer_resume (MetaRenderer *renderer)
|
||||
{
|
||||
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
|
||||
GList *l;
|
||||
|
||||
g_return_if_fail (priv->is_paused);
|
||||
priv->is_paused = FALSE;
|
||||
|
||||
for (l = priv->views; l; l = l->next)
|
||||
{
|
||||
ClutterStageView *stage_view = l->data;
|
||||
ClutterFrameClock *frame_clock =
|
||||
clutter_stage_view_get_frame_clock (stage_view);
|
||||
|
||||
clutter_frame_clock_uninhibit (frame_clock);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_renderer_is_hardware_accelerated (MetaRenderer *renderer)
|
||||
{
|
||||
|
@ -67,4 +67,8 @@ GList * meta_renderer_get_views (MetaRenderer *renderer);
|
||||
|
||||
gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer);
|
||||
|
||||
void meta_renderer_pause (MetaRenderer *renderer);
|
||||
|
||||
void meta_renderer_resume (MetaRenderer *renderer);
|
||||
|
||||
#endif /* META_RENDERER_H */
|
||||
|
@ -781,7 +781,6 @@ void
|
||||
meta_backend_native_pause (MetaBackendNative *native)
|
||||
{
|
||||
MetaBackend *backend = META_BACKEND (native);
|
||||
ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
||||
MetaMonitorManager *monitor_manager =
|
||||
meta_backend_get_monitor_manager (backend);
|
||||
MetaMonitorManagerKms *monitor_manager_kms =
|
||||
@ -789,12 +788,13 @@ meta_backend_native_pause (MetaBackendNative *native)
|
||||
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
|
||||
MetaSeatNative *seat =
|
||||
META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
|
||||
MetaRenderer *renderer = meta_backend_get_renderer (backend);
|
||||
|
||||
COGL_TRACE_BEGIN_SCOPED (MetaBackendNativePause,
|
||||
"Backend (pause)");
|
||||
|
||||
meta_seat_native_release_devices (seat);
|
||||
clutter_stage_freeze_updates (stage);
|
||||
meta_renderer_pause (renderer);
|
||||
|
||||
disconnect_udev_device_added_handler (native);
|
||||
|
||||
@ -814,6 +814,7 @@ void meta_backend_native_resume (MetaBackendNative *native)
|
||||
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
|
||||
MetaSeatNative *seat =
|
||||
META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
|
||||
MetaRenderer *renderer = meta_backend_get_renderer (backend);
|
||||
|
||||
COGL_TRACE_BEGIN_SCOPED (MetaBackendNativeResume,
|
||||
"Backend (resume)");
|
||||
@ -823,7 +824,7 @@ void meta_backend_native_resume (MetaBackendNative *native)
|
||||
connect_udev_device_added_handler (native);
|
||||
|
||||
meta_seat_native_reclaim_devices (seat);
|
||||
clutter_stage_thaw_updates (stage);
|
||||
meta_renderer_resume (renderer);
|
||||
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||
|
||||
|
@ -169,8 +169,6 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
|
||||
MetaDumbBuffer dumb_fbs[2];
|
||||
} cpu;
|
||||
|
||||
int pending_flips;
|
||||
|
||||
gboolean noted_primary_gpu_copy_ok;
|
||||
gboolean noted_primary_gpu_copy_failed;
|
||||
MetaSharedFramebufferImportStatus import_status;
|
||||
@ -199,15 +197,9 @@ typedef struct _MetaOnscreenNative
|
||||
} egl;
|
||||
#endif
|
||||
|
||||
gboolean pending_swap_notify;
|
||||
|
||||
gboolean pending_set_crtc;
|
||||
|
||||
int64_t pending_queue_swap_notify_frame_count;
|
||||
int64_t pending_swap_notify_frame_count;
|
||||
|
||||
MetaRendererView *view;
|
||||
int total_pending_flips;
|
||||
} MetaOnscreenNative;
|
||||
|
||||
struct _MetaRendererNative
|
||||
@ -720,60 +712,6 @@ meta_renderer_native_disconnect (CoglRenderer *cogl_renderer)
|
||||
g_slice_free (CoglRendererEGL, cogl_renderer_egl);
|
||||
}
|
||||
|
||||
static void
|
||||
flush_pending_swap_notify (CoglFramebuffer *framebuffer)
|
||||
{
|
||||
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
|
||||
{
|
||||
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
|
||||
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
|
||||
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
|
||||
|
||||
if (onscreen_native->pending_swap_notify)
|
||||
{
|
||||
CoglFrameInfo *info;
|
||||
|
||||
while ((info = g_queue_peek_head (&onscreen->pending_frame_infos)) &&
|
||||
info->global_frame_counter <= onscreen_native->pending_swap_notify_frame_count)
|
||||
{
|
||||
_cogl_onscreen_notify_frame_sync (onscreen, info);
|
||||
_cogl_onscreen_notify_complete (onscreen, info);
|
||||
cogl_object_unref (info);
|
||||
g_queue_pop_head (&onscreen->pending_frame_infos);
|
||||
}
|
||||
|
||||
onscreen_native->pending_swap_notify = FALSE;
|
||||
cogl_object_unref (onscreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flush_pending_swap_notify_idle (void *user_data)
|
||||
{
|
||||
CoglContext *cogl_context = user_data;
|
||||
CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys;
|
||||
MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
|
||||
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
|
||||
GList *l;
|
||||
|
||||
/* This needs to be disconnected before invoking the callbacks in
|
||||
* case the callbacks cause it to be queued again */
|
||||
_cogl_closure_disconnect (renderer_native->swap_notify_idle);
|
||||
renderer_native->swap_notify_idle = NULL;
|
||||
|
||||
l = cogl_context->framebuffers;
|
||||
while (l)
|
||||
{
|
||||
GList *next = l->next;
|
||||
CoglFramebuffer *framebuffer = l->data;
|
||||
|
||||
flush_pending_swap_notify (framebuffer);
|
||||
|
||||
l = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_current_secondary_bo (CoglOnscreen *onscreen)
|
||||
{
|
||||
@ -801,40 +739,14 @@ free_current_bo (CoglOnscreen *onscreen)
|
||||
static void
|
||||
meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
|
||||
{
|
||||
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
|
||||
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
|
||||
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
|
||||
CoglFrameInfo *info;
|
||||
|
||||
onscreen_native->pending_swap_notify_frame_count =
|
||||
onscreen_native->pending_queue_swap_notify_frame_count;
|
||||
g_assert (onscreen->pending_frame_infos.length == 1);
|
||||
|
||||
if (onscreen_native->pending_swap_notify)
|
||||
return;
|
||||
|
||||
/* We only want to notify that the swap is complete when the
|
||||
* application calls cogl_context_dispatch so instead of
|
||||
* immediately notifying we queue an idle callback */
|
||||
if (!renderer_native->swap_notify_idle)
|
||||
{
|
||||
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
|
||||
CoglContext *cogl_context = framebuffer->context;
|
||||
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
|
||||
|
||||
renderer_native->swap_notify_idle =
|
||||
_cogl_poll_renderer_add_idle (cogl_renderer,
|
||||
flush_pending_swap_notify_idle,
|
||||
cogl_context,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* The framebuffer will have its own referenc while the swap notify is
|
||||
* pending. Otherwise when destroying the view would drop the pending
|
||||
* notification with if the destruction happens before the idle callback
|
||||
* is invoked.
|
||||
*/
|
||||
cogl_object_ref (onscreen);
|
||||
onscreen_native->pending_swap_notify = TRUE;
|
||||
info = g_queue_pop_head (&onscreen->pending_frame_infos);
|
||||
_cogl_onscreen_notify_frame_sync (onscreen, info);
|
||||
_cogl_onscreen_notify_complete (onscreen, info);
|
||||
cogl_object_unref (info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -1147,10 +1059,9 @@ notify_view_crtc_presented (MetaRendererView *view,
|
||||
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
|
||||
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
|
||||
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
|
||||
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
|
||||
CoglFrameInfo *frame_info;
|
||||
MetaCrtc *crtc;
|
||||
MetaGpuKms *gpu_kms;
|
||||
MetaRendererNativeGpuData *renderer_gpu_data;
|
||||
|
||||
/* Only keep the frame info for the fastest CRTC in use, which may not be
|
||||
* the first one to complete a flip. By only telling the compositor about the
|
||||
@ -1162,35 +1073,21 @@ notify_view_crtc_presented (MetaRendererView *view,
|
||||
crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
|
||||
maybe_update_frame_info (crtc, frame_info, time_ns);
|
||||
|
||||
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
|
||||
if (gpu_kms != render_gpu)
|
||||
|
||||
meta_onscreen_native_queue_swap_notify (onscreen);
|
||||
|
||||
renderer_gpu_data =
|
||||
meta_renderer_native_get_gpu_data (renderer_native,
|
||||
onscreen_native->render_gpu);
|
||||
switch (renderer_gpu_data->mode)
|
||||
{
|
||||
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state =
|
||||
onscreen_native->secondary_gpu_state;
|
||||
|
||||
secondary_gpu_state->pending_flips--;
|
||||
}
|
||||
|
||||
onscreen_native->total_pending_flips--;
|
||||
if (onscreen_native->total_pending_flips == 0)
|
||||
{
|
||||
MetaRendererNativeGpuData *renderer_gpu_data;
|
||||
|
||||
meta_onscreen_native_queue_swap_notify (onscreen);
|
||||
|
||||
renderer_gpu_data =
|
||||
meta_renderer_native_get_gpu_data (renderer_native,
|
||||
onscreen_native->render_gpu);
|
||||
switch (renderer_gpu_data->mode)
|
||||
{
|
||||
case META_RENDERER_NATIVE_MODE_GBM:
|
||||
meta_onscreen_native_swap_drm_fb (onscreen);
|
||||
break;
|
||||
case META_RENDERER_NATIVE_MODE_GBM:
|
||||
meta_onscreen_native_swap_drm_fb (onscreen);
|
||||
break;
|
||||
#ifdef HAVE_EGL_DEVICE
|
||||
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
|
||||
break;
|
||||
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1404,10 +1301,6 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
|
||||
g_object_ref (view),
|
||||
kms_update);
|
||||
|
||||
onscreen_native->total_pending_flips++;
|
||||
if (secondary_gpu_state)
|
||||
secondary_gpu_state->pending_flips++;
|
||||
|
||||
break;
|
||||
#ifdef HAVE_EGL_DEVICE
|
||||
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
|
||||
@ -1417,7 +1310,6 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
|
||||
g_object_ref (view),
|
||||
custom_egl_stream_page_flip,
|
||||
onscreen_native);
|
||||
onscreen_native->total_pending_flips++;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@ -1485,40 +1377,6 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wait_for_pending_flips (CoglOnscreen *onscreen)
|
||||
{
|
||||
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
|
||||
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
|
||||
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
|
||||
GError *error = NULL;
|
||||
|
||||
secondary_gpu_state = onscreen_native->secondary_gpu_state;
|
||||
if (secondary_gpu_state)
|
||||
{
|
||||
while (secondary_gpu_state->pending_flips)
|
||||
{
|
||||
if (!meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, &error))
|
||||
{
|
||||
g_warning ("Failed to wait for flip on secondary GPU: %s",
|
||||
error->message);
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (onscreen_native->total_pending_flips)
|
||||
{
|
||||
if (!meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, &error))
|
||||
{
|
||||
g_warning ("Failed to wait for flip: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
import_shared_framebuffer (CoglOnscreen *onscreen,
|
||||
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
|
||||
@ -2095,15 +1953,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
|
||||
|
||||
kms_update = meta_kms_ensure_pending_update (kms);
|
||||
|
||||
/*
|
||||
* Wait for the flip callback before continuing, as we might have started the
|
||||
* animation earlier due to the animation being driven by some other monitor.
|
||||
*/
|
||||
COGL_TRACE_BEGIN (MetaRendererNativeSwapBuffersWait,
|
||||
"Onscreen (waiting for page flips)");
|
||||
wait_for_pending_flips (onscreen);
|
||||
COGL_TRACE_END (MetaRendererNativeSwapBuffersWait);
|
||||
|
||||
update_secondary_gpu_state_pre_swap_buffers (onscreen);
|
||||
|
||||
parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
|
||||
@ -2143,9 +1992,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
|
||||
update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
|
||||
|
||||
ensure_crtc_modes (onscreen, kms_update);
|
||||
|
||||
onscreen_native->pending_queue_swap_notify_frame_count =
|
||||
cogl_frame_info_get_global_frame_counter (frame_info);
|
||||
meta_onscreen_native_flip_crtcs (onscreen, kms_update);
|
||||
|
||||
/*
|
||||
@ -2315,8 +2161,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
|
||||
|
||||
kms_update = meta_kms_ensure_pending_update (kms);
|
||||
|
||||
wait_for_pending_flips (onscreen);
|
||||
|
||||
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
|
||||
render_gpu);
|
||||
|
||||
@ -2326,9 +2170,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
|
||||
g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout));
|
||||
|
||||
ensure_crtc_modes (onscreen, kms_update);
|
||||
|
||||
onscreen_native->pending_queue_swap_notify_frame_count =
|
||||
cogl_frame_info_get_global_frame_counter (frame_info);
|
||||
meta_onscreen_native_flip_crtcs (onscreen, kms_update);
|
||||
|
||||
meta_kms_post_pending_update_sync (kms);
|
||||
|
@ -52,86 +52,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaStageNative, meta_stage_native,
|
||||
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
|
||||
clutter_stage_window_iface_init))
|
||||
|
||||
static void
|
||||
frame_cb (CoglOnscreen *onscreen,
|
||||
CoglFrameEvent frame_event,
|
||||
CoglFrameInfo *frame_info,
|
||||
void *user_data)
|
||||
|
||||
{
|
||||
MetaStageNative *stage_native = user_data;
|
||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_native);
|
||||
int64_t global_frame_counter;
|
||||
int64_t presented_frame_counter;
|
||||
ClutterFrameInfo clutter_frame_info;
|
||||
|
||||
global_frame_counter = cogl_frame_info_get_global_frame_counter (frame_info);
|
||||
|
||||
switch (frame_event)
|
||||
{
|
||||
case COGL_FRAME_EVENT_SYNC:
|
||||
presented_frame_counter = stage_native->presented_frame_counter_sync;
|
||||
stage_native->presented_frame_counter_sync = global_frame_counter;
|
||||
break;
|
||||
case COGL_FRAME_EVENT_COMPLETE:
|
||||
presented_frame_counter = stage_native->presented_frame_counter_complete;
|
||||
stage_native->presented_frame_counter_complete = global_frame_counter;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (global_frame_counter <= presented_frame_counter)
|
||||
return;
|
||||
|
||||
clutter_frame_info = (ClutterFrameInfo) {
|
||||
.frame_counter = global_frame_counter,
|
||||
.refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
|
||||
.presentation_time = cogl_frame_info_get_presentation_time (frame_info)
|
||||
};
|
||||
|
||||
_clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_frame_callback (MetaStageNative *stage_native,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
CoglFramebuffer *framebuffer;
|
||||
CoglOnscreen *onscreen;
|
||||
CoglClosure *closure;
|
||||
|
||||
closure = g_object_get_qdata (G_OBJECT (stage_view),
|
||||
quark_view_frame_closure);
|
||||
if (closure)
|
||||
return;
|
||||
|
||||
framebuffer = clutter_stage_view_get_onscreen (stage_view);
|
||||
onscreen = COGL_ONSCREEN (framebuffer);
|
||||
closure = cogl_onscreen_add_frame_callback (onscreen,
|
||||
frame_cb,
|
||||
stage_native,
|
||||
NULL);
|
||||
g_object_set_qdata (G_OBJECT (stage_view),
|
||||
quark_view_frame_closure,
|
||||
closure);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_frame_callbacks (MetaStageNative *stage_native)
|
||||
{
|
||||
MetaBackend *backend = meta_get_backend ();
|
||||
MetaRenderer *renderer = meta_backend_get_renderer (backend);
|
||||
GList *l;
|
||||
|
||||
for (l = meta_renderer_get_views (renderer); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *stage_view = l->data;
|
||||
|
||||
ensure_frame_callback (stage_native, stage_view);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_stage_native_rebuild_views (MetaStageNative *stage_native)
|
||||
{
|
||||
@ -141,7 +61,6 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native)
|
||||
|
||||
meta_renderer_rebuild_views (renderer);
|
||||
clutter_stage_clear_stage_views (CLUTTER_STAGE (stage));
|
||||
ensure_frame_callbacks (stage_native);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -247,35 +247,11 @@ meta_stage_x11_unrealize (ClutterStageWindow *stage_window)
|
||||
GINT_TO_POINTER (stage_x11->xwin));
|
||||
}
|
||||
|
||||
if (stage_x11->frame_closure)
|
||||
{
|
||||
cogl_onscreen_remove_frame_callback (stage_x11->onscreen,
|
||||
stage_x11->frame_closure);
|
||||
stage_x11->frame_closure = NULL;
|
||||
}
|
||||
|
||||
clutter_stage_window_parent_iface->unrealize (stage_window);
|
||||
|
||||
g_clear_pointer (&stage_x11->onscreen, cogl_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
frame_cb (CoglOnscreen *onscreen,
|
||||
CoglFrameEvent frame_event,
|
||||
CoglFrameInfo *frame_info,
|
||||
void *user_data)
|
||||
|
||||
{
|
||||
ClutterStageCogl *stage_cogl = user_data;
|
||||
ClutterFrameInfo clutter_frame_info = {
|
||||
.frame_counter = cogl_frame_info_get_frame_counter (frame_info),
|
||||
.presentation_time = cogl_frame_info_get_presentation_time (frame_info),
|
||||
.refresh_rate = cogl_frame_info_get_refresh_rate (frame_info)
|
||||
};
|
||||
|
||||
_clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_stage_x11_realize (ClutterStageWindow *stage_window)
|
||||
{
|
||||
@ -291,12 +267,6 @@ meta_stage_x11_realize (ClutterStageWindow *stage_window)
|
||||
|
||||
stage_x11->onscreen = cogl_onscreen_new (backend->cogl_context, width, height);
|
||||
|
||||
stage_x11->frame_closure =
|
||||
cogl_onscreen_add_frame_callback (stage_x11->onscreen,
|
||||
frame_cb,
|
||||
stage_cogl,
|
||||
NULL);
|
||||
|
||||
if (META_IS_BACKEND_X11_CM (stage_x11->backend))
|
||||
{
|
||||
MetaRenderer *renderer = meta_backend_get_renderer (stage_x11->backend);
|
||||
|
@ -23,8 +23,10 @@ struct _MetaCompositorClass
|
||||
gboolean (* manage) (MetaCompositor *compositor,
|
||||
GError **error);
|
||||
void (* unmanage) (MetaCompositor *compositor);
|
||||
void (* before_paint) (MetaCompositor *compositor);
|
||||
void (* after_paint) (MetaCompositor *compositor);
|
||||
void (* before_paint) (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view);
|
||||
void (* after_paint) (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view);
|
||||
void (* remove_window) (MetaCompositor *compositor,
|
||||
MetaWindow *window);
|
||||
};
|
||||
|
@ -139,6 +139,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaCompositor, meta_compositor,
|
||||
|
||||
static void
|
||||
on_presented (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
ClutterFrameInfo *frame_info,
|
||||
MetaCompositor *compositor);
|
||||
|
||||
@ -1034,6 +1035,7 @@ meta_compositor_sync_window_geometry (MetaCompositor *compositor,
|
||||
|
||||
static void
|
||||
on_presented (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
ClutterFrameInfo *frame_info,
|
||||
MetaCompositor *compositor)
|
||||
{
|
||||
@ -1072,34 +1074,42 @@ on_presented (ClutterStage *stage,
|
||||
for (l = priv->windows; l; l = l->next)
|
||||
{
|
||||
ClutterActor *actor = l->data;
|
||||
GList *actor_stage_views;
|
||||
|
||||
meta_window_actor_frame_complete (META_WINDOW_ACTOR (actor),
|
||||
frame_info,
|
||||
presentation_time);
|
||||
actor_stage_views = clutter_actor_peek_stage_views (actor);
|
||||
if (g_list_find (actor_stage_views, stage_view))
|
||||
{
|
||||
meta_window_actor_frame_complete (META_WINDOW_ACTOR (actor),
|
||||
frame_info,
|
||||
presentation_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_real_before_paint (MetaCompositor *compositor)
|
||||
meta_compositor_real_before_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaCompositorPrivate *priv =
|
||||
meta_compositor_get_instance_private (compositor);
|
||||
GList *l;
|
||||
|
||||
for (l = priv->windows; l; l = l->next)
|
||||
meta_window_actor_before_paint (l->data);
|
||||
meta_window_actor_before_paint (l->data, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_before_paint (MetaCompositor *compositor)
|
||||
meta_compositor_before_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
COGL_TRACE_BEGIN_SCOPED (MetaCompositorPrePaint,
|
||||
"Compositor (before-paint)");
|
||||
META_COMPOSITOR_GET_CLASS (compositor)->before_paint (compositor);
|
||||
META_COMPOSITOR_GET_CLASS (compositor)->before_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_real_after_paint (MetaCompositor *compositor)
|
||||
meta_compositor_real_after_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaCompositorPrivate *priv =
|
||||
meta_compositor_get_instance_private (compositor);
|
||||
@ -1132,31 +1142,37 @@ meta_compositor_real_after_paint (MetaCompositor *compositor)
|
||||
for (l = priv->windows; l; l = l->next)
|
||||
{
|
||||
ClutterActor *actor = l->data;
|
||||
GList *actor_stage_views;
|
||||
|
||||
meta_window_actor_after_paint (META_WINDOW_ACTOR (actor));
|
||||
actor_stage_views = clutter_actor_peek_stage_views (actor);
|
||||
if (g_list_find (actor_stage_views, stage_view))
|
||||
meta_window_actor_after_paint (META_WINDOW_ACTOR (actor), stage_view);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_after_paint (MetaCompositor *compositor)
|
||||
meta_compositor_after_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
COGL_TRACE_BEGIN_SCOPED (MetaCompositorPostPaint,
|
||||
"Compositor (after-paint)");
|
||||
META_COMPOSITOR_GET_CLASS (compositor)->after_paint (compositor);
|
||||
META_COMPOSITOR_GET_CLASS (compositor)->after_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
on_before_paint (ClutterStage *stage,
|
||||
MetaCompositor *compositor)
|
||||
on_before_paint (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
MetaCompositor *compositor)
|
||||
{
|
||||
meta_compositor_before_paint (compositor);
|
||||
meta_compositor_before_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
on_after_paint (ClutterStage *stage,
|
||||
MetaCompositor *compositor)
|
||||
on_after_paint (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
MetaCompositor *compositor)
|
||||
{
|
||||
meta_compositor_after_paint (compositor);
|
||||
meta_compositor_after_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -116,14 +116,15 @@ maybe_assign_primary_plane (MetaCompositor *compositor)
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_native_before_paint (MetaCompositor *compositor)
|
||||
meta_compositor_native_before_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaCompositorClass *parent_class;
|
||||
|
||||
maybe_assign_primary_plane (compositor);
|
||||
|
||||
parent_class = META_COMPOSITOR_CLASS (meta_compositor_native_parent_class);
|
||||
parent_class->before_paint (compositor);
|
||||
parent_class->before_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
MetaCompositorNative *
|
||||
|
@ -293,8 +293,9 @@ out:
|
||||
}
|
||||
|
||||
static void
|
||||
on_before_update (ClutterStage *stage,
|
||||
MetaCompositor *compositor)
|
||||
on_before_update (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
MetaCompositor *compositor)
|
||||
{
|
||||
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
|
||||
|
||||
@ -330,7 +331,8 @@ on_before_update (ClutterStage *stage,
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_x11_before_paint (MetaCompositor *compositor)
|
||||
meta_compositor_x11_before_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
|
||||
MetaCompositorClass *parent_class;
|
||||
@ -338,11 +340,12 @@ meta_compositor_x11_before_paint (MetaCompositor *compositor)
|
||||
maybe_unredirect_top_window (compositor_x11);
|
||||
|
||||
parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
|
||||
parent_class->before_paint (compositor);
|
||||
parent_class->before_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_compositor_x11_after_paint (MetaCompositor *compositor)
|
||||
meta_compositor_x11_after_paint (MetaCompositor *compositor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
|
||||
MetaCompositorClass *parent_class;
|
||||
@ -356,7 +359,7 @@ meta_compositor_x11_after_paint (MetaCompositor *compositor)
|
||||
}
|
||||
|
||||
parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
|
||||
parent_class->after_paint (compositor);
|
||||
parent_class->after_paint (compositor, stage_view);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -165,8 +165,9 @@ run_repaint_laters (GSList **laters_list)
|
||||
}
|
||||
|
||||
static void
|
||||
on_before_update (ClutterStage *stage,
|
||||
MetaLaters *laters)
|
||||
on_before_update (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
MetaLaters *laters)
|
||||
{
|
||||
unsigned int i;
|
||||
GSList *l;
|
||||
|
@ -23,8 +23,10 @@ struct _MetaWindowActorClass
|
||||
void (*queue_frame_drawn) (MetaWindowActor *actor,
|
||||
gboolean skip_sync_delay);
|
||||
|
||||
void (*before_paint) (MetaWindowActor *actor);
|
||||
void (*after_paint) (MetaWindowActor *actor);
|
||||
void (*before_paint) (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view);
|
||||
void (*after_paint) (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view);
|
||||
|
||||
void (*queue_destroy) (MetaWindowActor *actor);
|
||||
void (*set_frozen) (MetaWindowActor *actor,
|
||||
@ -50,8 +52,10 @@ void meta_window_actor_size_change (MetaWindowActor *self,
|
||||
MetaRectangle *old_frame_rect,
|
||||
MetaRectangle *old_buffer_rect);
|
||||
|
||||
void meta_window_actor_before_paint (MetaWindowActor *self);
|
||||
void meta_window_actor_after_paint (MetaWindowActor *self);
|
||||
void meta_window_actor_before_paint (MetaWindowActor *self,
|
||||
ClutterStageView *stage_view);
|
||||
void meta_window_actor_after_paint (MetaWindowActor *self,
|
||||
ClutterStageView *stage_view);
|
||||
void meta_window_actor_frame_complete (MetaWindowActor *self,
|
||||
ClutterFrameInfo *frame_info,
|
||||
gint64 presentation_time);
|
||||
|
@ -119,12 +119,14 @@ meta_window_actor_wayland_queue_frame_drawn (MetaWindowActor *actor,
|
||||
}
|
||||
|
||||
static void
|
||||
meta_window_actor_wayland_before_paint (MetaWindowActor *actor)
|
||||
meta_window_actor_wayland_before_paint (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
meta_window_actor_wayland_after_paint (MetaWindowActor *actor)
|
||||
meta_window_actor_wayland_after_paint (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "compositor/meta-window-actor-x11.h"
|
||||
|
||||
#include "backends/meta-logical-monitor.h"
|
||||
#include "clutter/clutter-frame-clock.h"
|
||||
#include "compositor/compositor-private.h"
|
||||
#include "compositor/meta-cullable.h"
|
||||
#include "compositor/meta-shaped-texture-private.h"
|
||||
@ -58,6 +59,8 @@ struct _MetaWindowActorX11
|
||||
|
||||
guint send_frame_messages_timer;
|
||||
int64_t frame_drawn_time;
|
||||
gboolean pending_schedule_update_now;
|
||||
ClutterFrameClock *frame_clock;
|
||||
|
||||
gulong repaint_scheduled_id;
|
||||
gulong size_changed_id;
|
||||
@ -372,8 +375,8 @@ meta_window_actor_x11_frame_complete (MetaWindowActor *actor,
|
||||
g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
|
||||
window->desc);
|
||||
if (G_UNLIKELY (frame->frame_counter < frame_counter))
|
||||
g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
|
||||
window->desc, frame->frame_counter);
|
||||
g_debug ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
|
||||
window->desc, frame->frame_counter);
|
||||
|
||||
actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
|
||||
send_frame_timings (actor_x11, frame, frame_info, presentation_time);
|
||||
@ -451,8 +454,10 @@ meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
|
||||
|
||||
if (skip_sync_delay)
|
||||
{
|
||||
ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (actor_x11));
|
||||
clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
|
||||
if (actor_x11->frame_clock)
|
||||
clutter_frame_clock_schedule_update_now (actor_x11->frame_clock);
|
||||
else
|
||||
actor_x11->pending_schedule_update_now = TRUE;
|
||||
}
|
||||
|
||||
if (!actor_x11->repaint_scheduled)
|
||||
@ -473,9 +478,11 @@ meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
|
||||
* before_paint/after_paint functions get called, enabling us to
|
||||
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
|
||||
* consistent timing with non-empty frames. If the window
|
||||
* is completely obscured we fire off the send_frame_messages timeout.
|
||||
* is completely obscured, or completely off screen we fire off the
|
||||
* send_frame_messages timeout.
|
||||
*/
|
||||
if (is_obscured)
|
||||
if (is_obscured ||
|
||||
!clutter_actor_peek_stage_views (CLUTTER_ACTOR (actor)))
|
||||
{
|
||||
queue_send_frame_messages_timeout (actor_x11);
|
||||
}
|
||||
@ -1226,7 +1233,21 @@ handle_updates (MetaWindowActorX11 *actor_x11)
|
||||
}
|
||||
|
||||
static void
|
||||
meta_window_actor_x11_before_paint (MetaWindowActor *actor)
|
||||
handle_stage_views_changed (MetaWindowActorX11 *actor_x11)
|
||||
{
|
||||
ClutterActor *actor = CLUTTER_ACTOR (actor_x11);
|
||||
|
||||
actor_x11->frame_clock = clutter_actor_pick_frame_clock (actor);
|
||||
if (actor_x11->frame_clock && actor_x11->pending_schedule_update_now)
|
||||
{
|
||||
clutter_frame_clock_schedule_update_now (actor_x11->frame_clock);
|
||||
actor_x11->pending_schedule_update_now = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_window_actor_x11_before_paint (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
||||
|
||||
@ -1304,7 +1325,8 @@ meta_window_actor_x11_paint (ClutterActor *actor,
|
||||
}
|
||||
|
||||
static void
|
||||
meta_window_actor_x11_after_paint (MetaWindowActor *actor)
|
||||
meta_window_actor_x11_after_paint (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
||||
MetaWindow *window;
|
||||
@ -1641,6 +1663,9 @@ meta_window_actor_x11_init (MetaWindowActorX11 *self)
|
||||
/* We do this now since we might be going right back into the frozen state. */
|
||||
g_signal_connect (self, "thawed", G_CALLBACK (handle_updates), NULL);
|
||||
|
||||
g_signal_connect (self, "stage-views-changed",
|
||||
G_CALLBACK (handle_stage_views_changed), NULL);
|
||||
|
||||
self->shadow_factory = meta_shadow_factory_get_default ();
|
||||
self->shadow_factory_changed_handler_id =
|
||||
g_signal_connect_swapped (self->shadow_factory,
|
||||
|
@ -1021,21 +1021,23 @@ meta_window_actor_sync_visibility (MetaWindowActor *self)
|
||||
}
|
||||
|
||||
void
|
||||
meta_window_actor_before_paint (MetaWindowActor *self)
|
||||
meta_window_actor_before_paint (MetaWindowActor *self,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
if (meta_window_actor_is_destroyed (self))
|
||||
return;
|
||||
|
||||
META_WINDOW_ACTOR_GET_CLASS (self)->before_paint (self);
|
||||
META_WINDOW_ACTOR_GET_CLASS (self)->before_paint (self, stage_view);
|
||||
}
|
||||
|
||||
void
|
||||
meta_window_actor_after_paint (MetaWindowActor *self)
|
||||
meta_window_actor_after_paint (MetaWindowActor *self,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaWindowActorPrivate *priv =
|
||||
meta_window_actor_get_instance_private (self);
|
||||
|
||||
META_WINDOW_ACTOR_GET_CLASS (self)->after_paint (self);
|
||||
META_WINDOW_ACTOR_GET_CLASS (self)->after_paint (self, stage_view);
|
||||
|
||||
if (meta_window_actor_is_destroyed (self))
|
||||
return;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
static void
|
||||
on_presented (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterFrameInfo *frame_info,
|
||||
gboolean *was_presented)
|
||||
{
|
||||
|
@ -121,8 +121,9 @@ meta_test_stage_views_exist (void)
|
||||
}
|
||||
|
||||
static void
|
||||
on_after_paint (ClutterStage *stage,
|
||||
gboolean *was_painted)
|
||||
on_after_paint (ClutterStage *stage,
|
||||
ClutterStageView *view,
|
||||
gboolean *was_painted)
|
||||
{
|
||||
*was_painted = TRUE;
|
||||
}
|
||||
|
@ -196,6 +196,7 @@ meta_wayland_compositor_update (MetaWaylandCompositor *compositor,
|
||||
|
||||
static void
|
||||
on_after_update (ClutterStage *stage,
|
||||
ClutterStageView *stage_view,
|
||||
MetaWaylandCompositor *compositor)
|
||||
{
|
||||
GList *l;
|
||||
@ -209,6 +210,7 @@ on_after_update (ClutterStage *stage,
|
||||
GList *l_cur = l;
|
||||
MetaWaylandSurface *surface = l->data;
|
||||
MetaSurfaceActor *actor;
|
||||
GList *stage_views;
|
||||
MetaWaylandActorSurface *actor_surface;
|
||||
|
||||
l = l->next;
|
||||
@ -221,6 +223,10 @@ on_after_update (ClutterStage *stage,
|
||||
meta_surface_actor_is_obscured (actor))
|
||||
continue;
|
||||
|
||||
stage_views = clutter_actor_peek_stage_views (CLUTTER_ACTOR (actor));
|
||||
if (!g_list_find (stage_views, stage_view))
|
||||
continue;
|
||||
|
||||
actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role);
|
||||
meta_wayland_actor_surface_emit_frame_callbacks (actor_surface,
|
||||
now_us / 1000);
|
||||
|
Loading…
Reference in New Issue
Block a user