mirror of
https://github.com/brl/mutter.git
synced 2025-03-13 14:55:05 +00:00

This makes it possible to post KMS updates that will always defer until just before the scanout deadline. This is useful to allow queuing cursor updates where we don't want to post them to KMS immediately, but rather wait until as late as possible to get lower latency. We cannot delay primary plane compositions however, and this is due to how the kernel may prioritize GPU work - not until a pipeline gets attached to a atomic commit will it in some drivers get bumped to high priority. This means we still need to post any update that depends on OpenGL pipelines as soon as possible. To avoid working on compositing, then getting stomped on the feet by the deadline scheduler, the deadline timer is disarmed whenever there is a frame currently being painted. This will still allow new cursor updates to arrive during composition, but will delay the actual KMS commit until the primary plane update has been posted. Still, even for cursor-only we still need higher than default timing capabilities, thus the deadline scheduler depends on the KMS thread getting real-time scheduling priority. When the thread isn't realtime scheduled, the KMS thread instead asks the main thread to "flush" the commit as part of the regular frame update. A flushing update means one that isn't set to always defer and has a latching CRTC. The verbose KMS debug logging makes the processing take too long, making us more likely to miss the deadline. Avoid this by increasing the evasion length when debug logging is enabled. Not the best, but better than changing the behavior completely. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
583 lines
16 KiB
C
583 lines
16 KiB
C
/*
|
|
* Copyright (C) 2019 Red Hat
|
|
*
|
|
* 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-crtc.h"
|
|
#include "backends/native/meta-kms-crtc-private.h"
|
|
|
|
#include "backends/native/meta-kms-device-private.h"
|
|
#include "backends/native/meta-kms-impl-device.h"
|
|
#include "backends/native/meta-kms-impl-device-atomic.h"
|
|
#include "backends/native/meta-kms-impl-device-simple.h"
|
|
#include "backends/native/meta-kms-mode.h"
|
|
#include "backends/native/meta-kms-update-private.h"
|
|
#include "backends/native/meta-kms-utils.h"
|
|
|
|
#define DEADLINE_EVASION_US 500
|
|
#define DEADLINE_EVASION_WITH_KMS_TOPIC_US 1000
|
|
|
|
typedef struct _MetaKmsCrtcPropTable
|
|
{
|
|
MetaKmsProp props[META_KMS_CRTC_N_PROPS];
|
|
} MetaKmsCrtcPropTable;
|
|
|
|
struct _MetaKmsCrtc
|
|
{
|
|
GObject parent;
|
|
|
|
MetaKmsDevice *device;
|
|
|
|
uint32_t id;
|
|
int idx;
|
|
|
|
MetaKmsCrtcState current_state;
|
|
|
|
MetaKmsCrtcPropTable prop_table;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT)
|
|
|
|
MetaKmsDevice *
|
|
meta_kms_crtc_get_device (MetaKmsCrtc *crtc)
|
|
{
|
|
return crtc->device;
|
|
}
|
|
|
|
const MetaKmsCrtcState *
|
|
meta_kms_crtc_get_current_state (MetaKmsCrtc *crtc)
|
|
{
|
|
return &crtc->current_state;
|
|
}
|
|
|
|
uint32_t
|
|
meta_kms_crtc_get_id (MetaKmsCrtc *crtc)
|
|
{
|
|
return crtc->id;
|
|
}
|
|
|
|
int
|
|
meta_kms_crtc_get_idx (MetaKmsCrtc *crtc)
|
|
{
|
|
return crtc->idx;
|
|
}
|
|
|
|
uint32_t
|
|
meta_kms_crtc_get_prop_id (MetaKmsCrtc *crtc,
|
|
MetaKmsCrtcProp prop)
|
|
{
|
|
return crtc->prop_table.props[prop].prop_id;
|
|
}
|
|
|
|
const char *
|
|
meta_kms_crtc_get_prop_name (MetaKmsCrtc *crtc,
|
|
MetaKmsCrtcProp prop)
|
|
{
|
|
return crtc->prop_table.props[prop].name;
|
|
}
|
|
|
|
uint64_t
|
|
meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc,
|
|
MetaKmsCrtcProp property,
|
|
uint64_t value)
|
|
{
|
|
MetaKmsProp *prop = &crtc->prop_table.props[property];
|
|
return meta_kms_prop_convert_value (prop, value);
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_crtc_is_active (MetaKmsCrtc *crtc)
|
|
{
|
|
return crtc->current_state.is_active;
|
|
}
|
|
|
|
static void
|
|
read_crtc_gamma (MetaKmsCrtc *crtc,
|
|
MetaKmsCrtcState *crtc_state,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeCrtc *drm_crtc)
|
|
{
|
|
MetaKmsProp *prop_lut;
|
|
MetaKmsProp *prop_size;
|
|
uint64_t blob_id;
|
|
uint64_t lut_size;
|
|
int fd;
|
|
drmModePropertyBlobPtr blob;
|
|
int drm_lut_size;
|
|
struct drm_color_lut *drm_lut;
|
|
int i;
|
|
|
|
prop_lut = &crtc->prop_table.props[META_KMS_CRTC_PROP_GAMMA_LUT];
|
|
prop_size = &crtc->prop_table.props[META_KMS_CRTC_PROP_GAMMA_LUT_SIZE];
|
|
|
|
if (!prop_lut->prop_id || !prop_size->prop_id)
|
|
return;
|
|
|
|
lut_size = prop_size->value;
|
|
if (lut_size <= 0)
|
|
return;
|
|
|
|
crtc_state->gamma.size = lut_size;
|
|
crtc_state->gamma.supported = TRUE;
|
|
|
|
blob_id = prop_lut->value;
|
|
if (blob_id == 0)
|
|
return;
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
blob = drmModeGetPropertyBlob (fd, blob_id);
|
|
if (!blob)
|
|
return;
|
|
|
|
drm_lut_size = blob->length / sizeof (struct drm_color_lut);
|
|
if (drm_lut_size == 0)
|
|
{
|
|
drmModeFreePropertyBlob (blob);
|
|
return;
|
|
}
|
|
|
|
drm_lut = blob->data;
|
|
|
|
crtc_state->gamma.value = meta_gamma_lut_new_sized (drm_lut_size);
|
|
|
|
for (i = 0; i < drm_lut_size; i++)
|
|
{
|
|
crtc_state->gamma.value->red[i] = drm_lut[i].red;
|
|
crtc_state->gamma.value->green[i] = drm_lut[i].green;
|
|
crtc_state->gamma.value->blue[i] = drm_lut[i].blue;
|
|
}
|
|
|
|
drmModeFreePropertyBlob (blob);
|
|
}
|
|
|
|
static void
|
|
read_crtc_legacy_gamma (MetaKmsCrtc *crtc,
|
|
MetaKmsCrtcState *crtc_state,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeCrtc *drm_crtc)
|
|
{
|
|
crtc_state->gamma.size = drm_crtc->gamma_size;
|
|
crtc_state->gamma.supported = drm_crtc->gamma_size != 0;
|
|
crtc_state->gamma.value = meta_gamma_lut_new_sized (drm_crtc->gamma_size);
|
|
|
|
drmModeCrtcGetGamma (meta_kms_impl_device_get_fd (impl_device),
|
|
crtc->id,
|
|
crtc_state->gamma.value->size,
|
|
crtc_state->gamma.value->red,
|
|
crtc_state->gamma.value->green,
|
|
crtc_state->gamma.value->blue);
|
|
}
|
|
|
|
static void
|
|
read_gamma_state (MetaKmsCrtc *crtc,
|
|
MetaKmsCrtcState *crtc_state,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeCrtc *drm_crtc)
|
|
{
|
|
g_assert_null (crtc_state->gamma.value);
|
|
|
|
crtc_state->gamma.size = 0;
|
|
crtc_state->gamma.supported = FALSE;
|
|
|
|
if (META_IS_KMS_IMPL_DEVICE_ATOMIC (impl_device))
|
|
read_crtc_gamma (crtc, crtc_state, impl_device, drm_crtc);
|
|
else if (META_IS_KMS_IMPL_DEVICE_SIMPLE (impl_device))
|
|
read_crtc_legacy_gamma (crtc, crtc_state, impl_device, drm_crtc);
|
|
}
|
|
|
|
static gboolean
|
|
gamma_equal (MetaKmsCrtcState *state,
|
|
MetaKmsCrtcState *other_state)
|
|
{
|
|
return state->gamma.size == other_state->gamma.size &&
|
|
state->gamma.supported == other_state->gamma.supported &&
|
|
meta_gamma_lut_equal (state->gamma.value, other_state->gamma.value);
|
|
}
|
|
|
|
static MetaKmsResourceChanges
|
|
meta_kms_crtc_state_changes (MetaKmsCrtcState *state,
|
|
MetaKmsCrtcState *other_state)
|
|
{
|
|
if (state->is_active != other_state->is_active)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (!meta_rectangle_equal (&state->rect, &other_state->rect))
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->is_drm_mode_valid != other_state->is_drm_mode_valid)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (!meta_drm_mode_equal (&state->drm_mode, &other_state->drm_mode))
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (!gamma_equal (state, other_state))
|
|
return META_KMS_RESOURCE_CHANGE_GAMMA;
|
|
|
|
return META_KMS_RESOURCE_CHANGE_NONE;
|
|
}
|
|
|
|
static MetaKmsResourceChanges
|
|
meta_kms_crtc_read_state (MetaKmsCrtc *crtc,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeCrtc *drm_crtc,
|
|
drmModeObjectProperties *drm_props)
|
|
{
|
|
MetaKmsCrtcState crtc_state = {0};
|
|
MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE;
|
|
MetaKmsProp *active_prop;
|
|
|
|
meta_kms_impl_device_update_prop_table (impl_device,
|
|
drm_props->props,
|
|
drm_props->prop_values,
|
|
drm_props->count_props,
|
|
crtc->prop_table.props,
|
|
META_KMS_CRTC_N_PROPS);
|
|
|
|
crtc_state.rect = (MetaRectangle) {
|
|
.x = drm_crtc->x,
|
|
.y = drm_crtc->y,
|
|
.width = drm_crtc->width,
|
|
.height = drm_crtc->height,
|
|
};
|
|
|
|
crtc_state.is_drm_mode_valid = drm_crtc->mode_valid;
|
|
crtc_state.drm_mode = drm_crtc->mode;
|
|
|
|
active_prop = &crtc->prop_table.props[META_KMS_CRTC_PROP_ACTIVE];
|
|
|
|
if (active_prop->prop_id)
|
|
crtc_state.is_active = !!active_prop->value;
|
|
else
|
|
crtc_state.is_active = drm_crtc->mode_valid;
|
|
|
|
read_gamma_state (crtc, &crtc_state, impl_device, drm_crtc);
|
|
|
|
if (!crtc_state.is_active)
|
|
{
|
|
if (crtc->current_state.is_active)
|
|
changes |= META_KMS_RESOURCE_CHANGE_FULL;
|
|
}
|
|
else
|
|
{
|
|
changes = meta_kms_crtc_state_changes (&crtc->current_state, &crtc_state);
|
|
}
|
|
|
|
g_clear_pointer (&crtc->current_state.gamma.value,
|
|
meta_gamma_lut_free);
|
|
crtc->current_state = crtc_state;
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"Read CRTC %u state: active: %d, mode: %s, changed: %s",
|
|
crtc->id, crtc->current_state.is_active,
|
|
crtc->current_state.is_drm_mode_valid
|
|
? crtc->current_state.drm_mode.name
|
|
: "(nil)",
|
|
changes == META_KMS_RESOURCE_CHANGE_NONE
|
|
? "no"
|
|
: "yes");
|
|
|
|
return changes;
|
|
}
|
|
|
|
MetaKmsResourceChanges
|
|
meta_kms_crtc_update_state_in_impl (MetaKmsCrtc *crtc)
|
|
{
|
|
MetaKmsImplDevice *impl_device;
|
|
MetaKmsResourceChanges changes;
|
|
int fd;
|
|
drmModeCrtc *drm_crtc;
|
|
drmModeObjectProperties *drm_props;
|
|
|
|
impl_device = meta_kms_device_get_impl_device (crtc->device);
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
|
|
drm_crtc = drmModeGetCrtc (fd, crtc->id);
|
|
drm_props = drmModeObjectGetProperties (fd, crtc->id, DRM_MODE_OBJECT_CRTC);
|
|
|
|
if (!drm_crtc || !drm_props)
|
|
{
|
|
crtc->current_state.is_active = FALSE;
|
|
crtc->current_state.rect = (MetaRectangle) { };
|
|
crtc->current_state.is_drm_mode_valid = FALSE;
|
|
changes = META_KMS_RESOURCE_CHANGE_FULL;
|
|
goto out;
|
|
}
|
|
|
|
changes = meta_kms_crtc_read_state (crtc, impl_device, drm_crtc, drm_props);
|
|
|
|
out:
|
|
g_clear_pointer (&drm_props, drmModeFreeObjectProperties);
|
|
g_clear_pointer (&drm_crtc, drmModeFreeCrtc);
|
|
|
|
return changes;
|
|
}
|
|
|
|
void
|
|
meta_kms_crtc_disable_in_impl (MetaKmsCrtc *crtc)
|
|
{
|
|
crtc->current_state.is_active = FALSE;
|
|
crtc->current_state.rect = (MetaRectangle) { 0 };
|
|
crtc->current_state.is_drm_mode_valid = FALSE;
|
|
crtc->current_state.drm_mode = (drmModeModeInfo) { 0 };
|
|
}
|
|
|
|
void
|
|
meta_kms_crtc_predict_state_in_impl (MetaKmsCrtc *crtc,
|
|
MetaKmsUpdate *update)
|
|
{
|
|
GList *mode_sets;
|
|
GList *crtc_color_updates;
|
|
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->mode)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment;
|
|
const drmModeModeInfo *drm_mode;
|
|
|
|
plane_assignment =
|
|
meta_kms_update_get_primary_plane_assignment (update, crtc);
|
|
drm_mode = meta_kms_mode_get_drm_mode (mode_set->mode);
|
|
|
|
crtc->current_state.is_active = TRUE;
|
|
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 = *drm_mode;
|
|
}
|
|
else
|
|
{
|
|
crtc->current_state.is_active = FALSE;
|
|
crtc->current_state.rect = (MetaRectangle) { 0 };
|
|
crtc->current_state.is_drm_mode_valid = FALSE;
|
|
crtc->current_state.drm_mode = (drmModeModeInfo) { 0 };
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
crtc_color_updates = meta_kms_update_get_crtc_color_updates (update);
|
|
for (l = crtc_color_updates; l; l = l->next)
|
|
{
|
|
MetaKmsCrtcColorUpdate *color_update = l->data;
|
|
MetaGammaLut *gamma = color_update->gamma.state;
|
|
|
|
if (color_update->crtc != crtc)
|
|
continue;
|
|
|
|
if (color_update->gamma.has_update)
|
|
{
|
|
if (gamma)
|
|
gamma = meta_gamma_lut_copy (gamma);
|
|
|
|
g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free);
|
|
crtc->current_state.gamma.value = gamma;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
init_properties (MetaKmsCrtc *crtc,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeCrtc *drm_crtc)
|
|
{
|
|
MetaKmsCrtcPropTable *prop_table = &crtc->prop_table;
|
|
|
|
*prop_table = (MetaKmsCrtcPropTable) {
|
|
.props = {
|
|
[META_KMS_CRTC_PROP_MODE_ID] =
|
|
{
|
|
.name = "MODE_ID",
|
|
.type = DRM_MODE_PROP_BLOB,
|
|
},
|
|
[META_KMS_CRTC_PROP_ACTIVE] =
|
|
{
|
|
.name = "ACTIVE",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CRTC_PROP_GAMMA_LUT] =
|
|
{
|
|
.name = "GAMMA_LUT",
|
|
.type = DRM_MODE_PROP_BLOB,
|
|
},
|
|
[META_KMS_CRTC_PROP_GAMMA_LUT_SIZE] =
|
|
{
|
|
.name = "GAMMA_LUT_SIZE",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
}
|
|
};
|
|
}
|
|
|
|
MetaKmsCrtc *
|
|
meta_kms_crtc_new (MetaKmsImplDevice *impl_device,
|
|
drmModeCrtc *drm_crtc,
|
|
int idx,
|
|
GError **error)
|
|
{
|
|
int fd;
|
|
drmModeObjectProperties *drm_props;
|
|
MetaKmsCrtc *crtc;
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
drm_props = drmModeObjectGetProperties (fd, drm_crtc->crtc_id,
|
|
DRM_MODE_OBJECT_CRTC);
|
|
if (!drm_props)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
|
"drmModeObjectGetProperties: %s", g_strerror (errno));
|
|
return NULL;
|
|
}
|
|
|
|
crtc = g_object_new (META_TYPE_KMS_CRTC, NULL);
|
|
crtc->device = meta_kms_impl_device_get_device (impl_device);
|
|
crtc->id = drm_crtc->crtc_id;
|
|
crtc->idx = idx;
|
|
|
|
init_properties (crtc, impl_device, drm_crtc);
|
|
|
|
meta_kms_crtc_read_state (crtc, impl_device, drm_crtc, drm_props);
|
|
|
|
drmModeFreeObjectProperties (drm_props);
|
|
|
|
return crtc;
|
|
}
|
|
|
|
static void
|
|
meta_kms_crtc_finalize (GObject *object)
|
|
{
|
|
MetaKmsCrtc *crtc = META_KMS_CRTC (object);
|
|
|
|
g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free);
|
|
|
|
G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_kms_crtc_init (MetaKmsCrtc *crtc)
|
|
{
|
|
crtc->current_state.gamma.size = 0;
|
|
crtc->current_state.gamma.value = NULL;
|
|
}
|
|
|
|
static void
|
|
meta_kms_crtc_class_init (MetaKmsCrtcClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_kms_crtc_finalize;
|
|
}
|
|
|
|
static drmVBlankSeqType
|
|
get_crtc_type_bitmask (MetaKmsCrtc *crtc)
|
|
{
|
|
if (crtc->idx > 1)
|
|
{
|
|
return ((crtc->idx << DRM_VBLANK_HIGH_CRTC_SHIFT) &
|
|
DRM_VBLANK_HIGH_CRTC_MASK);
|
|
}
|
|
else if (crtc->idx > 0)
|
|
{
|
|
return DRM_VBLANK_SECONDARY;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_crtc_determine_deadline (MetaKmsCrtc *crtc,
|
|
int64_t *out_next_deadline_us,
|
|
int64_t *out_next_presentation_us,
|
|
GError **error)
|
|
{
|
|
MetaKmsImplDevice *impl_device;
|
|
int fd;
|
|
drmVBlank vblank;
|
|
int ret;
|
|
int64_t next_presentation_us;
|
|
int64_t next_deadline_us;
|
|
drmModeModeInfo *drm_mode;
|
|
int64_t vblank_duration_us;
|
|
int64_t deadline_evasion_us;
|
|
|
|
if (!crtc->current_state.is_drm_mode_valid)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
|
"Mode invalid");
|
|
return FALSE;
|
|
}
|
|
|
|
impl_device = meta_kms_device_get_impl_device (crtc->device);
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
|
|
vblank = (drmVBlank) {
|
|
.request.type = DRM_VBLANK_RELATIVE | get_crtc_type_bitmask (crtc),
|
|
.request.sequence = 0,
|
|
.request.signal = 0,
|
|
};
|
|
|
|
ret = drmWaitVBlank (fd, &vblank);
|
|
if (ret != 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmWaitVBlank failed: %s", g_strerror (-ret));
|
|
return FALSE;
|
|
}
|
|
|
|
drm_mode = &crtc->current_state.drm_mode;
|
|
next_presentation_us =
|
|
s2us (vblank.reply.tval_sec) + vblank.reply.tval_usec + 0.5 +
|
|
G_USEC_PER_SEC / meta_calculate_drm_mode_refresh_rate (drm_mode);
|
|
|
|
/*
|
|
* 1
|
|
* time per pixel = -----------------
|
|
* Pixel clock (Hz)
|
|
*
|
|
* number of pixels = vdisplay * htotal
|
|
*
|
|
* time spent scanning out = time per pixel * number of pixels
|
|
*
|
|
*/
|
|
|
|
if (meta_is_topic_enabled (META_DEBUG_KMS))
|
|
deadline_evasion_us = DEADLINE_EVASION_WITH_KMS_TOPIC_US;
|
|
else
|
|
deadline_evasion_us = DEADLINE_EVASION_US;
|
|
|
|
vblank_duration_us = meta_calculate_drm_mode_vblank_duration_us (drm_mode);
|
|
next_deadline_us = next_presentation_us - (vblank_duration_us +
|
|
deadline_evasion_us);
|
|
|
|
*out_next_presentation_us = next_presentation_us;
|
|
*out_next_deadline_us = next_deadline_us;
|
|
|
|
return TRUE;
|
|
}
|