From ae1a4ed28a0b10a61bc055b65f1e7aa9a01a074d Mon Sep 17 00:00:00 2001
From: nanahi <130121847+na-na-hi@users.noreply.github.com>
Date: Tue, 20 Feb 2024 01:35:00 -0500
Subject: [PATCH] vo_gpu: fix fragment coordinate calculation when dithering

When doing the dithering pass, the fragment coordinate is queried, but
doesn't take the fbo texture flipped property into account. This results
in different dithering patterns when toggling between fbo flipped and
non-flipped state. This can be reproduced with --vo=gpu --gpu-api=opengl
and easily seen with --dither-depth=1 when toggling between playing and
pausing.

Fix this by flipping the fragment y coordinate if the fbo is flipped
when calculating dithering coordinate.
---
 video/out/gpu/video.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c
index 2429b9b602..61af661e6f 100644
--- a/video/out/gpu/video.c
+++ b/video/out/gpu/video.c
@@ -2727,7 +2727,7 @@ void gl_video_set_fb_depth(struct gl_video *p, int fb_depth)
     p->fb_depth = fb_depth;
 }
 
-static void pass_dither(struct gl_video *p)
+static void pass_dither(struct gl_video *p, const struct ra_fbo *fbo)
 {
     // Assume 8 bits per component if unknown.
     int dst_depth = p->fb_depth > 0 ? p->fb_depth : 8;
@@ -2860,7 +2860,9 @@ static void pass_dither(struct gl_video *p)
 
     gl_sc_uniform_texture(p->sc, "dither", p->dither_texture);
 
-    GLSLF("vec2 dither_pos = gl_FragCoord.xy * 1.0/%d.0;\n", dither_size);
+    GLSLF("vec2 dither_coord = vec2(gl_FragCoord.x, %d.0 + %f * gl_FragCoord.y);",
+          fbo->flip ? p->dst_rect.y1 : 0, fbo->flip ? -1.0 : 1.0);
+    GLSLF("vec2 dither_pos = dither_coord * 1.0/%d.0;\n", dither_size);
 
     if (p->opts.temporal_dither) {
         int phase = (p->frames_rendered / p->opts.temporal_dither_period) % 8u;
@@ -3097,7 +3099,7 @@ static void pass_draw_to_screen(struct gl_video *p, const struct ra_fbo *fbo, in
     pass_opt_hook_point(p, "OUTPUT", NULL);
 
     if (flags & RENDER_SCREEN_COLOR)
-        pass_dither(p);
+        pass_dither(p, fbo);
     pass_describe(p, "output to screen");
     finish_pass_fbo(p, fbo, false, &p->dst_rect);
 }