SDL_Mixer threading
-
Heyho!
Ich habe ein kleines Problem mit SDL_Mixer und threading. Ich habe einen Abschnitt "init" in meinem Spiel, wo ich Sounds und Musik lade mithilfe von "SDL_LoadWAV". Klappt auch alles. Dann in einem der folgenden Abschnittent werden einige der Sounds gespielt mithilfe von "Mix_PlayChannel". Klappt auch alles.
Im Abschnitt "prepareLevel" soll einmalig ein neuer Thread gestartet werden, der parallel neue Sounds und andere Ressorcen laden soll. Das klappt auch alles. Allerdings hängt die ganze Anwendung kurz, bis "Mix_LoadWAV" fertig ist. Es ist auch definitiv diese Funktion, die die Applikation stoppt, weil ich die mal testweise rausgenommen habe und das parallel Laden dann flüssig geklappt hat und es kein Stoppen gab. Daher gibt es jetzt zwei Möglichkeiten:
1. Entweder beeinflusst "Mix_LoadWAV" den internen sample stream von SDL_Mixer, weil sich irgendwelche Ressorcen geteilt werden, die threading unmöglich machen.
2. Oder, da threading noch recht neu für mich ist, mache ich da irgendetwas falsch.
Hatte einer von euch schon einmal dieses Problem oder erkennt irgendeinen Fehler im Threading?
main.cpp:
int main(int argc, char * argv[]) { int result = 0; GameInstance game = new Game(); LEMoonInstance engine = new LEMoon(); MemoryOrganizer memoryOrganizer = new cMemoryOrganizer(); memoryOrganizer->registerEngine(engine); result = engine->init("Solar Light"); engine->setBackgroundColor(0, 0, 0); if(!result) {result = engine->initImage();} // game loop while(engine->pollEvent() || !result) { engine->beginFrame(); // choose game state switch(game->getGameState()) { case noState: {} break; case initPoster: { game->stageInitPoster(engine, memoryOrganizer); } break; case init: { game->stageInit(engine, memoryOrganizer); } break; case initInteraction: { game->stageInitInteraction(engine, memoryOrganizer); } break; case displayLogo: { game->stageDisplayLogo(engine, memoryOrganizer); } break; case displayMenuLoading: { game->stageDisplayMenuLoading(engine); } break; case menu: { game->stageMenu(engine); } break; case playButton: { game->stagePlayButton(engine); } break; case prepareLevel: { game->stagePrepareLevel(engine, memoryOrganizer); if(!game->t1Locked()) { thread proc01(&Game::level1LoadProlog, game, engine, memoryOrganizer); game->lockT1(LE_TRUE); proc01.join(); } } break; case controlsButton: { game->stageControlsButton(engine); } break; case credits: { game->stageCredits(engine); } break; case prolog: { game->stageProlog(engine); if(!game->t2Locked()) { thread proc01(&Game::level1LoadIntroFlight, game, engine, memoryOrganizer); game->lockT2(LE_TRUE); game->lockT1(LE_FALSE); proc01.join(); } } break; case prologVideo: { game->stagePrologVideo(engine); } break; case introFlight: { game->stageIntroFlight(engine); if(!game->t1Locked()) { thread proc01(&Game::level1LoadAsteroidFlight, game, engine, memoryOrganizer); game->lockT1(LE_TRUE); game->lockT2(LE_FALSE); proc01.join(); } } break; case asteroidFlight: { game->stageAsteroidFlight(engine); if(!game->t2Locked()) { thread proc01(&Game::level1LoadSatelliteLevel, game, engine, memoryOrganizer); game->lockT2(LE_TRUE); game->lockT1(LE_FALSE); proc01.join(); } } break; case satelliteReached: { game->stageSatelliteReached(engine); } break; case satellite: { game->stageSatellite(engine); if(!game->t1Locked()) { thread proc01(&Game::memoryLevel1ComputerQuiz, game, engine, memoryOrganizer); game->lockT1(LE_TRUE); game->lockT2(LE_FALSE); proc01.join(); } } break; case computerQuiz: { game->stageComputerQuiz(engine); if(!game->t2Locked()) { thread proc01(&Game::level1LoadBrokenStationLevel, game, engine, memoryOrganizer); game->lockT2(LE_TRUE); game->lockT1(LE_FALSE); proc01.join(); } } break; case satelliteQuiz: { game->stageSatelliteQuiz(engine); } break; case leaveSatellite: { game->stageLeaveSatellite(engine); } break; case stationDestroyed: { game->stageBrokenStation(engine); } break; case quit: { result = 1; } break; }; #ifdef BUILD_DESKTOP if(engine->keyEvent(SDL_KEYDOWN, SDLK_ESCAPE)) { if(game->getGameState() == menu) {result = 1;} else { if(game->getGameState() >= prolog) { game->level1Reset(engine); game->prepareMenuLoading(engine); game->setGameState(displayMenuLoading); } } } #endif if(game->getSkipFlag()) {game->setSkipFlag(LE_FALSE);} if(engine->drawFrame()) {result = 1;} engine->endFrame(); } engine->delay(1000); delete memoryOrganizer; delete game; delete engine; return 0; }
Game::level1LoadProlog:
void Game::level1LoadProlog(LEMoonInstance engine, MemoryOrganizer memoryOrganizer) { this->memoryLevel1PrologPrio1(engine, memoryOrganizer); this->memoryLevel1PrologPrio2(engine, memoryOrganizer); }
Game::memoryLevel1PrologPrio1:
void Game::memoryLevel1PrologPrio1(LEMoonInstance engine, MemoryOrganizer memoryOrganizer) { memoryOrganizer->create(MEMORY_LVL1_PROLOG_PRIO_1); this->loadSkipWindow(engine, memoryOrganizer); memoryOrganizer->complete(MEMORY_LVL1_PROLOG_PRIO_1, LE_TRUE); }
Game::memoryLevel1PrologPrio2:
void Game::memoryLevel1PrologPrio2(LEMoonInstance engine, MemoryOrganizer memoryOrganizer) { memoryOrganizer->create(MEMORY_LVL1_PROLOG_PRIO_2); loadSun(engine, memoryOrganizer); loadSpaceStation(engine, memoryOrganizer); loadPrologTE(engine, memoryOrganizer); loadPrologMusic(engine, memoryOrganizer); loadPrologText(engine, memoryOrganizer); memoryOrganizer->complete(MEMORY_LVL1_PROLOG_PRIO_2, LE_TRUE); }
loadPrologMusic:
void loadPrologMusic(LEMoonInstance engine, MemoryOrganizer memoryOrganizer) { engine->soundCreate(MUSIC_PROLOG); engine->soundLoadWAV(MUSIC_PROLOG, FILE_MUSIC_PROLOG); memoryOrganizer->recordLoadedObject(MEMORY_LVL1_PROLOG_PRIO_2, LE_SOUND, MUSIC_PROLOG); }
LEMoon::soundCreate:
int LEMoon::soundCreate(uint32_t id) { #ifdef LE_MUTEX this->mtxSound.mtxAdd.lock(); #endif int result = LE_NO_ERROR; LESound * pNew = this->soundGet(id); if(pNew == nullptr) { if(this->pSoundHead == nullptr) { this->pSoundHead = new LESound; this->pSoundHead->pLeft = this->pSoundHead; this->pSoundHead->pRight = this->pSoundHead; } pNew = new LESound; pNew->pLeft = this->pSoundHead->pLeft; pNew->pRight = this->pSoundHead; this->pSoundHead->pLeft->pRight = pNew; this->pSoundHead->pLeft = pNew; pNew->id = id; pNew->lock = LE_FALSE; pNew->pSample = nullptr; } else { #ifdef LE_DEBUG this->printErrorDialog(LE_SOUND_EXIST, "LEMoon::soundCreate()\n\n"); #endif result = LE_SOUND_EXIST; } #ifdef LE_MUTEX this->mtxSound.mtxAdd.unlock(); #endif return result; }
LEMoon::soundLoadWAV:
int LEMoon::soundLoadWAV(uint32_t id, const char * pFile) { int result = LE_NO_ERROR; LESound * pSound = this->soundGet(id); #ifdef LE_DEBUG char * pErrorString = new char[256 + 1]; #endif if(pSound != nullptr) { if(pSound->pSample == nullptr) { pSound->pSample = Mix_LoadWAV(pFile); if(pSound->pSample == nullptr) { #ifdef LE_DEBUG sprintf(pErrorString, "LEMoon::soundLoadWAV()\n\nPath: %s\n\n", pFile); this->printErrorDialog(LE_LOAD_WAV, pErrorString); #endif result = LE_LOAD_WAV; } } } else { #ifdef LE_DEBUG this->printErrorDialog(LE_SOUND_NOEXIST, "LEMoon::soundLoadWAV()\n\n"); #endif result = LE_SOUND_NOEXIST; } #ifdef LE_DEBUG delete [] pErrorString; #endif return result; }
LEMoon::soundGet
LESound * LEMoon::soundGet(uint32_t id) { LESound * pRet = nullptr; LESound * pCurrent = nullptr; if(this->pSoundHead != nullptr) { if(this->memory.pLastSound != nullptr && this->memory.pLastSound->id == id) {pRet = this->memory.pLastSound;} else { pCurrent = this->pSoundHead->pRight; while(pCurrent != this->pSoundHead) { if(pCurrent->id == id) { pRet = pCurrent; this->memory.pLastSound = pCurrent; break; } pCurrent = pCurrent->pRight; } } } return pRet; }
LEMoon::soundPlay:
int LEMoon::soundPlay(uint32_t id, int loops) { int result = LE_NO_ERROR; LESound * pSound = this->soundGet(id); if(pSound != nullptr) { if(!(pSound->lock)) { if(Mix_PlayChannel(-1, pSound->pSample, loops) == -1) { #ifdef LE_DEBUG this->printErrorDialog(LE_PLAY_CHANNEL, "LEMoon::soundPlay()\n\n"); #endif result = LE_PLAY_CHANNEL; } } } else { #ifdef LE_DEBUG this->printErrorDialog(LE_SOUND_NOEXIST, "LEMoon::soundPlay()\n\n"); #endif result = LE_SOUND_NOEXIST; } return result; }
Blockieren sich vielleicht die Mutex irgendwie? Ich würde nicht verstehen warum? Ich hoffe, dass die samples so ausreichen, da das ja doch ein sehr großes Projekt ist.
Gruß,
Patrick
-
ist SDL ueberhaupt threadsafe?
-
Scheint es tatsächlich zu sein, da ich rendern und neue Texturen zur selben Zeit erstellen kann. Ich habe auch kein Problem beim threading, wenn es um SDL_TTF und SDL_image geht.