Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML 2.X] Przyśpieszyć ładowanie tekstur

Ostatnio zmodyfikowano 2025-04-21 15:08
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML 2.X] Przyśpieszyć ładowanie tekstur
» 2025-04-19 14:14:39
Cześć. Potrzebuję przyśpieszyć algorytm wczytywania tekstur. Czy powodem tego, że trwa tak długo ( ponad 8 sekund bo mam jeszcze dla różnych itemów tekstury) może być to, że każdą klatkę mam w osobnym pliku ?


C/C++
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>

#include <vector>
#include <iostream>

class SingleTexture {
public:
   
string name;
   
float cx, cy; // coordinates of center on the texture
   
sf::Texture * texture = nullptr;
   
   
SingleTexture( string pathfile, float cx, float cy ) {
       
       
name = "";
       
       
short i = int( pathfile.size() ) - 5;
       
while( i >= 0 )
           
 name = pathfile[ i-- ] + name; // remove ".png"
       
       
texture = new sf::Texture;
       
texture->loadFromFile( "assets\\" + pathfile );
       
texture->setRepeated( true );
       
       
this->cx = cx;
       
this->cy = cy;
       
       
//cout << "load texture: " << pathfile << " as: " << name << endl;
   
}
}
;

std::vector < SingleTexture * > singleTextures;

void loadSingleTexture( string pathfile, float cx, float cy ) {
   
singleTextures.push_back( new SingleTexture( pathfile, cx, cy ) );
}


void loadTextures() {
   
   
singleTextures.clear();
   
short i = 0;
   
   
// BODY SETS
   
std::vector < string > bodySets;
   
bodySets.clear();
   
bodySets.push_back( "boy-redhaired" ); // face without beard
   
bodySets.push_back( "boy-blackhaired" ); // face without beard
   
bodySets.push_back( "boy-brownhaired" ); // face without beard
   
bodySets.push_back( "boy-greyhaired" ); // face without beard
   
   
bodySets.push_back( "man-redhaired" ); // face have a beard
   
bodySets.push_back( "man-blackhaired" ); // face have a beard
   
bodySets.push_back( "man-brownhaired" ); // face have a beard
   
bodySets.push_back( "man-greyhaired" ); // face have a beard
   
   
bodySets.push_back( "woman-redhaired" ); // face with long hair
   
bodySets.push_back( "woman-blackhaired" ); // face with long hair
   
bodySets.push_back( "woman-brownhaired" ); // face with long hair
   
bodySets.push_back( "woman-greyhaired" ); // face with long hair
   
   
   
for( auto & set: bodySets ) {
       
for( i = 0; i < 4; i++ ) {
           
           
loadSingleTexture( "sets\\body\\" + set + "\\idleTop" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\idleRight" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\idleBottom" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\idleLeft" + to_string( i ) + ".png", 32, 58 );
           
           
loadSingleTexture( "sets\\body\\" + set + "\\runTop" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\runRight" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\runBottom" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\runLeft" + to_string( i ) + ".png", 32, 58 );
           
           
loadSingleTexture( "sets\\body\\" + set + "\\attackTop" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\attackRight" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\attackBottom" + to_string( i ) + ".png", 32, 58 );
           
loadSingleTexture( "sets\\body\\" + set + "\\attackLeft" + to_string( i ) + ".png", 32, 58 );
       
}
       
    }
   
}

int main() {
   
   
sf::Clock clock1;
   
sf::Time start_time, end_time;
   
   
start_time = clock1.getElapsedTime();
   
   
loadTextures();
   
   
end_time = clock1.getElapsedTime();
   
   
std::cout << "load time : " <<( end_time - start_time ).asSeconds() << "seconds \n";
   
   
return 0;
}
P-182257
DejaVu
» 2025-04-19 18:06:44
Opcje:
1) zbudować plik binarny do którego wpakujesz wszystkie pliki, następnie wczytasz cały plik do pamięci i potem każdą teksturę wczytasz z pamięci.
2) zamiast wczytywać każdą klatkę pojedynczo, to możesz wrzucić je do jednej tekstury i wycinać fragmenty podczas ładowania.
3) zostawić AS-IS, jeżeli nie przeszkadza Ci to w developmencie, bo odciągnie Ciebie to od robienia właściwej gry.
4) zamiast wczytywać tekstury wszystkie naraz, to doczytywać je w razie potrzeby (niby fajne, ale generuje inne problemy)

Upewnij się również, że dajesz użytkownikom Release build, bo w debug może być on znacznie wolniejszy.
P-182258
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-19 18:57:59
Dzięki za odpowiedź :-) No właśnie taki wynik mam w Release.

1) jak zbudować taki plik?
2) właśnie to miałem na myśli
3) ?
4) raczej odpada ta opcja...
P-182259
DejaVu
» 2025-04-19 19:56:18
Musisz 'zaprojektować' sobie plik binarny, który będziesz potrafił 'parsować', czyli:
4 bajty - ilość plików
1 bajt - długość nazwy plików (N)
4 bajty - rozmiar pliku PNG (X)
n-bajtów - nazwa pliku PNG
x-bajtów - zawartość pliku PNG
... (i tu znowu 1 bajt długość nazwy pliku itd.)

Potem musisz używać wczytywania sf::Image lub sf::Texture z pamięci zamiast podawać ścieżkę do pliku.
P-182260
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-19 20:22:24
Ok. Dzięki :-)

Z pomocą Twoją i ChataGPT napisałem program, który wczytuje tekstury z jednego pliku "assets/textures.txs"
Plik binarny ma 70mb - pora go odchudzić ! :D

wczytano 2636 tekstur
load time : 8.90906 seconds

wczytano 2487 tekstur
load time : 0.180041 seconds


C/C++
void saveAssetsToFile( const std::string & outputFile ) {
   
std::ofstream out( outputFile, std::ios::binary );
   
   
for( const auto & texture: singleTextures ) {
       
sf::Image image;
       
std::string pathfile = "assets\\" + texture->name + ".png";
       
if( !image.loadFromFile( pathfile ) ) {
           
std::cerr << "Nie udało się załadować: " << pathfile << "\n";
           
continue;
       
}
       
       
std::string name = texture->name;
       
name = name.substr( 0, name.find_last_of( '.' ) ); // bez rozszerzenia
       
       
short width = image.getSize().x;
       
short height = image.getSize().y;
       
const sf::Uint8 * pixels = image.getPixelsPtr();
       
int nameLength = name.length();
       
       
out.write( reinterpret_cast < char * >( & nameLength ), sizeof( int ) );
       
out.write( name.c_str(), nameLength );
       
out.write( reinterpret_cast < char * >( & width ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( & height ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( & texture->cx ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( & texture->cy ), sizeof( short ) );
       
out.write( reinterpret_cast < const char * >( pixels ), width * height * 4 );
   
}
   
   
out.close();
}

void loadAssetsFromFile( const std::string & inputFile ) {
   
std::vector < SingleTexture * > textures;
   
   
std::ifstream in( inputFile, std::ios::binary );
   
while( in.peek() != EOF ) {
       
int nameLength;
       
in.read( reinterpret_cast < char * >( & nameLength ), sizeof( int ) );
       
       
std::string name( nameLength, '\0' );
       
in.read( & name[ 0 ], nameLength );
       
       
short width, height;
       
in.read( reinterpret_cast < char * >( & width ), sizeof( short ) );
       
in.read( reinterpret_cast < char * >( & height ), sizeof( short ) );
       
       
float cx, cy;
       
in.read( reinterpret_cast < char * >( & cx ), sizeof( short ) );
       
in.read( reinterpret_cast < char * >( & cy ), sizeof( short ) );
       
       
std::vector < sf::Uint8 > pixels( width * height * 4 );
       
in.read( reinterpret_cast < char * >( pixels.data() ), pixels.size() );
       
       
SingleTexture * texture = new SingleTexture( name, width, height, pixels, 0, 0 );
       
textures.push_back( texture );
       
       
std::cout << texture->name << "\n";
   
}
   
   
std::cout << "wczytano " << textures.size() << " tekstur\n";
   
   
in.close();
}
P-182261
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-19 21:02:22
Pozbyłem się kanału alfa podczas zapisywania assetów, teraz rozmiar pliku to 52mb. Nadal prawie pięciokrotnie więcej niż orginalne pliki. Da się jeszcze coś z tym zrobić ?

C/C++
void saveAssetsToFile( const std::string & outputFile ) {
   
std::ofstream out( outputFile, std::ios::binary );
   
   
for( const auto & texture: singleTextures ) {
       
sf::Image image;
       
std::string pathfile = "assets\\" + texture->name + ".png";
       
if( !image.loadFromFile( pathfile ) ) {
           
std::cerr << "Nie udało się załadować: " << pathfile << "\n";
           
continue;
       
}
       
       
std::string name = texture->name;
       
name = name.substr( 0, name.find_last_of( '.' ) ); // bez rozszerzenia
       
       
short width = image.getSize().x;
       
short height = image.getSize().y;
       
       
std::vector < sf::Uint8 > rgbPixels;
       
rgbPixels.reserve( width * height * 3 );
       
       
const sf::Uint8 * rgba = image.getPixelsPtr();
       
for( int i = 0; i < width * height; ++i ) {
           
rgbPixels.push_back( rgba[ i * 4 + 0 ] ); // R
           
rgbPixels.push_back( rgba[ i * 4 + 1 ] ); // G
           
rgbPixels.push_back( rgba[ i * 4 + 2 ] ); // B
       
}
       
       
int nameLength = name.length();
       
       
out.write( reinterpret_cast < char * >( & nameLength ), sizeof( int ) );
       
out.write( name.c_str(), nameLength );
       
out.write( reinterpret_cast < char * >( & width ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( & height ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( & texture->cx ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( & texture->cy ), sizeof( short ) );
       
out.write( reinterpret_cast < char * >( rgbPixels.data() ), rgbPixels.size() );
   
}
   
   
out.close();
}

void loadAssetsFromFile( const std::string & inputFile ) {
   
std::vector < SingleTexture * > textures;
   
   
std::ifstream in( inputFile, std::ios::binary );
   
while( in.peek() != EOF ) {
       
int nameLength;
       
in.read( reinterpret_cast < char * >( & nameLength ), sizeof( int ) );
       
       
std::string name( nameLength, '\0' );
       
in.read( & name[ 0 ], nameLength );
       
       
short width, height;
       
in.read( reinterpret_cast < char * >( & width ), sizeof( short ) );
       
in.read( reinterpret_cast < char * >( & height ), sizeof( short ) );
       
       
float cx, cy;
       
in.read( reinterpret_cast < char * >( & cx ), sizeof( short ) );
       
in.read( reinterpret_cast < char * >( & cy ), sizeof( short ) );
       
       
std::vector < sf::Uint8 > rgbPixels( width * height * 3 );
       
in.read( reinterpret_cast < char * >( rgbPixels.data() ), rgbPixels.size() );
       
       
std::vector < sf::Uint8 > rgbaPixels( width * height * 4 );
       
for( int i = 0, j = 0; i < width * height; ++i, j += 3 ) {
           
rgbaPixels[ i * 4 + 0 ] = rgbPixels[ j + 0 ]; // R
           
rgbaPixels[ i * 4 + 1 ] = rgbPixels[ j + 1 ]; // G
           
rgbaPixels[ i * 4 + 2 ] = rgbPixels[ j + 2 ]; // B
           
rgbaPixels[ i * 4 + 3 ] = 255; // A = pełna nieprzezroczystość
       
}
       
       
SingleTexture * texture = new SingleTexture( name, width, height, rgbaPixels, 0, 0 );
       
textures.push_back( texture );
       
       
//std::cout << texture->name << "\n";
   
}
   
   
std::cout << "wczytano " << textures.size() << " tekstur\n";
   
   
in.close();
}
P-182262
DejaVu
» 2025-04-20 07:30:08
Jeżeli plik jest 5-krotnie większy niż suma oryginalnych to znaczy, że źle zrobiłeś zapisywanie informacji. Nie powinieneś wczytywać obrazka z pliku do sf::Image, aby potem go zapisywać pixel za pixelem. Powinieneś wczytać cały plik BINARNIE do sf::vector<> i go 'binarnie' zapisać do paczki.
P-182263
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-20 09:00:16
Mhm.. w takim razie jak to zrobić? Nie pracowałem jeszcze z danymi binarnymi i nie wiem jak się za to zabrać... :-/
P-182264
« 1 » 2
  Strona 1 z 2 Następna strona