2019-03-11 06:13:01 -04:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2013-2019 Red Hat
|
|
|
|
* Copyright (c) 2018 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-utils.h"
|
|
|
|
|
|
|
|
#include <drm_fourcc.h>
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
/* added in libdrm 2.4.95 */
|
|
|
|
#ifndef DRM_FORMAT_INVALID
|
|
|
|
#define DRM_FORMAT_INVALID 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
float
|
|
|
|
meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *drm_mode)
|
|
|
|
{
|
backend/native: Calculate refresh rate in double-precision
The old calculation was introduced to improve the precision
with commit c16a5ec1cf14d67ee8100afd31a2e85e7003f9a8.
Here, I call the calculation as "revision 2", and the
calculation even older as "revision 1", and the new
calculation introduced with this commit as "reivion 3".
Revision 2 has two problems:
1. The calculation is mixed with fixed-point numbers and
floating-point numbers.
To overcome the precision loss of fixed-point numbers division,
it first "calculates refresh rate in milliHz first for extra
precision", but this requires converting the value back to Hz.
An extra calculation has performance and precision costs.
It is also hard to understand for programmers.
2. The calculation has a bias.
In the process, it does:
refresh += (drm_mode->vtotal / 2);
It prevents the value from being rounded to a smaller value in
a fixed-point integer arithmetics, but it only adds a small
bias (0.0005) and consumes some fraction bits for
floating point arithmetic.
Revision 3, introduced with this commit always uses
double-precision floating-point values for true precision and
to ease understanding of this code. It also removes the bias.
Another change is that it now has two internal values, numerator
and denominator. Revision 1 also calculated those two values
first, and later performed a division with them, which minimizes
the precision loss caused by divisions. This method has risks of
overflowing the two values and revision 1 caused problems due to
that, but revision 3 won't thanks to double-precision. Therefore,
revision 3 will theoretically have the result identical with
the calculation with infinite-precision.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1737>
2021-02-17 21:27:06 -05:00
|
|
|
double numerator;
|
|
|
|
double denominator;
|
2019-03-11 06:13:01 -04:00
|
|
|
|
backend/native: Calculate refresh rate in double-precision
The old calculation was introduced to improve the precision
with commit c16a5ec1cf14d67ee8100afd31a2e85e7003f9a8.
Here, I call the calculation as "revision 2", and the
calculation even older as "revision 1", and the new
calculation introduced with this commit as "reivion 3".
Revision 2 has two problems:
1. The calculation is mixed with fixed-point numbers and
floating-point numbers.
To overcome the precision loss of fixed-point numbers division,
it first "calculates refresh rate in milliHz first for extra
precision", but this requires converting the value back to Hz.
An extra calculation has performance and precision costs.
It is also hard to understand for programmers.
2. The calculation has a bias.
In the process, it does:
refresh += (drm_mode->vtotal / 2);
It prevents the value from being rounded to a smaller value in
a fixed-point integer arithmetics, but it only adds a small
bias (0.0005) and consumes some fraction bits for
floating point arithmetic.
Revision 3, introduced with this commit always uses
double-precision floating-point values for true precision and
to ease understanding of this code. It also removes the bias.
Another change is that it now has two internal values, numerator
and denominator. Revision 1 also calculated those two values
first, and later performed a division with them, which minimizes
the precision loss caused by divisions. This method has risks of
overflowing the two values and revision 1 caused problems due to
that, but revision 3 won't thanks to double-precision. Therefore,
revision 3 will theoretically have the result identical with
the calculation with infinite-precision.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1737>
2021-02-17 21:27:06 -05:00
|
|
|
if (drm_mode->htotal <= 0 || drm_mode->vtotal <= 0)
|
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
numerator = drm_mode->clock * 1000.0;
|
|
|
|
denominator = (double) drm_mode->vtotal * drm_mode->htotal;
|
|
|
|
if (drm_mode->vscan > 1)
|
|
|
|
denominator *= drm_mode->vscan;
|
|
|
|
|
|
|
|
return numerator / denominator;
|
2019-03-11 06:13:01 -04:00
|
|
|
}
|
|
|
|
|
2021-01-06 03:36:04 -05:00
|
|
|
int64_t
|
|
|
|
meta_calculate_drm_mode_vblank_duration_us (const drmModeModeInfo *drm_mode)
|
|
|
|
{
|
|
|
|
int64_t value;
|
|
|
|
|
|
|
|
if (drm_mode->htotal <= 0 || drm_mode->vtotal <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Convert to int64_t early. */
|
|
|
|
value = drm_mode->vtotal - drm_mode->vdisplay;
|
|
|
|
value *= drm_mode->htotal;
|
|
|
|
|
|
|
|
if (drm_mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
|
|
value *= 2;
|
|
|
|
|
|
|
|
/* Round the duration up as it is used for buffer swap deadline computation. */
|
|
|
|
value = (value * 1000 + drm_mode->clock - 1) / drm_mode->clock;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-03-11 06:13:01 -04:00
|
|
|
/**
|
|
|
|
* meta_drm_format_to_string:
|
|
|
|
* @tmp: temporary buffer
|
|
|
|
* @drm_format: DRM fourcc pixel format
|
|
|
|
*
|
|
|
|
* Returns a pointer to a string naming the given pixel format,
|
|
|
|
* usually a pointer to the temporary buffer but not always.
|
|
|
|
* Invalid formats may return nonsense names.
|
|
|
|
*
|
|
|
|
* When calling this, allocate one MetaDrmFormatBuf on the stack to
|
|
|
|
* be used as the temporary buffer.
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
meta_drm_format_to_string (MetaDrmFormatBuf *tmp,
|
|
|
|
uint32_t drm_format)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (drm_format == DRM_FORMAT_INVALID)
|
|
|
|
return "INVALID";
|
|
|
|
|
|
|
|
G_STATIC_ASSERT (sizeof (tmp->s) == 5);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
char c = (drm_format >> (i * 8)) & 0xff;
|
|
|
|
tmp->s[i] = g_ascii_isgraph (c) ? c : '.';
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp->s[i] = 0;
|
|
|
|
|
|
|
|
return tmp->s;
|
|
|
|
}
|
|
|
|
|