From 209cdbcd050b7cdc76f58638cc99230a1d301351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 1 Oct 2018 12:55:11 +0200 Subject: [PATCH] TextureTools: make distanceField() work better on bad/old shader compilers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- doc/changelog.dox | 3 ++ src/Magnum/TextureTools/DistanceField.cpp | 22 ++++++--------- .../TextureTools/DistanceFieldShader.frag | 26 +++++++++--------- .../Test/DistanceFieldGLTestFiles/output.tga | Bin 4114 -> 4114 bytes 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 3af4adb4a..149f0a82b 100644 --- a/doc/changelog.dox +++ b/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 diff --git a/src/Magnum/TextureTools/DistanceField.cpp b/src/Magnum/TextureTools/DistanceField.cpp index 0736db4b3..9b637b0f0 100644 --- a/src/Magnum/TextureTools/DistanceField.cpp +++ b/src/Magnum/TextureTools/DistanceField.cpp @@ -25,6 +25,7 @@ #include "DistanceField.h" +#include #include #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()) #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 diff --git a/src/Magnum/TextureTools/DistanceFieldShader.frag b/src/Magnum/TextureTools/DistanceFieldShader.frag index 2a4828000..e75ab7da4 100644 --- a/src/Magnum/TextureTools/DistanceFieldShader.frag +++ b/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; } diff --git a/src/Magnum/TextureTools/Test/DistanceFieldGLTestFiles/output.tga b/src/Magnum/TextureTools/Test/DistanceFieldGLTestFiles/output.tga index 278ec6a320b32770d42009f0934995a90c13835a..f6982a590b4b3d72e3020d92e783652587ffbb30 100644 GIT binary patch delta 20 acmbQFFiBwnC*%6fjGT##Kq{GQJu3i2p9Wz7 delta 20 acmbQFFiBwnC*!)!jGT##Kq{GQJu3i2j0Rr-