Bug 84396

Summary: cairo-tor-scan-converter is off by half of a subrow.
Product: cairo Reporter: Massimo <sixtysix>
Component: image backendAssignee: Chris Wilson <chris>
Status: RESOLVED MOVED QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: normal    
Priority: medium    
Version: unspecified   
Hardware: Other   
OS: All   
i915 platform: i915 features:
Attachments: quick hack
move sample locations wrt edges
proof of concept
Fix loss of precision from projection onto sample grid
Fix loss of precision from projection onto sample grid
Fix loss of precision from projection onto sample grid
Walk fullrow edges between precise end-points.
Analytical coverage inside the pixel, not sample points
increase coverage area precision

Description Massimo 2014-09-27 15:13:55 UTC
Created attachment 106961 [details]
quick hack

With the attached patch running:

> (cd test && CAIRO_TEST_TARGET=image DISPLAY=:2 .libs/cairo-test-suite record{,flip}-paint-alpha-clip-mask)

produces 2 pairs of images that are the 180°-rotated version of each other


Comment 1 Chris Wilson 2014-09-27 16:23:03 UTC
Hmm, that is a convincing argument. I will have to think carefully to make sure my understanding of sample cells/locations, sample resolution and edges is correct.
Comment 2 Chris Wilson 2014-09-28 07:55:03 UTC
Right, the other argument is that the edges are not projected onto the sample grid correctly - the effect is that we are sampling each cell at (0, 0) not (.5, .5). I wonder if using a round not truncation in the projection of the edges would fix it.
Comment 3 Chris Wilson 2014-09-28 07:58:35 UTC
Untested, but I am thinking along the lines of:

diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 89ef20f..e271c31 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -1469,19 +1469,19 @@ glitter_scan_converter_reset(
  * shifts if possible, and something saneish if not.
-#  define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#  define INPUT_TO_GRID_Y(in, out) (out) = ((in) + ((GLITTER_INPUT_BITS - GRID_Y_BITS) >> 1) - 1) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
 #  define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
-#  define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#  define INPUT_TO_GRID_X(in, out) (out) = ((in) + ((GLITTER_INPUT_BITS - GRID_X_BITS) >> 1) - 1) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
 #  define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
 #define INPUT_TO_GRID_general(in, out, grid_scale) do {                \
-    long long tmp__ = (long long)(grid_scale) * (in);  \
+    long long tmp__ = (long long)(grid_scale) * (in) + ((grid_scale)+1)/2-1;   \
     tmp__ >>= GLITTER_INPUT_BITS;                              \
     (out) = tmp__;                                             \
 } while (0)
Comment 4 Chris Wilson 2014-09-28 07:59:54 UTC
Created attachment 106989 [details] [review]
move sample locations wrt edges

Pasting fail.
Comment 5 Massimo 2014-09-28 10:39:28 UTC
(In reply to comment #4)
> Created attachment 106989 [details] [review] [review]
> move sample locations wrt edges
> Pasting fail.

I tried this patch, but it does not produce the rotated images.

The problem is that the record* tests clip the rendering in many
2x2 rectangles, in that situation the scan-converter hardly executes
full_row, mainly sub_row (in every row there is a new or end edge)
and my idea above (offsetting half of a subrow) works in that case,
but it breaks the more general fullrow case in which that offset is
not necessary/correct.
Comment 6 Chris Wilson 2014-09-29 07:43:50 UTC
commit 5c03b20732b84370950f0c7e5648da86ef45a571
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Mon Sep 29 08:37:56 2014 +0100

    test/coverage: Exercise invariance under mirror symmetry
    Massimo noticed that the record/record-flip were not being rasterised as
    identical mirror images due to a half-subpixel offset in the tor scan
    converter. This test attempts to reproduce this error by rendering a
    rhombus around the origin of each cell (that is it generates 4 mirror
    images of a triangle in the 4 different orientations0. The expectation
    is that each pixel in the group is lit identically as the coverage is
    References: https://bugs.freedesktop.org/show_bug.cgi?id=84396
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

should be a good explicit test of this property. It is marked as a slow test, so needs to be run as

./cairo-test-suite -s coverage-rhombus

(Not that it is that slow and cairo-test-suite should just run tests that are named explicitly.)
Comment 7 Chris Wilson 2014-09-29 18:43:12 UTC
Short answer, we need both. We need to take greater care inside tor to not lose precision in the intermediate projections.
Comment 8 Massimo 2014-09-30 04:38:38 UTC
Created attachment 107095 [details] [review]
proof of concept

Two other problems are that the grid cairo-tor-scan-converter uses is asymmetric
so edges are snapped to grid vertices on different coordinates on the x/y axis.

And there is a maximum precision issue that prevents increasing GRID_{X,Y}_BITS
too much. With the attached patch #defining them to 7 (not in the patch, 
at lines 118-119), coverage-rhombus passes, but tests with longer edges fail, 
probably because an overflow in certain multiplication or whatever.
Comment 9 Chris Wilson 2014-09-30 07:52:02 UTC
Created attachment 107101 [details] [review]
Fix loss of precision from projection onto sample grid

This fixes up the subrow sampling at least. I think the fullrow sampling is probably still broken though...
Comment 10 Chris Wilson 2014-09-30 07:53:37 UTC
(In reply to comment #8)
> Created attachment 107095 [details] [review] [review]
> proof of concept
> Two other problems are that the grid cairo-tor-scan-converter uses is
> asymmetric
> so edges are snapped to grid vertices on different coordinates on the x/y
> axis.

Cool. Looks like we are working along the same lines.
Comment 11 Chris Wilson 2014-09-30 08:09:23 UTC
Created attachment 107103 [details] [review]
Fix loss of precision from projection onto sample grid

The only issue now is that the asserts are triggering on the overflows in big-line and big-trap.
Comment 12 Chris Wilson 2014-09-30 08:18:33 UTC
Created attachment 107104 [details] [review]
Fix loss of precision from projection onto sample grid

And don't forget to bump the precision of the walkers.
Comment 13 Chris Wilson 2014-09-30 11:06:15 UTC
Worked through the test suite, and I am happy with the results of the last patch:

commit 03c3d4b7c159a3004071522bac2461e553fec211
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Tue Sep 30 08:44:43 2014 +0100

    tor: Fix loss of precision from projection onto sample grid
    The goal is to preserve the precision in the gradients of the edges and
    only apply the projection into the final cell location. We also include
    the half-subrow offset as spotted by Massimo.
    References: https://bugs.freedesktop.org/show_bug.cgi?id=84396
    Testcase: coverage-rhombus
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

Now, it's time to answer the question of whether the flipped rendering is now correct...
Comment 14 Massimo 2014-09-30 12:34:30 UTC
With the last patch, comparing the output of the test a8-clear
with its 180°-rotated version, shows that all pixels computed
via sub_row are correct, while those with full_row are slighlty

I've obtained the opposite result not altering the way e->x.quo .rem
is computed, but adjusting the rest.

The problem is that to share the same walker between full_row and 
sub_row, it would be necessary to use a grid with twice as many y
samples as the number of subrows. Cell corners for full_row and
cell centers for sub_row.
Comment 15 Chris Wilson 2014-09-30 12:58:52 UTC
I found one outright bug in cell_list_render_edge():

commit 167561f2823767058e2be3a26131b5f820b35c35
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Tue Sep 30 10:22:41 2014 +0100

    tor: Review full-row walker
    When updating the quorem between cells, we would lose the overflow
    increment as it was only applied locally and not preserved by updating
    the quorem.
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

Fixing the discrepancy between the fullrow and subrow processing is ongoing.
Comment 16 Chris Wilson 2014-09-30 13:37:07 UTC
I regenerated the recordflip references,

commit 95e147bfa05a122541645f32be52cf1902c3a4b2
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Tue Sep 30 14:30:45 2014 +0100

    test: Explicitly flip the reference image for recordflip
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

and I will add a new test in recordflip (and probably the other pixel exact transformed replays) for replaying the whole image.
Comment 17 Chris Wilson 2014-09-30 15:17:14 UTC
I think the rendering errors are now just the seams between fullrow stepping (e.g. bug-seams). I added

commit 0c42d5c176b27725ac8ab293c3e941be64f51613
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Tue Sep 30 16:14:57 2014 +0100

    test: Add another coverage example demonstrating the seams in tor
    References: https://bugs.freedesktop.org/show_bug.cgi?id=84396
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

./cairo-test-suite coverage-abutting

to demonstrate the problem succinctly.
Comment 18 Chris Wilson 2014-09-30 15:20:11 UTC
Hmm, bad test. Forgot that it would require ADD to test the coverage.
Comment 19 Massimo 2014-10-01 11:52:03 UTC
(In reply to comment #17)
> I think the rendering errors are now just the seams between fullrow stepping
> (e.g. bug-seams). I added

Confirming, with a note: the errors in bug-seams are not full_row,
but the upper half of row 74 is rendered by sub_row whereas the lower
part is rendered by full_row. The two are not complementary.

Skimming the new code I'm not sure whether (lines 795...) it is correct
to use edge->cell there, to me it seems that edge->cell is the x at the
y of first subrow's center and for edges nearly horizontal it could be
slightly off with respect to the x corresponding to the y at the start
of the row. (it could be compensated in the code later though)
Comment 20 Chris Wilson 2014-10-01 12:04:37 UTC
Created attachment 107186 [details] [review]
Walk fullrow edges between precise end-points.

For the inside a pixel check, I think cell is correct (the line has to go between the cell end-points), but it does lose some precision when stepping between pixels. I was looking at that, but didn't get my math right when I spotted the bug and went to fix that.

bug-seams is indeed a different class (fullrow does the analytical coverage computation, subrow does the sample coverage) and so there does remain yet another asymmetry bug in tor somewhere.
Comment 21 Chris Wilson 2014-10-01 12:14:48 UTC
Nope, you are right. The fullrow walker is analytical and shouldn't be messing around with sample locations.
Comment 22 Chris Wilson 2014-10-01 13:49:52 UTC
Created attachment 107189 [details] [review]
Analytical coverage inside the pixel, not sample points

This should be closer...
Comment 23 Chris Wilson 2014-10-01 23:10:57 UTC
Wrote a simple 256x256 rasteriser to highlight the issue with sampling along the fullrow.

commit 50b41e214533ea5fd3b64128306b6cb94d353145
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Wed Oct 1 22:50:10 2014 +0100

    test: Add a simple rasteriser to check fidelity of edge rendering
    In order to check the behaviour of the analytic rasteriser inside tor,
    let's compare it against a very simple rasteriser that uses a rectiliner
    256x256 sample grid.
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

./cairo-test-suite simple-edges

Sadly it also says that my patch is inadequate.
Comment 24 Chris Wilson 2014-10-02 07:49:49 UTC
Better. Realised we could do_full_row if insert new edges at the beginning of the fullrow (and have no other edge operations within the row), which fixed up the issue with switching from subsampling to analytic within bug-seams and simple-edges.

commit 950f1e7103a3b4f3405fbb3ee2844ed24b902834
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Thu Oct 2 07:52:05 2014 +0100

    tor: Enable analytic processing for starting rows
    If all the edges start at the very beginning of the whole row, we can
    merge them and include check for intersections/endings during the row.
    This allows us to enable fast analytic processing for even the very
    first row on pixel aligned vertices.
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk

Still the rasteriser is not symmetric though. :|
Comment 25 Massimo 2014-10-05 07:39:01 UTC
Created attachment 107344 [details] [review]
increase coverage area precision

There is a loss of precision in the last step of the analytical
coverage computation, that is, the uncovered area does not depend
on the number of subrows and can be computed to a greater accuracy,
improving the overall symmetrical behaviour:

the output images of record{,flip}-paint-alpha-clip-mask become
the 180°-rotated version of each other

and the output of a8-clear is within +-1 from its 180°-rotated

If something like this is considered worth it, perhaps after playing
with GRID_Y or GRID_Y_BITS and EXTRA_Y, a specialized version
of GRID_AREA_TO_ALPHA could be added for the new value of GRID_XY.
Comment 26 Chris Wilson 2014-10-05 07:57:04 UTC
Indeed. We could just use the full input precision when doing the analytic pass. We would have to apply a couple of scale factors so that the sampling and analytic generate the same total coverage.
Comment 27 Bryce Harrington 2015-06-18 23:08:29 UTC
Thanks; fix pushed:

To ssh://git.cairographics.org/git/cairo
   b4a922f..e7acf4b  master -> master
Comment 28 Bryce Harrington 2015-06-18 23:09:38 UTC
[Sorry; ignore the previous comment.  Posted on wrong bug.]
Comment 29 GitLab Migration User 2018-08-25 13:46:25 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/cairo/cairo/issues/187.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.