Commit aecd98b8 authored by Jonas Ådahl's avatar Jonas Ådahl
Browse files

backends/native: Support drawing onto multiple onscreen framebuffers

Add support for drawing the stage using multiple stage views, where
each stage view has its own onscreen framebuffer.

https://bugzilla.gnome.org/show_bug.cgi?id=768976
parent d7b87799
......@@ -144,4 +144,6 @@ ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend);
void meta_backend_monitors_changed (MetaBackend *backend);
gboolean meta_is_stage_views_enabled (void);
#endif /* META_BACKEND_PRIVATE_H */
......@@ -748,3 +748,19 @@ meta_clutter_init (void)
meta_backend_post_init (_backend);
}
gboolean
meta_is_stage_views_enabled (void)
{
const gchar *mutter_stage_views;
if (!meta_is_wayland_compositor ())
return FALSE;
mutter_stage_views = g_getenv ("MUTTER_STAGE_VIEWS");
if (!mutter_stage_views)
return FALSE;
return strcmp (mutter_stage_views, "1") == 0;
}
......@@ -22,6 +22,17 @@
#include "backends/meta-renderer.h"
#include "clutter/clutter-mutter.h"
enum
{
PROP_0,
PROP_MONITOR_INFO,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST];
struct _MetaRendererView
{
ClutterStageView parent;
......@@ -32,6 +43,50 @@ struct _MetaRendererView
G_DEFINE_TYPE (MetaRendererView, meta_renderer_view,
CLUTTER_TYPE_STAGE_VIEW_COGL)
MetaMonitorInfo *
meta_renderer_view_get_monitor_info (MetaRendererView *view)
{
return view->monitor_info;
}
static void
meta_renderer_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaRendererView *view = META_RENDERER_VIEW (object);
switch (prop_id)
{
case PROP_MONITOR_INFO:
g_value_set_pointer (value, view->monitor_info);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_renderer_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaRendererView *view = META_RENDERER_VIEW (object);
switch (prop_id)
{
case PROP_MONITOR_INFO:
view->monitor_info = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_renderer_view_init (MetaRendererView *view)
{
......@@ -40,4 +95,17 @@ meta_renderer_view_init (MetaRendererView *view)
static void
meta_renderer_view_class_init (MetaRendererViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = meta_renderer_view_get_property;
object_class->set_property = meta_renderer_view_set_property;
obj_props[PROP_MONITOR_INFO] =
g_param_spec_pointer ("monitor-info",
"MetaMonitorInfo",
"The monitor info of the view",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
}
......@@ -26,4 +26,6 @@ G_DECLARE_FINAL_TYPE (MetaRendererView, meta_renderer_view,
META, RENDERER_VIEW,
ClutterStageViewCogl)
MetaMonitorInfo *meta_renderer_view_get_monitor_info (MetaRendererView *view);
#endif /* META_RENDERER_VIEW_H */
......@@ -390,7 +390,10 @@ meta_backend_native_update_screen_size (MetaBackend *backend,
ClutterActor *stage = meta_backend_get_stage (backend);
stage_native = meta_clutter_backend_native_get_stage_native (clutter_backend);
meta_stage_native_legacy_set_size (stage_native, width, height);
if (meta_is_stage_views_enabled ())
meta_stage_native_rebuild_views (stage_native);
else
meta_stage_native_legacy_set_size (stage_native, width, height);
clutter_actor_set_size (stage, width, height);
}
......
......@@ -76,14 +76,6 @@ typedef struct {
uint32_t rotation_map[ALL_TRANSFORMS];
} MetaCRTCKms;
typedef struct
{
int pending;
MetaKmsFlipCallback callback;
void *user_data;
} MetaKmsFlipClosure;
typedef struct
{
GSource source;
......@@ -1065,12 +1057,8 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
unsigned int n_outputs)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
MetaBackend *backend;
MetaRenderer *renderer;
unsigned i;
int screen_width, screen_height;
gboolean ok;
GError *error;
screen_width = 0; screen_height = 0;
for (i = 0; i < n_crtcs; i++)
......@@ -1156,20 +1144,6 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
crtc->current_mode = NULL;
}
backend = meta_get_backend ();
renderer = meta_backend_get_renderer (backend);
error = NULL;
ok = meta_renderer_native_set_layout (META_RENDERER_NATIVE (renderer),
screen_width, screen_height,
&error);
if (!ok)
{
meta_warning ("Applying display configuration failed: %s\n", error->message);
g_error_free (error);
return;
}
for (i = 0; i < n_outputs; i++)
{
MetaOutputInfo *output_info = outputs[i];
......@@ -1344,79 +1318,95 @@ get_crtc_connectors (MetaMonitorManager *manager,
}
void
meta_monitor_manager_kms_apply_crtc_modes (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id)
meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms,
MetaCRTC *crtc,
int x,
int y,
uint32_t fb_id)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
unsigned int i;
uint32_t *connectors;
unsigned int n_connectors;
drmModeModeInfo *mode;
for (i = 0; i < manager->n_crtcs; i++)
{
MetaCRTC *crtc = &manager->crtcs[i];
uint32_t *connectors;
unsigned int n_connectors;
drmModeModeInfo *mode;
get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
if (connectors)
mode = crtc->current_mode->driver_private;
else
mode = NULL;
if (connectors)
mode = crtc->current_mode->driver_private;
else
mode = NULL;
if (drmModeSetCrtc (manager_kms->fd,
crtc->crtc_id,
fb_id,
x, y,
connectors, n_connectors,
mode) != 0)
g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
if (drmModeSetCrtc (manager_kms->fd,
crtc->crtc_id,
fb_id,
crtc->rect.x, crtc->rect.y,
connectors, n_connectors,
mode) != 0)
g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
g_free (connectors);
}
g_free (connectors);
}
static void
invoke_flip_closure (GClosure *flip_closure)
{
GValue param = G_VALUE_INIT;
g_value_init (&param, G_TYPE_POINTER);
g_value_set_pointer (&param, flip_closure);
g_closure_invoke (flip_closure, NULL, 1, &param, NULL);
g_closure_unref (flip_closure);
}
gboolean
meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id,
MetaKmsFlipCallback flip_callback,
void *user_data)
meta_monitor_manager_kms_is_crtc_active (MetaMonitorManagerKms *manager_kms,
MetaCRTC *crtc)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
MetaKmsFlipClosure *flip_closure = NULL;
unsigned int i;
gboolean fb_in_use = FALSE;
gboolean connected_crtc_found;
if (manager->power_save_mode != META_POWER_SAVE_ON)
return FALSE;
for (i = 0; i < manager->n_crtcs; i++)
connected_crtc_found = FALSE;
for (i = 0; i < manager->n_outputs; i++)
{
MetaCRTC *crtc = &manager->crtcs[i];
uint32_t *connectors;
unsigned int n_connectors;
int ret;
MetaOutput *output = &manager->outputs[i];
get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
if (output->crtc == crtc)
{
connected_crtc_found = TRUE;
break;
}
}
if (n_connectors == 0)
continue;
if (!connected_crtc_found)
return FALSE;
fb_in_use = TRUE;
return TRUE;
}
if (manager_kms->page_flips_not_supported)
continue;
gboolean
meta_monitor_manager_kms_flip_crtc (MetaMonitorManagerKms *manager_kms,
MetaCRTC *crtc,
int x,
int y,
uint32_t fb_id,
GClosure *flip_closure)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
uint32_t *connectors;
unsigned int n_connectors;
int ret = -1;
if (!flip_closure)
{
flip_closure = g_new0 (MetaKmsFlipClosure, 1);
*flip_closure = (MetaKmsFlipClosure) {
.pending = 0,
.callback = flip_callback,
.user_data = user_data
};
}
g_assert (manager->power_save_mode == META_POWER_SAVE_ON);
get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
g_assert (n_connectors > 0);
if (!manager_kms->page_flips_not_supported)
{
ret = drmModePageFlip (manager_kms->fd,
crtc->crtc_id,
fb_id,
......@@ -1426,24 +1416,21 @@ meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
{
g_warning ("Failed to flip: %s", strerror (-ret));
manager_kms->page_flips_not_supported = TRUE;
break;
}
if (ret == 0)
flip_closure->pending++;
}
if (manager_kms->page_flips_not_supported && fb_in_use)
{
/* If the driver doesn't support page flipping, just set the mode directly
* with the new framebuffer.
*/
meta_monitor_manager_kms_apply_crtc_modes (manager_kms, fb_id);
flip_callback (user_data);
g_free (flip_closure);
}
if (manager_kms->page_flips_not_supported)
meta_monitor_manager_kms_apply_crtc_mode (manager_kms,
crtc,
x, y,
fb_id);
if (ret != 0)
return FALSE;
g_closure_ref (flip_closure);
return fb_in_use;
return TRUE;
}
static void
......@@ -1453,11 +1440,9 @@ page_flip_handler (int fd,
unsigned int usec,
void *data)
{
MetaKmsFlipClosure *flip_closure = data;
GClosure *flip_closure = data;
flip_closure->pending--;
if (flip_closure->pending == 0)
flip_closure->callback (flip_closure->user_data);
invoke_flip_closure (flip_closure);
}
void
......
......@@ -39,13 +39,21 @@ GType meta_monitor_manager_kms_get_type (void);
typedef void (*MetaKmsFlipCallback) (void *user_data);
void meta_monitor_manager_kms_apply_crtc_modes (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id);
gboolean meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id,
MetaKmsFlipCallback flip_callback,
void *user_data);
void meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms,
MetaCRTC *crtc,
int x,
int y,
uint32_t fb_id);
gboolean meta_monitor_manager_kms_is_crtc_active (MetaMonitorManagerKms *manager_kms,
MetaCRTC *crtc);
gboolean meta_monitor_manager_kms_flip_crtc (MetaMonitorManagerKms *manager_kms,
MetaCRTC *crtc,
int x,
int y,
uint32_t fb_id,
GClosure *flip_closure);
void meta_monitor_manager_kms_wait_for_flip (MetaMonitorManagerKms *manager_kms);
......
This diff is collapsed.
......@@ -44,13 +44,20 @@ int meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native);
void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
gboolean meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
int width,
int height,
GError **error);
gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native,
MetaRendererView *view,
int width,
int height,
GError **error);
void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,
uint32_t id,
gboolean ignore);
MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native);
void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native);
int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native);
#endif /* META_RENDERER_NATIVE_H */
......@@ -27,16 +27,22 @@
#include "backends/native/meta-stage-native.h"
#include "backends/meta-backend-private.h"
#include "backends/native/meta-renderer-native.h"
#include "meta/meta-backend.h"
#include "meta/meta-monitor-manager.h"
#include "meta/util.h"
static GQuark quark_view_frame_closure = 0;
struct _MetaStageNative
{
ClutterStageCogl parent;
CoglOnscreen *pending_onscreen;
CoglClosure *frame_closure;
int64_t presented_frame_counter_sync;
int64_t presented_frame_counter_complete;
};
static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
......@@ -63,27 +69,96 @@ get_legacy_view (MetaRenderer *renderer)
return NULL;
}
static CoglOnscreen *
get_legacy_onscreen (MetaStageNative *stage_native)
static void
frame_cb (CoglOnscreen *onscreen,
CoglFrameEvent frame_event,
CoglFrameInfo *frame_info,
void *user_data)
{
if (stage_native->pending_onscreen)
MetaStageNative *stage_native = user_data;
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_native);
int64_t global_frame_counter;
int64_t presented_frame_counter;
ClutterFrameInfo clutter_frame_info;
global_frame_counter = cogl_frame_info_get_global_frame_counter (frame_info);
switch (frame_event)
{
return stage_native->pending_onscreen;
case COGL_FRAME_EVENT_SYNC:
presented_frame_counter = stage_native->presented_frame_counter_sync;
stage_native->presented_frame_counter_sync = global_frame_counter;
break;
case COGL_FRAME_EVENT_COMPLETE:
presented_frame_counter = stage_native->presented_frame_counter_complete;
stage_native->presented_frame_counter_complete = global_frame_counter;
break;
default:
g_assert_not_reached ();
}
else
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
ClutterStageView *stage_view;
CoglFramebuffer *framebuffer;
stage_view = CLUTTER_STAGE_VIEW (get_legacy_view (renderer));
framebuffer = clutter_stage_view_get_framebuffer (stage_view);
if (global_frame_counter <= presented_frame_counter)
return;
clutter_frame_info = (ClutterFrameInfo) {
.frame_counter = global_frame_counter,
.refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
.presentation_time = cogl_frame_info_get_presentation_time (frame_info)
};
_clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
}
static void
ensure_frame_callback (MetaStageNative *stage_native,
ClutterStageView *stage_view)
{
CoglFramebuffer *framebuffer;
CoglOnscreen *onscreen;
CoglClosure *closure;
closure = g_object_get_qdata (G_OBJECT (stage_view),
quark_view_frame_closure);
if (closure)
return;
framebuffer = clutter_stage_view_get_framebuffer (stage_view);
onscreen = COGL_ONSCREEN (framebuffer);
closure = cogl_onscreen_add_frame_callback (onscreen,
frame_cb,
stage_native,
NULL);
g_object_set_qdata (G_OBJECT (stage_view),
quark_view_frame_closure,
closure);
}
static void
ensure_frame_callbacks (MetaStageNative *stage_native)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
GList *l;
for (l = meta_renderer_get_views (renderer); l; l = l->next)
{
ClutterStageView *stage_view = l->data;
return COGL_ONSCREEN (framebuffer);
ensure_frame_callback (stage_native, stage_view);
}
}
void
meta_stage_native_rebuild_views (MetaStageNative *stage_native)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
meta_renderer_rebuild_views (renderer);
ensure_frame_callbacks (stage_native);
}
void
meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
int width,
......@@ -91,13 +166,26 @@ meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
MetaRendererView *legacy_view;
GError *error = NULL;
cairo_rectangle_int_t view_layout;
legacy_view = get_legacy_view (renderer);
if (!legacy_view)
return;
if (!meta_renderer_native_set_legacy_view_size (renderer_native,
legacy_view,
width, height,
&error))
{
meta_warning ("Applying display configuration failed: %s\n",
error->message);
g_error_free (error);
return;
}
view_layout = (cairo_rectangle_int_t) {
.width = width,
.height = height
......@@ -107,29 +195,6 @@ meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
NULL);
}
static gboolean
meta_stage_native_realize (ClutterStageWindow *stage_window)
{
MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
MetaBackend *backend = meta_get_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglFramebuffer *framebuffer;
GError *error = NULL;
stage_native->pending_onscreen =
cogl_onscreen_new (clutter_backend->cogl_context, 1, 1);
framebuffer = COGL_FRAMEBUFFER (stage_native->pending_onscreen);
if (!cogl_framebuffer_allocate (framebuffer, &error))