-
I'm drawing some tiled sprites in the background on a RenderTexture, and about 50% of the time it'll show completely corrupted graphics for the first frame, and then after that it'll be fine, here's an example:
(http://i50.tinypic.com/302nts8.png)
Has anyone else had this problem? I'm using one of the newer revisions currently available in the repo
-
Can you show your code?
-
Sure, on each game loop's draw command the following takes place:
void VideoManager::Draw(){
std::lock_guard<std::mutex> lk(videoMutex);
// Clear screen
Game.Application->clear();
Game.CurrentMap->Draw();
Game.Application->display();
for(unsigned i=0;i<RenderQueue.size();++i) {
if(RenderQueue[i]){
RenderQueue[i]->Draw();
}
}
if(consoleView->Visible()){
consoleView->Draw();
}
// Finally, display the rendered frame on screen
Game.Application->display();
}
Where RenderQueue is a vector of SSprites, which are shared pointers to my classes that implement drawing. One of these is the control shown above in the screenshot, that draws tiled images.
On the create method it currently defines some hard coded values, all of the images referenced exist, so the problem's not to do with that:
//Create our section data
std::vector<MapSection> sectionRow;
MapSection section;
//Create a fake tileset with our debug tiles
Tileset[0] = Data.TextureResourceWithID("Data/Map/BlankTile.png");
Tileset[1] = Data.TextureResourceWithID("Data/Map/DebugTile.png");
Tileset[2] = Data.TextureResourceWithID("Data/Map/BlockedTile.png");
//Fill our section data with tiles
for(int X = 0; X < SECTION_TILE_COUNT_X; X++){
std::vector<int> currentRow;
for(int Y=0; Y < SECTION_TILE_COUNT_Y; Y++){
currentRow.push_back(2);
}
section.Tiles.push_back(currentRow);
}
//Set up our data
currentSectionX = 0;
currentSectionY = 0;
currentX = 0;
currentY = 0;
sectionRow.push_back(section);
Sections.push_back(sectionRow);
//Finally assign our Sprite & Texture, and refresh the view
MapTexture.create(Video.GetScreenDimensions().width+TILE_WIDTH,Video.GetScreenDimensions().height+TILE_HEIGHT);
MapSprite = sf::Sprite(MapTexture.getTexture());
MapSprite.setPosition(-TILE_WIDTH, -TILE_HEIGHT);
the Draw command firstly calls Refresh, and then calls the RenderWindow's Draw command, pointing it to the MapSprite sprite:
Game.Application->draw(MapSprite);
The Refresh command looks like this:
void Map::Refresh(){
MapTexture.clear(sf::Color::Transparent);
MapTexture.display();
for(int X = 0 - TILE_WIDTH; X < SECTION_TILE_COUNT_X; X++){
for(int Y=0 - TILE_HEIGHT; Y < SECTION_TILE_COUNT_Y; Y++){
sf::Texture *tex = Tileset[2].get();
tileBlitter = sf::Sprite(*tex);
tileBlitter.setPosition(X*TILE_WIDTH, Y*TILE_HEIGHT);
MapTexture.draw(tileBlitter);
}
}
MapTexture.display();
}
I should note that yes I know calling refresh on every draw is expensive, but it's the only way to ever get anything more than garbage displaying for this view.
I've used an identical method for another view and that seems to work fine, I think it might be my reuse of sprites, but I did remember trying creating a new sprite to draw each tile during the loop and that didn't help either. Does SFML require sprites to exist after the data is rendered to the texture?
-
It's hard to debug something which is part of such a big project.
Would you be able to write a complete and minimal example that reproduces the problem?
-
Sure, here's it all flattened down into a main.cpp. Note that the problem is still happening with this example:
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>
#include "ResourcePath.hpp"
#include <tr1/memory>
#include <map>
using namespace std;
class MapSection {
public:
std::vector<std::vector<int> > Tiles;
private:
};
typedef std::tr1::shared_ptr<sf::Texture> STexture;
#define TILE_WIDTH 128
#define TILE_HEIGHT 72
#define SECTION_TILE_COUNT_X 11
#define SECTION_TILE_COUNT_Y 11
int main (int argc, const char * argv[])
{
std::vector<std::vector<MapSection> >Sections;
sf::RenderTexture MapTexture;
sf::Sprite MapSprite;
map<int, STexture> Tileset;
// Create the main window
sf::RenderWindow window(sf::VideoMode(1280, 720), "SFML window");
//Create our section data
std::vector<MapSection> sectionRow;
MapSection section;
//Create a fake tileset with our debug tiles
Tileset[0] = STexture(new sf::Texture());
Tileset[0]->loadFromFile(resourcePath() + "BlankTile.png");
Tileset[1] = STexture(new sf::Texture());
Tileset[1]->loadFromFile(resourcePath() + "BlockedTile.png");
Tileset[2] = STexture(new sf::Texture());
Tileset[2]->loadFromFile(resourcePath() + "DebugTile.png");
//Fill our section data with tiles
for(int X = 0; X < SECTION_TILE_COUNT_X; X++){
std::vector<int> currentRow;
for(int Y=0; Y < SECTION_TILE_COUNT_Y; Y++){
currentRow.push_back(2);
}
section.Tiles.push_back(currentRow);
}
//Set up our data
unsigned int currentSectionX;
unsigned int currentSectionY;
unsigned int currentX; //These map the TOP LEFT viewable tile
unsigned int currentY;
sf::Sprite tileBlitter;
currentSectionX = 0;
currentSectionY = 0;
currentX = 0;
currentY = 0;
sectionRow.push_back(section);
Sections.push_back(sectionRow);
//Finally assign our Sprite & Texture, and refresh the view
MapTexture.create(1280+TILE_WIDTH,720+TILE_HEIGHT);
MapSprite = sf::Sprite(MapTexture.getTexture());
MapSprite.setPosition(-TILE_WIDTH, -TILE_HEIGHT);
// Start the game loop
while (window.isOpen())
{
// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
window.close();
// Escape pressed : exit
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
window.close();
}
// Clear screen
window.clear();
MapTexture.clear(sf::Color::Transparent);
MapTexture.display();
for(int X = 0 - TILE_WIDTH; X < SECTION_TILE_COUNT_X; X++){
for(int Y=0 - TILE_HEIGHT; Y < SECTION_TILE_COUNT_Y; Y++){
sf::Texture *tex = Tileset[2].get();
tileBlitter = sf::Sprite(*tex);
tileBlitter.setPosition(X*TILE_WIDTH, Y*TILE_HEIGHT);
MapTexture.draw(tileBlitter);
}
}
MapTexture.display();
// Draw the sprite
window.draw(MapSprite);
// Draw the string
//window.draw(text);
// Update the window
window.display();
}
return EXIT_SUCCESS;
}
-
Thanks :)
But can't it be even simpler? Can't you get rid of all the "tilemap" logic, and just draw a simple sprite on a simple render-texture?
By the way, what's your OS and graphics card?
-
OS X 10.7, and I have a Radeon HD 6870.
I've simplified it a bit and discovered it's the RenderTexture I'm drawing to that's getting corrupted, specifically if I do this:
sf::Texture *tex = Tileset[0].get();
tileBlitter = sf::Sprite(*tex);
tileBlitter.setPosition(X*TILE_WIDTH, Y*TILE_HEIGHT);
MapTexture.draw(tileBlitter);
I get corruption, but if I do this:
sf::Texture *tex = Tileset[0].get();
tileBlitter = sf::Sprite(*tex);
tileBlitter.setPosition(X*TILE_WIDTH, Y*TILE_HEIGHT);
window.draw(tileBlitter);
I don't.
I'm creating the RenderTexture and its Sprite the same way I always do, with the following code:
MapTexture.create(1280+TILE_WIDTH,720+TILE_HEIGHT);
MapSprite = sf::Sprite(MapTexture.getTexture());
MapSprite.setPosition(-TILE_WIDTH, -TILE_HEIGHT);
I've tested and it's not due to creating a large RenderTexture, any size seems to still cause the corruption.
-
Can't you just put these 4 lines in a typical minimal example? If you want us to help you, we need something that we can test quickly, and which focuses on the problem -- not on drawing tiles.
-
Sorry, I didn't realize copying and pasting the sample I've given was such a time consuming process. Don't bother after all, I'll just go use something else.
-
It's not copying and pasting which is time consuming, it's finding the error within all these lines of code. Since it's obvious that 75% of the code is still irrelevant to the problem, it's much easier if you remove it before posting the code.
There's no reason to draw a complete tilemap if your problem is about drawing a sprite on a render-texture. Unless you tell me that a simpler code doesn't reproduce the problem, of course.