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.