Skip to content

Commit

Permalink
TextureTools: make distanceField() work better on bad/old shader comp…
Browse files Browse the repository at this point in the history
…ilers.

The nested for loop is a big problem. Worked around this by putting a
fixed upper bound and some `break`s. This might result in the code
being slower on desktop drivers, needs to be redone from scratch later
by generating the code directly.

Even this minor change caused Mesa drivers to output a slightly
different file. Test output is verbatim below:

============================================================================

  FAIL [1] test() at
../src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp on line 107
        Images actualOutputImage and
Utility::Directory::join(DISTANCEFIELDGLTEST_FILES_DIR, "output.tga")
have both max and mean delta above threshold, actual 1/0.000488281 but
at most 0/0 expected. Delta image:
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                M                                               |
          |                                                                |
          |                                                                |
          |                                              M                 |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
          |                                                                |
        Pixels above max/mean threshold:
          [16,41] Vector(175), expected Vector(174) (Δ = 1)
          [46,35] Vector(175), expected Vector(174) (Δ = 1)
  • Loading branch information
mosra committed Oct 1, 2018
1 parent dfb7e08 commit 209cdbc
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 27 deletions.
3 changes: 3 additions & 0 deletions doc/changelog.dox
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ See also:
a proper documented default value instead of being left uninitialized.
- @ref Math::sclerp() was not properly interpolating the translation if
rotation was the same on both sides
- Improved @ref TextureTools::distanceField() to work better on shader
compilers that have problems compiling nested loops (WebGL implementations,
some ES2 devices)

@subsection changelog-latest-docs Documentation

Expand Down
22 changes: 8 additions & 14 deletions src/Magnum/TextureTools/DistanceField.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include "DistanceField.h"

#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/Resource.h>

#include "Magnum/Math/Range.h"
Expand Down Expand Up @@ -52,12 +53,7 @@ class DistanceFieldShader: public GL::AbstractShaderProgram {
public:
typedef GL::Attribute<0, Vector2> Position;

explicit DistanceFieldShader();

DistanceFieldShader& setRadius(Int radius) {
setUniform(radiusUniform, radius);
return *this;
}
explicit DistanceFieldShader(Int radius);

DistanceFieldShader& setScaling(const Vector2& scaling) {
setUniform(scalingUniform, scaling);
Expand All @@ -79,12 +75,11 @@ class DistanceFieldShader: public GL::AbstractShaderProgram {
units, so be careful to not step over that. ES3 on the same has 16. */
enum: Int { TextureUnit = 7 };

Int radiusUniform{0},
scalingUniform{1},
Int scalingUniform{0},
imageSizeInvertedUniform;
};

DistanceFieldShader::DistanceFieldShader() {
DistanceFieldShader::DistanceFieldShader(Int radius) {
#ifdef MAGNUM_BUILD_STATIC
/* Import resources on static build, if not already */
if(!Utility::Resource::hasGroup("MagnumTextureTools"))
Expand All @@ -103,7 +98,8 @@ DistanceFieldShader::DistanceFieldShader() {

vert.addSource(rs.get("FullScreenTriangle.glsl"))
.addSource(rs.get("DistanceFieldShader.vert"));
frag.addSource(rs.get("DistanceFieldShader.frag"));
frag.addSource(Utility::formatString("#define RADIUS {}\n", radius))
.addSource(rs.get("DistanceFieldShader.frag"));

CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));

Expand All @@ -125,7 +121,6 @@ DistanceFieldShader::DistanceFieldShader() {
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::explicit_uniform_location>())
#endif
{
radiusUniform = uniformLocation("radius");
scalingUniform = uniformLocation("scaling");

#ifndef MAGNUM_TARGET_GLES
Expand Down Expand Up @@ -175,9 +170,8 @@ void distanceField(GL::Texture2D& input, GL::Texture2D& output, const Range2Di&
return;
}

DistanceFieldShader shader;
shader.setRadius(radius)
.setScaling(Vector2(imageSize)/Vector2(rectangle.size()))
DistanceFieldShader shader{radius};
shader.setScaling(Vector2(imageSize)/Vector2(rectangle.size()))
.bindTexture(input);

#ifndef MAGNUM_TARGET_GLES
Expand Down
26 changes: 13 additions & 13 deletions src/Magnum/TextureTools/DistanceFieldShader.frag
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 0)
#endif
uniform lowp int radius;

#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 1)
#endif
uniform mediump vec2 scaling;

#ifdef EXPLICIT_TEXTURE_LAYER
Expand All @@ -58,7 +53,7 @@ layout(pixel_center_integer) in mediump vec4 gl_FragCoord;
#endif
#else
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2)
layout(location = 1)
#endif
uniform mediump vec2 imageSizeInverted;
#endif
Expand Down Expand Up @@ -108,12 +103,13 @@ void main() {
const highp float sign = isInside ? 1.0 : -1.0;

/* Minimal found distance is just out of the radius (i.e. infinity) */
highp float minDistanceSquared = float((radius+1)*(radius+1));
highp float minDistanceSquared = float((RADIUS+1)*(RADIUS+1));

/* Go in circles around the point and find nearest value */
int radiusLimit = radius;
for(int i = 1; i <= radiusLimit; ++i) {
for(int j = 0, jmax = i*2; j < jmax; ++j) {
int radiusLimit = RADIUS;
for(int i = 1; i <= RADIUS; ++i) {
int jmax = i*2;
for(int j = 0; j < RADIUS*2; ++j) {
#ifdef TEXELFETCH_USABLE
const lowp ivec2 offset = ivec2(-i+j, i);
#else
Expand Down Expand Up @@ -142,14 +138,18 @@ void main() {
value, e.g. for distance 3.5 we can find smaller value even
in radius 3 */
#ifdef NEW_GLSL
radiusLimit = min(radius, int(floor(length(vec2(offset)))));
radiusLimit = min(RADIUS, int(floor(length(vec2(offset)))));
#else
radiusLimit = int(min(float(radius), floor(length(vec2(offset)))));
radiusLimit = int(min(float(RADIUS), floor(length(vec2(offset)))));
#endif
}

if(j + 1 >= jmax) break;
}

if(i + 1 > radiusLimit) break;
}

/* Final signed distance, normalized from [-radius-1, radius+1] to [0, 1] */
value = sign*sqrt(minDistanceSquared)/float(radius*2+2)+0.5;
value = sign*sqrt(minDistanceSquared)/float(RADIUS*2+2)+0.5;
}
Binary file modified src/Magnum/TextureTools/Test/DistanceFieldGLTestFiles/output.tga
Binary file not shown.

0 comments on commit 209cdbc

Please sign in to comment.