I've recently run in to this bug while trying to implement post-effect shaders without copying the contents of the window and I believe that I may have come across some additional information which may help quash this bug.
When running with a shader on, renderTexture.clear() appears to be working as intended but when the shader is switched off the problem appears again. While the previous work-around of drawing a transparent shape over the entire renderTexture works I've found another work-around which may help narrow down where the problem lies. If you call window.resetGLStates() at any point in the rendering loop the problem disappears. Looking at the source for RenderTarget::resetGLStates I've tried using all of the OpenGL calls there in my code after calling window.setActive() and it doesn't seem to have any effect. So it must be one of the other parts of resetGLStates() that is correcting the problem. It also seems weird that the call is to the window and not the renderTexture to fix the problem. I tried calling it on the renderTexture but it had no effect.
So far this seems to only be affecting ATI cards. I'm running Win 7 x64 with a Radeon 7970. My graphics card drivers are Catalyst 13.12 and I'm using the pre-compiled SFML 2.1 libs statically linked. My compiler is VS2012 x64 and the problem appears in both debug and release builds. There are no error messages sent to the console window and I've broken the shader program before and the error messages were sent to the console so the error output is working.
I've attached a piece of sample code below which demonstrates the problem on my machine. The shader, the original work-around, and my work around are all toggleable from within the program by using the F1, F2, and F3 keys respectively.
// Includes
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>
#include <iostream>
int main()
{
// Constants
const int windowWidth = 800, windowHeight = 600;
const float ballWidth = 50.0f, ballHeight = 50.0f;
sf::Vector2f ballVelocity(-300.0f,-300.0f);
// Toggles
bool ShaderEnabled = false;
bool hackOneEnabled = false;
bool hackTwoEnabled = false;
// Fragment Shader Code
const std::string fragmentShader = \
"uniform sampler2D texture;" \
"void main()" \
"{" \
" gl_FragColor = texture2D(texture, gl_TexCoord[0].xy) * vec4(1.0, 0.0, 0.0, 1.0);" \
"}";
// Create Main Window
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "RenderTexture Issue Demo");
window.setVerticalSyncEnabled(true);
// Load Shader
sf::Shader Shader;
if (!Shader.loadFromMemory(fragmentShader, sf::Shader::Fragment))
{
std::cout << "ERROR: Unable to load shader" << std::endl;
std::cout << "Press any key to continue..." << std::endl;
std::cin.get();
return EXIT_FAILURE;
}
// Create RenderTexture
sf::RenderTexture renderTexture;
renderTexture.create(windowWidth,windowHeight);
// Create Sprite for the RenderTexture
sf::Sprite renderSprite;
renderSprite.setTexture(renderTexture.getTexture());
// Create Ball
sf::RectangleShape ball(sf::Vector2f(ballWidth,ballHeight));
ball.setOrigin(sf::Vector2f(ballWidth * 0.5f, ballHeight * 0.5f));
ball.setPosition(sf::Vector2f(0.5f * windowWidth, 0.5f * windowHeight));
ball.setFillColor(sf::Color::White);
// Create Transparent Shape with the same size as the window
sf::RectangleShape screenClearer;
screenClearer.setSize(sf::Vector2f(windowWidth, windowHeight));
screenClearer.setFillColor(sf::Color::Transparent);
// Time and Timers Initialisation
sf::Clock frameTimer;
sf::Time lastFrameTime;
const sf::Time physicsTimeStep = sf::microseconds(10);
// Main Loop
while (window.isOpen())
{
// Simulation Timing
lastFrameTime += frameTimer.restart();
// Clamp the maximum frame time to prevent self-perpetuating slowdowns
if (lastFrameTime.asMilliseconds() > 250)
lastFrameTime = sf::milliseconds(250);
while (lastFrameTime >= physicsTimeStep)
{
// Move Ball
ball.move(ballVelocity * physicsTimeStep.asSeconds());
// Check Wall Colisions
if ((ball.getPosition().x - 0.5f * ballWidth) < 0)
{
ballVelocity.x = std::abs(ballVelocity.x);
}
if ((ball.getPosition().x + 0.5f * ballWidth) > windowWidth)
{
ballVelocity.x = -std::abs(ballVelocity.x);
}
if (((ball.getPosition().y - 0.5f * ballHeight) < 0) || ((ball.getPosition().y + 0.5f * ballHeight) > windowHeight))
ballVelocity.y = -ballVelocity.y;
// Advance Simulation Step
lastFrameTime -= physicsTimeStep;
}
// Render
renderTexture.clear();
renderTexture.draw(ball);
// Hack to fix bug where the renderTexture doesn't clear
if (hackOneEnabled)
renderTexture.draw(screenClearer);
renderTexture.display();
// Alternative hack to fix aforementioned bug.
// It doesn't seem to matter when this happens as long as it happens at least once per loop.
if (hackTwoEnabled)
window.resetGLStates();
window.clear();
if (ShaderEnabled)
window.draw(renderSprite, &Shader);
else
window.draw(renderSprite);
window.display();
// Event Handler
sf::Event event;
while (window.pollEvent(event))
{
switch(event.type)
{
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
{
// Toggle Shader
if (event.key.code == sf::Keyboard::F1)
ShaderEnabled = !ShaderEnabled;
// Toggle first RenderTexture clearing hack (Transparent texture over screen)
if (event.key.code ==sf::Keyboard::F2)
hackOneEnabled = !hackOneEnabled;
// Toggle second RenderTexture clearing hack (window.resetGLStates())
if (event.key.code ==sf::Keyboard::F3)
hackTwoEnabled = !hackTwoEnabled;
break;
}
}
}
}
return EXIT_SUCCESS;
}