kms: Predict state changes when processing update

We can't just update the state of the connector and CRTC from KMS since
it might contain too new updates, e.g. from a from a future hot plug. In
order to not add ad-hoc hot plug detection everywhere, predict the state
changes by looking inside the MetaKmsUpdate object, and let the hot-plug
state changes happen after the actual hot-plug event.

This fixes issues where connectors were discovered as disconnected while
doing a mode-set, meaning assumptions about the connectedness of
monitors elsewhere were broken until the hot plug event was processed.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/782

https://gitlab.gnome.org/GNOME/mutter/merge_requests/826
This commit is contained in:
Jonas Ådahl 2019-10-04 11:54:29 +02:00
parent 2a990cc140
commit 104bdde746
10 changed files with 157 additions and 59 deletions

View File

@ -25,6 +25,9 @@
void meta_kms_connector_update_state (MetaKmsConnector *connector,
drmModeRes *drm_resources);
void meta_kms_connector_predict_state (MetaKmsConnector *connector,
MetaKmsUpdate *update);
MetaKmsConnector * meta_kms_connector_new (MetaKmsImplDevice *impl_device,
drmModeConnector *drm_connector,
drmModeRes *drm_resources);

View File

@ -24,6 +24,7 @@
#include <errno.h>
#include "backends/native/meta-kms-crtc.h"
#include "backends/native/meta-kms-device-private.h"
#include "backends/native/meta-kms-impl-device.h"
#include "backends/native/meta-kms-update-private.h"
@ -495,6 +496,35 @@ meta_kms_connector_update_state (MetaKmsConnector *connector,
drmModeFreeConnector (drm_connector);
}
void
meta_kms_connector_predict_state (MetaKmsConnector *connector,
MetaKmsUpdate *update)
{
GList *mode_sets;
GList *l;
if (!connector->current_state)
return;
mode_sets = meta_kms_update_get_mode_sets (update);
for (l = mode_sets; l; l = l->next)
{
MetaKmsModeSet *mode_set = l->data;
MetaKmsCrtc *crtc;
if (!g_list_find (mode_set->connectors, connector))
continue;
crtc = mode_set->crtc;
if (crtc)
connector->current_state->current_crtc_id = meta_kms_crtc_get_id (crtc);
else
connector->current_state->current_crtc_id = 0;
break;
}
}
static void
find_property_ids (MetaKmsConnector *connector,
MetaKmsImplDevice *impl_device,

View File

@ -30,4 +30,7 @@ MetaKmsCrtc * meta_kms_crtc_new (MetaKmsImplDevice *impl_device,
void meta_kms_crtc_update_state (MetaKmsCrtc *crtc);
void meta_kms_crtc_predict_state (MetaKmsCrtc *crtc,
MetaKmsUpdate *update);
#endif /* META_KMS_CRTC_PRIVATE_H */

View File

@ -143,6 +143,63 @@ meta_kms_crtc_update_state (MetaKmsCrtc *crtc)
drmModeFreeCrtc (drm_crtc);
}
void
meta_kms_crtc_predict_state (MetaKmsCrtc *crtc,
MetaKmsUpdate *update)
{
GList *mode_sets;
GList *crtc_gammas;
GList *l;
mode_sets = meta_kms_update_get_mode_sets (update);
for (l = mode_sets; l; l = l->next)
{
MetaKmsModeSet *mode_set = l->data;
if (mode_set->crtc != crtc)
continue;
if (mode_set->drm_mode)
{
MetaKmsPlaneAssignment *plane_assignment;
plane_assignment =
meta_kms_update_get_primary_plane_assignment (update, crtc);
crtc->current_state.rect =
meta_fixed_16_rectangle_to_rectangle (plane_assignment->src_rect);
crtc->current_state.is_drm_mode_valid = TRUE;
crtc->current_state.drm_mode = *mode_set->drm_mode;
}
else
{
crtc->current_state.rect = (MetaRectangle) { 0 };
crtc->current_state.is_drm_mode_valid = FALSE;
crtc->current_state.drm_mode = (drmModeModeInfo) { 0 };
}
break;
}
crtc_gammas = meta_kms_update_get_crtc_gammas (update);
for (l = crtc_gammas; l; l = l->next)
{
MetaKmsCrtcGamma *gamma = l->data;
if (gamma->crtc != crtc)
continue;
crtc->current_state.gamma.size = gamma->size;
crtc->current_state.gamma.red =
g_memdup (gamma->red, gamma->size * sizeof (uint16_t));
crtc->current_state.gamma.green =
g_memdup (gamma->green, gamma->size * sizeof (uint16_t));
crtc->current_state.gamma.blue =
g_memdup (gamma->blue, gamma->size * sizeof (uint16_t));
break;
}
}
MetaKmsCrtc *
meta_kms_crtc_new (MetaKmsImplDevice *impl_device,
drmModeCrtc *drm_crtc,

View File

@ -24,7 +24,9 @@
MetaKmsImplDevice * meta_kms_device_get_impl_device (MetaKmsDevice *device);
void meta_kms_device_update_states_in_impl (MetaKmsDevice *device,
MetaKmsUpdateStatesFlags flags);
void meta_kms_device_update_states_in_impl (MetaKmsDevice *device);
void meta_kms_device_predict_states_in_impl (MetaKmsDevice *device,
MetaKmsUpdate *update);
#endif /* META_KMS_DEVICE_PRIVATE_H */

View File

@ -109,28 +109,34 @@ meta_kms_device_get_primary_plane_for (MetaKmsDevice *device,
}
void
meta_kms_device_update_states_in_impl (MetaKmsDevice *device,
MetaKmsUpdateStatesFlags flags)
meta_kms_device_update_states_in_impl (MetaKmsDevice *device)
{
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
meta_assert_in_kms_impl (device->kms);
meta_assert_is_waiting_for_kms_impl_task (device->kms);
meta_kms_impl_device_update_states (impl_device);
g_list_free (device->crtcs);
device->crtcs = meta_kms_impl_device_copy_crtcs (impl_device);
g_list_free (device->connectors);
device->connectors = meta_kms_impl_device_copy_connectors (impl_device);
g_list_free (device->planes);
device->planes = meta_kms_impl_device_copy_planes (impl_device);
}
void
meta_kms_device_predict_states_in_impl (MetaKmsDevice *device,
MetaKmsUpdate *update)
{
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
meta_assert_in_kms_impl (device->kms);
meta_kms_impl_device_update_states (impl_device, flags);
if (flags & META_KMS_UPDATE_STATES_FLAG_HOTPLUG)
{
meta_assert_is_waiting_for_kms_impl_task (device->kms);
g_list_free (device->crtcs);
device->crtcs = meta_kms_impl_device_copy_crtcs (impl_device);
g_list_free (device->connectors);
device->connectors = meta_kms_impl_device_copy_connectors (impl_device);
g_list_free (device->planes);
device->planes = meta_kms_impl_device_copy_planes (impl_device);
}
meta_kms_impl_device_predict_states (impl_device, update);
}
static gboolean

View File

@ -319,8 +319,7 @@ init_planes (MetaKmsImplDevice *impl_device)
}
void
meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device,
MetaKmsUpdateStatesFlags flags)
meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device)
{
drmModeRes *drm_resources;
@ -328,8 +327,7 @@ meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device,
drm_resources = drmModeGetResources (impl_device->fd);
if (flags & META_KMS_UPDATE_STATES_FLAG_HOTPLUG)
update_connectors (impl_device, drm_resources);
update_connectors (impl_device, drm_resources);
g_list_foreach (impl_device->crtcs, (GFunc) meta_kms_crtc_update_state,
NULL);
@ -338,6 +336,16 @@ meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device,
drmModeFreeResources (drm_resources);
}
void
meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update)
{
g_list_foreach (impl_device->crtcs, (GFunc) meta_kms_crtc_predict_state,
update);
g_list_foreach (impl_device->connectors, (GFunc) meta_kms_connector_predict_state,
update);
}
MetaKmsImplDevice *
meta_kms_impl_device_new (MetaKmsDevice *device,
MetaKmsImpl *impl,

View File

@ -53,8 +53,10 @@ int meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device);
int meta_kms_impl_device_leak_fd (MetaKmsImplDevice *impl_device);
void meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device,
MetaKmsUpdateStatesFlags flags);
void meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device);
void meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update);
int meta_kms_impl_device_close (MetaKmsImplDevice *impl_device);

View File

@ -56,10 +56,4 @@ typedef enum _MetaKmsDeviceFlag
META_KMS_DEVICE_FLAG_PLATFORM_DEVICE = 1 << 1,
} MetaKmsDeviceFlag;
typedef enum _MetaKmsUpdateStatesFlags
{
META_KMS_UPDATE_STATES_FLAG_NONE = 0,
META_KMS_UPDATE_STATES_FLAG_HOTPLUG = 1 << 0,
} MetaKmsUpdateStatesFlags;
#endif /* META_KMS_IMPL_TYPES_H */

View File

@ -175,10 +175,6 @@ struct _MetaKms
G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT)
static void
meta_kms_update_states_in_impl (MetaKms *kms,
MetaKmsUpdateStatesFlags flags);
MetaKmsUpdate *
meta_kms_ensure_pending_update (MetaKms *kms)
{
@ -194,6 +190,17 @@ meta_kms_get_pending_update (MetaKms *kms)
return kms->pending_update;
}
static void
meta_kms_predict_states_in_impl (MetaKms *kms,
MetaKmsUpdate *update)
{
meta_assert_in_kms_impl (kms);
g_list_foreach (kms->devices,
(GFunc) meta_kms_device_predict_states_in_impl,
update);
}
static gboolean
meta_kms_update_process_in_impl (MetaKmsImpl *impl,
gpointer user_data,
@ -205,8 +212,7 @@ meta_kms_update_process_in_impl (MetaKmsImpl *impl,
ret = meta_kms_impl_process_update (impl, update, error);
if (meta_kms_update_has_mode_set (update))
meta_kms_update_states_in_impl (meta_kms_impl_get_kms (impl),
META_KMS_UPDATE_STATES_FLAG_NONE);
meta_kms_predict_states_in_impl (meta_kms_impl_get_kms (impl), update);
return ret;
}
@ -461,22 +467,16 @@ meta_kms_is_waiting_for_impl_task (MetaKms *kms)
}
static void
meta_kms_update_states_in_impl (MetaKms *kms,
MetaKmsUpdateStatesFlags flags)
meta_kms_update_states_in_impl (MetaKms *kms)
{
GList *l;
COGL_TRACE_BEGIN_SCOPED (MetaKmsUpdateStates,
"KMS (update states)");
meta_assert_in_kms_impl (kms);
for (l = kms->devices; l; l = l->next)
{
MetaKmsDevice *device = l->data;
meta_kms_device_update_states_in_impl (device, flags);
}
g_list_foreach (kms->devices,
(GFunc) meta_kms_device_update_states_in_impl,
NULL);
}
static gboolean
@ -485,22 +485,17 @@ update_states_in_impl (MetaKmsImpl *impl,
GError **error)
{
MetaKms *kms = meta_kms_impl_get_kms (impl);;
MetaKmsUpdateStatesFlags flags = GPOINTER_TO_UINT (user_data);
meta_kms_update_states_in_impl (kms, flags);
meta_kms_update_states_in_impl (kms);
return TRUE;
}
static gboolean
meta_kms_update_states_sync (MetaKms *kms,
MetaKmsUpdateStatesFlags flags,
GError **error)
meta_kms_update_states_sync (MetaKms *kms,
GError **error)
{
return meta_kms_run_impl_task_sync (kms,
update_states_in_impl,
GUINT_TO_POINTER (flags),
error);
return meta_kms_run_impl_task_sync (kms, update_states_in_impl, NULL, error);
}
static void
@ -508,9 +503,7 @@ handle_hotplug_event (MetaKms *kms)
{
g_autoptr (GError) error = NULL;
if (!meta_kms_update_states_sync (kms,
META_KMS_UPDATE_STATES_FLAG_HOTPLUG,
&error))
if (!meta_kms_update_states_sync (kms, &error))
g_warning ("Updating KMS state failed: %s", error->message);
g_signal_emit (kms, signals[RESOURCES_CHANGED], 0);