Browse Source

TextureTools: make distanceField() work better on bad/old shader compilers.

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)
pull/289/head
Vladimír Vondruš 8 years ago
parent
commit
209cdbcd05
  1. 3
      doc/changelog.dox
  2. 22
      src/Magnum/TextureTools/DistanceField.cpp
  3. 26
      src/Magnum/TextureTools/DistanceFieldShader.frag
  4. BIN
      src/Magnum/TextureTools/Test/DistanceFieldGLTestFiles/output.tga

3
doc/changelog.dox

@ -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

22
src/Magnum/TextureTools/DistanceField.cpp

@ -25,6 +25,7 @@
#include "DistanceField.h"
#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/Resource.h>
#include "Magnum/Math/Range.h"
@ -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);
@ -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"))
@ -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}));
@ -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
@ -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

26
src/Magnum/TextureTools/DistanceFieldShader.frag

@ -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
@ -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
@ -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
@ -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;
}

BIN
src/Magnum/TextureTools/Test/DistanceFieldGLTestFiles/output.tga

Binary file not shown.
Loading…
Cancel
Save