CrossPatchFilter fails on Mac

TestCrossHatch fails on Mac becuase of float-int comparison on fragment shader.

If it isn’t strange to have float type for line width, line distance in CrossHatch,

Using float values seems to be better because shader handles float better.

Or let CrossHatchFilter get/set int from user instead of float and read/write to gfx card using float?



[patch]

Index: src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java

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

— src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java (revision 7050)

+++ src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java (working copy)

@@ -61,8 +61,8 @@

private float luminance3 = 0.5f;

private float luminance4 = 0.3f;

private float luminance5 = 0.0f;

  • private int lineThickness = 1;
  • private int lineDistance = 4;
  • private float lineThickness = 1;
  • private float lineDistance = 4;



    public CrossHatchFilter() {

    super("CrossHatchFilter");

    @@ -96,8 +96,8 @@

    material.setFloat("Luminance4", luminance4);

    material.setFloat("Luminance5", luminance5);


  •    material.setInt(&quot;LineThickness&quot;, lineThickness);<br />
    
  •    material.setInt(&quot;LineDistance&quot;, lineDistance);<br />
    
  •    material.setFloat(&quot;LineThickness&quot;, lineThickness);<br />
    
  •    material.setFloat(&quot;LineDistance&quot;, lineDistance);<br />
    

}



@Override

@@ -191,10 +191,10 @@

/*

  • Sets the thickness of lines drawn

    */
  • public void setLineThickness(int lineThickness) {
  • public void setLineThickness(float lineThickness) {

    this.lineThickness = lineThickness;

    if (material != null) {
  •        material.setInt(&quot;LineThickness&quot;, lineThickness);<br />
    
  •        material.setFloat(&quot;LineThickness&quot;, lineThickness);<br />
    

}

}



@@ -203,10 +203,10 @@

  • Primary lines are drawn at 2*lineDistance
  • Secondary lines are drawn at lineDistance

    */
  • public void setLineDistance(int lineDistance) {
  • public void setLineDistance(float lineDistance) {

    this.lineDistance = lineDistance;

    if (material != null) {
  •        material.setInt(&quot;LineDistance&quot;, lineDistance);<br />
    
  •        material.setFloat(&quot;LineDistance&quot;, lineDistance);<br />
    

}

}



@@ -248,14 +248,14 @@

/*

  • Returns the thickness of the lines drawn

    */
  • public int getLineThickness() {
  • public float getLineThickness() {

    return lineThickness;

    }



    /*
  • Returns minimum distance between lines

    */
  • public int getLineDistance() {
  • public float getLineDistance() {

    return lineDistance;

    }



    Index: src/core-data/Common/MatDefs/Post/CrossHatch.frag

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

    — src/core-data/Common/MatDefs/Post/CrossHatch.frag (revision 7050)

    +++ src/core-data/Common/MatDefs/Post/CrossHatch.frag (working copy)

    @@ -13,30 +13,30 @@

    uniform float m_Luminance4;

    uniform float m_Luminance5;



    -uniform int m_LineDistance;

    -uniform int m_LineThickness;

    +uniform float m_LineDistance;

    +uniform float m_LineThickness;



    void main() {

    vec4 texVal = texture2D(m_Texture, texCoord);
  • float linePixel = 0;
  • float linePixel = 0.0;



    float lum = texVal.r0.2126 + texVal.g0.7152 + texVal.b*0.0722;



    if (lum < m_Luminance1){
  •    if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2) &lt; m_LineThickness)<br />
    
  •        linePixel = 1;<br />
    
  •    if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2.0) &lt; m_LineThickness)<br />
    
  •        linePixel = 1.0;<br />
    

}

if (lum < m_Luminance2){

  •    if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2) &lt; m_LineThickness)<br />
    
  •        linePixel = 1;<br />
    
  •    if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2.0) &lt; m_LineThickness)<br />
    
  •        linePixel = 1.0;<br />
    

}

if (lum < m_Luminance3){

if (mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness)

  •        linePixel = 1;<br />
    
  •        linePixel = 1.0;<br />
    

}

if (lum < m_Luminance4){

if (mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness)

  •        linePixel = 1;<br />
    
  •        linePixel = 1.0;<br />
    

}

if (lum < m_Luminance5){ // No line, make a blob instead

linePixel = m_FillValue;

Index: src/core-data/Common/MatDefs/Post/CrossHatch.j3md

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

— src/core-data/Common/MatDefs/Post/CrossHatch.j3md (revision 7050)

+++ src/core-data/Common/MatDefs/Post/CrossHatch.j3md (working copy)

@@ -13,8 +13,8 @@

Float Luminance3;

Float Luminance4;

Float Luminance5;

  •    Int LineThickness;<br />
    
  •    Int LineDistance;<br />
    
  •    Float LineThickness;<br />
    
  •    Float LineDistance;<br />
    

}



Technique {



[/patch]

My pleasure. :smiley:

committed. rev.7055

Cool, go ahead and commit.

just a remark though line 11 and 12 of the patch, be sure to use float initialization with a .0



private float lineThickness = 1.0;

private float lineDistance = 4.0;



Some card are sensible to this.



Thanks once again for your help :wink:

Ah, good to know, I’ll keep that in mind in future shaders :wink: Didn’t know about such errors on a mac ^^

baalgarnaal said:
Ah, good to know, I'll keep that in mind in future shaders ;) Didn't know about such errors on a mac ^^


Shaders are glorious in their power but frustrating to get working right on all platforms. :)

Well if it’s just a matter of dodging ints and using floats instead I think I can get along with them :stuck_out_tongue:

I wanted to know how much ‘if’ statment affect performance.

I changed CrossHatch fragment shader to test performance diff because it uses many ‘if’ statement,

Actually the visual result is somewhat different to the original shader.

I don’t know what is wrong… :frowning:



Anyway, when I removed the ‘if’ statement, it shows 7% of performance boost.

Original FPS: 210

Test FPS: 225

Please count this when creating shaders.



[patch]

Index: src/core-data/Common/MatDefs/Post/CrossHatch15.frag

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

— src/core-data/Common/MatDefs/Post/CrossHatch15.frag (revision 7061)

+++ src/core-data/Common/MatDefs/Post/CrossHatch15.frag (working copy)

@@ -15,39 +15,28 @@

uniform float m_Luminance4;

uniform float m_Luminance5;



-uniform int m_LineDistance;

-uniform int m_LineThickness;

+uniform float m_LineDistance;

+uniform float m_LineThickness;

+

+const vec3 luminance = vec3(0.2126, 0.7152, 0.0722);



void main() {

vec4 texVal = getColor(m_Texture, texCoord);

  • float linePixel = 0;

    -
  • float lum = texVal.r0.2126 + texVal.g0.7152 + texVal.b*0.0722;

    -
  • if (lum < m_Luminance1){
  •    if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2) &lt; m_LineThickness)<br />
    
  •        linePixel = 1;<br />
    
  • }
  • if (lum < m_Luminance2){
  •    if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2) &lt; m_LineThickness)<br />
    
  •        linePixel = 1;<br />
    
  • }
  • if (lum < m_Luminance3){
  •    if (mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance) &lt; m_LineThickness)<br />
    
  •        linePixel = 1;<br />
    
  • }
  • if (lum < m_Luminance4){
  •    if (mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance) &lt; m_LineThickness)<br />
    
  •        linePixel = 1;<br />
    
  • }
  • if (lum < m_Luminance5){ // No line, make a blob instead
  •    linePixel = m_FillValue;<br />
    
  • }

    -
  • float linePixel = 0.0;
  • float lum = dot(texVal.rgb, luminance);

    +
  • vec4 lhs = step(vec4(lum), vec4(m_Luminance1, m_Luminance2, m_Luminance3, m_Luminance4));
  • vec4 rhs = step(vec4(mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2.0),
  •                     mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2.0),<br />
    
  •                     mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance),<br />
    
  •                     mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance)), vec4(m_LineThickness));<br />
    
  • linePixel = min(dot(lhs, rhs), 1.0);
  • linePixel = mix(linePixel, m_FillValue, step(lum, m_Luminance5));

    +

    // Mix line color with existing color information

    vec4 lineColor = mix(m_LineColor, texVal, m_ColorInfluenceLine);

    // Mix paper color with existing color information

    vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper);



    gl_FragColor = mix(paperColor, lineColor, linePixel);

    -}

    No newline at end of file

    +}



    [/patch]

yeah branching can kill shaders performance,

mulova said:
Please count this when creating shaders.


Well I didn't really look into the actual performance but I know about the if statement slow downs. 7% difference is less than I expected actually.
Looking at your code, that step function is new for me but it seems pretty much a hidden if statement to me, how is that thing working any different than a normal if-statement? (other than getting around a number of branches of course)

I tried looking into the different results you say, and indeed it is visually different. All I can say is that the difference is already there before the blobbing part, seems lines are drawn thicker than they should be, compare the results when using:
setLineDistance(4);
setLineThickness(2);
With your code some areas seem to have gone completely drawn full of lines while there should be spaces in between.


Update: I think your line thickness is off by 1 pixel. Try comparing the results for lineThickness -1, 0 and 1. Original 0 seems to match yours with -1, and original 1 matches yours with 0, whereas 0 line thickness should explain itself as having no lines at all of course ;)

As I understand it, step() can be handled efficiently by the GPU and inlined like any other math operation… which is why it’s the recommended way around if() statements.

pspeed said:
As I understand it, step() can be handled efficiently by the GPU and inlined like any other math operation... which is why it's the recommended way around if() statements.

An efficient shader compiler will actually convert control flow to step() like instructions, as in the case of this shader for example. This is why you only see a nearly insignificant 7% improvement.

However, if you're using control flow to avoid doing an expensive operation (like reading a texture), it will run more efficiently on GPUs that support hardware control flow.

Example code for crappy GPU (GeForce 5)
[java]SLT R1.x, fragment.position, c[0].z;
ABS R2.x, R1;
TEX R0, c[0].y, texture[1], 2D;
TEX R1, c[0].y, texture[0], 2D;
CMP R2.x, -R2, c[0].y, c[0];
CMP result.color, -R2.x, R0, R1;[/java]

Example code for modern GPU (GeForce 200)
[java]SLT.F R0.x, fragment.position, {0.5};
TRUNC.U.CC HC.x, R0;
IF NE.x;
TEX.F result_color0, {0}.x, texture0, 2D;
ELSE;
TEX.F result_color0, {0}.x, texture1, 2D;
ENDIF;[/java]
NOTE: Just because there's if-statements in the code I posted does NOT mean the other texture fetch fetch won't be executed, it just means that whatever troubles are there with control flow will be handled by the hardware and not software.
baalgarnaal said:
Update: I think your line thickness is off by 1 pixel. Try comparing the results for lineThickness -1, 0 and 1. Original 0 seems to match yours with -1, and original 1 matches yours with 0, whereas 0 line thickness should explain itself as having no lines at all of course ;)

Thank you for the finding the reason. :)

Momoko_Fan said:
An efficient shader compiler will actually convert control flow to step() like instructions, as in the case of this shader for example. This is why you only see a nearly insignificant 7% improvement.

However, if you're using control flow to avoid doing an expensive operation (like reading a texture), it will run more efficiently on GPUs that support hardware control flow.

Thank you for this invaluable tip!! :)
It would be good to have a wiki to share the tip for efficient shader writing.

I’d like to point out that the crosshatch filter as it is currently in the core is not working correctly. No lines are drawn at all, and by inspecting the differences between what is in the core atm and what I made is the int to float change stated in this post.



I think the problem is in the crossHatch15.frag file:

[java]uniform int m_LineDistance;

uniform int m_LineThickness;[/java]



These are still int and we are passing float values into the shader, so I suppose the system has no clue what’s going on there.

I suggest we turn those ints into floats aswell to keep the 15 frag shader up to date, it really messed up the shader. :wink:

Should be fixed now

1 Like