First off I just want to say what I'm making to give you a little better understanding of what it is that I am trying to achieve. I'm trying to make an optimized way of drawing huge amounts of tiled textures onto to screen, or rather an optimized way of storing them and choosing which of them to be drawn onto the screen. So far I've managed to have 3 million 16x16 tiles spread all over my map with an fps of about 1300 - 1400, although it chews up alot of my memory at the moment. To maintain a high fps i render the tiles offscreen to several rendertextures that have the size 1920 x 1088 and draw these onto the screen. My big is that preparing these rendertextures take quite some time, and I preparing them when the texture gets close to the camera. The loading of the texture takes so long that it freezes the screen for the whole duration that it loads. So what i tried is using an sf::thread that is in charge of preparing these textures to be drawn so that the whole program does not freeze, so far I haven't succeeded very well and I have no idea if I'm doing it wrong or if this is the way threads work. Basically the way I'm doing it is this way:
int main ()
{
sf::RenderWindow app(sf::VideoMode(1920, 1080), "test");
MyClass myClass;
sf::thread loadThread(&MyClass::loadTextures, &myClass);
loadthread.launch();
while ( app.isOpen() )
{
// Program here
// Check for textures that are intersecting the view and have been prepared by the thread
// Then draw them onto the screen
}
return 0;
}
//MyClass.h
class MyClass
{
public:
MyClass();
void loadTextures();
private:
bool doLoad;
};
//MyClass.cpp
MyClass::MyClass():
doLoad(true)
{
}
void MyClass::loadTextures()
{
while ( doLoad )
{
//Check for textures intersecting the view and update these here
}
}
What happens is that the main loop " while ( app.isOpen() ) " freezes when the thread finds a texture to update and starts updating it, the main loop then continues when the updating of the texture has been completed. What I'm trying to do is to make them run in parallel.
I thank you for taking the time to read this! :)
The code above is NOT actual code from my program, this is the basic logic of the draw and texture update parts of my program stripped down to its absolute basics.
I dont exactly know how i would reduce this to a minimal example the way it is done now so I'm hoping this will do.
void OptiNode::DrawToTexture()
{
//Draw all the sprites that this node contains to an offscreen texture,
//then ready the texture to be drawn by sfml
for ( int i = 0; i < mNumberOfLayers; i++ )
{
//Create a new rendertexture that we can draw to
sf::RenderTexture *rendTex = new sf::RenderTexture();
//Copy the rendertexture into the vector holding the rendertextures
mOptiTextures.push_back(rendTex);
//Call the create command for it be valid for rendering
//This is vector containing sf::RenderTexture*
mOptiTextures[i]->create(mSize.x, mSize.y);
//Set a view for the rendertexture
//The view specifies in which area of the screen that it should " look " at
mOptiTextures[i]->setView(mNodeView);
//Clear the textures with the color magenta
//The reason for this is cause it's a weird color that will probably never be used otherwise,
//which makes it ideal for masking out all the transparent areas of the texture
mOptiTextures[i]->clear(sf::Color::Magenta);
for ( std::vector<OptiSprite*>::size_type j = mCurrentSprite; j < mStaticSprites.size(); j++ )
{
if ( mStaticSprites[j]->GetLayer() == i )
{
mOptiTextures[i]->draw(mStaticSprites[j]->GetSprite());
}
}
//When all the tiles have been added to the texture, display it
mOptiTextures[i]->display();
//Mask away the magenta color to make the texture transparent in the right places
mMaskImage = mOptiTextures[i]->getTexture().copyToImage();
mMaskImage.createMaskFromColor(sf::Color::Magenta);
sf::Texture maskedTexture;
mMaskedTextures.push_back(maskedTexture);
mMaskedTextures[i].loadFromImage(mMaskImage);
sf::Sprite optiSprite(mMaskedTextures[i]);
optiSprite.setPosition(mPosition);
//Push back the ready sprite contianing the 1920 x 1088 texture into the mOptiSprites vector
mOptiSprites.push_back(optiSprite);
//Signal that we are done
mStatus = READY;
mIsUnloaded = false;
}
}
This is the code that is being looped in the load texture thread. It is only being done on the nodes that intersect the camera view. From what i have seen it seems that the RenderTexture.Create() function is one of the things making it freeze. But should the whole program freeze while it loads when I'm doing it in a separate thread than the main loop? If not, what could I be doing wrong?
I tried recreating the problem but with minimal code. The result was the same, while the texture is being prepared the main the whole program goes horribly slow. I can't figure out what is causing this.
#include <SFML/Graphics.hpp>
bool doLoad = true;
bool status = false;
std::vector<sf::Sprite> SpriteVector;
sf::RenderTexture *OptiTexture;
sf::Texture MaskedTexture;
sf::Sprite OptiSprite;
sf::Image mMaskImage;
sf::View mNodeView;
void loadTextures()
{
while ( doLoad )
{
//Create a new rendertexture that we can draw to
if ( !OptiTexture )
{
OptiTexture = new sf::RenderTexture();
}
//Call the create command for it be valid for rendering
//This is vector containing sf::RenderTexture*
OptiTexture->create(1920, 1080);
//Set a view for the rendertexture
//The view specifies in which area of the screen that it should " look " at
OptiTexture->setView(mNodeView);
//Clear the textures with the color magenta
//The reason for this is cause it's a weird color that will probably never be used otherwise,
//which makes it ideal for masking out all the transparent areas of the texture
OptiTexture->clear(sf::Color::Magenta);
for ( std::vector<sf::Sprite>::size_type j = 0; j < SpriteVector.size(); j++ )
{
OptiTexture->draw(SpriteVector[j]);
}
//When all the tiles have been added to the texture, display it
OptiTexture->display();
//Mask away the magenta color to make the texture transparent in the right places
mMaskImage = OptiTexture->getTexture().copyToImage();
mMaskImage.createMaskFromColor(sf::Color::Magenta);
MaskedTexture.loadFromImage(mMaskImage);
OptiSprite.setTexture(MaskedTexture);
OptiSprite.setPosition(0,0);
//Signal that we are done
status = true;
}
}
int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(1920, 1080), "SFML window");
window.setFramerateLimit(60);
window.setVerticalSyncEnabled(true);
sf::View view;
view.setSize(1920,1080);
// Load a sprite to display
sf::Texture texture;
if (!texture.loadFromFile("dirt.png"))
return EXIT_FAILURE;
sf::Sprite sprite(texture);
mNodeView.setSize(1920,1088);
mNodeView.setCenter((1920/2),(1088/2));
for ( int i = 0; i < 10; i++ )
{
for ( int j = 0; j < 10; j++ )
{
sprite.setPosition(i*16,j*16);
SpriteVector.push_back(sprite);
}
}
status = false;
sf::Thread loadThread(&loadTextures);
loadThread.launch();
// 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();
}
if ( sf::Keyboard::isKeyPressed(sf::Keyboard::W) )
{
view.move(sf::Vector2f(0,-10));
}
if ( sf::Keyboard::isKeyPressed(sf::Keyboard::S) )
{
view.move(sf::Vector2f(0,10));
}
if ( sf::Keyboard::isKeyPressed(sf::Keyboard::A) )
{
view.move(sf::Vector2f(-10,0));
}
if ( sf::Keyboard::isKeyPressed(sf::Keyboard::D) )
{
view.move(sf::Vector2f(10,0));
}
// Clear screen
window.clear();
window.setView(view);
sf::IntRect cameraBounds;
cameraBounds.left = view.getCenter().x - (view.getSize().x/2);
cameraBounds.top = view.getCenter().y - (view.getSize().y/2);
cameraBounds.width = view.getSize().x;
cameraBounds.height = view.getSize().y;
if ( cameraBounds.intersects(
sf::IntRect(OptiSprite.getPosition().x,
OptiSprite.getPosition().y,
1920,
1088)) )
{
if ( status == true )
{
window.draw(OptiSprite);
window.draw(sprite);
}
}
// Update the window
window.display();
}
return EXIT_SUCCESS;
}
I hope that you will be able to help me in some way, I've been trying to do this for a couple of days now.
Thank you!