Bildschirmriss im Fragment-Shader auf R9 380-GPUs

8

Bei zwei Spielern tritt bei meinem Spiel ein Problem auf, bei dem der Bildschirm bei Verwendung eines Fragment-Shaders reißt. Dies scheint jedoch nur für Spieler mit einer R9 380-GPU der Fall zu sein. So sieht es im Spiel aus:

Geben Sie hier die Bildbeschreibung ein

Nachdem ich mit einem der Spieler gearbeitet hatte, beschränkte ich mich auf die Verwendung des Shaders, aber es könnte etwas sein, das im aufrufenden Code nicht richtig gemacht wurde. So sieht der Shader aus (beachten Sie, dass ich noch GLSL lerne).

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

Hier ist der Backend-Code (c ++) mit 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);

Gibt es hier etwas Offensichtliches, das mir fehlt, was ich nicht tun sollte? Kennt jemand Probleme mit R9 380-Treibern? Wenn ich mir das Zerreißen anschaue, würde ich vermuten, dass wir falsch von der Zieltextur abtasten, aber ich verstehe nicht, wie oder warum.

jmcmorris
quelle
Direkt aus Wikipedia: "Bildschirmrisse sind ein visuelles Artefakt in der Videoanzeige, bei dem ein Anzeigegerät Informationen aus mehreren Bildern in einer einzigen Bildschirmzeichnung anzeigt." Dies beruht höchstwahrscheinlich auf der Tatsache, dass Ihre Freunde eine AMD-basierte GPU haben, da Nvidia-Treiber Monitore normalerweise gut mit Bildraten synchronisieren können. Es tut uns leid, wenn es nicht viel ist, aber leiten Sie Ihre Forschung in Richtung AMD-Architektur um.
Java Man Tea Man
1
Haben Sie sichergestellt, dass sie die neuesten Treiber verwenden? Auch dies ist eine gut zusammengestellte Frage, aber bestimmte Probleme vom Typ Debugging wie diese sind ziemlich schwer zu beantworten, sodass Sie möglicherweise nicht das beste Glück haben. Nur damit Sie wissen, ist es in Ordnung, Links zu Ihrem Spiel in Ihrem Profil zu haben.
MichaelHouse
1
Wenn Sie die diagonale Linie von Punkten meinen, die von ungefähr der Mitte des Bildschirms bis zur oberen rechten Ecke verläuft, dann ist das kein Bildschirmriss. Zerreißen ist nichts, was Sie in einem Screenshot festhalten können.
Ross Ridge
1
Von seiner Platzierung aus sieht es aus wie eine ganze Bildschirmdreieckskante.
MichaelHouse
Ja, ich wusste nicht, wie ich das nennen sollte, und Zerreißen war das Beste, was ich mir einfallen lassen konnte. Ich spreche über die einzelnen Zeilen. Es sieht so aus, als würde es falsch von der Textur abgetastet oder so. Ich sehe jedoch nicht ein, wie das der Fall sein könnte. Ich schätze die Hinweise und kann gerne weitere Vorschläge machen. Prost! (Aktualisiertes Profil)
jmcmorris

Antworten:

8

Ich bin heute darauf zurückgekommen und nach einigen weiteren Untersuchungen und Versuchen habe ich festgestellt, dass der Schuldige targetTexture war. Mit einigen weiteren Untersuchungen habe ich gelernt, dass das Lesen und Schreiben in derselben Textur in einem Shader eine schlechte Praxis ist (nicht überraschend) und undefiniertes Verhalten auf GPUs verursacht.

Die Lösung bestand darin, die Zieltextur in eine neue Textur zu kopieren und dann aus der Kopie zu lesen, während noch auf die ursprüngliche Zieltextur geschrieben wurde.

jmcmorris
quelle
1
Es ist auf allen GPUs undefiniert.
Andreas