diff options
-rw-r--r-- | cogl/Makefile.am | 5 | ||||
-rw-r--r-- | cogl/cogl-frame-info.h | 6 | ||||
-rw-r--r-- | cogl/cogl-framebuffer.h | 1 | ||||
-rw-r--r-- | cogl/cogl-mode-private.h | 58 | ||||
-rw-r--r-- | cogl/cogl-mode.c | 73 | ||||
-rw-r--r-- | cogl/cogl-mode.h | 51 | ||||
-rw-r--r-- | cogl/cogl-onscreen.h | 6 | ||||
-rw-r--r-- | cogl/cogl-output-private.h | 46 | ||||
-rw-r--r-- | cogl/cogl-output.c | 288 | ||||
-rw-r--r-- | cogl/cogl-output.h | 245 | ||||
-rw-r--r-- | cogl/cogl-overlay-private.h | 76 | ||||
-rw-r--r-- | cogl/cogl-overlay.c | 249 | ||||
-rw-r--r-- | cogl/cogl-overlay.h | 114 | ||||
-rw-r--r-- | cogl/cogl-renderer-private.h | 3 | ||||
-rw-r--r-- | cogl/cogl-renderer.c | 89 | ||||
-rw-r--r-- | cogl/cogl-renderer.h | 27 | ||||
-rw-r--r-- | cogl/cogl-xlib-renderer.c | 123 | ||||
-rw-r--r-- | cogl/cogl.h | 1 | ||||
-rw-r--r-- | cogl/winsys/cogl-winsys-egl-kms.c | 523 | ||||
-rw-r--r-- | cogl/winsys/cogl-winsys-private.h | 12 |
20 files changed, 1840 insertions, 156 deletions
diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 642e9c94..512b3c1c 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -59,6 +59,8 @@ DISTCLEANFILES += $(pc_files) # public api headers cogl_public_h = \ + $(srcdir)/cogl-mode.h \ + $(srcdir)/cogl-overlay.h \ $(srcdir)/cogl-attribute-buffer.h \ $(srcdir)/cogl-attribute.h \ $(srcdir)/cogl-bitmap.h \ @@ -365,6 +367,9 @@ cogl_sources_c = \ $(srcdir)/cogl-closure-list.c \ $(srcdir)/cogl-fence.c \ $(srcdir)/cogl-fence-private.h \ + $(srcdir)/cogl-overlay-private.h \ + $(srcdir)/cogl-overlay.c \ + $(srcdir)/cogl-mode.c \ $(NULL) if USE_GLIB diff --git a/cogl/cogl-frame-info.h b/cogl/cogl-frame-info.h index 36f8a046..f20d0853 100644 --- a/cogl/cogl-frame-info.h +++ b/cogl/cogl-frame-info.h @@ -31,13 +31,17 @@ #ifndef __COGL_FRAME_INFO_H #define __COGL_FRAME_INFO_H +/* We forward declare the CoglFrameInfo type here to avoid some + * circular dependency issues with the following headers. + */ +typedef struct _CoglFrameInfo CoglFrameInfo; + #include <cogl/cogl-types.h> #include <cogl/cogl-output.h> #include <glib.h> G_BEGIN_DECLS -typedef struct _CoglFrameInfo CoglFrameInfo; #define COGL_FRAME_INFO(X) ((CoglFrameInfo *)(X)) /** diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h index 2ad41f8f..8106095d 100644 --- a/cogl/cogl-framebuffer.h +++ b/cogl/cogl-framebuffer.h @@ -43,6 +43,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer; #include <cogl/cogl-texture.h> #include <cogl/cogl-quaternion.h> #include <cogl/cogl-euler.h> +#include <cogl/cogl-primitive.h> COGL_BEGIN_DECLS diff --git a/cogl/cogl-mode-private.h b/cogl/cogl-mode-private.h new file mode 100644 index 00000000..fddd1cad --- /dev/null +++ b/cogl/cogl-mode-private.h @@ -0,0 +1,58 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 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 _COGL_MODE_PRIVATE_H_ +#define _COGL_MODE_PRIVATE_H_ + +#include "cogl-mode.h" +#include "cogl-object-private.h" + +struct _CoglMode +{ + CoglObjectClass _parent; + + char *name; + + /* NB: _cogl_mode_equal() expects everything from the width member + * to the end of the struct to be comparable using memcmp(). + */ + int width; + int height; + + float refresh_rate; + +#if 0 + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t flags; + uint32_t type; +#endif +}; + +CoglMode * +_cogl_mode_new (const char *name); + +#endif /* _COGL_MODE_PRIVATE_H_ */ diff --git a/cogl/cogl-mode.c b/cogl/cogl-mode.c new file mode 100644 index 00000000..d742609f --- /dev/null +++ b/cogl/cogl-mode.c @@ -0,0 +1,73 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 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/>. + * + * + */ + +#include <config.h> + +#include "cogl-mode-private.h" +#include "cogl-object-private.h" + +static void _cogl_mode_free (CoglMode *mode); + +COGL_OBJECT_DEFINE (Mode, mode); + +static void +_cogl_mode_free (CoglMode *mode) +{ + g_free (mode->name); + g_slice_free (CoglMode, mode); +} + +CoglMode * +_cogl_mode_new (const char *name) +{ + CoglMode *mode = g_slice_new0 (CoglMode); + + mode->name = g_strdup (name); + + return _cogl_mode_object_new (mode); +} + +const char * +cogl_mode_get_name (CoglMode *mode) +{ + return mode->name; +} + +float +cogl_mode_get_refresh_rate (CoglMode *mode) +{ + return mode->refresh_rate; +} + +int +cogl_mode_get_width (CoglMode *mode) +{ + return mode->width; +} + +int +cogl_mode_get_height (CoglMode *mode) +{ + return mode->height; +} diff --git a/cogl/cogl-mode.h b/cogl/cogl-mode.h new file mode 100644 index 00000000..6d25f95b --- /dev/null +++ b/cogl/cogl-mode.h @@ -0,0 +1,51 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 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 _COGL_MODE_H_ +#define _COGL_MODE_H_ + +#include <cogl/cogl-types.h> + +COGL_BEGIN_DECLS + +typedef struct _CoglMode CoglMode; + +CoglBool +cogl_is_mode (void *object); + +const char * +cogl_mode_get_name (CoglMode *mode); + +float +cogl_mode_get_refresh_rate (CoglMode *mode); + +int +cogl_mode_get_width (CoglMode *mode); + +int +cogl_mode_get_height (CoglMode *mode); + +COGL_END_DECLS + +#endif /* _COGL_MODE_H_ */ diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h index d9582fa4..b0231584 100644 --- a/cogl/cogl-onscreen.h +++ b/cogl/cogl-onscreen.h @@ -32,6 +32,11 @@ #ifndef __COGL_ONSCREEN_H #define __COGL_ONSCREEN_H +/* We forward declare the CoglOnscreen type here to avoid some + * circular dependency issues with the following headers. + */ +typedef struct _CoglOnscreen CoglOnscreen; + #include <cogl/cogl-context.h> #include <cogl/cogl-framebuffer.h> #include <cogl/cogl-frame-info.h> @@ -39,7 +44,6 @@ COGL_BEGIN_DECLS -typedef struct _CoglOnscreen CoglOnscreen; #define COGL_ONSCREEN(X) ((CoglOnscreen *)(X)) /** diff --git a/cogl/cogl-output-private.h b/cogl/cogl-output-private.h index f0e8d57b..82bb4919 100644 --- a/cogl/cogl-output-private.h +++ b/cogl/cogl-output-private.h @@ -24,24 +24,51 @@ #ifndef __COGL_OUTPUT_PRIVATE_H #define __COGL_OUTPUT_PRIVATE_H +#include <glib.h> + #include "cogl-output.h" #include "cogl-object-private.h" -struct _CoglOutput +#warning "TODO: remove if not used in the end" +typedef enum _CoglOutputChange { - CoglObject _parent; + COGL_OUTPUT_CHANGE_OVERLAYS = 1<<0, + COGL_OUTPUT_CHANGE_MODE = 1<<1, +} CoglOutputChange; +typedef struct _CoglOutputState +{ char *name; - int x; /* Must be first field for _cogl_output_values_equal() */ + GList *overlays; + + CoglMode *mode; + + /* x must be first field for _cogl_output_state_equal() + * and all following members should be comparable using + * memcmp() */ + int x; int y; - int width; - int height; int mm_width; int mm_height; - float refresh_rate; CoglSubpixelOrder subpixel_order; + CoglDpmsMode dpms_mode; + +#warning "TODO: remove if not used in the end" + CoglOutputChange changes; + +} CoglOutputState; + +struct _CoglOutput +{ + CoglObject _parent; + + GList *modes; + + CoglOutputState *pending; + CoglOutputState *state; + void *winsys; CoglUserDataDestroyCallback winsys_destroy_callback; }; @@ -54,8 +81,11 @@ _cogl_output_set_winsys_data (CoglOutput *output, void *winsys, CoglUserDataDestroyCallback destroy_callback); +void +_cogl_output_update_state (CoglOutput *output); + CoglBool -_cogl_output_values_equal (CoglOutput *output, - CoglOutput *other); +_cogl_output_equal (CoglOutput *output, + CoglOutput *other); #endif /* __COGL_OUTPUT_PRIVATE_H */ diff --git a/cogl/cogl-output.c b/cogl/cogl-output.c index bf2ba217..cd79f5a2 100644 --- a/cogl/cogl-output.c +++ b/cogl/cogl-output.c @@ -24,6 +24,8 @@ #include <config.h> #include "cogl-output-private.h" +#include "cogl-overlay-private.h" +#include "cogl-mode-private.h" #include <string.h> @@ -37,18 +39,40 @@ _cogl_output_new (const char *name) CoglOutput *output; output = g_slice_new0 (CoglOutput); - output->name = g_strdup (name); + + output->state = g_slice_new0 (CoglOutputState); + output->state->name = g_strdup (name); + + output->pending = output->state; return _cogl_output_object_new (output); } static void +free_output_state (CoglOutputState *state) +{ + GList *l; + + for (l = state->overlays; l; l = l->next) + cogl_object_unref (l->data); + g_list_free (state->overlays); + + g_free (state->name); + + g_slice_free (CoglOutputState, state); +} + +static void _cogl_output_free (CoglOutput *output) { if (output->winsys_destroy_callback) output->winsys_destroy_callback (output->winsys); - g_free (output->name); + if (output->pending != output->state) + free_output_state (output->pending); + + free_output_state (output->state); + g_slice_free (CoglOutput, output); } @@ -61,59 +85,291 @@ _cogl_output_set_winsys_data (CoglOutput *output, output->winsys_destroy_callback = destroy_callback; } -gboolean -_cogl_output_values_equal (CoglOutput *output, - CoglOutput *other) +static CoglBool +_cogl_mode_equal (CoglMode *mode0, CoglMode *mode1) +{ + if (memcmp ((const char *)mode0 + G_STRUCT_OFFSET (CoglMode, width), + (const char *)mode1 + G_STRUCT_OFFSET (CoglMode, width), + sizeof (CoglMode) - G_STRUCT_OFFSET (CoglMode, width)) != 0) + return FALSE; + + return TRUE; +} + +static CoglBool +_cogl_mode_lists_equal (GList *modes0, GList *modes1) { - return memcmp ((const char *)output + G_STRUCT_OFFSET (CoglOutput, x), - (const char *)other + G_STRUCT_OFFSET (CoglOutput, x), - sizeof (CoglOutput) - G_STRUCT_OFFSET (CoglOutput, x)) == 0; + GList *l, *m; + + for (l = modes0, m = modes1; + l && m; + l = l->next, m = m->next) + { + if (!_cogl_mode_equal (l->data, m->data)) + return FALSE; + } + + if (l || m) + return FALSE; + + return TRUE; +} + +CoglBool +_cogl_output_equal (CoglOutput *output, + CoglOutput *other) +{ + GList *l, *l2; + + if (output == other) + return TRUE; + + if (!_cogl_mode_lists_equal (output->modes, other->modes)) + return FALSE; + + if (!_cogl_mode_equal (output->pending->mode, other->pending->mode)) + return FALSE; + + if (memcmp ((const char *)output->pending + + G_STRUCT_OFFSET (CoglOutputState, x), + (const char *)other->pending + + G_STRUCT_OFFSET (CoglOutputState, x), + sizeof (CoglOutputState) - + G_STRUCT_OFFSET (CoglOutputState, x)) != 0) + return FALSE; + + for (l = output->pending->overlays, l2 = other->pending->overlays; + l && l2; + l = l->next, l2 = l2->next) + { + if (!_cogl_overlay_equal (l->data, l2->data)) + return FALSE; + } + + if (l || l2) + return FALSE; + + return TRUE; } int cogl_output_get_x (CoglOutput *output) { - return output->x; + return output->pending->x; } int cogl_output_get_y (CoglOutput *output) { - return output->y; + return output->pending->y; } int cogl_output_get_width (CoglOutput *output) { - return output->width; + return output->pending->mode->width; } int cogl_output_get_height (CoglOutput *output) { - return output->height; + return output->pending->mode->height; } int cogl_output_get_mm_width (CoglOutput *output) { - return output->mm_width; + return output->pending->mm_width; } int cogl_output_get_mm_height (CoglOutput *output) { - return output->mm_height; + return output->pending->mm_height; } CoglSubpixelOrder cogl_output_get_subpixel_order (CoglOutput *output) { - return output->subpixel_order; + return output->pending->subpixel_order; } float cogl_output_get_refresh_rate (CoglOutput *output) { - return output->refresh_rate; + return output->pending->mode->refresh_rate; +} + +CoglOverlay * +cogl_output_get_overlay0 (CoglOutput *output) +{ + return output->pending->overlays->data; +} + +static void +ensure_pending_state (CoglOutput *output) +{ + CoglOutputState *state = output->state; + CoglOutputState *pending; + + if (output->pending != state) + return; + + pending = g_slice_dup (CoglOutputState, state); + pending->name = g_strdup (state->name); + pending->overlays = g_list_copy (state->overlays); + + output->pending = state; +} + +void +_cogl_output_update_state (CoglOutput *output) +{ + if (output->pending == output->state) + return; + + free_output_state (output->state); + + output->state = output->pending; +} + +void +cogl_output_append_overlay (CoglOutput *output, + CoglOverlay *overlay) +{ + ensure_pending_state (output); + + output->pending->overlays = + g_list_append (output->pending->overlays, overlay); +} + +void +cogl_output_put_overlay_above (CoglOutput *output, + CoglOverlay *overlay, + CoglOverlay *sibling) +{ + GList *l; + + ensure_pending_state (output); + + if (sibling) + { + l = g_list_find (output->pending->overlays, sibling); + + _COGL_RETURN_IF_FAIL (l != NULL); + + l = l->next; + } + else + l = NULL; + + output->pending->overlays = + g_list_insert_before (output->pending->overlays, + l, + overlay); +} + +void +cogl_output_put_overlay_below (CoglOutput *output, + CoglOverlay *overlay, + CoglOverlay *sibling) +{ + GList *l; + + ensure_pending_state (output); + + if (sibling) + { + l = g_list_find (output->pending->overlays, sibling); + + _COGL_RETURN_IF_FAIL (l != NULL); + } + else + l = NULL; + + output->pending->overlays = + g_list_insert_before (output->pending->overlays, + l, + overlay); +} + +void +cogl_output_remove_overlay (CoglOutput *output, + CoglOverlay *overlay) +{ + GList *l; + + ensure_pending_state (output); + + l = g_list_find (output->pending->overlays, overlay); + + _COGL_RETURN_IF_FAIL (l != NULL); + + cogl_object_unref (l->data); + + output->pending->overlays = + g_list_delete_link (output->pending->overlays, l); +} + +void +cogl_output_foreach_mode (CoglOutput *output, + CoglOutputModeCallback callback, + void *user_data) +{ + GList *l; + for (l = output->modes; l; l = l->next) + callback (l->data, user_data); +} + +void +cogl_output_set_mode (CoglOutput *output, + CoglMode *mode) +{ + _COGL_RETURN_IF_FAIL (mode); + + if (_cogl_mode_equal (output->pending->mode, mode)) + return; + + ensure_pending_state (output); + + if (output->pending->mode) + cogl_object_unref (output->pending->mode); + + output->pending->mode = cogl_object_ref (mode); +} + +CoglMode * +cogl_output_get_mode (CoglOutput *output) +{ + _COGL_RETURN_VAL_IF_FAIL (output->pending->mode, NULL); + + return output->pending->mode; +} + +void +cogl_output_set_dpms_mode (CoglOutput *output, + CoglDpmsMode dpms_mode) +{ + if (output->pending->dpms_mode == dpms_mode) + return; + + ensure_pending_state (output); + + output->pending->dpms_mode = dpms_mode; +} + +CoglDpmsMode +cogl_output_get_dpms_mode (CoglOutput *output) +{ + return output->pending->dpms_mode; +} + +void +cogl_output_foreach_overlay (CoglOutput *output, + CoglOutputOverlayCallback callback, + void *user_data) +{ + GList *l; + for (l = output->pending->overlays; l; l = l->next) + callback (l->data, user_data); } diff --git a/cogl/cogl-output.h b/cogl/cogl-output.h index c0cc7cce..1f275a3f 100644 --- a/cogl/cogl-output.h +++ b/cogl/cogl-output.h @@ -31,7 +31,14 @@ #ifndef __COGL_OUTPUT_H #define __COGL_OUTPUT_H +/* We forward declare the CoglOutput type here to avoid some circular + * dependency issues with the following headers. + */ +typedef struct _CoglOutput CoglOutput; + #include <cogl/cogl-types.h> +#include <cogl/cogl-overlay.h> +#include <cogl/cogl-mode.h> COGL_BEGIN_DECLS @@ -57,7 +64,6 @@ COGL_BEGIN_DECLS * to the #CoglOnscreen. */ -typedef struct _CoglOutput CoglOutput; #define COGL_OUTPUT(X) ((CoglOutput *)(X)) /** @@ -234,6 +240,243 @@ cogl_output_get_subpixel_order (CoglOutput *output); float cogl_output_get_refresh_rate (CoglOutput *output); +/** + * cogl_output_get_overlay0: + * @output: a #CoglOutput + * + * Queries the first, base overlay associated with the given @output. + * + * Return value: A pointer to the first #CoglOverlay associated with + * @output. + * Since: 1.16 + * Stability: unstable + */ +CoglOverlay * +cogl_output_get_overlay0 (CoglOutput *output); + +/** + * cogl_output_append_overlay: + * @output: a #CoglOutput + * @overlay: A #CoglOverlay to add + * + * Stacks @overlay above all the other overlays currently associated + * with the given @output. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_append_overlay (CoglOutput *output, + CoglOverlay *overlay); + +/** + * cogl_output_put_overlay_above: + * @output: a #CoglOutput + * @overlay: A #CoglOverlay to position + * @sibling: A #CoglOverlay to position @overlay above or %NULL + * + * Stacks @overlay above @sibling. If @sibling is %NULL then @overlay + * is stacked at the lowest position. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_put_overlay_above (CoglOutput *output, + CoglOverlay *overlay, + CoglOverlay *sibling); + +/** + * cogl_output_put_overlay_below: + * @output: a #CoglOutput + * @overlay: A #CoglOverlay to position + * @sibling: A #CoglOverlay to position @overlay below or %NULL + * + * Stacks @overlay below @sibling. If @sibling is %NULL then @overlay + * is stacked at the highest position. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_put_overlay_below (CoglOutput *output, + CoglOverlay *overlay, + CoglOverlay *sibling); + +/** + * cogl_output_remove_overlay: + * @output: a #CoglOutput + * @overlay: A #CoglOverlay to remove + * + * Removes @overlay from the given @output. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_remove_overlay (CoglOutput *output, + CoglOverlay *overlay); + +/** + * CoglOutputModeCallback: + * @overlay: The current overlay being iterated + * @user_data: The private data passed to + * cogl_output_foreach_mode() + * + * A callback type for use with cogl_output_foreach_mode() + * + * Since: 1.16 + * Stability: unstable + */ +typedef void (*CoglOutputModeCallback) (CoglMode *mode, + void *user_data); + +/** + * cogl_output_foreach_mode: + * @output: a #CoglOutput + * @callback: A #CoglOutputModeCallback to call for each mode. + * @user_data: A private pointer to pass to @callback + * + * Iterates through the possible #CoglMode<!-- -->'s usable with @output. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_foreach_mode (CoglOutput *output, + CoglOutputModeCallback callback, + void *user_data); + +/** + * cogl_output_set_mode: + * @output: a #CoglOutput + * @mode: A #CoglMode + * + * Requests to set the given @mode on the given @output the next + * time output configurations are comitted via + * cogl_renderer_commit_outputs(). + * + * <note>@mode should be a mode that was found via + * cogl_output_foreach_mode()</note> + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_set_mode (CoglOutput *output, + CoglMode *mode); + +/** + * cogl_output_get_mode: + * @output: a #CoglOutput + * + * Queries the mode that's currently set on the given @output. + * + * <note>If the mode is set via cogl_output_set_mode() but + * cogl_renderer_commit_outputs() hasn't since been called then this + * will return the last set mode, which might not correspond to the + * outputs actual mode.</note> + * + * Since: 1.16 + * Stability: unstable + */ +CoglMode * +cogl_output_get_mode (CoglOutput *output); + +/** + * CoglDpmsMode: + * @COGL_DPMS_MODE_ON: Set when the display is use + * @COGL_DPMS_MODE_STANDBY: Uses less than 80% of the power compared + * to @COGL_DPMS_MODE_ON. Recovery time about + * 1 second. + * @COGL_DPMS_MODE_SUSPEND: Uses less the 30W. Recovery time may be + * about 5 seconds. + * @COGL_DPMS_MODE_OFF: Uses less than 8W. Recovery time may be about + * 20 seconds. + * + * Standard VESA Display Power Management modes that can be controlled + * for each #CoglOutput via cogl_output_set_dpms_mode(). + * + * Since: 1.16 + * Stability: unstable + */ +typedef enum _CoglDpmsMode +{ + COGL_DPMS_MODE_ON, + COGL_DPMS_MODE_STANDBY, + COGL_DPMS_MODE_SUSPEND, + COGL_DPMS_MODE_OFF, +} CoglDpmsMode; + +/** + * cogl_output_set_dpms_mode: + * @output: a #CoglOutput + * @dpms_mode: A #CoglDpmsMode + * + * Requests to set the given @dpms_mode on the given @output the next + * time that cogl_renderer_commit_outputs() is called. + * + * This can be used to save power when a display is not in active + * use. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_set_dpms_mode (CoglOutput *output, + CoglDpmsMode dpms_mode); + +/** + * cogl_output_get_dpms_mode: + * @output: a #CoglOutput + * + * Queries the dpms mode last set on @output. + * + * <note>If the dpms mode has been set with + * cogl_output_set_dpms_mode() but cogl_renderer_commit_outputs() + * hasn't since been called then this will return the last set + * dpms mode that might not correspond to the actual state of + * @output.<note. + * + * Return value: The dpms mode set on @output. + * + * Since: 1.16 + * Stability: unstable + */ +CoglDpmsMode +cogl_output_get_dpms_mode (CoglOutput *output); + +/** + * CoglOutputOverlayCallback: + * @overlay: The current overlay being iterated + * @user_data: The private data passed to + * cogl_output_foreach_overlay() + * + * A callback type for use with cogl_output_foreach_overlay() + * + * Since: 1.16 + * Stability: unstable + */ +typedef void (*CoglOutputOverlayCallback) (CoglOverlay *overlay, + void *user_data); + +/** + * cogl_output_foreach_overlay: + * @output: a #CoglOutput + * @callback: A #CoglOutputOverlayCallback to call for each overlay. + * @user_data: A private pointer to pass to @callback + * + * Iterates through the current @output overlays, starting from + * overlay 0. + * + * Since: 1.16 + * Stability: unstable + */ +void +cogl_output_foreach_overlay (CoglOutput *output, + CoglOutputOverlayCallback callback, + void *user_data); + COGL_END_DECLS #endif /* __COGL_OUTPUT_H */ diff --git a/cogl/cogl-overlay-private.h b/cogl/cogl-overlay-private.h new file mode 100644 index 00000000..1943a9a6 --- /dev/null +++ b/cogl/cogl-overlay-private.h @@ -0,0 +1,76 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 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 _COGL_OVERLAY_PRIVATE_H_ +#define _COGL_OVERLAY_PRIVATE_H_ + +#include <cogl/cogl-overlay.h> + +typedef enum _CoglOverlayChange +{ + COGL_OVERLAY_CHANGE_SOURCE = 1<<0, + COGL_OVERLAY_CHANGE_TRANSFORM = 1<<1, +} CoglOverlayChange; + +typedef struct _CoglOverlayState +{ + /* NB: to avoid a circular reference we don't keep a reference + * here... */ + CoglOutput *output; + + /* Note: In the future we might support other sources, such as for + * video */ + CoglOnscreen *onscreen_source; + + /* XXX: src_x must be the first member for _cogl_overlay_equal() to + * work and all remaining members should be comparable via + * memcpy() */ + + /* What region of the source should be overlayed? */ + int src_x; + int src_y; + int src_width; + int src_height; + + int dst_x; + int dst_y; + +} CoglOverlayState; + +struct _CoglOverlay +{ + CoglObjectClass _parent; + + CoglOverlayState *state; + CoglOverlayState *pending; +}; + +void +_cogl_overlay_update_state (CoglOverlay *overlay); + +CoglBool +_cogl_overlay_equal (CoglOverlay *overlay0, + CoglOverlay *overlay1); + +#endif /* _COGL_OVERLAY_PRIVATE_H_ */ diff --git a/cogl/cogl-overlay.c b/cogl/cogl-overlay.c new file mode 100644 index 00000000..0abe6218 --- /dev/null +++ b/cogl/cogl-overlay.c @@ -0,0 +1,249 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 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/>. + * + * + */ + +#include <config.h> + +#include <string.h> + +#include "cogl-object-private.h" +#include "cogl-overlay-private.h" +#include "cogl-onscreen.h" + +static void _cogl_overlay_free (CoglOverlay *overlay); + +COGL_OBJECT_DEFINE (Overlay, overlay); + +static void +free_overlay_state (CoglOverlayState *state) +{ + if (state->onscreen_source) + cogl_object_unref (state->onscreen_source); + + g_slice_free (CoglOverlayState, state); +} + +static void +ensure_pending_state (CoglOverlay *overlay) +{ + CoglOverlayState *state = overlay->state; + CoglOverlayState *pending; + + if (state != overlay->pending) + return; + + pending = g_slice_dup (CoglOverlayState, state); + + pending->onscreen_source = state->onscreen_source; + if (pending->onscreen_source) + cogl_object_ref (pending->onscreen_source); + + overlay->pending = pending; +} + +static void +_cogl_overlay_free (CoglOverlay *overlay) +{ + if (overlay->pending != overlay->state) + free_overlay_state (overlay->pending); + + free_overlay_state (overlay->state); + + g_slice_free (CoglOverlay, overlay); +} + +CoglOverlay * +cogl_overlay_new (CoglOutput *output) +{ + CoglOverlay *overlay = g_slice_new0 (CoglOverlay); + + overlay->state = g_slice_new0 (CoglOverlayState); + + /* Note: to avoid a circular reference we don't reference + * the output here. */ + overlay->state->output = output; + + overlay->pending = overlay->state; + + overlay = _cogl_overlay_object_new (overlay); + + cogl_output_append_overlay (output, overlay); + + return overlay; +} + +void +_cogl_overlay_update_state (CoglOverlay *overlay) +{ + if (overlay->pending == overlay->state) + return; + + free_overlay_state (overlay->state); + + overlay->state = overlay->pending; +} + +void +cogl_overlay_set_onscreen_source (CoglOverlay *overlay, + CoglOnscreen *onscreen_source) +{ + if (overlay->pending->onscreen_source == onscreen_source) + return; + + ensure_pending_state (overlay); + + if (overlay->pending->onscreen_source) + cogl_object_unref (overlay->pending->onscreen_source); + + overlay->pending->onscreen_source = cogl_object_ref (onscreen_source); +} + +CoglOnscreen * +cogl_overlay_get_onscreen_source (CoglOverlay *overlay) +{ + return overlay->pending->onscreen_source; +} + +int +cogl_overlay_get_source_x (CoglOverlay *overlay) +{ + return overlay->pending->src_x; +} + +void +cogl_overlay_set_source_x (CoglOverlay *overlay, int src_x) +{ + if (overlay->pending->src_x == src_x) + return; + + ensure_pending_state (overlay); + + overlay->pending->src_x = src_x; +} + +int +cogl_overlay_get_source_y (CoglOverlay *overlay) +{ + return overlay->pending->src_y; +} + +void +cogl_overlay_set_source_y (CoglOverlay *overlay, int src_y) +{ + if (overlay->pending->src_y == src_y) + return; + + ensure_pending_state (overlay); + + overlay->pending->src_y = src_y; +} + +int +cogl_overlay_get_source_width (CoglOverlay *overlay) +{ + return overlay->pending->src_width; +} + +void +cogl_overlay_set_source_width (CoglOverlay *overlay, int src_width) +{ + if (overlay->pending->src_width == src_width) + return; + + ensure_pending_state (overlay); + + overlay->pending->src_width = src_width; +} + +int +cogl_overlay_get_source_height (CoglOverlay *overlay) +{ + return overlay->pending->src_height; +} + +void +cogl_overlay_set_source_height (CoglOverlay *overlay, int src_height) +{ + if (overlay->pending->src_height == src_height) + return; + + ensure_pending_state (overlay); + + overlay->pending->src_height = src_height; +} + +int +cogl_overlay_get_x (CoglOverlay *overlay) +{ + return overlay->pending->dst_x; +} + +void +cogl_overlay_set_x (CoglOverlay *overlay, int dst_x) +{ + if (overlay->pending->dst_x == dst_x) + return; + + ensure_pending_state (overlay); + + overlay->pending->dst_x = dst_x; +} + +int +cogl_overlay_get_y (CoglOverlay *overlay) +{ + return overlay->pending->dst_y; +} + +void +cogl_overlay_set_y (CoglOverlay *overlay, int dst_y) +{ + if (overlay->pending->dst_y == dst_y) + return; + + ensure_pending_state (overlay); + + overlay->pending->dst_y = dst_y; +} + +CoglBool +_cogl_overlay_equal (CoglOverlay *overlay0, + CoglOverlay *overlay1) +{ + if (overlay0 == overlay1) + return TRUE; + + if (memcmp ((const char *)overlay0->pending + + G_STRUCT_OFFSET (CoglOverlayState, src_x), + (const char *)overlay1->pending + + G_STRUCT_OFFSET (CoglOverlayState, src_x), + sizeof (CoglOverlayState) - + G_STRUCT_OFFSET (CoglOverlayState, src_x)) != 0) + return FALSE; + + if (overlay0->pending->onscreen_source != + overlay1->pending->onscreen_source) + return FALSE; + + return TRUE; +} diff --git a/cogl/cogl-overlay.h b/cogl/cogl-overlay.h new file mode 100644 index 00000000..1a3c793e --- /dev/null +++ b/cogl/cogl-overlay.h @@ -0,0 +1,114 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 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 _COGL_OVERLAY_H_ +#define _COGL_OVERLAY_H_ + +/* We forward declare the CoglOverlay type here to avoid some + * circular dependency issues with the following headers. + */ +typedef struct _CoglOverlay CoglOverlay; + +#include <cogl/cogl-onscreen.h> +#include <cogl/cogl-output.h> + +COGL_BEGIN_DECLS + +/** + * SECTION:cogl-overlay + * @short_description: A single overlay to composite on a #CoglOutput + * + * A #CoglOverlay represents a single image source to composite on a + * given #CoglOutput. Depending on the capabilities of the output an + * overlay can - for example - be offset and scaled within that + * output. + * + * If an output can be composited with overlyas using dedicated, + * fixed-function hardware then it can be more power effecient than + * compositing with the more general purpose GPU pipeline. + */ + +CoglBool cogl_is_overlay (void *object); + +/** + * cogl_overlay_new: + * @output: A #CoglOutput pointer + * + * Creates a new #CoglOverlay that is associated with the given @output, + * positioned on top of any existing overlays. + * + * Return value: A newly allocated #CoglOverlay. + * + * Since: 1.16 + * Stability: unstable + */ +CoglOverlay * +cogl_overlay_new (CoglOutput *output); + +void +cogl_overlay_set_onscreen_source (CoglOverlay *overlay, + CoglOnscreen *onscreen_source); + +CoglOnscreen * +cogl_overlay_get_onscreen_source (CoglOverlay *overlay); + +int +cogl_overlay_get_source_x (CoglOverlay *overlay); + +void +cogl_overlay_set_source_x (CoglOverlay *overlay, int src_x); + +int +cogl_overlay_get_source_y (CoglOverlay *overlay); + +void +cogl_overlay_set_source_y (CoglOverlay *overlay, int src_y); + +int +cogl_overlay_get_source_width (CoglOverlay *overlay); + +void +cogl_overlay_set_source_width (CoglOverlay *overlay, int src_width); + +int +cogl_overlay_get_source_height (CoglOverlay *overlay); + +void +cogl_overlay_set_source_height (CoglOverlay *overlay, int src_height); + +int +cogl_overlay_get_x (CoglOverlay *overlay); + +void +cogl_overlay_set_x (CoglOverlay *overlay, int dst_x); + +int +cogl_overlay_get_y (CoglOverlay *overlay); + +void +cogl_overlay_set_y (CoglOverlay *overlay, int dst_y); + +COGL_END_DECLS + +#endif /* _COGL_OVERLAY_H_ */ diff --git a/cogl/cogl-renderer-private.h b/cogl/cogl-renderer-private.h index 8b8d2bfe..7bca274f 100644 --- a/cogl/cogl-renderer-private.h +++ b/cogl/cogl-renderer-private.h @@ -117,4 +117,7 @@ _cogl_renderer_get_proc_address (CoglRenderer *renderer, const char *name, CoglBool in_core); +void +_cogl_renderer_notify_outputs_changed (CoglRenderer *renderer); + #endif /* __COGL_RENDERER_PRIVATE_H */ diff --git a/cogl/cogl-renderer.c b/cogl/cogl-renderer.c index 2564c9d2..ab9b37b9 100644 --- a/cogl/cogl-renderer.c +++ b/cogl/cogl-renderer.c @@ -44,6 +44,7 @@ #include "cogl-winsys-stub-private.h" #include "cogl-config-private.h" #include "cogl-error-private.h" +#include "cogl-output-private.h" #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT #include "cogl-winsys-egl-x11-private.h" @@ -111,7 +112,7 @@ static CoglDriverDescription _cogl_drivers[] = { COGL_DRIVER_GL3, "gl3", - 0, + COGL_RENDERER_CONSTRAINT_USES_KMS, COGL_PRIVATE_FEATURE_ANY_GL | COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, &_cogl_driver_gl, @@ -121,7 +122,7 @@ static CoglDriverDescription _cogl_drivers[] = { COGL_DRIVER_GL, "gl", - 0, + COGL_RENDERER_CONSTRAINT_USES_KMS, COGL_PRIVATE_FEATURE_ANY_GL | COGL_PRIVATE_FEATURE_GL_FIXED | COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, @@ -134,7 +135,8 @@ static CoglDriverDescription _cogl_drivers[] = { COGL_DRIVER_GLES2, "gles2", - COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2, + COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2 | + COGL_RENDERER_CONSTRAINT_USES_KMS, COGL_PRIVATE_FEATURE_ANY_GL | COGL_PRIVATE_FEATURE_GL_EMBEDDED | COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, @@ -147,7 +149,7 @@ static CoglDriverDescription _cogl_drivers[] = { COGL_DRIVER_GLES1, "gles1", - 0, + COGL_RENDERER_CONSTRAINT_USES_KMS, COGL_PRIVATE_FEATURE_ANY_GL | COGL_PRIVATE_FEATURE_GL_EMBEDDED | COGL_PRIVATE_FEATURE_GL_FIXED, @@ -173,7 +175,7 @@ static CoglDriverDescription _cogl_drivers[] = { COGL_DRIVER_NOP, "nop", - 0, /* constraints satisfied */ + COGL_RENDERER_CONSTRAINT_USES_KMS, /* constraints satisfied */ 0, /* flags */ &_cogl_driver_nop, NULL, /* texture driver */ @@ -843,3 +845,80 @@ cogl_renderer_foreach_output (CoglRenderer *renderer, for (l = renderer->outputs; l; l = l->next) callback (l->data, user_data); } + +CoglBool +cogl_renderer_commit_outputs (CoglRenderer *renderer, + CoglError **error) +{ + CoglWinsysVtable *winsys; + + _COGL_RETURN_IF_FAIL (!renderer->connected); + + winsys = renderer->winsys; + + if (winsys->commit_outputs) + return winsys->commit_outputs (renderer, error); + else + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "The current Cogl window system doesn't support " + "display configuration"); + return FALSE; + } +} + +void +_cogl_renderer_notify_outputs_changed (CoglRenderer *renderer) +{ + const CoglWinsysVtable *winsys = renderer->winsys_vtable; + GList *l; + + COGL_NOTE (WINSYS, "Outputs changed:"); + + for (l = renderer->outputs; l; l = l->next) + { + CoglOutput *output = l->data; + const char *subpixel_string; + + switch (output->state->subpixel_order) + { + case COGL_SUBPIXEL_ORDER_UNKNOWN: + default: + subpixel_string = "unknown"; + break; + case COGL_SUBPIXEL_ORDER_NONE: + subpixel_string = "none"; + break; + case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: + subpixel_string = "horizontal_rgb"; + break; + case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: + subpixel_string = "horizontal_bgr"; + break; + case COGL_SUBPIXEL_ORDER_VERTICAL_RGB: + subpixel_string = "vertical_rgb"; + break; + case COGL_SUBPIXEL_ORDER_VERTICAL_BGR: + subpixel_string = "vertical_bgr"; + break; + } + + COGL_NOTE (WINSYS, + " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f " + "subpixel_order=%s refresh_rate=%.3f", + output->state->name, + output->state->x, output->state->y, + cogl_output_get_width (output), + cogl_output_get_height (output), + output->state->mm_width, output->state->mm_height, + cogl_output_get_width (output) / (output->state->mm_width / 25.4), + cogl_output_get_height (output) / (output->state->mm_height / 25.4), + subpixel_string, + cogl_output_get_refresh_rate (output)); + } + + if (winsys->renderer_outputs_changed) + winsys->renderer_outputs_changed (renderer); +} diff --git a/cogl/cogl-renderer.h b/cogl/cogl-renderer.h index c7e6a5ab..1f81faa3 100644 --- a/cogl/cogl-renderer.h +++ b/cogl/cogl-renderer.h @@ -28,6 +28,11 @@ #ifndef __COGL_RENDERER_H__ #define __COGL_RENDERER_H__ +/* We forward declare the CoglRenderer type here to avoid some + * circular dependency issues with the following headers. + */ +typedef struct _CoglRenderer CoglRenderer; + #include <cogl/cogl-types.h> #include <cogl/cogl-onscreen-template.h> #include <cogl/cogl-error.h> @@ -78,8 +83,6 @@ COGL_BEGIN_DECLS uint32_t cogl_renderer_error_domain (void); -typedef struct _CoglRenderer CoglRenderer; - /** * cogl_is_renderer: * @object: A #CoglObject pointer @@ -421,6 +424,26 @@ cogl_renderer_foreach_output (CoglRenderer *renderer, CoglOutputCallback callback, void *user_data); +/** + * cogl_renderer_commit_outputs: + * @renderer: A connected #CoglRenderer + * + * Attempts to apply all outstanding #CoglOutput changes to the + * underlying hardware. If for some reason it is not possible to + * support the whole configuration then all outstanding changes + * will be reverted and a #CoglError will be returned. + * + * XXX: Instead of reverting the outstanding changes should we + * instead have a separate api for manually reverting outstanding + * changes? + * + * Since: 1.16 + * Stability: Unstable + */ +CoglBool +cogl_renderer_commit_outputs (CoglRenderer *renderer, + CoglError **error); + COGL_END_DECLS #endif /* __COGL_RENDERER_H__ */ diff --git a/cogl/cogl-xlib-renderer.c b/cogl/cogl-xlib-renderer.c index 4d332fa8..99f1f895 100644 --- a/cogl/cogl-xlib-renderer.c +++ b/cogl/cogl-xlib-renderer.c @@ -39,6 +39,7 @@ #include "cogl-winsys-private.h" #include "cogl-error-private.h" #include "cogl-poll-private.h" +#include "cogl-mode-private.h" #include <X11/Xlib.h> #include <X11/extensions/Xdamage.h> @@ -198,7 +199,7 @@ static int compare_outputs (CoglOutput *a, CoglOutput *b) { - return strcmp (a->name, b->name); + return strcmp (a->pending->name, b->pending->name); } #define CSO(X) COGL_SUBPIXEL_ORDER_ ## X @@ -243,8 +244,9 @@ update_outputs (CoglRenderer *renderer, { XRRCrtcInfo *crtc_info = NULL; XRROutputInfo *output_info = NULL; + GList *new_modes = NULL; + CoglMode *current_mode = NULL; CoglOutput *output; - float refresh_rate = 0; int j; crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy, @@ -260,10 +262,17 @@ update_outputs (CoglRenderer *renderer, for (j = 0; j < resources->nmode; j++) { - if (resources->modes[j].id == crtc_info->mode) - refresh_rate = (resources->modes[j].dotClock / - ((float)resources->modes[j].hTotal * - resources->modes[j].vTotal)); + XRRModeInfo *info = &resources->modes[j]; + + CoglMode *mode = _cogl_mode_new (info->name); + mode->width = info->width; + mode->height = info->height; + mode->refresh_rate = + (info->dotClock / ((float)info->hTotal * info->vTotal)); + new_modes = g_list_prepend (new_modes, mode); + + if (info->id == crtc_info->mode) + current_mode = mode; } output_info = XRRGetOutputInfo (xlib_renderer->xdpy, @@ -276,53 +285,51 @@ update_outputs (CoglRenderer *renderer, } output = _cogl_output_new (output_info->name); - output->x = crtc_info->x; - output->y = crtc_info->y; - output->width = crtc_info->width; - output->height = crtc_info->height; + output->modes = g_list_reverse (new_modes); + output->state->mode = cogl_object_ref (current_mode); + output->state->x = crtc_info->x; + output->state->y = crtc_info->y; if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0) { - output->mm_width = output_info->mm_height; - output->mm_height = output_info->mm_width; + output->state->mm_width = output_info->mm_height; + output->state->mm_height = output_info->mm_width; } else { - output->mm_width = output_info->mm_width; - output->mm_height = output_info->mm_height; + output->state->mm_width = output_info->mm_width; + output->state->mm_height = output_info->mm_height; } - output->refresh_rate = refresh_rate; - switch (output_info->subpixel_order) { case SubPixelUnknown: default: - output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; break; case SubPixelNone: - output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; break; case SubPixelHorizontalRGB: - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; break; case SubPixelHorizontalBGR: - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; break; case SubPixelVerticalRGB: - output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; break; case SubPixelVerticalBGR: - output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; break; } - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; /* Handle the effect of rotation and reflection on subpixel order (ugh) */ for (j = 0; j < 6; j++) { if ((crtc_info->rotation & (1 << j)) != 0) - output->subpixel_order = subpixel_map[j][output->subpixel_order]; + output->state->subpixel_order = subpixel_map[j][output->state->subpixel_order]; } new_outputs = g_list_prepend (new_outputs, output); @@ -350,6 +357,14 @@ update_outputs (CoglRenderer *renderer, CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL; CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL; + if (output_m && output_m->pending != output_m->state) + { + g_warning ("Unexpected pending state associated with CoglOutput " + "%s while processing events. Pending output state " + "shouldn't be maintained between mainloop " + "iterations\n", output_m->state->name); + } + if (l && m) cmp = compare_outputs (output_l, output_m); else if (l) @@ -361,7 +376,7 @@ update_outputs (CoglRenderer *renderer, { GList *m_next = m->next; - if (!_cogl_output_values_equal (output_l, output_m)) + if (!_cogl_output_equal (output_l, output_m)) { renderer->outputs = g_list_remove_link (renderer->outputs, m); renderer->outputs = g_list_insert_before (renderer->outputs, @@ -396,57 +411,7 @@ update_outputs (CoglRenderer *renderer, _cogl_xlib_renderer_untrap_errors (renderer, &state); if (changed) - { - const CoglWinsysVtable *winsys = renderer->winsys_vtable; - - if (notify) - COGL_NOTE (WINSYS, "Outputs changed:"); - else - COGL_NOTE (WINSYS, "Outputs:"); - - for (l = renderer->outputs; l; l = l->next) - { - CoglOutput *output = l->data; - const char *subpixel_string; - - switch (output->subpixel_order) - { - case COGL_SUBPIXEL_ORDER_UNKNOWN: - default: - subpixel_string = "unknown"; - break; - case COGL_SUBPIXEL_ORDER_NONE: - subpixel_string = "none"; - break; - case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: - subpixel_string = "horizontal_rgb"; - break; - case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: - subpixel_string = "horizontal_bgr"; - break; - case COGL_SUBPIXEL_ORDER_VERTICAL_RGB: - subpixel_string = "vertical_rgb"; - break; - case COGL_SUBPIXEL_ORDER_VERTICAL_BGR: - subpixel_string = "vertical_bgr"; - break; - } - - COGL_NOTE (WINSYS, - " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f " - "subpixel_order=%s refresh_rate=%.3f", - output->name, - output->x, output->y, output->width, output->height, - output->mm_width, output->mm_height, - output->width / (output->mm_width / 25.4), - output->height / (output->mm_height / 25.4), - subpixel_string, - output->refresh_rate); - } - - if (notify && winsys->renderer_outputs_changed != NULL) - winsys->renderer_outputs_changed (renderer); - } + _cogl_renderer_notify_outputs_changed (renderer); } static CoglFilterReturn @@ -634,8 +599,10 @@ _cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer, for (l = renderer->outputs; l; l = l->next) { CoglOutput *output = l->data; - int xb1 = output->x, xb2 = output->x + output->width; - int yb1 = output->y, yb2 = output->y + output->height; + int xb1 = output->state->x; + int xb2 = output->state->x + cogl_output_get_width (output); + int yb1 = output->state->y; + int yb2 = output->state->y + cogl_output_get_height (output); int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1); int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1); diff --git a/cogl/cogl.h b/cogl/cogl.h index c5ad6619..5ec316bc 100644 --- a/cogl/cogl.h +++ b/cogl/cogl.h @@ -46,6 +46,7 @@ #endif #include <cogl/cogl-renderer.h> +#include <cogl/cogl-overlay.h> #include <cogl/cogl-output.h> #include <cogl/cogl-display.h> #include <cogl/cogl-context.h> diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c index a81237de..260342ff 100644 --- a/cogl/winsys/cogl-winsys-egl-kms.c +++ b/cogl/winsys/cogl-winsys-egl-kms.c @@ -334,7 +334,6 @@ update_outputs (CoglRenderer *renderer) { CoglOutput *output = NULL; CoglOutputKMS *kms_output; - const char *type_name; GList *l; drmModeConnector *connector = @@ -356,7 +355,7 @@ update_outputs (CoglRenderer *renderer) } /* If we already have a CoglOutput corresponding to this - * connector id then we can simply keep it and move on... */ + * connector id then we keep it... */ for (l = renderer->outputs; l; l = l->next) { CoglOutput *existing_output = l->data; @@ -366,53 +365,82 @@ update_outputs (CoglRenderer *renderer) { renderer->outputs = g_list_delete_link (renderer->outputs, l); output = existing_output; + kms_output = output->winsys; + + if (output->pending != output->state) + { + g_warning ("Unexpected pending state associated with CoglOutput " + "%s while processing events. Pending output state " + "shouldn't be maintained between mainloop " + "iterations\n", output->state->name); + } + + break; } } - if (output) + if (!output) { - new_outputs = g_list_prepend (new_outputs, output); - drmModeFreeConnector (connector); - continue; - } + const char *type_name; - if (connector->connector_type < G_N_ELEMENTS (kms_connector_types)) - type_name = kms_connector_types[connector->connector_type]; - else - type_name = kms_connector_types[0]; + if (connector->connector_type < G_N_ELEMENTS (kms_connector_types)) + type_name = kms_connector_types[connector->connector_type]; + else + type_name = kms_connector_types[0]; - output = _cogl_output_new (type_name); + output = _cogl_output_new (type_name); - kms_output = g_slice_new0 (CoglOutputKMS); - kms_output->connector_id = connector->connector_id; - kms_output->connector = connector; + kms_output = g_slice_new0 (CoglOutputKMS); + kms_output->connector_id = connector->connector_id; + kms_output->connector = connector; + + _cogl_output_set_winsys_data (output, + kms_output, + kms_output_destroy_cb); + } + + for (j = 0; j < connector->count_modes; j++) + { + drmModeModeInfo *info = &connector->modes[j]; + + CoglMode *mode = _cogl_mode_new (info->name); + mode->width = info->hdisplay; + mode->height = info->vdisplay; + mode->refresh_rate = + (info->clock / ((float)info->htotal * info->vtotal)); + new_modes = g_list_prepend (new_modes, mode); + } + + if (output->modes) + g_list_free_full (output->modes, cogl_object_unref); + output->modes = g_list_reverse (new_modes); /* We can't determinine anything about the relative position * of the outputs... */ - output->x = output->y = 0; + output->state->x = output->state->y = 0; - output->mm_width = connector->mmWidth; - output->mm_height = connector->mmHeight; + output->state->mm_width = connector->mmWidth; + output->state->mm_height = connector->mmHeight; switch (connector->subpixel) { case DRM_MODE_SUBPIXEL_UNKNOWN: - output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; break; case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; break; case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: - output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; break; case DRM_MODE_SUBPIXEL_VERTICAL_RGB: - output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; break; case DRM_MODE_SUBPIXEL_VERTICAL_BGR: - output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; break; case DRM_MODE_SUBPIXEL_NONE: - output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; + output->state->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; break; } @@ -439,29 +467,19 @@ update_outputs (CoglRenderer *renderer) } } - if (kms_output->saved_crtc) - { - output->width = kms_output->saved_crtc->width; - output->height = kms_output->saved_crtc->height; + if (output->state->mode) + cogl_object_unref (output->state->mode); + output->state->mode = NULL; - if (kms_output->saved_crtc->mode_valid) - { - drmModeModeInfo *mode = &kms_output->saved_crtc->mode; - output->refresh_rate = mode->vrefresh; - } - } - else + if (kms_output->saved_crtc && + kms_output->saved_crtc->mode_valid) { - /* If there is no encoder associated with the connector then - * there is no crtc mode and so there's currently no basis - * to specify a width/height */ - output->width = 0; - output->height = 0; - } + output->state->mode = + find_mode (output->modes, kms_output->saved_crtc->mode.name); + cogl_object_ref (output->state->mode); - _cogl_output_set_winsys_data (output, - kms_output, - kms_output_destroy_cb); + g_warn_if_fail (output->state->mode); + } new_outputs = g_list_prepend (new_outputs, output); } @@ -476,6 +494,8 @@ update_outputs (CoglRenderer *renderer) renderer->outputs = new_outputs; drmModeFreeResources (resources); + + _cogl_renderer_notify_outputs_changed (renderer); } static void @@ -1242,6 +1262,419 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) onscreen->winsys = NULL; } +static drmModeProperty * +get_connector_property (int fd, + drmModeConnector *connector, + const char *name) +{ + drmModeProperty *property; + int i; + + for (i = 0; i < connector->count_props; i++) + { + property = drmModeGetProperty (fd, connector->props[i]); + if (!property) + continue; + + if (strcmp (property->name, name) == 0) + return props; + + drmModeFreeProperty (property); + } + + return NULL; +} + +/* XXX: NB: Don't assume that the output->state is a reliable cache + * of the real hardware state such that state changes can be avoided + * by looking for NOP changes between ->state and ->pending since we + * sometimes have to deal with the display being changed behind our + * back. + * + * TODO: Support incremental updates in certain cases + * + * XXX: The corner cases to consider... + * + * Q: How do we _commit an overlay configuration? + * A: During a _commit we always check that each overlay has + * an associated framebuffer source that has a valid + * ->next_bo or ->current_bo (otherwise we report an error) + * since we can't set a mode without a framebuffer and we + * don't want to be automatically allocating place holder + * framebuffers in corner cases or displaying undefined + * buffer contents. + * + * If we need to call drmModeSetCrtc() we will pass ->next_bo + * if available, otherwise ->current_bo. + * + * We always assume there could be outstanding rendering + * associated with a bo when calling drmModeSetCrtc() and so + * we explicitly synchronize with the GPU to make sure all + * rendering to the buffer is complete first. + * + * If we are using ->next_bo that implies there is currently + * a pending flip that hasn't completed. We should mark the + * pending flip as a "stale" flip by incrementing + * ->stale_flips++. Later when we reach page_flip_handler() + * we will check the ->stale_flips counter before + * decrementing it and whenever it is set we should only make + * sure to issue any necessary _FRAME_SYNC events but we + * should avoid making any updates to ->current_bo/->next_bo + * pointers. + * + *XXX: why does stale_flips need to be a counter instead of just + * a boolean + * + * After successfully calling drmModeSetCrtc() we insert a + * reference to the overlay in kms_onscreen->overlays. + * + * Q: what if there is an error when calling drmModeSetCrtc? + * A: We roll-back the whole configuration + * + * Q: What are the semantics for calling drmModeSetCrtc while + * there are pending page flips? + * A: Looking at the intel drm driver it looks like setting + * a mode starts by disabling the crtc, which involves + * waiting for pending flips so I think we can assume + * page flip events will *always* be delivered by drm. + * + * Q: How do we deal with roll-back when this is the first + * configuration to be committed? + * A: We should be assuming that update_outputs has been + * called before any configuration so we should know the + * previous state. XXX: what about previous framebuffer + * state? It seems likely that we'll need to special case + * restoring a saved state where the saved drmModeFB doesn't + * correspond to a CoglFramebuffer. + * + * Q: What if we disassociate a framebuffer from an overlay + * while it still has a pending flip? (considering what + * might go wrong if we then immediately tried to associate + * the framebuffer with a different overlay) + * A: XXX + * + * Q: When do we remove references from the + * kms_onscreen->overlays list? + * A: XXX + * + * Q: How do we handle _swap_buffers + * A: We iterate through each output that is associated with + * the onscreen framebuffer and for the first output + * we use drmModePageFlip (passing DRM_MODE_PAGE_FLIP_EVENT) + * to post the framebuffer for overlay0 and use + * drmModeSetPlane() for any additional overlays. For + * all other outputs we use drmModeSetCrtc to post the + * framebuffer for overlay0 while also using + * drmModeSetPlane() for additional overlays. + * + * Note: until we have the atomic page flipping api we + * don't have a very sane way of synchronizing changes + * to overlay planes. + * + * Note: we are intentionally only trying to synchronize + * with one output but we will probably want to provide + * api for controlling which output is synchronized. + * + * Q: Would it be better when handling multiple outputs to + * call drmModeSetCrtc() after being notified of the flip + * for the first output? + * A: Since using drmModeSetCrtc() implies needing to + * synchronize with the GPU to make sure rendering is + * complete, then waiting until the flip should minimize + * blocking on the CPU, especially in the case where the same + * buffer is being posted to multiple outputs since the flip + * should end up waiting for the GPU to finish for us so we + * won't have to. + * + * We should create a queue_swap_outputs utility for us to + * queue the swapping of all unsynchronized outputs. + * + * + * Q: What if we call _swap_buffers() before we have committed + * an output configuration associating the framebuffer with + * a hardware overlay? + * A: In this case we'll see that kms_output->overlays is NULL. + * We lock the front buffer, set it on ->next_bo and directly + * call page_flip_handler() to behave as if the flip + * completed immediately. This will make sure we issue a + * _FRAME_SYNC event for the swap and move the bo to + * ->current_bo. + * + * + * Q: How do we deal with an error in drmModePageFlip() also + * considering that after the error we might commit a + * new display configuration which will want to find + * a next/current_bo to reference. + * A: We can pretend there was no error and that the page + * flip completed immediately by calling page_flip_handler() + * directly in this case. This will make sure cogl + * dispatches a _FRAME_SYNC event for the frame otherwise + * applications may freeze. The main problem here is that + * we don't have meaningful timestamp data to pass to + * page_flip_handler(). XXX: wont this potentially break the + * invariable that we should be able to assume ->current_bo + * has no outstanding rendering? + * + * Q: How do we handle an onscreen framebuffer resize? + * A: We don't, you just have to create a new framebuffer + */ +static void +_cogl_winsys_commit_outputs (CoglRenderer *renderer, + CoglError **error) +{ + CoglRendererEGL *egl_renderer = renderer->winsys; + CoglRendererKMS *kms_renderer = egl_renderer->platform; + GList *l; + CoglBool roll_back = FALSE; + + + /* If we hit an error at any point while committing the new + * configuration then we revert back to here with roll_back + * set to TRUE and instead re-commit the previous + * configuration */ +ROLL_BACK: + + + for (l = renderer->outputs; l; l = l->next) + { + CoglOutput *output = l->data; + CoglOutputKMS *kms_output = output->winsys; + CoglOutputState *state = roll_back ? output->state : output->pending; + CoglOverlay *overlay0; + drmModeConnector *connector; + drmModeProperty *property; + int i; + + connector = drmModeGetConnector (kms_renderer->fd, + kms_output->connector_id); + if (!connector) + { + g_warn_if_reached (); + continue; + } + + if (state->overlays) + overlay0 = state->overlays->data; + else + overlay0 = NULL; + + /* If the output has no associated overlays then we assume + * it's ok to try and put it into DPMS_MODE_OFF */ + dpms_mode = overlay0_new ? state_new->dpms_mode : DRM_MODE_DPMS_OFF; + + property = get_connector_property (kms_renderer->fd, + connector, + "DPMS"); + if (property) + { + int status = drmModeConnectorSetProperty (kms_renderer->fd, + connector->connector_id, + property->prop_id, + dpms_mode); + drmModeFreeProperty (property); + + if (status < 0) + { + const char *dpms_mode_names[] = { + "ON", + "STANDBY", + "SUSPEND", + "OFF" + }; + + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("Failed to set DPMS state for " + "connector %d to %s", + kms_output->connector_id, + dpms_mode_names[dpms_mode]); + goto ROLL_BACK; + } + } + + if (!overlay0) + { + drmModeFreeConnector (connector); + continue; + } + + if (!overlay0->onscreen_source) + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("An overlay must be associated with an " + "onscreen framebuffer as a color source"); + goto ROLL_BACK; + } + + for (i = 0; i < connector->count_modes; i++) + { + drmModeInfo *mode_info = &connector->modes[i]; + int n_planes; + + if (strcmp (mode_info->name, state->mode->name) == 0) + { + CoglOnscreen *onscreen = overlay0->onscreen_source; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + uint32_t fb_id; + int status; + + if (overlay0->src_x != 0 || + overlay0->src_y != 0 || + overlay0->src_width != mode_info->width || + overlay0->src_height != mode_info->height) + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("On KMS the first overlay's source can " + "not be offset and its size must match " + "the mode resolution"); + goto ROLL_BACK; + } + + if (kms_onscreen->next_fb_id) + fb_id = kms_onscreen->next_fb_id; + else if (kms_onscreen->current_fb_id) + fb_id = kms_onscreen->current_fb_id; + else + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("Can't commit output changes involving " + "an uninitialized (un-swapped) onscreen " + "framebuffer since it has no actual " + "data to scan out yet"); + goto ROLL_BACK; + } + + /* drmModeSetCrtc isn't synchronized with the GPU + * pipeline so we explicitly wait for any out standing + * rendering to complete before setting the new + * mode... */ + cogl_framebuffer_finish (overlay0->onscreen_source); + + status = drmModeSetCrtc (kms_renderer->fd, + kms_output->encoder->crtc_id, + fb_id, 0, 0, + &kms_output->connector_id, 1, + &kms_mode_info); + if (status < 0) + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("KMS was unable to set the requested " + "mode (%s) on connector %d, possibly " + "due to a hardware limitation", + dpms_mode_names[dpms_mode], + kms_output->connector_id); + goto ROLL_BACK; + } + } + + /* + * The remaining overlays should be setup via drmModeSetPlane() + */ + + for (n_planes = 0, l = state->overlays->next; + l; + n_planes++, l = l->next) + ; + + if (n_planes) + { + drmModePlaneRes *planes = + drmModeGetPlaneResources (kms_renderer->fd); + + if (!planes) + g_warn_if_reached (); + + if (planes == NULL || planes->count_planes < n_planes) + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("Hardware doesn't support enough " + "overlays to support configuration."); + goto ROLL_BACK; + } + + for (n = 0, l = state->overlays->next; l; n++, l = l->next) + { + CoglOverlay *overlay = l->data; + CoglOnscreen *onscreen = overlay0->onscreen_source; + CoglOnscreenEGL *egl_onscreen = onscreen->winsys; + CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; + uint32_t fb_id; + + if (!overlay->onscreen_source) + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("An overlay must be associated with an " + "onscreen framebuffer as a color source"); + goto ROLL_BACK; + } + + if (kms_onscreen->next_fb_id) + fb_id = kms_onscreen->next_fb_id; + else if (kms_onscreen->current_fb_id) + fb_id = kms_onscreen->current_fb_id; + else + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("Can't commit output changes involving " + "an uninitialized (un-swapped) onscreen " + "framebuffer since it has no actual " + "data to scan out yet"); + goto ROLL_BACK; + } + + if (drmModeSetPlane (kms_renderer->fd, + planes->planes[n], + kms_output->encoder->crtc_id, + fb_id, + /* FIXME: flags? */, + /* FIXME: ctc_x/y/w/h, + * src_x/y/w/h */) < 0) + { + g_return_if_fail (roll_back == FALSE); + + roll_back = TRUE; + roll_back_error = + g_strdup_printf ("KMS was unable to setup the " + "overlays as requested, possibly " + "due to a hardware limitation."); + goto ROLL_BACK; + } + } + + drmModeFreePlaneResources (planes); + } + } + + drmModeFreeConnector (connector); + + _cogl_output_update_state (output); + } +} + static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable = { @@ -1282,6 +1715,8 @@ _cogl_winsys_egl_kms_get_vtable (void) vtable.onscreen_swap_buffers_with_damage = _cogl_winsys_onscreen_swap_buffers_with_damage; + vtable.commit_outputs = _cogl_winsys_commit_outputs; + vtable_inited = TRUE; } diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h index 123419e1..43f34638 100644 --- a/cogl/winsys/cogl-winsys-private.h +++ b/cogl/winsys/cogl-winsys-private.h @@ -194,6 +194,18 @@ typedef struct _CoglWinsysVtable void (*fence_destroy) (CoglContext *ctx, void *fence); + /* optional + * + * This iterates the current renderer->outputs and for each output + * with pending changes they should be applied to the window system + * or hardware. + * + * If it's not possible to apply the pending changes then the + * previous state should be restored and an error returned. + */ + CoglBool + (*commit_outputs) (CoglRenderer *renderer, CoglError **error); + } CoglWinsysVtable; CoglBool |