Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrupt radial gradient on Apple Silicon #2014

Closed
lmdsp opened this issue Feb 28, 2024 · 5 comments
Closed

Corrupt radial gradient on Apple Silicon #2014

lmdsp opened this issue Feb 28, 2024 · 5 comments
Assignees
Labels
bug Something isn't working portability Portability Issues among the machines showstopper Regression bugs / Critical errors
Milestone

Comments

@lmdsp
Copy link
Collaborator

lmdsp commented Feb 28, 2024

I've encountered a strange bug when rendering an svg with certain focal point values.
Strangely enough, the rendering is correct on Intel x64 (mac/PC) and the online ThorVG viewer,
but Apple Silicon / ARM show weird horizontal lines instead of the expected gradient.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="800" height="590" version="1.1" id="svg13" xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
   <defs>
      <radialGradient cx="429" cy="3" fx="429" fy="-600" gradientUnits="userSpaceOnUse" id="b" r="600">
         <stop offset="0" stop-color="red" id="stop10" />
         <stop offset="1" stop-color="white" id="stop11" />
      </radialGradient>
   </defs>

   <path d="M 800,200 H 0 V 590 H 800 V 0 Z" fill="url(#b)" id="svg_4" />
</svg>

Note that if fx is changed to 600, then there are no artefacts anymore.
This is using latest ThorVG 0.12.6, XCode 15.2 and macos Sonoma 14.3.1.

ThorVG viewer:

image

Test application | x64:

image

Test application | Apple Silicon:

image

@hermet hermet added portability Portability Issues among the machines bug Something isn't working showstopper Regression bugs / Critical errors labels Feb 29, 2024
@hermet hermet added this to the 0.13 milestone Feb 29, 2024
@mgrudzinska
Copy link
Collaborator

@lmdsp could you please try after this slight change?
#2018
this is a random guess since for now I'm able to test on apple silicon.

Also could you please test what will happen if you change fy="-600" to fy="-597" ?

@lmdsp
Copy link
Collaborator Author

lmdsp commented Mar 2, 2024

@mgrudzinska Thanks for looking into this.

Changing from fy="-600" to fy="-597" doesn't change anything.
Changing line 207 from if (fill->radial.a > 0); to if (fill->radial.a > RADIAL_A_THRESHOLD); doesn't fix the problem either I'm afraid.

Perhaps a hint; those are the contents before and after executing the instructions at line 198

 //This condition fulfills the SVG 1.1 std:
 //the focal point, if outside the end circle, is moved to be on the end circle
 //See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
 if (fill->radial.a < 0) {
(lldb) p fill->radial
(SwFill::SwRadial)  (a11 = 0, a12 = 0, a13 = 0, a21 = 0, a22 = 0, a23 = 0, fx = 429, fy = -600, fr = 0, dx = 0, dy = 603, dr = 599.999939, invA = 0, a = -3609.0625)

(lldb) p fill->radial
(SwFill::SwRadial)  (a11 = 0, a12 = 0, a13 = 0, a21 = 0, a22 = 0, a23 = 0, fx = 429, fy = -596.999939, fr = 0, dx = 0, dy = 599.999939, dr = 599.999939, invA = 0, a = 0.0107421838)

@lmdsp
Copy link
Collaborator Author

lmdsp commented Mar 2, 2024

So it turns out in this case fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy; at line 204 evaluates to 0 on Intel, but to 0.0107421838 on Apple Silicon !
The correct result is indeed 0 as a=dr²-dx²-dy² with dx=0, dr=dy=599.999939

It seems clang is doing some instruction reordering / FMA which is causing loss of precision.
This happens in both release/debug builds, with options such as GCC_FAST_MATH set to off.

I found that storing intermediary results in temporary variables as shown below fixes the problem:

// Prevent loss of precision on Apple Silicon when dr=dy and dx=0
// https://github.com/thorvg/thorvg/issues/2014
const float dr2 = fill->radial.dr * fill->radial.dr;
const float dx2 = fill->radial.dx * fill->radial.dx;
const float dy2 = fill->radial.dy * fill->radial.dy;

fill->radial.a = dr2 - dx2 - dy2;

@mgrudzinska Do you want me to submit a PR ?

@hermet
Copy link
Member

hermet commented Mar 3, 2024

@lmdsp ThorVG welcomes contributors! If you have solutions or improvements, please don't hesitate to submit patches. Thank you for your support!

lmdsp added a commit to lmdsp/thorvg that referenced this issue Mar 5, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](thorvg#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We use compiler specific float contraction control pragmas to avoid this when they exist,
and revert to manual de-optimization in other cases.
lmdsp added a commit to lmdsp/thorvg that referenced this issue Mar 7, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](thorvg#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We rely on temporary variables to prevent FMA.
We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future
lmdsp added a commit to lmdsp/thorvg that referenced this issue Mar 7, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](thorvg#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We rely on temporary variables to prevent FMA.
We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future
hermet pushed a commit that referenced this issue Mar 7, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We rely on temporary variables to prevent FMA.
We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future
@hermet
Copy link
Member

hermet commented Mar 7, 2024

@lmdsp Your fix will be applied in 0.12.7. Thanks for your contribution

@hermet hermet closed this as completed Mar 7, 2024
hermet pushed a commit that referenced this issue Mar 8, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We rely on temporary variables to prevent FMA.
We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future
hermet pushed a commit that referenced this issue Apr 5, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We rely on temporary variables to prevent FMA.
We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future
hermet pushed a commit that referenced this issue Apr 6, 2024
…ARM/Apple Silicon

sw_engine: radial gradient
[issues 2014: radial gradient](#2014)

Radial gradient results in a corrupted image when the focal point is outside the circle on Apple Silicon.
This happens because some compilers use FMA to optimize the a = dr² - dx² - dy² calculation,
which cause loss of precision.

We rely on temporary variables to prevent FMA.
We could also use compiler specific float contraction control pragmas to avoid this if this doesn't work in the future
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working portability Portability Issues among the machines showstopper Regression bugs / Critical errors
Projects
Status: Done 0.13
Development

No branches or pull requests

3 participants