mirror of
https://github.com/brl/mutter.git
synced 2024-11-12 17:27:03 -05:00
backend: Invoke ClutterStageWindow::redraw by default
Instead of asking all backends to do that for us, we can call ClutterStageWindow::redraw ourselves by default. This changeset fixes all backends to actually do the right thing, and move the stage implementation redraw inside the ClutterStageWindow implementation itself.
This commit is contained in:
parent
7da930fb75
commit
224280be22
@ -204,6 +204,22 @@ clutter_backend_real_font_changed (ClutterBackend *backend)
|
||||
CLUTTER_NOTE (BACKEND, "Units per em: %.2f", priv->units_per_em);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_backend_real_redraw (ClutterBackend *backend,
|
||||
ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *impl;
|
||||
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
||||
return;
|
||||
|
||||
impl = _clutter_stage_get_window (stage);
|
||||
if (impl == NULL)
|
||||
return;
|
||||
|
||||
_clutter_stage_window_redraw (impl);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_backend_class_init (ClutterBackendClass *klass)
|
||||
{
|
||||
@ -243,6 +259,7 @@ clutter_backend_class_init (ClutterBackendClass *klass)
|
||||
|
||||
klass->resolution_changed = clutter_backend_real_resolution_changed;
|
||||
klass->font_changed = clutter_backend_real_font_changed;
|
||||
klass->redraw = clutter_backend_real_redraw;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -333,7 +350,6 @@ void
|
||||
_clutter_backend_redraw (ClutterBackend *backend,
|
||||
ClutterStage *stage)
|
||||
{
|
||||
ClutterBackendClass *klass;
|
||||
CLUTTER_STATIC_COUNTER (redraw_counter,
|
||||
"_clutter_backend_redraw counter",
|
||||
"Increments for each _clutter_backend_redraw call",
|
||||
@ -347,9 +363,7 @@ _clutter_backend_redraw (ClutterBackend *backend,
|
||||
CLUTTER_COUNTER_INC (_clutter_uprof_context, redraw_counter);
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, redraw_timer);
|
||||
|
||||
klass = CLUTTER_BACKEND_GET_CLASS (backend);
|
||||
if (G_LIKELY (klass->redraw))
|
||||
klass->redraw (backend, stage);
|
||||
CLUTTER_BACKEND_GET_CLASS (backend)->redraw (backend, stage);
|
||||
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, redraw_timer);
|
||||
}
|
||||
|
@ -505,21 +505,6 @@ clutter_backend_egl_ensure_context (ClutterBackend *backend,
|
||||
#endif /* COGL_HAS_XLIB_SUPPORT */
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_backend_egl_redraw (ClutterBackend *backend,
|
||||
ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *impl;
|
||||
|
||||
impl = _clutter_stage_get_window (stage);
|
||||
if (!impl)
|
||||
return;
|
||||
|
||||
g_assert (CLUTTER_IS_STAGE_EGL (impl));
|
||||
|
||||
_clutter_stage_egl_redraw (CLUTTER_STAGE_EGL (impl), stage);
|
||||
}
|
||||
|
||||
#ifndef COGL_HAS_XLIB_SUPPORT
|
||||
static ClutterDeviceManager *
|
||||
clutter_backend_egl_get_device_manager (ClutterBackend *backend)
|
||||
@ -879,7 +864,6 @@ _clutter_backend_egl_class_init (ClutterBackendEGLClass *klass)
|
||||
backend_class->create_stage = clutter_backend_egl_create_stage;
|
||||
backend_class->create_context = clutter_backend_egl_create_context;
|
||||
backend_class->ensure_context = clutter_backend_egl_ensure_context;
|
||||
backend_class->redraw = clutter_backend_egl_redraw;
|
||||
|
||||
#ifdef COGL_HAS_XLIB_SUPPORT
|
||||
backendx11_class->get_visual_info = clutter_backend_egl_get_visual_info;
|
||||
|
@ -297,6 +297,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
||||
iface->add_redraw_clip = clutter_stage_egl_add_redraw_clip;
|
||||
iface->has_redraw_clips = clutter_stage_egl_has_redraw_clips;
|
||||
iface->ignoring_redraw_clips = clutter_stage_egl_ignoring_redraw_clips;
|
||||
iface->redraw = clutter_stage_egl_redraw;
|
||||
}
|
||||
|
||||
#ifdef COGL_HAS_X11_SUPPORT
|
||||
@ -336,18 +337,18 @@ _clutter_stage_egl_init (ClutterStageEGL *stage)
|
||||
|
||||
#endif /* COGL_HAS_X11_SUPPORT */
|
||||
|
||||
void
|
||||
_clutter_stage_egl_redraw (ClutterStageEGL *stage_egl,
|
||||
ClutterStage *stage)
|
||||
static void
|
||||
clutter_stage_egl_redraw (ClutterStageWindow *stage_window)
|
||||
{
|
||||
ClutterBackend *backend = clutter_get_default_backend ();
|
||||
ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend);
|
||||
ClutterActor *wrapper;
|
||||
EGLSurface egl_surface;
|
||||
gboolean may_use_clipped_redraw;
|
||||
gboolean use_clipped_redraw;
|
||||
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
|
||||
ClutterBackend *backend = clutter_get_default_backend ();
|
||||
ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend);
|
||||
ClutterActor *wrapper;
|
||||
EGLSurface egl_surface;
|
||||
gboolean may_use_clipped_redraw;
|
||||
gboolean use_clipped_redraw;
|
||||
#ifdef COGL_HAS_X11_SUPPORT
|
||||
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_egl);
|
||||
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_egl);
|
||||
|
||||
wrapper = CLUTTER_ACTOR (stage_x11->wrapper);
|
||||
egl_surface = stage_egl->egl_surface;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "clutter-egl-headers.h"
|
||||
#include "clutter-backend-egl.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CLUTTER_TYPE_STAGE_EGL (_clutter_stage_egl_get_type ())
|
||||
#define CLUTTER_STAGE_EGL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_EGL, ClutterStageEGL))
|
||||
@ -69,7 +70,6 @@ struct _ClutterStageEGLClass
|
||||
|
||||
GType _clutter_stage_egl_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void _clutter_stage_egl_redraw (ClutterStageEGL *stage_egl,
|
||||
ClutterStage *stage);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_STAGE_EGL_H__ */
|
||||
|
@ -772,28 +772,6 @@ clutter_backend_glx_ensure_context (ClutterBackend *backend,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: we should remove backend_class->redraw() and just
|
||||
* have stage_window_iface->redraw()
|
||||
*/
|
||||
static void
|
||||
clutter_backend_glx_redraw (ClutterBackend *backend,
|
||||
ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWindow *impl = _clutter_stage_get_window (stage);
|
||||
|
||||
if (G_UNLIKELY (impl == NULL))
|
||||
{
|
||||
CLUTTER_NOTE (BACKEND, "Stage [%p] has no implementation", stage);
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert (CLUTTER_IS_STAGE_GLX (impl));
|
||||
|
||||
_clutter_stage_glx_redraw (CLUTTER_STAGE_GLX (impl),
|
||||
stage);
|
||||
}
|
||||
|
||||
static ClutterStageWindow *
|
||||
clutter_backend_glx_create_stage (ClutterBackend *backend,
|
||||
ClutterStage *wrapper,
|
||||
@ -844,7 +822,6 @@ clutter_backend_glx_class_init (ClutterBackendGLXClass *klass)
|
||||
backend_class->create_stage = clutter_backend_glx_create_stage;
|
||||
backend_class->add_options = clutter_backend_glx_add_options;
|
||||
backend_class->get_features = clutter_backend_glx_get_features;
|
||||
backend_class->redraw = clutter_backend_glx_redraw;
|
||||
backend_class->create_context = clutter_backend_glx_create_context;
|
||||
backend_class->ensure_context = clutter_backend_glx_ensure_context;
|
||||
|
||||
|
@ -311,6 +311,324 @@ clutter_stage_glx_add_redraw_clip (ClutterStageWindow *stage_window,
|
||||
stage_glx->initialized_redraw_clip = TRUE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DRM
|
||||
static int
|
||||
drm_wait_vblank(int fd, drm_wait_vblank_t *vbl)
|
||||
{
|
||||
int ret, rc;
|
||||
|
||||
do
|
||||
{
|
||||
ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
|
||||
vbl->request.type &= ~_DRM_VBLANK_RELATIVE;
|
||||
rc = errno;
|
||||
}
|
||||
while (ret && rc == EINTR);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* HAVE_DRM */
|
||||
|
||||
static void
|
||||
wait_for_vblank (ClutterBackendGLX *backend_glx)
|
||||
{
|
||||
if (backend_glx->vblank_type == CLUTTER_VBLANK_NONE)
|
||||
return;
|
||||
|
||||
if (backend_glx->wait_video_sync)
|
||||
{
|
||||
unsigned int retraceCount;
|
||||
|
||||
CLUTTER_NOTE (BACKEND, "Waiting for vblank (wait_video_sync)");
|
||||
backend_glx->get_video_sync (&retraceCount);
|
||||
backend_glx->wait_video_sync (2,
|
||||
(retraceCount + 1) % 2,
|
||||
&retraceCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_DRM
|
||||
drm_wait_vblank_t blank;
|
||||
|
||||
CLUTTER_NOTE (BACKEND, "Waiting for vblank (drm)");
|
||||
blank.request.type = _DRM_VBLANK_RELATIVE;
|
||||
blank.request.sequence = 1;
|
||||
blank.request.signal = 0;
|
||||
drm_wait_vblank (backend_glx->dri_fd, &blank);
|
||||
#else
|
||||
CLUTTER_NOTE (BACKEND, "No vblank mechanism found");
|
||||
#endif /* HAVE_DRM */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_glx_redraw (ClutterStageWindow *stage_window)
|
||||
{
|
||||
ClutterBackendX11 *backend_x11;
|
||||
ClutterBackendGLX *backend_glx;
|
||||
ClutterStageX11 *stage_x11;
|
||||
ClutterStageGLX *stage_glx;
|
||||
GLXDrawable drawable;
|
||||
unsigned int video_sync_count;
|
||||
gboolean may_use_clipped_redraw;
|
||||
gboolean use_clipped_redraw;
|
||||
|
||||
CLUTTER_STATIC_TIMER (painting_timer,
|
||||
"Redrawing", /* parent */
|
||||
"Painting actors",
|
||||
"The time spent painting actors",
|
||||
0 /* no application private data */);
|
||||
CLUTTER_STATIC_TIMER (swapbuffers_timer,
|
||||
"Redrawing", /* parent */
|
||||
"glXSwapBuffers",
|
||||
"The time spent blocked by glXSwapBuffers",
|
||||
0 /* no application private data */);
|
||||
CLUTTER_STATIC_TIMER (blit_sub_buffer_timer,
|
||||
"Redrawing", /* parent */
|
||||
"glx_blit_sub_buffer",
|
||||
"The time spent in _glx_blit_sub_buffer",
|
||||
0 /* no application private data */);
|
||||
|
||||
stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
||||
if (stage_x11->xwin == None)
|
||||
return;
|
||||
|
||||
stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
||||
|
||||
backend_x11 = stage_x11->backend;
|
||||
backend_glx = CLUTTER_BACKEND_GLX (backend_x11);
|
||||
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer);
|
||||
|
||||
if (G_LIKELY (backend_glx->can_blit_sub_buffer) &&
|
||||
/* NB: a zero width redraw clip == full stage redraw */
|
||||
stage_glx->bounding_redraw_clip.width != 0 &&
|
||||
/* some drivers struggle to get going and produce some junk
|
||||
* frames when starting up... */
|
||||
G_LIKELY (stage_glx->frame_count > 3) &&
|
||||
/* While resizing a window clipped redraws are disabled to avoid
|
||||
* artefacts. See clutter-event-x11.c:event_translate for a
|
||||
* detailed explanation */
|
||||
G_LIKELY (stage_x11->clipped_redraws_cool_off == 0))
|
||||
{
|
||||
may_use_clipped_redraw = TRUE;
|
||||
}
|
||||
else
|
||||
may_use_clipped_redraw = FALSE;
|
||||
|
||||
if (may_use_clipped_redraw &&
|
||||
G_LIKELY (!(clutter_paint_debug_flags &
|
||||
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
|
||||
use_clipped_redraw = TRUE;
|
||||
else
|
||||
use_clipped_redraw = FALSE;
|
||||
|
||||
if (use_clipped_redraw)
|
||||
{
|
||||
cogl_clip_push_window_rectangle (stage_glx->bounding_redraw_clip.x,
|
||||
stage_glx->bounding_redraw_clip.y,
|
||||
stage_glx->bounding_redraw_clip.width,
|
||||
stage_glx->bounding_redraw_clip.height);
|
||||
_clutter_stage_do_paint (stage_x11->wrapper,
|
||||
&stage_glx->bounding_redraw_clip);
|
||||
cogl_clip_pop ();
|
||||
}
|
||||
else
|
||||
_clutter_stage_do_paint (stage_x11->wrapper, NULL);
|
||||
|
||||
if (may_use_clipped_redraw &&
|
||||
G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
|
||||
{
|
||||
static CoglMaterial *outline = NULL;
|
||||
ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
|
||||
ClutterActor *actor = CLUTTER_ACTOR (stage_x11->wrapper);
|
||||
CoglHandle vbo;
|
||||
float x_1 = clip->x;
|
||||
float x_2 = clip->x + clip->width;
|
||||
float y_1 = clip->y;
|
||||
float y_2 = clip->y + clip->height;
|
||||
float quad[8] = {
|
||||
x_1, y_1,
|
||||
x_2, y_1,
|
||||
x_2, y_2,
|
||||
x_1, y_2
|
||||
};
|
||||
CoglMatrix modelview;
|
||||
|
||||
if (outline == NULL)
|
||||
{
|
||||
outline = cogl_material_new ();
|
||||
cogl_material_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff);
|
||||
}
|
||||
|
||||
vbo = cogl_vertex_buffer_new (4);
|
||||
cogl_vertex_buffer_add (vbo,
|
||||
"gl_Vertex",
|
||||
2, /* n_components */
|
||||
COGL_ATTRIBUTE_TYPE_FLOAT,
|
||||
FALSE, /* normalized */
|
||||
0, /* stride */
|
||||
quad);
|
||||
cogl_vertex_buffer_submit (vbo);
|
||||
|
||||
cogl_push_matrix ();
|
||||
cogl_matrix_init_identity (&modelview);
|
||||
_clutter_actor_apply_modelview_transform (actor, &modelview);
|
||||
cogl_set_modelview_matrix (&modelview);
|
||||
cogl_set_source (outline);
|
||||
cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_LINE_LOOP,
|
||||
0 , 4);
|
||||
cogl_pop_matrix ();
|
||||
cogl_object_unref (vbo);
|
||||
}
|
||||
|
||||
cogl_flush ();
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer);
|
||||
|
||||
drawable = stage_glx->glxwin
|
||||
? stage_glx->glxwin
|
||||
: stage_x11->xwin;
|
||||
|
||||
/* If we might ever use _clutter_backend_glx_blit_sub_buffer then we
|
||||
* always need to keep track of the video_sync_count so that we can
|
||||
* throttle blits.
|
||||
*
|
||||
* Note: we get the count *before* we issue any glXCopySubBuffer or
|
||||
* blit_sub_buffer request in case the count would go up before
|
||||
* returning control to us.
|
||||
*/
|
||||
if (backend_glx->can_blit_sub_buffer && backend_glx->get_video_sync)
|
||||
backend_glx->get_video_sync (&video_sync_count);
|
||||
|
||||
/* push on the screen */
|
||||
if (use_clipped_redraw)
|
||||
{
|
||||
ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
|
||||
ClutterGeometry copy_area;
|
||||
ClutterActor *actor;
|
||||
|
||||
CLUTTER_NOTE (BACKEND,
|
||||
"_glx_blit_sub_buffer (window: 0x%lx, "
|
||||
"x: %d, y: %d, "
|
||||
"width: %d, height: %d)",
|
||||
(unsigned long) drawable,
|
||||
stage_glx->bounding_redraw_clip.x,
|
||||
stage_glx->bounding_redraw_clip.y,
|
||||
stage_glx->bounding_redraw_clip.width,
|
||||
stage_glx->bounding_redraw_clip.height);
|
||||
|
||||
/* XXX: It seems there will be a race here in that the stage
|
||||
* window may be resized before glXCopySubBufferMESA is handled
|
||||
* and so we may copy the wrong region. I can't really see how
|
||||
* we can handle this with the current state of X but at least
|
||||
* in this case a full redraw should be queued by the resize
|
||||
* anyway so it should only exhibit temporary artefacts.
|
||||
*/
|
||||
actor = CLUTTER_ACTOR (stage_x11->wrapper);
|
||||
copy_area.y = clutter_actor_get_height (actor)
|
||||
- clip->y
|
||||
- clip->height;
|
||||
copy_area.x = clip->x;
|
||||
copy_area.width = clip->width;
|
||||
copy_area.height = clip->height;
|
||||
|
||||
/* glXCopySubBufferMESA and glBlitFramebuffer are not integrated
|
||||
* with the glXSwapIntervalSGI mechanism which we usually use to
|
||||
* throttle the Clutter framerate to the vertical refresh and so
|
||||
* we have to manually wait for the vblank period...
|
||||
*/
|
||||
|
||||
/* Here 'is_synchronized' only means that the blit won't cause a
|
||||
* tear, ie it won't prevent multiple blits per retrace if they
|
||||
* can all be performed in the blanking period. If that's the
|
||||
* case then we still want to use the vblank sync menchanism but
|
||||
* we only need it to throttle redraws.
|
||||
*/
|
||||
if (!backend_glx->blit_sub_buffer_is_synchronized)
|
||||
{
|
||||
/* XXX: note that glXCopySubBuffer, at least for Intel, is
|
||||
* synchronized with the vblank but glBlitFramebuffer may
|
||||
* not be so we use the same scheme we do when calling
|
||||
* glXSwapBuffers without the swap_control extension and
|
||||
* call glFinish () before waiting for the vblank period.
|
||||
*
|
||||
* See where we call glXSwapBuffers for more details.
|
||||
*/
|
||||
glFinish ();
|
||||
wait_for_vblank (backend_glx);
|
||||
}
|
||||
else if (backend_glx->get_video_sync)
|
||||
{
|
||||
/* If we have the GLX_SGI_video_sync extension then we can
|
||||
* be a bit smarter about how we throttle blits by avoiding
|
||||
* any waits if we can see that the video sync count has
|
||||
* already progressed. */
|
||||
if (backend_glx->last_video_sync_count == video_sync_count)
|
||||
wait_for_vblank (backend_glx);
|
||||
}
|
||||
else
|
||||
wait_for_vblank (backend_glx);
|
||||
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer);
|
||||
_clutter_backend_glx_blit_sub_buffer (backend_glx,
|
||||
drawable,
|
||||
copy_area.x,
|
||||
copy_area.y,
|
||||
copy_area.width,
|
||||
copy_area.height);
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, blit_sub_buffer_timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)",
|
||||
backend_x11->xdpy,
|
||||
(unsigned long) drawable);
|
||||
|
||||
/* If we have GLX swap buffer events then glXSwapBuffers will return
|
||||
* immediately and we need to track that there is a swap in
|
||||
* progress... */
|
||||
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
|
||||
stage_glx->pending_swaps++;
|
||||
|
||||
if (backend_glx->vblank_type != CLUTTER_VBLANK_GLX_SWAP &&
|
||||
backend_glx->vblank_type != CLUTTER_VBLANK_NONE)
|
||||
{
|
||||
/* If we are going to wait for VBLANK manually, we not only
|
||||
* need to flush out pending drawing to the GPU before we
|
||||
* sleep, we need to wait for it to finish. Otherwise, we
|
||||
* may end up with the situation:
|
||||
*
|
||||
* - We finish drawing - GPU drawing continues
|
||||
* - We go to sleep - GPU drawing continues
|
||||
* VBLANK - We call glXSwapBuffers - GPU drawing continues
|
||||
* - GPU drawing continues
|
||||
* - Swap buffers happens
|
||||
*
|
||||
* Producing a tear. Calling glFinish() first will cause us
|
||||
* to properly wait for the next VBLANK before we swap. This
|
||||
* obviously does not happen when we use _GLX_SWAP and let
|
||||
* the driver do the right thing
|
||||
*/
|
||||
glFinish ();
|
||||
|
||||
wait_for_vblank (backend_glx);
|
||||
}
|
||||
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
|
||||
glXSwapBuffers (backend_x11->xdpy, drawable);
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);
|
||||
|
||||
_cogl_swap_buffers_notify ();
|
||||
}
|
||||
|
||||
backend_glx->last_video_sync_count = video_sync_count;
|
||||
|
||||
/* reset the redraw clipping for the next paint... */
|
||||
stage_glx->initialized_redraw_clip = FALSE;
|
||||
|
||||
stage_glx->frame_count++;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
||||
{
|
||||
@ -323,6 +641,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
||||
iface->add_redraw_clip = clutter_stage_glx_add_redraw_clip;
|
||||
iface->has_redraw_clips = clutter_stage_glx_has_redraw_clips;
|
||||
iface->ignoring_redraw_clips = clutter_stage_glx_ignoring_redraw_clips;
|
||||
iface->redraw = clutter_stage_glx_redraw;
|
||||
|
||||
/* the rest is inherited from ClutterStageX11 */
|
||||
}
|
||||
@ -375,316 +694,3 @@ clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
|
||||
|
||||
iface->translate_event = clutter_stage_glx_translate_event;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DRM
|
||||
static int
|
||||
drm_wait_vblank(int fd, drm_wait_vblank_t *vbl)
|
||||
{
|
||||
int ret, rc;
|
||||
|
||||
do
|
||||
{
|
||||
ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
|
||||
vbl->request.type &= ~_DRM_VBLANK_RELATIVE;
|
||||
rc = errno;
|
||||
}
|
||||
while (ret && rc == EINTR);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* HAVE_DRM */
|
||||
|
||||
static void
|
||||
wait_for_vblank (ClutterBackendGLX *backend_glx)
|
||||
{
|
||||
if (backend_glx->vblank_type == CLUTTER_VBLANK_NONE)
|
||||
return;
|
||||
|
||||
if (backend_glx->wait_video_sync)
|
||||
{
|
||||
unsigned int retraceCount;
|
||||
|
||||
CLUTTER_NOTE (BACKEND, "Waiting for vblank (wait_video_sync)");
|
||||
backend_glx->get_video_sync (&retraceCount);
|
||||
backend_glx->wait_video_sync (2,
|
||||
(retraceCount + 1) % 2,
|
||||
&retraceCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_DRM
|
||||
drm_wait_vblank_t blank;
|
||||
|
||||
CLUTTER_NOTE (BACKEND, "Waiting for vblank (drm)");
|
||||
blank.request.type = _DRM_VBLANK_RELATIVE;
|
||||
blank.request.sequence = 1;
|
||||
blank.request.signal = 0;
|
||||
drm_wait_vblank (backend_glx->dri_fd, &blank);
|
||||
#else
|
||||
CLUTTER_NOTE (BACKEND, "No vblank mechanism found");
|
||||
#endif /* HAVE_DRM */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_clutter_stage_glx_redraw (ClutterStageGLX *stage_glx,
|
||||
ClutterStage *stage)
|
||||
{
|
||||
ClutterBackend *backend;
|
||||
ClutterBackendX11 *backend_x11;
|
||||
ClutterBackendGLX *backend_glx;
|
||||
ClutterStageX11 *stage_x11;
|
||||
GLXDrawable drawable;
|
||||
unsigned int video_sync_count;
|
||||
gboolean may_use_clipped_redraw;
|
||||
gboolean use_clipped_redraw;
|
||||
CLUTTER_STATIC_TIMER (painting_timer,
|
||||
"Redrawing", /* parent */
|
||||
"Painting actors",
|
||||
"The time spent painting actors",
|
||||
0 /* no application private data */);
|
||||
CLUTTER_STATIC_TIMER (swapbuffers_timer,
|
||||
"Redrawing", /* parent */
|
||||
"glXSwapBuffers",
|
||||
"The time spent blocked by glXSwapBuffers",
|
||||
0 /* no application private data */);
|
||||
CLUTTER_STATIC_TIMER (blit_sub_buffer_timer,
|
||||
"Redrawing", /* parent */
|
||||
"glx_blit_sub_buffer",
|
||||
"The time spent in _glx_blit_sub_buffer",
|
||||
0 /* no application private data */);
|
||||
|
||||
stage_x11 = CLUTTER_STAGE_X11 (stage_glx);
|
||||
if (stage_x11->xwin == None)
|
||||
return;
|
||||
|
||||
backend = clutter_get_default_backend ();
|
||||
backend_x11 = CLUTTER_BACKEND_X11 (backend);
|
||||
backend_glx = CLUTTER_BACKEND_GLX (backend);
|
||||
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer);
|
||||
|
||||
if (G_LIKELY (backend_glx->can_blit_sub_buffer) &&
|
||||
/* NB: a zero width redraw clip == full stage redraw */
|
||||
stage_glx->bounding_redraw_clip.width != 0 &&
|
||||
/* some drivers struggle to get going and produce some junk
|
||||
* frames when starting up... */
|
||||
G_LIKELY (stage_glx->frame_count > 3) &&
|
||||
/* While resizing a window clipped redraws are disabled to avoid
|
||||
* artefacts. See clutter-event-x11.c:event_translate for a
|
||||
* detailed explanation */
|
||||
G_LIKELY (stage_x11->clipped_redraws_cool_off == 0))
|
||||
{
|
||||
may_use_clipped_redraw = TRUE;
|
||||
}
|
||||
else
|
||||
may_use_clipped_redraw = FALSE;
|
||||
|
||||
if (may_use_clipped_redraw &&
|
||||
G_LIKELY (!(clutter_paint_debug_flags &
|
||||
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
|
||||
use_clipped_redraw = TRUE;
|
||||
else
|
||||
use_clipped_redraw = FALSE;
|
||||
|
||||
if (use_clipped_redraw)
|
||||
{
|
||||
cogl_clip_push_window_rectangle (stage_glx->bounding_redraw_clip.x,
|
||||
stage_glx->bounding_redraw_clip.y,
|
||||
stage_glx->bounding_redraw_clip.width,
|
||||
stage_glx->bounding_redraw_clip.height);
|
||||
_clutter_stage_do_paint (stage, &stage_glx->bounding_redraw_clip);
|
||||
cogl_clip_pop ();
|
||||
}
|
||||
else
|
||||
_clutter_stage_do_paint (stage, NULL);
|
||||
|
||||
if ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS) &&
|
||||
may_use_clipped_redraw)
|
||||
{
|
||||
ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
|
||||
static CoglMaterial *outline = NULL;
|
||||
CoglHandle vbo;
|
||||
float x_1 = clip->x;
|
||||
float x_2 = clip->x + clip->width;
|
||||
float y_1 = clip->y;
|
||||
float y_2 = clip->y + clip->height;
|
||||
float quad[8] = {
|
||||
x_1, y_1,
|
||||
x_2, y_1,
|
||||
x_2, y_2,
|
||||
x_1, y_2
|
||||
};
|
||||
CoglMatrix modelview;
|
||||
|
||||
if (outline == NULL)
|
||||
{
|
||||
outline = cogl_material_new ();
|
||||
cogl_material_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff);
|
||||
}
|
||||
|
||||
vbo = cogl_vertex_buffer_new (4);
|
||||
cogl_vertex_buffer_add (vbo,
|
||||
"gl_Vertex",
|
||||
2, /* n_components */
|
||||
COGL_ATTRIBUTE_TYPE_FLOAT,
|
||||
FALSE, /* normalized */
|
||||
0, /* stride */
|
||||
quad);
|
||||
cogl_vertex_buffer_submit (vbo);
|
||||
|
||||
cogl_push_matrix ();
|
||||
cogl_matrix_init_identity (&modelview);
|
||||
_clutter_actor_apply_modelview_transform (CLUTTER_ACTOR (stage),
|
||||
&modelview);
|
||||
cogl_set_modelview_matrix (&modelview);
|
||||
cogl_set_source (outline);
|
||||
cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_LINE_LOOP,
|
||||
0 , 4);
|
||||
cogl_pop_matrix ();
|
||||
cogl_object_unref (vbo);
|
||||
}
|
||||
|
||||
cogl_flush ();
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer);
|
||||
|
||||
drawable = stage_glx->glxwin
|
||||
? stage_glx->glxwin
|
||||
: stage_x11->xwin;
|
||||
|
||||
/* If we might ever use _clutter_backend_glx_blit_sub_buffer then we
|
||||
* always need to keep track of the video_sync_count so that we can
|
||||
* throttle blits.
|
||||
*
|
||||
* Note: we get the count *before* we issue any glXCopySubBuffer or
|
||||
* blit_sub_buffer request in case the count would go up before
|
||||
* returning control to us.
|
||||
*/
|
||||
if (backend_glx->can_blit_sub_buffer && backend_glx->get_video_sync)
|
||||
backend_glx->get_video_sync (&video_sync_count);
|
||||
|
||||
/* push on the screen */
|
||||
if (use_clipped_redraw)
|
||||
{
|
||||
ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
|
||||
ClutterGeometry copy_area;
|
||||
|
||||
CLUTTER_NOTE (BACKEND,
|
||||
"_glx_blit_sub_buffer (window: 0x%lx, "
|
||||
"x: %d, y: %d, "
|
||||
"width: %d, height: %d)",
|
||||
(unsigned long) drawable,
|
||||
stage_glx->bounding_redraw_clip.x,
|
||||
stage_glx->bounding_redraw_clip.y,
|
||||
stage_glx->bounding_redraw_clip.width,
|
||||
stage_glx->bounding_redraw_clip.height);
|
||||
|
||||
/* XXX: It seems there will be a race here in that the stage
|
||||
* window may be resized before glXCopySubBufferMESA is handled
|
||||
* and so we may copy the wrong region. I can't really see how
|
||||
* we can handle this with the current state of X but at least
|
||||
* in this case a full redraw should be queued by the resize
|
||||
* anyway so it should only exhibit temporary artefacts.
|
||||
*/
|
||||
copy_area.y = clutter_actor_get_height (CLUTTER_ACTOR (stage))
|
||||
- clip->y
|
||||
- clip->height;
|
||||
copy_area.x = clip->x;
|
||||
copy_area.width = clip->width;
|
||||
copy_area.height = clip->height;
|
||||
|
||||
/* glXCopySubBufferMESA and glBlitFramebuffer are not integrated
|
||||
* with the glXSwapIntervalSGI mechanism which we usually use to
|
||||
* throttle the Clutter framerate to the vertical refresh and so
|
||||
* we have to manually wait for the vblank period...
|
||||
*/
|
||||
|
||||
/* Here 'is_synchronized' only means that the blit won't cause a
|
||||
* tear, ie it won't prevent multiple blits per retrace if they
|
||||
* can all be performed in the blanking period. If that's the
|
||||
* case then we still want to use the vblank sync menchanism but
|
||||
* we only need it to throttle redraws.
|
||||
*/
|
||||
if (!backend_glx->blit_sub_buffer_is_synchronized)
|
||||
{
|
||||
/* XXX: note that glXCopySubBuffer, at least for Intel, is
|
||||
* synchronized with the vblank but glBlitFramebuffer may
|
||||
* not be so we use the same scheme we do when calling
|
||||
* glXSwapBuffers without the swap_control extension and
|
||||
* call glFinish () before waiting for the vblank period.
|
||||
*
|
||||
* See where we call glXSwapBuffers for more details.
|
||||
*/
|
||||
glFinish ();
|
||||
wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
|
||||
}
|
||||
else if (backend_glx->get_video_sync)
|
||||
{
|
||||
/* If we have the GLX_SGI_video_sync extension then we can
|
||||
* be a bit smarter about how we throttle blits by avoiding
|
||||
* any waits if we can see that the video sync count has
|
||||
* already progressed. */
|
||||
if (backend_glx->last_video_sync_count == video_sync_count)
|
||||
wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
|
||||
}
|
||||
else
|
||||
wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
|
||||
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer);
|
||||
_clutter_backend_glx_blit_sub_buffer (backend_glx,
|
||||
drawable,
|
||||
copy_area.x,
|
||||
copy_area.y,
|
||||
copy_area.width,
|
||||
copy_area.height);
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, blit_sub_buffer_timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)",
|
||||
backend_x11->xdpy,
|
||||
(unsigned long) drawable);
|
||||
|
||||
/* If we have GLX swap buffer events then glXSwapBuffers will return
|
||||
* immediately and we need to track that there is a swap in
|
||||
* progress... */
|
||||
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
|
||||
stage_glx->pending_swaps++;
|
||||
|
||||
if (backend_glx->vblank_type != CLUTTER_VBLANK_GLX_SWAP &&
|
||||
backend_glx->vblank_type != CLUTTER_VBLANK_NONE)
|
||||
{
|
||||
/* If we are going to wait for VBLANK manually, we not only
|
||||
* need to flush out pending drawing to the GPU before we
|
||||
* sleep, we need to wait for it to finish. Otherwise, we
|
||||
* may end up with the situation:
|
||||
*
|
||||
* - We finish drawing - GPU drawing continues
|
||||
* - We go to sleep - GPU drawing continues
|
||||
* VBLANK - We call glXSwapBuffers - GPU drawing continues
|
||||
* - GPU drawing continues
|
||||
* - Swap buffers happens
|
||||
*
|
||||
* Producing a tear. Calling glFinish() first will cause us
|
||||
* to properly wait for the next VBLANK before we swap. This
|
||||
* obviously does not happen when we use _GLX_SWAP and let
|
||||
* the driver do the right thing
|
||||
*/
|
||||
glFinish ();
|
||||
|
||||
wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
|
||||
}
|
||||
|
||||
CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
|
||||
glXSwapBuffers (backend_x11->xdpy, drawable);
|
||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);
|
||||
_cogl_swap_buffers_notify ();
|
||||
}
|
||||
|
||||
backend_glx->last_video_sync_count = video_sync_count;
|
||||
|
||||
/* reset the redraw clipping for the next paint... */
|
||||
stage_glx->initialized_redraw_clip = FALSE;
|
||||
|
||||
stage_glx->frame_count++;
|
||||
}
|
||||
|
@ -69,9 +69,6 @@ struct _ClutterStageGLXClass
|
||||
|
||||
GType _clutter_stage_glx_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx,
|
||||
ClutterStage *stage);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_STAGE_H__ */
|
||||
|
@ -191,30 +191,6 @@ clutter_backend_osx_ensure_context (ClutterBackend *backend,
|
||||
CLUTTER_OSX_POOL_RELEASE();
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_backend_osx_redraw (ClutterBackend *backend,
|
||||
ClutterStage *wrapper)
|
||||
{
|
||||
ClutterStageWindow *impl;
|
||||
ClutterStageOSX *stage_osx;
|
||||
|
||||
if (CLUTTER_ACTOR_IN_DESTRUCTION (wrapper))
|
||||
return;
|
||||
|
||||
impl = _clutter_stage_get_window (wrapper);
|
||||
if (impl == NULL)
|
||||
return;
|
||||
|
||||
stage_osx = CLUTTER_STAGE_OSX (impl);
|
||||
|
||||
CLUTTER_OSX_POOL_ALLOC();
|
||||
|
||||
if (stage_osx->view != NULL)
|
||||
[stage_osx->view setNeedsDisplay: YES];
|
||||
|
||||
CLUTTER_OSX_POOL_RELEASE();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
static void
|
||||
@ -267,7 +243,6 @@ clutter_backend_osx_class_init (ClutterBackendOSXClass *klass)
|
||||
backend_class->create_context = clutter_backend_osx_create_context;
|
||||
backend_class->ensure_context = clutter_backend_osx_ensure_context;
|
||||
backend_class->init_events = clutter_backend_osx_init_events;
|
||||
backend_class->redraw = clutter_backend_osx_redraw;
|
||||
backend_class->get_device_manager = clutter_backend_osx_get_device_manager;
|
||||
}
|
||||
|
||||
|
@ -523,6 +523,19 @@ clutter_stage_osx_set_accept_focus (ClutterStageWindow *stage_window,
|
||||
CLUTTER_OSX_POOL_RELEASE();
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_osx_redraw (ClutterStageWindow *stage_window)
|
||||
{
|
||||
ClutterStageOSX *stage_osx = CLUTTER_STAGE_OSX (stage_window);
|
||||
|
||||
CLUTTER_OSX_POOL_ALLOC();
|
||||
|
||||
if (stage_osx->view != NULL)
|
||||
[stage_osx->view setNeedsDisplay: YES];
|
||||
|
||||
CLUTTER_OSX_POOL_RELEASE();
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
||||
{
|
||||
@ -538,6 +551,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
||||
iface->set_cursor_visible = clutter_stage_osx_set_cursor_visible;
|
||||
iface->set_user_resizable = clutter_stage_osx_set_user_resizable;
|
||||
iface->set_accept_focus = clutter_stage_osx_set_accept_focus;
|
||||
iface->redraw = clutter_stage_osx_redraw;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
@ -478,32 +478,6 @@ clutter_backend_win32_ensure_context (ClutterBackend *backend,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_backend_win32_redraw (ClutterBackend *backend,
|
||||
ClutterStage *stage)
|
||||
{
|
||||
ClutterStageWin32 *stage_win32;
|
||||
ClutterStageWindow *impl;
|
||||
|
||||
impl = _clutter_stage_get_window (stage);
|
||||
if (impl == NULL)
|
||||
return;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_STAGE_WIN32 (impl));
|
||||
|
||||
stage_win32 = CLUTTER_STAGE_WIN32 (impl);
|
||||
|
||||
/* this will cause the stage implementation to be painted */
|
||||
_clutter_stage_do_paint (stage, NULL);
|
||||
cogl_flush ();
|
||||
|
||||
if (stage_win32->client_dc)
|
||||
{
|
||||
SwapBuffers (stage_win32->client_dc);
|
||||
_cogl_swap_buffers_notify ();
|
||||
}
|
||||
}
|
||||
|
||||
static ClutterStageWindow *
|
||||
clutter_backend_win32_create_stage (ClutterBackend *backend,
|
||||
ClutterStage *wrapper,
|
||||
@ -580,7 +554,6 @@ clutter_backend_win32_class_init (ClutterBackendWin32Class *klass)
|
||||
backend_class->create_stage = clutter_backend_win32_create_stage;
|
||||
backend_class->add_options = clutter_backend_win32_add_options;
|
||||
backend_class->get_features = clutter_backend_win32_get_features;
|
||||
backend_class->redraw = clutter_backend_win32_redraw;
|
||||
backend_class->create_context = clutter_backend_win32_create_context;
|
||||
backend_class->ensure_context = clutter_backend_win32_ensure_context;
|
||||
backend_class->get_device_manager = clutter_backend_win32_get_device_manager;
|
||||
|
@ -518,6 +518,22 @@ clutter_stage_win32_unrealize (ClutterStageWindow *stage_window)
|
||||
clutter_stage_win32_unprepare_window (stage_win32);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_win32_redraw (ClutterStageWindow *stage_window)
|
||||
{
|
||||
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (stage_window);
|
||||
|
||||
/* this will cause the stage implementation to be painted */
|
||||
_clutter_stage_do_paint (stage_win32->wrapper, NULL);
|
||||
cogl_flush ();
|
||||
|
||||
if (stage_win32->client_dc)
|
||||
{
|
||||
SwapBuffers (stage_win32->client_dc);
|
||||
_cogl_swap_buffers_notify ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_stage_win32_dispose (GObject *gobject)
|
||||
{
|
||||
@ -575,6 +591,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
||||
iface->get_geometry = clutter_stage_win32_get_geometry;
|
||||
iface->realize = clutter_stage_win32_realize;
|
||||
iface->unrealize = clutter_stage_win32_unrealize;
|
||||
iface->redraw = clutter_stage_win32_redraw;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user