mutter/src/backends/native/meta-kms-impl-simple.c
Jonas Ådahl ce3409b2b7 kms-impl-simple: Handle mode set race conditions gracefully
If we end up trying to do a mode set on a DRM state that has already
changed behind our back without us yet having seen the hotplug event we
may fail with `EINVAL`. Since the renderer layer doesn't handle mode set
failure, it'll still try to page flip later on, which will then also
fail. When failing, it'll try to look up the cached mode set in order to
retry the mode set later on, as is needed to handle other error
conditions. However, if the mode set prior to the page flip failed, we
won't cache the mode set, and the page flip error handling code will get
confused.

Instead of asserting that a page flip always has a valid cached mode set
ready to look up, handle it being missing more gracefully by failing to
mode set. It is expected that things will correct themself as there
should be a hotplug event waiting around the the corner, to reconfigure
the monitor configuration setting new modes.

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

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1007
2020-01-16 14:50:34 +00:00

884 lines
27 KiB
C

/*
* Copyright (C) 2018-2019 Red Hat
* Copyright (C) 2019 DisplayLink (UK) Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include "backends/native/meta-kms-impl-simple.h"
#include <errno.h>
#include <gbm.h>
#include <xf86drmMode.h>
#include "backends/native/meta-kms-connector.h"
#include "backends/native/meta-kms-crtc.h"
#include "backends/native/meta-kms-device-private.h"
#include "backends/native/meta-kms-page-flip-private.h"
#include "backends/native/meta-kms-plane.h"
#include "backends/native/meta-kms-private.h"
#include "backends/native/meta-kms-update-private.h"
#include "backends/native/meta-kms-utils.h"
typedef struct _CachedModeSet
{
GList *connectors;
drmModeModeInfo *drm_mode;
} CachedModeSet;
struct _MetaKmsImplSimple
{
MetaKmsImpl parent;
GSource *mode_set_fallback_feedback_source;
GList *mode_set_fallback_page_flip_datas;
GList *pending_page_flip_retries;
GSource *retry_page_flips_source;
GList *postponed_page_flip_datas;
GList *postponed_mode_set_fallback_datas;
GHashTable *cached_mode_sets;
};
G_DEFINE_TYPE (MetaKmsImplSimple, meta_kms_impl_simple,
META_TYPE_KMS_IMPL)
static void
flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple);
MetaKmsImplSimple *
meta_kms_impl_simple_new (MetaKms *kms,
GError **error)
{
return g_object_new (META_TYPE_KMS_IMPL_SIMPLE,
"kms", kms,
NULL);
}
static gboolean
process_connector_property (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsConnectorProperty *connector_property,
GError **error)
{
MetaKmsConnector *connector = connector_property->connector;
MetaKmsDevice *device = meta_kms_connector_get_device (connector);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
int fd;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeObjectSetProperty (fd,
meta_kms_connector_get_id (connector),
DRM_MODE_OBJECT_CONNECTOR,
connector_property->prop_id,
connector_property->value);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set connector %u property %u: %s",
meta_kms_connector_get_id (connector),
connector_property->prop_id,
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
process_plane_property (MetaKmsImpl *impl,
MetaKmsPlane *plane,
MetaKmsProperty *prop,
GError **error)
{
MetaKmsDevice *device = meta_kms_plane_get_device (plane);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
int fd;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeObjectSetProperty (fd,
meta_kms_plane_get_id (plane),
DRM_MODE_OBJECT_PLANE,
prop->prop_id,
prop->value);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set plane %u property %u: %s",
meta_kms_plane_get_id (plane),
prop->prop_id,
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static CachedModeSet *
cached_mode_set_new (GList *connectors,
const drmModeModeInfo *drm_mode)
{
CachedModeSet *cached_mode_set;
cached_mode_set = g_new0 (CachedModeSet, 1);
*cached_mode_set = (CachedModeSet) {
.connectors = g_list_copy (connectors),
.drm_mode = g_memdup (drm_mode, sizeof *drm_mode),
};
return cached_mode_set;
}
static void
cached_mode_set_free (CachedModeSet *cached_mode_set)
{
g_list_free (cached_mode_set->connectors);
g_free (cached_mode_set->drm_mode);
g_free (cached_mode_set);
}
static void
fill_connector_ids_array (GList *connectors,
uint32_t **out_connectors,
int *out_n_connectors)
{
GList *l;
int i;
*out_n_connectors = g_list_length (connectors);
*out_connectors = g_new0 (uint32_t, *out_n_connectors);
i = 0;
for (l = connectors; l; l = l->next)
{
MetaKmsConnector *connector = l->data;
(*out_connectors)[i++] = meta_kms_connector_get_id (connector);
}
}
static gboolean
process_mode_set (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsModeSet *mode_set,
GError **error)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
MetaKmsCrtc *crtc = mode_set->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
g_autofree uint32_t *connectors = NULL;
int n_connectors;
MetaKmsPlaneAssignment *plane_assignment;
uint32_t x, y;
uint32_t fb_id;
int fd;
int ret;
crtc = mode_set->crtc;
if (mode_set->drm_mode)
{
GList *l;
fill_connector_ids_array (mode_set->connectors,
&connectors,
&n_connectors);
plane_assignment = meta_kms_update_get_primary_plane_assignment (update,
crtc);
if (!plane_assignment)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing primary plane assignment for legacy mode set on CRTC %u",
meta_kms_crtc_get_id (crtc));
return FALSE;
}
x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
for (l = plane_assignment->plane_properties; l; l = l->next)
{
MetaKmsProperty *prop = l->data;
if (!process_plane_property (impl, plane_assignment->plane,
prop, error))
return FALSE;
}
fb_id = plane_assignment->fb_id;
}
else
{
x = y = 0;
n_connectors = 0;
connectors = NULL;
fb_id = 0;
}
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeSetCrtc (fd,
meta_kms_crtc_get_id (crtc),
fb_id,
x, y,
connectors, n_connectors,
mode_set->drm_mode);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set mode %s on CRTC %u: %s",
mode_set->drm_mode ? mode_set->drm_mode->name : "off",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
if (mode_set->drm_mode)
{
g_hash_table_replace (impl_simple->cached_mode_sets,
crtc,
cached_mode_set_new (mode_set->connectors,
mode_set->drm_mode));
}
else
{
g_hash_table_remove (impl_simple->cached_mode_sets, crtc);
}
return TRUE;
}
static gboolean
process_crtc_gamma (MetaKmsImpl *impl,
MetaKmsCrtcGamma *gamma,
GError **error)
{
MetaKmsCrtc *crtc = gamma->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
int fd;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeCrtcSetGamma (fd, meta_kms_crtc_get_id (crtc),
gamma->size,
gamma->red,
gamma->green,
gamma->blue);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeCrtcSetGamma on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
is_timestamp_earlier_than (uint64_t ts1,
uint64_t ts2)
{
if (ts1 == ts2)
return FALSE;
else
return ts2 - ts1 < UINT64_MAX / 2;
}
typedef struct _RetryPageFlipData
{
MetaKmsCrtc *crtc;
uint32_t fb_id;
MetaKmsPageFlipData *page_flip_data;
float refresh_rate;
uint64_t retry_time_us;
} RetryPageFlipData;
static void
retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
{
g_assert (!retry_page_flip_data->page_flip_data);
g_free (retry_page_flip_data);
}
static CachedModeSet *
get_cached_mode_set (MetaKmsImplSimple *impl_simple,
MetaKmsCrtc *crtc)
{
return g_hash_table_lookup (impl_simple->cached_mode_sets, crtc);
}
static float
get_cached_crtc_refresh_rate (MetaKmsImplSimple *impl_simple,
MetaKmsCrtc *crtc)
{
CachedModeSet *cached_mode_set;
cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets,
crtc);
g_assert (cached_mode_set);
return meta_calculate_drm_mode_refresh_rate (cached_mode_set->drm_mode);
}
static gboolean
retry_page_flips (gpointer user_data)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (user_data);
uint64_t now_us;
GList *l;
meta_assert_in_kms_impl (meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)));
now_us = g_source_get_time (impl_simple->retry_page_flips_source);
l = impl_simple->pending_page_flip_retries;
while (l)
{
RetryPageFlipData *retry_page_flip_data = l->data;
MetaKmsCrtc *crtc = retry_page_flip_data->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
GList *l_next = l->next;
int fd;
int ret;
MetaKmsPageFlipData *page_flip_data;
if (is_timestamp_earlier_than (now_us,
retry_page_flip_data->retry_time_us))
{
l = l_next;
continue;
}
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
retry_page_flip_data->fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
retry_page_flip_data->page_flip_data);
if (ret == -EBUSY)
{
float refresh_rate;
refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc);
retry_page_flip_data->retry_time_us +=
(uint64_t) (G_USEC_PER_SEC / refresh_rate);
l = l_next;
continue;
}
impl_simple->pending_page_flip_retries =
g_list_remove_link (impl_simple->pending_page_flip_retries, l);
page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
if (ret != 0)
{
g_autoptr (GError) error = NULL;
g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModePageFlip on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
if (!g_error_matches (error,
G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED))
g_critical ("Failed to page flip: %s", error->message);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, error);
}
retry_page_flip_data_free (retry_page_flip_data);
l = l_next;
}
if (impl_simple->pending_page_flip_retries)
{
GList *l;
uint64_t earliest_retry_time_us = 0;
for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *retry_page_flip_data = l->data;
if (l == impl_simple->pending_page_flip_retries ||
is_timestamp_earlier_than (retry_page_flip_data->retry_time_us,
earliest_retry_time_us))
earliest_retry_time_us = retry_page_flip_data->retry_time_us;
}
g_source_set_ready_time (impl_simple->retry_page_flips_source,
earliest_retry_time_us);
return G_SOURCE_CONTINUE;
}
else
{
g_clear_pointer (&impl_simple->retry_page_flips_source,
g_source_unref);
flush_postponed_page_flip_datas (impl_simple);
return G_SOURCE_REMOVE;
}
}
static void
schedule_retry_page_flip (MetaKmsImplSimple *impl_simple,
MetaKmsCrtc *crtc,
uint32_t fb_id,
float refresh_rate,
MetaKmsPageFlipData *page_flip_data)
{
RetryPageFlipData *retry_page_flip_data;
uint64_t now_us;
uint64_t retry_time_us;
now_us = g_get_monotonic_time ();
retry_time_us = now_us + (uint64_t) (G_USEC_PER_SEC / refresh_rate);
retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
*retry_page_flip_data = (RetryPageFlipData) {
.crtc = crtc,
.fb_id = fb_id,
.page_flip_data = meta_kms_page_flip_data_ref (page_flip_data),
.refresh_rate = refresh_rate,
.retry_time_us = retry_time_us,
};
if (!impl_simple->retry_page_flips_source)
{
MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple));
GSource *source;
source = meta_kms_add_source_in_impl (kms, retry_page_flips,
impl_simple, NULL);
g_source_set_ready_time (source, retry_time_us);
impl_simple->retry_page_flips_source = source;
}
else
{
GList *l;
for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *pending_retry_page_flip_data = l->data;
uint64_t pending_retry_time_us =
pending_retry_page_flip_data->retry_time_us;
if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us))
{
g_source_set_ready_time (impl_simple->retry_page_flips_source,
retry_time_us);
break;
}
}
}
impl_simple->pending_page_flip_retries =
g_list_append (impl_simple->pending_page_flip_retries,
retry_page_flip_data);
}
static void
invoke_page_flip_datas (GList *page_flip_datas,
MetaPageFlipDataFeedbackFunc func)
{
g_list_foreach (page_flip_datas, (GFunc) func, NULL);
}
static void
clear_page_flip_datas (GList **page_flip_datas)
{
g_list_free_full (*page_flip_datas,
(GDestroyNotify) meta_kms_page_flip_data_unref);
*page_flip_datas = NULL;
}
static gboolean
mode_set_fallback_feedback_idle (gpointer user_data)
{
MetaKmsImplSimple *impl_simple = user_data;
g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source,
g_source_unref);
if (impl_simple->pending_page_flip_retries)
{
impl_simple->postponed_mode_set_fallback_datas =
g_steal_pointer (&impl_simple->mode_set_fallback_page_flip_datas);
}
else
{
invoke_page_flip_datas (impl_simple->mode_set_fallback_page_flip_datas,
meta_kms_page_flip_data_mode_set_fallback_in_impl);
clear_page_flip_datas (&impl_simple->mode_set_fallback_page_flip_datas);
}
return G_SOURCE_REMOVE;
}
static gboolean
mode_set_fallback (MetaKmsImplSimple *impl_simple,
MetaKmsUpdate *update,
MetaKmsPageFlip *page_flip,
MetaKmsPlaneAssignment *plane_assignment,
MetaKmsPageFlipData *page_flip_data,
GError **error)
{
MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple));
MetaKmsCrtc *crtc = page_flip->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
CachedModeSet *cached_mode_set;
g_autofree uint32_t *connectors = NULL;
int n_connectors;
uint32_t x, y;
int fd;
int ret;
cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets,
crtc);
g_assert (cached_mode_set);
fill_connector_ids_array (cached_mode_set->connectors,
&connectors,
&n_connectors);
x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeSetCrtc (fd,
meta_kms_crtc_get_id (crtc),
plane_assignment->fb_id,
x, y,
connectors, n_connectors,
cached_mode_set->drm_mode);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeSetCrtc mode '%s' on CRTC %u failed: %s",
cached_mode_set->drm_mode->name,
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
if (!impl_simple->mode_set_fallback_feedback_source)
{
GSource *source;
source = meta_kms_add_source_in_impl (kms,
mode_set_fallback_feedback_idle,
impl_simple,
NULL);
impl_simple->mode_set_fallback_feedback_source = source;
}
impl_simple->mode_set_fallback_page_flip_datas =
g_list_prepend (impl_simple->mode_set_fallback_page_flip_datas,
meta_kms_page_flip_data_ref (page_flip_data));
return TRUE;
}
static gboolean
process_page_flip (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsPageFlip *page_flip,
GError **error)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
MetaKmsCrtc *crtc;
MetaKmsDevice *device;
MetaKmsImplDevice *impl_device;
MetaKmsPlaneAssignment *plane_assignment;
MetaKmsPageFlipData *page_flip_data;
MetaKmsCustomPageFlipFunc custom_page_flip_func;
int fd;
int ret;
crtc = page_flip->crtc;
plane_assignment = meta_kms_update_get_primary_plane_assignment (update,
crtc);
page_flip_data = meta_kms_page_flip_data_new (impl,
crtc,
page_flip->feedback,
page_flip->user_data);
device = meta_kms_crtc_get_device (crtc);
impl_device = meta_kms_device_get_impl_device (device);
fd = meta_kms_impl_device_get_fd (impl_device);
custom_page_flip_func = page_flip->custom_page_flip_func;
if (custom_page_flip_func)
{
ret = custom_page_flip_func (page_flip->custom_page_flip_user_data,
meta_kms_page_flip_data_ref (page_flip_data));
}
else
{
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
plane_assignment->fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
meta_kms_page_flip_data_ref (page_flip_data));
}
if (ret == -EBUSY)
{
CachedModeSet *cached_mode_set;
cached_mode_set = get_cached_mode_set (impl_simple, crtc);
if (cached_mode_set)
{
drmModeModeInfo *drm_mode;
float refresh_rate;
drm_mode = cached_mode_set->drm_mode;
refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
schedule_retry_page_flip (impl_simple,
crtc,
plane_assignment->fb_id,
refresh_rate,
page_flip_data);
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Page flip of %u failed, and no mode set available",
meta_kms_crtc_get_id (crtc));
meta_kms_page_flip_data_unref (page_flip_data);
return FALSE;
}
}
else if (ret == -EINVAL)
{
if (!mode_set_fallback (impl_simple,
update,
page_flip,
plane_assignment,
page_flip_data,
error))
{
meta_kms_page_flip_data_unref (page_flip_data);
return FALSE;
}
}
else if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModePageFlip on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
meta_kms_page_flip_data_unref (page_flip_data);
return FALSE;
}
meta_kms_page_flip_data_unref (page_flip_data);
return TRUE;
}
static void
discard_page_flip (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsPageFlip *page_flip)
{
MetaKmsCrtc *crtc;
MetaKmsPageFlipData *page_flip_data;
crtc = page_flip->crtc;
page_flip_data = meta_kms_page_flip_data_new (impl,
crtc,
page_flip->feedback,
page_flip->user_data);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
meta_kms_page_flip_data_unref (page_flip_data);
}
static gboolean
meta_kms_impl_simple_process_update (MetaKmsImpl *impl,
MetaKmsUpdate *update,
GError **error)
{
GList *l;
meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl));
for (l = meta_kms_update_get_connector_properties (update); l; l = l->next)
{
MetaKmsConnectorProperty *connector_property = l->data;
if (!process_connector_property (impl, update, connector_property, error))
goto discard_page_flips;
}
for (l = meta_kms_update_get_mode_sets (update); l; l = l->next)
{
MetaKmsModeSet *mode_set = l->data;
if (!process_mode_set (impl, update, mode_set, error))
goto discard_page_flips;
}
for (l = meta_kms_update_get_crtc_gammas (update); l; l = l->next)
{
MetaKmsCrtcGamma *gamma = l->data;
if (!process_crtc_gamma (impl, gamma, error))
goto discard_page_flips;
}
for (l = meta_kms_update_get_page_flips (update); l; l = l->next)
{
MetaKmsPageFlip *page_flip = l->data;
if (!process_page_flip (impl, update, page_flip, error))
goto discard_page_flips;
}
return TRUE;
discard_page_flips:
for (l = meta_kms_update_get_page_flips (update); l; l = l->next)
{
MetaKmsPageFlip *page_flip = l->data;
discard_page_flip (impl, update, page_flip);
}
return FALSE;
}
static void
flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple)
{
invoke_page_flip_datas (impl_simple->postponed_page_flip_datas,
meta_kms_page_flip_data_flipped_in_impl);
clear_page_flip_datas (&impl_simple->postponed_page_flip_datas);
invoke_page_flip_datas (impl_simple->postponed_mode_set_fallback_datas,
meta_kms_page_flip_data_mode_set_fallback_in_impl);
clear_page_flip_datas (&impl_simple->postponed_mode_set_fallback_datas);
}
static void
meta_kms_impl_simple_handle_page_flip_callback (MetaKmsImpl *impl,
MetaKmsPageFlipData *page_flip_data)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
if (impl_simple->pending_page_flip_retries)
{
impl_simple->postponed_page_flip_datas =
g_list_append (impl_simple->postponed_page_flip_datas,
meta_kms_page_flip_data_ref (page_flip_data));
}
else
{
meta_kms_page_flip_data_flipped_in_impl (page_flip_data);
}
meta_kms_page_flip_data_unref (page_flip_data);
}
static void
meta_kms_impl_simple_discard_pending_page_flips (MetaKmsImpl *impl)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
GList *l;
if (!impl_simple->pending_page_flip_retries)
return;
for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *retry_page_flip_data = l->data;
MetaKmsPageFlipData *page_flip_data;
page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
retry_page_flip_data_free (retry_page_flip_data);
}
g_clear_pointer (&impl_simple->pending_page_flip_retries, g_list_free);
g_clear_pointer (&impl_simple->retry_page_flips_source,
g_source_destroy);
}
static void
meta_kms_impl_simple_dispatch_idle (MetaKmsImpl *impl)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
if (impl_simple->mode_set_fallback_feedback_source)
mode_set_fallback_feedback_idle (impl_simple);
}
static void
meta_kms_impl_simple_finalize (GObject *object)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (object);
g_list_free_full (impl_simple->pending_page_flip_retries,
(GDestroyNotify) retry_page_flip_data_free);
g_list_free_full (impl_simple->postponed_page_flip_datas,
(GDestroyNotify) meta_kms_page_flip_data_unref);
g_list_free_full (impl_simple->postponed_mode_set_fallback_datas,
(GDestroyNotify) meta_kms_page_flip_data_unref);
g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source,
g_source_destroy);
g_hash_table_destroy (impl_simple->cached_mode_sets);
G_OBJECT_CLASS (meta_kms_impl_simple_parent_class)->finalize (object);
}
static void
meta_kms_impl_simple_init (MetaKmsImplSimple *impl_simple)
{
impl_simple->cached_mode_sets =
g_hash_table_new_full (NULL,
NULL,
NULL,
(GDestroyNotify) cached_mode_set_free);
}
static void
meta_kms_impl_simple_class_init (MetaKmsImplSimpleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaKmsImplClass *impl_class = META_KMS_IMPL_CLASS (klass);
object_class->finalize = meta_kms_impl_simple_finalize;
impl_class->process_update = meta_kms_impl_simple_process_update;
impl_class->handle_page_flip_callback = meta_kms_impl_simple_handle_page_flip_callback;
impl_class->discard_pending_page_flips = meta_kms_impl_simple_discard_pending_page_flips;
impl_class->dispatch_idle = meta_kms_impl_simple_dispatch_idle;
}