2007-03-22 18:21:59 +00:00
|
|
|
/* Clutter.
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
|
|
* Copyright (C) 2006-2007 OpenedHand
|
|
|
|
*
|
|
|
|
* 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
|
2010-03-01 12:56:10 +00:00
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*
|
2007-03-22 18:21:59 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "clutter-backend-glx.h"
|
|
|
|
#include "clutter-stage-glx.h"
|
|
|
|
#include "clutter-glx.h"
|
2009-11-30 17:47:55 +00:00
|
|
|
#include "clutter-profile.h"
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2010-10-21 11:16:05 +00:00
|
|
|
#include "clutter-actor-private.h"
|
2010-10-21 10:29:09 +00:00
|
|
|
#include "clutter-debug.h"
|
event/x11: Rework the way we translate X11 events
This is a lump commit that is fairly difficult to break down without
either breaking bisecting or breaking the test cases.
The new design for handling X11 event translation works this way:
- ClutterBackend::translate_event() has been added as the central
point used by a ClutterBackend implementation to translate a
native event into a ClutterEvent;
- ClutterEventTranslator is a private interface that should be
implemented by backend-specific objects, like stage
implementations and ClutterDeviceManager sub-classes, and
allows dealing with class-specific event translation;
- ClutterStageX11 implements EventTranslator, and deals with the
stage-relative X11 events coming from the X11 event source;
- ClutterStageGLX overrides EventTranslator, in order to
deal with the INTEL_GLX_swap_event extension, and it chains up
to the X11 default implementation;
- ClutterDeviceManagerX11 has been split into two separate classes,
one that deals with core and (optionally) XI1 events, and the
other that deals with XI2 events; the selection is done at run-time,
since the core+XI1 and XI2 mechanisms are mutually exclusive.
All the other backends we officially support still use their own
custom event source and translation function, but the end goal is to
migrate them to the translate_event() virtual function, and have the
event source be a shared part of Clutter core.
2011-01-04 12:32:04 +00:00
|
|
|
#include "clutter-device-manager.h"
|
2010-10-21 10:29:09 +00:00
|
|
|
#include "clutter-event.h"
|
|
|
|
#include "clutter-enum-types.h"
|
|
|
|
#include "clutter-feature.h"
|
|
|
|
#include "clutter-main.h"
|
|
|
|
#include "clutter-private.h"
|
|
|
|
#include "clutter-stage-private.h"
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2008-04-25 13:37:36 +00:00
|
|
|
#include "cogl/cogl.h"
|
2007-04-27 21:13:06 +00:00
|
|
|
|
2007-03-22 18:21:59 +00:00
|
|
|
#include <GL/glx.h>
|
|
|
|
#include <GL/gl.h>
|
|
|
|
|
2009-11-30 17:47:55 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2010-08-09 10:29:03 +00:00
|
|
|
#ifdef HAVE_DRM
|
|
|
|
#include <drm.h>
|
|
|
|
#endif
|
2009-11-30 17:47:55 +00:00
|
|
|
|
event/x11: Rework the way we translate X11 events
This is a lump commit that is fairly difficult to break down without
either breaking bisecting or breaking the test cases.
The new design for handling X11 event translation works this way:
- ClutterBackend::translate_event() has been added as the central
point used by a ClutterBackend implementation to translate a
native event into a ClutterEvent;
- ClutterEventTranslator is a private interface that should be
implemented by backend-specific objects, like stage
implementations and ClutterDeviceManager sub-classes, and
allows dealing with class-specific event translation;
- ClutterStageX11 implements EventTranslator, and deals with the
stage-relative X11 events coming from the X11 event source;
- ClutterStageGLX overrides EventTranslator, in order to
deal with the INTEL_GLX_swap_event extension, and it chains up
to the X11 default implementation;
- ClutterDeviceManagerX11 has been split into two separate classes,
one that deals with core and (optionally) XI1 events, and the
other that deals with XI2 events; the selection is done at run-time,
since the core+XI1 and XI2 mechanisms are mutually exclusive.
All the other backends we officially support still use their own
custom event source and translation function, but the end goal is to
migrate them to the translate_event() virtual function, and have the
event source be a shared part of Clutter core.
2011-01-04 12:32:04 +00:00
|
|
|
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
|
2008-04-04 15:02:11 +00:00
|
|
|
|
2011-01-21 10:49:12 +00:00
|
|
|
static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
|
|
|
|
|
|
|
|
#define clutter_stage_glx_get_type _clutter_stage_glx_get_type
|
2009-08-13 11:34:07 +00:00
|
|
|
|
2008-04-04 15:02:11 +00:00
|
|
|
G_DEFINE_TYPE_WITH_CODE (ClutterStageGLX,
|
2011-01-21 10:49:12 +00:00
|
|
|
clutter_stage_glx,
|
2008-04-04 15:02:11 +00:00
|
|
|
CLUTTER_TYPE_STAGE_X11,
|
|
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
|
2010-11-05 12:28:33 +00:00
|
|
|
clutter_stage_window_iface_init));
|
2007-03-22 18:21:59 +00:00
|
|
|
|
|
|
|
static void
|
2009-08-13 11:34:07 +00:00
|
|
|
clutter_stage_glx_unrealize (ClutterStageWindow *stage_window)
|
2007-03-22 18:21:59 +00:00
|
|
|
{
|
2010-01-14 14:03:23 +00:00
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2008-04-04 15:02:11 +00:00
|
|
|
/* Note unrealize should free up any backend stage related resources */
|
event/x11: Rework the way we translate X11 events
This is a lump commit that is fairly difficult to break down without
either breaking bisecting or breaking the test cases.
The new design for handling X11 event translation works this way:
- ClutterBackend::translate_event() has been added as the central
point used by a ClutterBackend implementation to translate a
native event into a ClutterEvent;
- ClutterEventTranslator is a private interface that should be
implemented by backend-specific objects, like stage
implementations and ClutterDeviceManager sub-classes, and
allows dealing with class-specific event translation;
- ClutterStageX11 implements EventTranslator, and deals with the
stage-relative X11 events coming from the X11 event source;
- ClutterStageGLX overrides EventTranslator, in order to
deal with the INTEL_GLX_swap_event extension, and it chains up
to the X11 default implementation;
- ClutterDeviceManagerX11 has been split into two separate classes,
one that deals with core and (optionally) XI1 events, and the
other that deals with XI2 events; the selection is done at run-time,
since the core+XI1 and XI2 mechanisms are mutually exclusive.
All the other backends we officially support still use their own
custom event source and translation function, but the end goal is to
migrate them to the translate_event() virtual function, and have the
event source be a shared part of Clutter core.
2011-01-04 12:32:04 +00:00
|
|
|
CLUTTER_NOTE (BACKEND, "Unrealizing GLX stage [%p]", stage_glx);
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2011-04-14 14:44:22 +00:00
|
|
|
/* chain up to the StageX11 implementation */
|
|
|
|
clutter_stage_window_parent_iface->unrealize (stage_window);
|
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
cogl_object_unref (stage_glx->onscreen);
|
|
|
|
stage_glx->onscreen = NULL;
|
|
|
|
}
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
static void
|
|
|
|
handle_swap_complete_cb (CoglFramebuffer *framebuffer,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
ClutterStageGLX *stage_glx = user_data;
|
2007-07-22 22:40:18 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
/* 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_glx->pending_swaps > 0)
|
|
|
|
stage_glx->pending_swaps--;
|
2007-03-22 18:21:59 +00:00
|
|
|
}
|
|
|
|
|
2009-08-13 11:34:07 +00:00
|
|
|
static gboolean
|
|
|
|
clutter_stage_glx_realize (ClutterStageWindow *stage_window)
|
2007-03-22 18:21:59 +00:00
|
|
|
{
|
2010-03-17 17:41:52 +00:00
|
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
2010-11-05 12:28:33 +00:00
|
|
|
ClutterBackend *backend;
|
|
|
|
CoglFramebuffer *framebuffer;
|
|
|
|
GError *error = NULL;
|
|
|
|
gfloat width;
|
|
|
|
gfloat height;
|
|
|
|
const char *clutter_vblank;
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2009-05-11 11:36:14 +00:00
|
|
|
CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]",
|
2009-08-13 11:34:07 +00:00
|
|
|
G_OBJECT_TYPE_NAME (stage_window),
|
|
|
|
stage_window);
|
2007-03-22 18:21:59 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
backend = CLUTTER_BACKEND (stage_x11->backend);
|
2008-01-31 23:10:30 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper), &width, &height);
|
2011-02-14 12:00:31 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
stage_glx->onscreen = cogl_onscreen_new (backend->cogl_context,
|
|
|
|
width, height);
|
|
|
|
if (stage_x11->xwin != None)
|
|
|
|
cogl_onscreen_x11_set_foreign_window_xid (stage_glx->onscreen,
|
|
|
|
stage_x11->xwin);
|
2010-01-14 14:03:23 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
clutter_vblank = _clutter_backend_glx_get_vblank ();
|
|
|
|
if (clutter_vblank && strcmp (clutter_vblank, "none") == 0)
|
|
|
|
cogl_onscreen_set_swap_throttled (stage_glx->onscreen, FALSE);
|
2010-01-14 14:03:23 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
framebuffer = COGL_FRAMEBUFFER (stage_glx->onscreen);
|
|
|
|
if (!cogl_framebuffer_allocate (framebuffer, &error))
|
glx: Unconditionally select X11/GLX events
Currently, we select input events and GLX events conditionally,
depending on whether the user has disabled event retrieval.
We should, instead, unconditionally select input events even with event
retrieval disabled because we need to guarantee that the Clutter
internal state is maintained when calling clutter_x11_handle_event()
without requiring applications or embedding toolkits to select events
themselves. If we did that, we'd have to document the events to be
selected, and also update applications and embedding toolkits each time
we added a new mask, or a new class of events - something that's clearly
not possible.
See:
http://bugzilla.clutter-project.org/show_bug.cgi?id=998
for the rationale of why we did conditional selection. It is now clear
that a compositor should clear out the input region, since it cannot
assume a perfectly clean slate coming from us.
See:
http://bugzilla.clutter-project.org/show_bug.cgi?id=2228
for an example of things that break if we do conditional event
selection on GLX events. In that specific case, the X11 server ≤ 1.8
always pushed GLX events on the queue, even without selecting them; this
has been fixed in the X11 server ≥ 1.9, which means that applications
like Mutter or toolkit integration libraries like Clutter-GTK would stop
working on recent Intel drivers providing the GLX_INTEL_swap_event
extension.
This change has been tested with Mutter and Clutter-GTK.
2010-08-03 15:11:52 +00:00
|
|
|
{
|
2010-11-05 12:28:33 +00:00
|
|
|
g_warning ("Failed to allocate stage: %s", error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
cogl_object_unref (stage_glx->onscreen);
|
|
|
|
stage_glx->onscreen = NULL;
|
|
|
|
return FALSE;
|
|
|
|
}
|
glx: Unconditionally select X11/GLX events
Currently, we select input events and GLX events conditionally,
depending on whether the user has disabled event retrieval.
We should, instead, unconditionally select input events even with event
retrieval disabled because we need to guarantee that the Clutter
internal state is maintained when calling clutter_x11_handle_event()
without requiring applications or embedding toolkits to select events
themselves. If we did that, we'd have to document the events to be
selected, and also update applications and embedding toolkits each time
we added a new mask, or a new class of events - something that's clearly
not possible.
See:
http://bugzilla.clutter-project.org/show_bug.cgi?id=998
for the rationale of why we did conditional selection. It is now clear
that a compositor should clear out the input region, since it cannot
assume a perfectly clean slate coming from us.
See:
http://bugzilla.clutter-project.org/show_bug.cgi?id=2228
for an example of things that break if we do conditional event
selection on GLX events. In that specific case, the X11 server ≤ 1.8
always pushed GLX events on the queue, even without selecting them; this
has been fixed in the X11 server ≥ 1.9, which means that applications
like Mutter or toolkit integration libraries like Clutter-GTK would stop
working on recent Intel drivers providing the GLX_INTEL_swap_event
extension.
This change has been tested with Mutter and Clutter-GTK.
2010-08-03 15:11:52 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
if (stage_x11->xwin == None)
|
|
|
|
stage_x11->xwin = cogl_onscreen_x11_get_window_xid (stage_glx->onscreen);
|
|
|
|
|
|
|
|
if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT))
|
|
|
|
{
|
|
|
|
stage_glx->swap_callback_id =
|
|
|
|
cogl_framebuffer_add_swap_buffers_callback (framebuffer,
|
|
|
|
handle_swap_complete_cb,
|
|
|
|
stage_glx);
|
2007-03-22 18:21:59 +00:00
|
|
|
}
|
2009-05-13 21:21:48 +00:00
|
|
|
|
2009-08-13 11:34:07 +00:00
|
|
|
/* chain up to the StageX11 implementation */
|
2011-01-21 10:49:12 +00:00
|
|
|
return clutter_stage_window_parent_iface->realize (stage_window);
|
2007-03-22 18:21:59 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 20:37:01 +00:00
|
|
|
static int
|
|
|
|
clutter_stage_glx_get_pending_swaps (ClutterStageWindow *stage_window)
|
|
|
|
{
|
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
|
|
|
|
|
|
|
return stage_glx->pending_swaps;
|
|
|
|
}
|
|
|
|
|
2007-03-22 18:21:59 +00:00
|
|
|
static void
|
2011-01-21 10:49:12 +00:00
|
|
|
clutter_stage_glx_class_init (ClutterStageGLXClass *klass)
|
2007-03-22 18:21:59 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-01-21 10:49:12 +00:00
|
|
|
clutter_stage_glx_init (ClutterStageGLX *stage)
|
2007-03-22 18:21:59 +00:00
|
|
|
{
|
2008-04-04 15:02:11 +00:00
|
|
|
}
|
|
|
|
|
2009-11-30 17:47:55 +00:00
|
|
|
static gboolean
|
|
|
|
clutter_stage_glx_has_redraw_clips (ClutterStageWindow *stage_window)
|
|
|
|
{
|
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
|
|
|
|
2010-11-23 16:05:44 +00:00
|
|
|
/* NB: at the start of each new frame there is an implied clip that
|
|
|
|
* clips everything (i.e. nothing would be drawn) so we need to make
|
|
|
|
* sure we return True in the un-initialized case here.
|
|
|
|
*
|
|
|
|
* NB: a clip width of 0 means a full stage redraw has been queued
|
|
|
|
* so we effectively don't have any redraw clips in that case.
|
|
|
|
*/
|
|
|
|
if (!stage_glx->initialized_redraw_clip ||
|
|
|
|
(stage_glx->initialized_redraw_clip &&
|
|
|
|
stage_glx->bounding_redraw_clip.width != 0))
|
2009-11-30 17:47:55 +00:00
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
clutter_stage_glx_ignoring_redraw_clips (ClutterStageWindow *stage_window)
|
|
|
|
{
|
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
|
|
|
|
2010-11-23 16:05:44 +00:00
|
|
|
/* NB: a clip width of 0 means a full stage redraw is required */
|
2009-11-30 17:47:55 +00:00
|
|
|
if (stage_glx->initialized_redraw_clip &&
|
|
|
|
stage_glx->bounding_redraw_clip.width == 0)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A redraw clip represents (in stage coordinates) the bounding box of
|
|
|
|
* something that needs to be redraw. Typically they are added to the
|
|
|
|
* StageWindow as a result of clutter_actor_queue_clipped_redraw() by
|
|
|
|
* actors such as ClutterGLXTexturePixmap. All redraw clips are
|
|
|
|
* discarded after the next paint.
|
|
|
|
*
|
|
|
|
* A NULL stage_clip means the whole stage needs to be redrawn.
|
|
|
|
*
|
|
|
|
* What we do with this information:
|
|
|
|
* - we keep track of the bounding box for all redraw clips
|
|
|
|
* - when we come to redraw; if the bounding box is smaller than the
|
|
|
|
* stage we scissor the redraw to that box and use
|
|
|
|
* GLX_MESA_copy_sub_buffer to present the redraw to the front
|
2011-02-14 12:00:31 +00:00
|
|
|
* buffer.
|
2009-11-30 17:47:55 +00:00
|
|
|
*
|
2011-02-14 12:00:31 +00:00
|
|
|
* XXX - In theory, we should have some sort of heuristics to promote
|
|
|
|
* a clipped redraw to a full screen redraw; in reality, it turns out
|
|
|
|
* that promotion is fairly expensive. See the Clutter bug described
|
|
|
|
* at: http://bugzilla.clutter-project.org/show_bug.cgi?id=2136 .
|
2009-11-30 17:47:55 +00:00
|
|
|
*
|
|
|
|
* TODO - we should use different heuristics depending on whether the
|
|
|
|
* framebuffer is on screen and not redirected by a compositor VS
|
|
|
|
* offscreen (either due to compositor redirection or because we are
|
|
|
|
* rendering to a CoglOffscreen framebuffer)
|
|
|
|
*
|
|
|
|
* When not redirected glXCopySubBuffer (on intel hardware at least)
|
|
|
|
* will block the GPU until the vertical trace is at the optimal point
|
|
|
|
* so the copy can be done without tearing. In this case we don't want
|
|
|
|
* to copy tall regions because they increase the average time spent
|
|
|
|
* blocking the GPU.
|
|
|
|
*
|
|
|
|
* When rendering offscreen (CoglOffscreen or redirected by
|
|
|
|
* compositor) then no extra synchronization is needed before the copy
|
|
|
|
* can start.
|
|
|
|
*
|
|
|
|
* In all cases we need to consider that glXCopySubBuffer implies a
|
|
|
|
* blit which may be avoided by promoting to a full stage redraw if:
|
|
|
|
* - the framebuffer is redirected offscreen or a CoglOffscreen.
|
|
|
|
* - the framebuffer is onscreen and fullscreen.
|
|
|
|
* By promoting to a full stage redraw we trade off the cost involved
|
|
|
|
* in rasterizing the extra pixels vs avoiding to use a blit to
|
|
|
|
* present the back buffer.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clutter_stage_glx_add_redraw_clip (ClutterStageWindow *stage_window,
|
|
|
|
ClutterGeometry *stage_clip)
|
|
|
|
{
|
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
|
|
|
|
|
|
|
/* If we are already forced to do a full stage redraw then bail early */
|
|
|
|
if (clutter_stage_glx_ignoring_redraw_clips (stage_window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* A NULL stage clip means a full stage redraw has been queued and
|
2010-11-23 16:05:44 +00:00
|
|
|
* we keep track of this by setting a zero width
|
2009-11-30 17:47:55 +00:00
|
|
|
* stage_glx->bounding_redraw_clip */
|
2010-03-19 11:39:23 +00:00
|
|
|
if (stage_clip == NULL)
|
2009-11-30 17:47:55 +00:00
|
|
|
{
|
|
|
|
stage_glx->bounding_redraw_clip.width = 0;
|
2010-09-08 00:41:01 +00:00
|
|
|
stage_glx->initialized_redraw_clip = TRUE;
|
2009-11-30 17:47:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-08 00:41:01 +00:00
|
|
|
/* Ignore requests to add degenerate/empty clip rectangles */
|
2010-03-19 18:18:17 +00:00
|
|
|
if (stage_clip->width == 0 || stage_clip->height == 0)
|
|
|
|
return;
|
|
|
|
|
2009-11-30 17:47:55 +00:00
|
|
|
if (!stage_glx->initialized_redraw_clip)
|
|
|
|
{
|
|
|
|
stage_glx->bounding_redraw_clip.x = stage_clip->x;
|
|
|
|
stage_glx->bounding_redraw_clip.y = stage_clip->y;
|
|
|
|
stage_glx->bounding_redraw_clip.width = stage_clip->width;
|
|
|
|
stage_glx->bounding_redraw_clip.height = stage_clip->height;
|
|
|
|
}
|
2010-03-18 17:55:01 +00:00
|
|
|
else if (stage_glx->bounding_redraw_clip.width > 0)
|
2009-11-30 17:47:55 +00:00
|
|
|
{
|
2011-02-14 12:00:31 +00:00
|
|
|
clutter_geometry_union (&stage_glx->bounding_redraw_clip,
|
|
|
|
stage_clip,
|
2010-03-18 17:55:01 +00:00
|
|
|
&stage_glx->bounding_redraw_clip);
|
2009-11-30 17:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
redraw_area = (stage_glx->bounding_redraw_clip.width *
|
|
|
|
stage_glx->bounding_redraw_clip.height);
|
|
|
|
stage_area = stage_x11->xwin_width * stage_x11->xwin_height;
|
|
|
|
|
|
|
|
/* Redrawing and blitting >70% of the stage is assumed to be more
|
|
|
|
* expensive than redrawing the additional 30% to avoid the blit.
|
|
|
|
*
|
|
|
|
* FIXME: This threshold was plucked out of thin air!
|
2011-02-14 12:00:31 +00:00
|
|
|
*
|
|
|
|
* The threshold has been disabled after verifying that it indeed
|
|
|
|
* made redraws more expensive than intended; see bug reference:
|
|
|
|
*
|
|
|
|
* http://bugzilla.clutter-project.org/show_bug.cgi?id=2136
|
2009-11-30 17:47:55 +00:00
|
|
|
*/
|
|
|
|
if (redraw_area > (stage_area * 0.7f))
|
|
|
|
{
|
|
|
|
g_print ("DEBUG: clipped redraw too big, forcing full redraw\n");
|
2010-11-23 16:05:44 +00:00
|
|
|
/* Set a zero width clip to force a full redraw */
|
2009-11-30 17:47:55 +00:00
|
|
|
stage_glx->bounding_redraw_clip.width = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
stage_glx->initialized_redraw_clip = TRUE;
|
|
|
|
}
|
|
|
|
|
2011-02-04 15:09:41 +00:00
|
|
|
static void
|
|
|
|
clutter_stage_glx_redraw (ClutterStageWindow *stage_window)
|
2009-11-30 17:47:55 +00:00
|
|
|
{
|
|
|
|
ClutterBackendGLX *backend_glx;
|
2011-02-04 15:09:41 +00:00
|
|
|
ClutterStageX11 *stage_x11;
|
|
|
|
ClutterStageGLX *stage_glx;
|
|
|
|
gboolean may_use_clipped_redraw;
|
|
|
|
gboolean use_clipped_redraw;
|
|
|
|
|
2009-11-30 17:47:55 +00:00
|
|
|
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 */);
|
2010-06-05 10:51:32 +00:00
|
|
|
CLUTTER_STATIC_TIMER (blit_sub_buffer_timer,
|
2009-11-30 17:47:55 +00:00
|
|
|
"Redrawing", /* parent */
|
2010-06-05 10:51:32 +00:00
|
|
|
"glx_blit_sub_buffer",
|
|
|
|
"The time spent in _glx_blit_sub_buffer",
|
2009-11-30 17:47:55 +00:00
|
|
|
0 /* no application private data */);
|
|
|
|
|
2011-02-04 15:09:41 +00:00
|
|
|
stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
2011-01-21 10:49:12 +00:00
|
|
|
if (stage_x11->xwin == None)
|
|
|
|
return;
|
|
|
|
|
2011-02-04 15:09:41 +00:00
|
|
|
stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
backend_glx = CLUTTER_BACKEND_GLX (stage_x11->backend);
|
2009-11-30 17:47:55 +00:00
|
|
|
|
|
|
|
CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer);
|
|
|
|
|
2010-09-07 22:07:52 +00:00
|
|
|
if (G_LIKELY (backend_glx->can_blit_sub_buffer) &&
|
2010-11-23 16:05:44 +00:00
|
|
|
/* NB: a zero width redraw clip == full stage redraw */
|
2010-09-08 00:15:00 +00:00
|
|
|
stage_glx->bounding_redraw_clip.width != 0 &&
|
2010-09-08 00:18:30 +00:00
|
|
|
/* some drivers struggle to get going and produce some junk
|
|
|
|
* frames when starting up... */
|
|
|
|
G_LIKELY (stage_glx->frame_count > 3) &&
|
2010-09-08 00:15:00 +00:00
|
|
|
/* 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))
|
2011-01-21 10:49:12 +00:00
|
|
|
{
|
|
|
|
may_use_clipped_redraw = TRUE;
|
|
|
|
}
|
2010-09-07 22:07:52 +00:00
|
|
|
else
|
|
|
|
may_use_clipped_redraw = FALSE;
|
|
|
|
|
|
|
|
if (may_use_clipped_redraw &&
|
2009-11-30 17:47:55 +00:00
|
|
|
G_LIKELY (!(clutter_paint_debug_flags &
|
|
|
|
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
|
2010-09-07 22:07:52 +00:00
|
|
|
use_clipped_redraw = TRUE;
|
|
|
|
else
|
|
|
|
use_clipped_redraw = FALSE;
|
|
|
|
|
|
|
|
if (use_clipped_redraw)
|
2009-11-30 17:47:55 +00:00
|
|
|
{
|
2011-02-01 18:32:08 +00:00
|
|
|
CLUTTER_NOTE (CLIPPING,
|
|
|
|
"Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
|
|
|
|
stage_glx->bounding_redraw_clip.x,
|
|
|
|
stage_glx->bounding_redraw_clip.y,
|
|
|
|
stage_glx->bounding_redraw_clip.width,
|
|
|
|
stage_glx->bounding_redraw_clip.height);
|
2009-11-30 17:47:55 +00:00
|
|
|
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);
|
2011-02-04 15:09:41 +00:00
|
|
|
_clutter_stage_do_paint (stage_x11->wrapper,
|
|
|
|
&stage_glx->bounding_redraw_clip);
|
2009-11-30 17:47:55 +00:00
|
|
|
cogl_clip_pop ();
|
|
|
|
}
|
|
|
|
else
|
2011-02-01 18:32:08 +00:00
|
|
|
{
|
|
|
|
CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n");
|
2011-03-15 12:08:46 +00:00
|
|
|
|
|
|
|
/* If we are trying to debug redraw issues then we want to pass
|
|
|
|
* the bounding_redraw_clip so it can be visualized */
|
|
|
|
if (G_UNLIKELY (clutter_paint_debug_flags &
|
|
|
|
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) &&
|
|
|
|
may_use_clipped_redraw)
|
|
|
|
{
|
|
|
|
_clutter_stage_do_paint (stage_x11->wrapper,
|
|
|
|
&stage_glx->bounding_redraw_clip);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_clutter_stage_do_paint (stage_x11->wrapper, NULL);
|
2011-02-01 18:32:08 +00:00
|
|
|
}
|
2009-11-30 17:47:55 +00:00
|
|
|
|
2011-02-04 15:09:41 +00:00
|
|
|
if (may_use_clipped_redraw &&
|
|
|
|
G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
|
2010-09-07 22:07:52 +00:00
|
|
|
{
|
|
|
|
static CoglMaterial *outline = NULL;
|
2011-02-04 15:09:41 +00:00
|
|
|
ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
|
|
|
|
ClutterActor *actor = CLUTTER_ACTOR (stage_x11->wrapper);
|
2010-09-07 22:07:52 +00:00
|
|
|
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);
|
2011-02-04 15:09:41 +00:00
|
|
|
_clutter_actor_apply_modelview_transform (actor, &modelview);
|
2010-09-07 22:07:52 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-11-30 17:47:55 +00:00
|
|
|
CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer);
|
|
|
|
|
|
|
|
/* push on the screen */
|
2010-09-07 22:07:52 +00:00
|
|
|
if (use_clipped_redraw)
|
2009-11-30 17:47:55 +00:00
|
|
|
{
|
|
|
|
ClutterGeometry *clip = &stage_glx->bounding_redraw_clip;
|
2010-11-05 12:28:33 +00:00
|
|
|
int copy_area[4];
|
2011-02-04 15:09:41 +00:00
|
|
|
ClutterActor *actor;
|
2009-11-30 17:47:55 +00:00
|
|
|
|
|
|
|
/* XXX: It seems there will be a race here in that the stage
|
2010-11-05 12:28:33 +00:00
|
|
|
* window may be resized before the cogl_framebuffer_swap_region
|
|
|
|
* 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.
|
2009-11-30 17:47:55 +00:00
|
|
|
*/
|
2010-11-05 12:28:33 +00:00
|
|
|
|
2011-02-04 15:09:41 +00:00
|
|
|
actor = CLUTTER_ACTOR (stage_x11->wrapper);
|
2011-04-01 17:36:56 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
copy_area[0] = clip->x;
|
|
|
|
copy_area[1] = clutter_actor_get_height (actor) - clip->y - clip->height;
|
|
|
|
copy_area[2] = clip->width;
|
|
|
|
copy_area[3] = clip->height;
|
2010-06-03 19:03:20 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
CLUTTER_NOTE (BACKEND,
|
|
|
|
"cogl_framebuffer_swap_region (onscreen: %p, "
|
|
|
|
"x: %d, y: %d, "
|
|
|
|
"width: %d, height: %d)",
|
|
|
|
stage_glx->onscreen,
|
|
|
|
copy_area[0], copy_area[1], copy_area[2], copy_area[3]);
|
2010-06-03 19:03:20 +00:00
|
|
|
|
2010-06-05 10:51:32 +00:00
|
|
|
CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer);
|
2010-11-05 12:28:33 +00:00
|
|
|
|
|
|
|
cogl_framebuffer_swap_region (COGL_FRAMEBUFFER (stage_glx->onscreen),
|
|
|
|
copy_area, 1);
|
|
|
|
|
2010-06-05 10:51:32 +00:00
|
|
|
CLUTTER_TIMER_STOP (_clutter_uprof_context, blit_sub_buffer_timer);
|
2009-11-30 17:47:55 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-11-05 12:28:33 +00:00
|
|
|
CLUTTER_NOTE (BACKEND, "cogl_framebuffer_swap_buffers (onscreen: %p)",
|
|
|
|
stage_glx->onscreen);
|
2009-11-30 17:47:55 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
/* If we have swap buffer events then
|
|
|
|
* cogl_framebuffer_swap_buffers will return immediately and we
|
|
|
|
* need to track that there is a swap in progress... */
|
2009-11-30 17:47:55 +00:00
|
|
|
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
|
|
|
|
stage_glx->pending_swaps++;
|
|
|
|
|
|
|
|
CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
|
2010-11-05 12:28:33 +00:00
|
|
|
cogl_framebuffer_swap_buffers (COGL_FRAMEBUFFER (stage_glx->onscreen));
|
2009-11-30 17:47:55 +00:00
|
|
|
CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset the redraw clipping for the next paint... */
|
|
|
|
stage_glx->initialized_redraw_clip = FALSE;
|
2010-09-08 00:18:30 +00:00
|
|
|
|
|
|
|
stage_glx->frame_count++;
|
2009-11-30 17:47:55 +00:00
|
|
|
}
|
2011-02-04 15:09:41 +00:00
|
|
|
|
2010-11-05 12:28:33 +00:00
|
|
|
static CoglFramebuffer *
|
|
|
|
clutter_stage_glx_get_active_framebuffer (ClutterStageWindow *stage_window)
|
|
|
|
{
|
|
|
|
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window);
|
|
|
|
|
|
|
|
return COGL_FRAMEBUFFER (stage_glx->onscreen);
|
|
|
|
}
|
|
|
|
|
2011-02-04 15:09:41 +00:00
|
|
|
static void
|
|
|
|
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
|
|
|
{
|
|
|
|
clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
|
|
|
|
|
|
|
|
iface->realize = clutter_stage_glx_realize;
|
|
|
|
iface->unrealize = clutter_stage_glx_unrealize;
|
|
|
|
iface->get_pending_swaps = clutter_stage_glx_get_pending_swaps;
|
|
|
|
|
|
|
|
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;
|
2010-11-05 12:28:33 +00:00
|
|
|
iface->get_active_framebuffer = clutter_stage_glx_get_active_framebuffer;
|
2011-02-04 15:09:41 +00:00
|
|
|
|
|
|
|
/* the rest is inherited from ClutterStageX11 */
|
|
|
|
}
|