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:
Jonas Ådahl 2020-05-30 00:27:56 +02:00
parent 57a2f7b4a3
commit a9a9a0d1c5
38 changed files with 542 additions and 1910 deletions

View File

@ -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"

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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__ */

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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)
*/

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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 */

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
};

View File

@ -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

View File

@ -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 *

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)
{
}

View File

@ -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,

View File

@ -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;

View File

@ -7,6 +7,7 @@
static void
on_presented (ClutterStage *stage,
ClutterStageView *view,
ClutterFrameInfo *frame_info,
gboolean *was_presented)
{

View File

@ -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;
}

View File

@ -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);