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 ? #include <SFML/Graphics.hpp> #include <SFML/Audio.hpp>
#include <vector> #include <iostream>
class SingleTexture { public: string name; float cx, cy; 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; texture = new sf::Texture; texture->loadFromFile( "assets\\" + pathfile ); texture->setRepeated( true ); this->cx = cx; this->cy = cy; } };
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; std::vector < string > bodySets; bodySets.clear(); bodySets.push_back( "boy-redhaired" ); bodySets.push_back( "boy-blackhaired" ); bodySets.push_back( "boy-brownhaired" ); bodySets.push_back( "boy-greyhaired" ); bodySets.push_back( "man-redhaired" ); bodySets.push_back( "man-blackhaired" ); bodySets.push_back( "man-brownhaired" ); bodySets.push_back( "man-greyhaired" ); bodySets.push_back( "woman-redhaired" ); bodySets.push_back( "woman-blackhaired" ); bodySets.push_back( "woman-brownhaired" ); bodySets.push_back( "woman-greyhaired" ); 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; }
|
|
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. |
|
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... |
|
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. |
|
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 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( '.' ) ); 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(); }
|
|
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ć ? 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( '.' ) ); 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 ] ); rgbPixels.push_back( rgba[ i * 4 + 1 ] ); rgbPixels.push_back( rgba[ i * 4 + 2 ] ); } 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 ]; rgbaPixels[ i * 4 + 1 ] = rgbPixels[ j + 1 ]; rgbaPixels[ i * 4 + 2 ] = rgbPixels[ j + 2 ]; rgbaPixels[ i * 4 + 3 ] = 255; } SingleTexture * texture = new SingleTexture( name, width, height, rgbaPixels, 0, 0 ); textures.push_back( texture ); } std::cout << "wczytano " << textures.size() << " tekstur\n"; in.close(); }
|
|
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. |
|
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ć... :-/ |
|
« 1 » 2 |