8
\$\begingroup\$

two players are encountering an issue with my game where the screen is tearing when it uses a fragment shader but it only seems to be doing this for players with a R9 380 GPU. Here is what it looks like in game:

enter image description here

After working with one of the players I narrowed it down to using the shader but it could be something not being done properly in the calling code. Here is what the shader looks like (note I'm still learning GLSL).

uniform sampler2D lightTexture;
uniform sampler2D targetTexture;
uniform sampler2D backgroundTexture;
uniform vec2 tileSize;

vec3 SaturationBrightness(vec3 color, float brt, float sat)
{
    // Increase or decrease theese values to adjust r, g and b color channels seperately
    const float AvgLumR = 0.5;
    const float AvgLumG = 0.5;
    const float AvgLumB = 0.5;

    const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);

    vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
    vec3 brtColor = color * brt;
    vec3 intensity = vec3(dot(brtColor, LumCoeff));
    vec3 satColor = mix(intensity, brtColor, sat);
    return satColor;
}

void main(void)
{
    vec2 position;
    position.s = gl_TexCoord[0].s;
    position.t = gl_TexCoord[0].t;
    vec4 lightColor = texture2D(lightTexture, position);

    //Return the lighting if the light is pure dark since the tile behind it was not rendered
    if (lightColor.r == 0.0 && lightColor.g == 0.0 && lightColor.b == 0.0) {
        gl_FragColor = lightColor;
        return;
    }

    //Get the average of the the nearby light
    position.t -= tileSize.t;
    vec4 lightColorUp = texture2D(lightTexture, position);
    position.t += tileSize.t*2.0;
    vec4 lightColorDown = texture2D(lightTexture, position);
    position -= tileSize;
    vec4 lightColorLeft = texture2D(lightTexture, position);
    position.s += tileSize.s*2.0;
    vec4 lightColorRight = texture2D(lightTexture, position);
    position.s += tileSize.s;
    vec4 lightColorFarRight = texture2D(lightTexture, position);
    position.s -= tileSize.s*4.0;
    vec4 lightColorFarLeft = texture2D(lightTexture, position);
    position.s += tileSize.s*2.0;
    position.t -= tileSize.t*2.0;
    vec4 lightColorFarUp = texture2D(lightTexture, position);
    position.t += tileSize.t*4.0;
    vec4 lightColorFarDown = texture2D(lightTexture, position);
    lightColor = lightColorRight + lightColorUp + lightColorDown + lightColorLeft + lightColorFarRight + lightColorFarUp + lightColorFarDown + lightColorFarLeft;
    lightColor.r /= 8.0;
    lightColor.g /= 8.0;
    lightColor.b /= 8.0;
    lightColor.a /= 8.0;

    //Get the target (foreground) that we apply the light to
    vec4 targetColor = texture2D(targetTexture, gl_TexCoord[0].st);
    if (targetColor.a == 0.0) {
        //Foreground is transparent meaning that we never rendered to it so instead render the background without light
        gl_FragColor = texture2D(backgroundTexture, gl_TexCoord[0].st);
    } else {
        //Apply averaged light to target
        gl_FragColor = vec4(SaturationBrightness(lightColor.rgb, 1.15, 1.1), lightColor.a) * targetColor;
    }
}

Here is the backend code (c++) using SFML.

SpecialRenderTexturePtr targetTexture = boost::static_pointer_cast<SpecialRenderTexture>(target);
targetTexture->display();

m_texture->setView(m_view);
m_texture->clear(Color(0, 0, 0, 255));
m_texture->draw(m_vertices);
m_texture->display();

m_shader.setParameter("lightTexture", m_texture->getTexture());
m_shader.setParameter("targetTexture", targetTexture->getTexture());
m_shader.setParameter("backgroundTexture", m_render->getBackgroundTexture()->getTexture());
Vector tileSize(Sizes::SUBTILE / 2.0 / m_texture->getSize().x, Sizes::SUBTILE / 2.0 / m_texture->getSize().y);
m_shader.setParameter("tileSize", tileSize.toSfml());
sf::Sprite lightSprite(m_texture->getTexture());
Vector viewPosition = m_view.getCenter();
const Vector& viewSize = m_view.getSize();
viewPosition.x = ceil(viewPosition.x - (viewSize.x / 2.0f));
viewPosition.y = ceil(viewPosition.y - (viewSize.y / 2.0f));
lightSprite.setPosition(viewPosition.toSfml());

target->draw(lightSprite, &m_shader);

Is there anything obvious here that I'm missing that I shouldn't be doing. Anyone know of any issues with R9 380 drivers? Looking at the tearing from what I'd guess is that we're sampling from the target texture incorrectly but I don't see how or why.

\$\endgroup\$
  • \$\begingroup\$ Straight from Wikipedia: "Screen tearing is a visual artifact in video display where a display device shows information from multiple frames in a single screen draw." This is most likely based on the fact that your friends have an AMD based gpu, as Nvidia drivers are usually good at syncing monitors to frame rates. Sorry if it is not a lot, but redirect your research towards AMD architecture. \$\endgroup\$ – Java Man Tea Man Mar 2 '16 at 20:40
  • 1
    \$\begingroup\$ Have you ensured they're using the latest drivers? Also, this is a well put together question, but specific debugging type issues like this are pretty difficult to answer, so you might not have the best of luck. Also, just so you know, it's OK to have links to your game in your profile. \$\endgroup\$ – MichaelHouse Mar 2 '16 at 21:14
  • 1
    \$\begingroup\$ If you mean the diagonal line of dots going from about them middle of the screen to the upper right corner then that's not screen tearing. Tearing isn't something you can capture in a screenshot. \$\endgroup\$ – Ross Ridge Mar 2 '16 at 22:31
  • 1
    \$\begingroup\$ From its placement it looks like a whole screen triangle edge. \$\endgroup\$ – MichaelHouse Mar 2 '16 at 23:10
  • \$\begingroup\$ Yeah, I didn't know what to call this and tearing was the best I could come up with. I am talking about the individual lines. It looks like it is sampling from the texture incorrectly or something. I don't see how that could be the case though. I appreciate the leads and feel free to suggest more. Cheers! (Updated profile) \$\endgroup\$ – jmcmorris Mar 3 '16 at 21:47
8
\$\begingroup\$

I came back to this today and after some more investigation and trial and error I discovered that the culprit was targetTexture. With some more investigation I learned that reading and writing to the same texture in a shader is bad practice (not surprisingly) and will cause undefined behavior on GPUs.

The solution was to copy the target texture to a new texture and then read from the copy while still writing to the original target texture.

\$\endgroup\$
  • 1
    \$\begingroup\$ It is undefined on all GPUs. \$\endgroup\$ – Andreas May 21 '16 at 13:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.