drm/rect: Handle rounding errors in drm_rect_clip_scaled, v3.

Instead of relying on a scale which may increase rounding errors,
clip src by doing: src * (dst - clip) / dst and rounding the result
away from 1, so the new coordinates get closer to 1. We won't need
to fix up with a magic macro afterwards, because our scaling factor
will never go to the other side of 1.

Changes since v1:
- Adjust dst immediately, else drm_rect_width/height on dst gives bogus
  results.
Change since v2:
- Get rid of macros and use 64-bits math.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
[mlankhorst: Add Villes comment, and rename newsrc to tmp. (Ville)]
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180503112217.37292-3-maarten.lankhorst@linux.intel.com
This commit is contained in:
Maarten Lankhorst 2018-05-03 13:22:14 +02:00
parent 6f96f2000a
commit f96bdf564f
4 changed files with 39 additions and 17 deletions

View file

@ -766,7 +766,7 @@ int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state,
if (crtc_state->enable) if (crtc_state->enable)
drm_mode_get_hv_timing(&crtc_state->mode, &clip.x2, &clip.y2); drm_mode_get_hv_timing(&crtc_state->mode, &clip.x2, &clip.y2);
plane_state->visible = drm_rect_clip_scaled(src, dst, &clip, hscale, vscale); plane_state->visible = drm_rect_clip_scaled(src, dst, &clip);
drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);

View file

@ -50,13 +50,25 @@ bool drm_rect_intersect(struct drm_rect *r1, const struct drm_rect *r2)
} }
EXPORT_SYMBOL(drm_rect_intersect); EXPORT_SYMBOL(drm_rect_intersect);
static u32 clip_scaled(u32 src, u32 dst, u32 clip)
{
u64 tmp = mul_u32_u32(src, dst - clip);
/*
* Round toward 1.0 when clipping so that we don't accidentally
* change upscaling to downscaling or vice versa.
*/
if (src < (dst << 16))
return DIV_ROUND_UP_ULL(tmp, dst);
else
return DIV_ROUND_DOWN_ULL(tmp, dst);
}
/** /**
* drm_rect_clip_scaled - perform a scaled clip operation * drm_rect_clip_scaled - perform a scaled clip operation
* @src: source window rectangle * @src: source window rectangle
* @dst: destination window rectangle * @dst: destination window rectangle
* @clip: clip rectangle * @clip: clip rectangle
* @hscale: horizontal scaling factor
* @vscale: vertical scaling factor
* *
* Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the
* same amounts multiplied by @hscale and @vscale. * same amounts multiplied by @hscale and @vscale.
@ -66,33 +78,44 @@ EXPORT_SYMBOL(drm_rect_intersect);
* %false otherwise * %false otherwise
*/ */
bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst,
const struct drm_rect *clip, const struct drm_rect *clip)
int hscale, int vscale)
{ {
int diff; int diff;
diff = clip->x1 - dst->x1; diff = clip->x1 - dst->x1;
if (diff > 0) { if (diff > 0) {
int64_t tmp = src->x1 + (int64_t) diff * hscale; u32 new_src_w = clip_scaled(drm_rect_width(src),
src->x1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); drm_rect_width(dst), diff);
src->x1 = clamp_t(int64_t, src->x2 - new_src_w, INT_MIN, INT_MAX);
dst->x1 = clip->x1;
} }
diff = clip->y1 - dst->y1; diff = clip->y1 - dst->y1;
if (diff > 0) { if (diff > 0) {
int64_t tmp = src->y1 + (int64_t) diff * vscale; u32 new_src_h = clip_scaled(drm_rect_height(src),
src->y1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); drm_rect_height(dst), diff);
src->y1 = clamp_t(int64_t, src->y2 - new_src_h, INT_MIN, INT_MAX);
dst->y1 = clip->y1;
} }
diff = dst->x2 - clip->x2; diff = dst->x2 - clip->x2;
if (diff > 0) { if (diff > 0) {
int64_t tmp = src->x2 - (int64_t) diff * hscale; u32 new_src_w = clip_scaled(drm_rect_width(src),
src->x2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); drm_rect_width(dst), diff);
src->x2 = clamp_t(int64_t, src->x1 + new_src_w, INT_MIN, INT_MAX);
dst->x2 = clip->x2;
} }
diff = dst->y2 - clip->y2; diff = dst->y2 - clip->y2;
if (diff > 0) { if (diff > 0) {
int64_t tmp = src->y2 - (int64_t) diff * vscale; u32 new_src_h = clip_scaled(drm_rect_height(src),
src->y2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); drm_rect_height(dst), diff);
src->y2 = clamp_t(int64_t, src->y1 + new_src_h, INT_MIN, INT_MAX);
dst->y2 = clip->y2;
} }
return drm_rect_intersect(dst, clip); return drm_rect_visible(dst);
} }
EXPORT_SYMBOL(drm_rect_clip_scaled); EXPORT_SYMBOL(drm_rect_clip_scaled);

View file

@ -1003,7 +1003,7 @@ intel_check_sprite_plane(struct intel_plane *plane,
drm_mode_get_hv_timing(&crtc_state->base.mode, drm_mode_get_hv_timing(&crtc_state->base.mode,
&clip.x2, &clip.y2); &clip.x2, &clip.y2);
state->base.visible = drm_rect_clip_scaled(src, dst, &clip, hscale, vscale); state->base.visible = drm_rect_clip_scaled(src, dst, &clip);
crtc_x = dst->x1; crtc_x = dst->x1;
crtc_y = dst->y1; crtc_y = dst->y1;

View file

@ -175,8 +175,7 @@ static inline bool drm_rect_equals(const struct drm_rect *r1,
bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip);
bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst,
const struct drm_rect *clip, const struct drm_rect *clip);
int hscale, int vscale);
int drm_rect_calc_hscale(const struct drm_rect *src, int drm_rect_calc_hscale(const struct drm_rect *src,
const struct drm_rect *dst, const struct drm_rect *dst,
int min_hscale, int max_hscale); int min_hscale, int max_hscale);