[colobot] 01/74: Asynchronous sound/music loading

Didier Raboud odyx at moszumanska.debian.org
Mon Nov 7 07:49:58 UTC 2016


This is an automated email from the git hooks/post-receive script.

odyx pushed a commit to branch debian/master
in repository colobot.

commit 29f0631a2c322d5e86ec2e64b7940d966d7b00f0
Author: krzys-h <krzys_h at interia.pl>
Date:   Sun Jul 10 14:56:34 2016 +0200

    Asynchronous sound/music loading
    
    This improves the loading time a lot. Time from starting the app to opening game window decreased by almost 5 seconds (it's almost instant now). Mission loading times are significantly better too. As a bonus, the playmusic() CBot function doesn't hang the game if you don't preload the files with CacheAudio in scene file.
---
 src/CMakeLists.txt                         |   2 +
 src/app/app.cpp                            |  18 ++++-
 src/common/thread/resource_owning_thread.h |  15 +++-
 src/common/thread/sdl_cond_wrapper.h       |  12 +++
 src/common/thread/sdl_mutex_wrapper.h      |  10 +++
 src/common/thread/thread.h                 |  77 ++++++++++++++++++++
 src/common/thread/worker_thread.h          |  92 +++++++++++++++++++++++
 src/sound/oalsound/alsound.cpp             | 113 ++++++++++++-----------------
 src/sound/oalsound/alsound.h               |  11 +--
 src/sound/sound.cpp                        |  28 +------
 src/sound/sound.h                          |  26 ++-----
 src/ui/mainui.cpp                          |   3 +-
 12 files changed, 289 insertions(+), 118 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bc6f12a..4d5a640 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -151,6 +151,8 @@ set(BASE_SOURCES
     common/thread/resource_owning_thread.h
     common/thread/sdl_cond_wrapper.h
     common/thread/sdl_mutex_wrapper.h
+    common/thread/thread.h
+    common/thread/worker_thread.h
     graphics/core/color.cpp
     graphics/core/color.h
     graphics/core/device.h
diff --git a/src/app/app.cpp b/src/app/app.cpp
index d34532f..6241ef0 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -34,6 +34,8 @@
 
 #include "common/resources/resourcemanager.h"
 
+#include "common/thread/thread.h"
+
 #include "graphics/core/nulldevice.h"
 
 #include "graphics/opengl/glutil.h"
@@ -514,8 +516,6 @@ bool CApplication::Create()
     #endif
 
     m_sound->Create();
-    m_sound->CacheAll();
-    m_sound->CacheCommonMusic();
 
     GetLogger()->Info("CApplication created successfully\n");
 
@@ -685,6 +685,20 @@ bool CApplication::Create()
     // Create the robot application.
     m_controller = MakeUnique<CController>();
 
+    CThread musicLoadThread([this]() {
+        GetLogger()->Debug("Cache sounds...\n");
+        SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp();
+        m_systemUtils->GetCurrentTimeStamp(musicLoadStart);
+
+        m_sound->CacheAll();
+
+        SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp();
+        m_systemUtils->GetCurrentTimeStamp(musicLoadEnd);
+        float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC);
+        GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime);
+    }, "Sound loading thread");
+    musicLoadThread.Start();
+
     if (m_runSceneCategory == LevelCategory::Max)
         m_controller->StartApp();
     else
diff --git a/src/common/thread/resource_owning_thread.h b/src/common/thread/resource_owning_thread.h
index 0e4c200..0423796 100644
--- a/src/common/thread/resource_owning_thread.h
+++ b/src/common/thread/resource_owning_thread.h
@@ -59,6 +59,11 @@ public:
           m_name(name)
     {}
 
+    ~CResourceOwningThread()
+    {
+        SDL_DetachThread(m_thread);
+    }
+
     void Start()
     {
         CSDLMutexWrapper mutex;
@@ -74,7 +79,7 @@ public:
 
         SDL_LockMutex(*mutex);
 
-        SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast<void*>(&data));
+        m_thread = SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast<void*>(&data));
 
         while (!condition)
         {
@@ -84,6 +89,13 @@ public:
         SDL_UnlockMutex(*mutex);
     }
 
+    void Join()
+    {
+        if (m_thread == nullptr) return;
+        SDL_WaitThread(m_thread, nullptr);
+        m_thread = nullptr;
+    }
+
 private:
     static int Run(void* data)
     {
@@ -117,4 +129,5 @@ private:
     ThreadFunctionPtr m_threadFunction;
     ResourceUPtr m_resource;
     std::string m_name;
+    SDL_Thread* m_thread = nullptr;
 };
diff --git a/src/common/thread/sdl_cond_wrapper.h b/src/common/thread/sdl_cond_wrapper.h
index 5b09d2f..8c8be5e 100644
--- a/src/common/thread/sdl_cond_wrapper.h
+++ b/src/common/thread/sdl_cond_wrapper.h
@@ -19,6 +19,8 @@
 
 #pragma once
 
+#include "common/thread/sdl_mutex_wrapper.h"
+
 #include <SDL_thread.h>
 
 /**
@@ -45,6 +47,16 @@ public:
         return m_cond;
     }
 
+    void Signal()
+    {
+        SDL_CondSignal(m_cond);
+    }
+
+    void Wait(SDL_mutex* mutex)
+    {
+        SDL_CondWait(m_cond, mutex);
+    }
+
 private:
     SDL_cond* m_cond;
 };
diff --git a/src/common/thread/sdl_mutex_wrapper.h b/src/common/thread/sdl_mutex_wrapper.h
index bd7d907..59afc0a 100644
--- a/src/common/thread/sdl_mutex_wrapper.h
+++ b/src/common/thread/sdl_mutex_wrapper.h
@@ -45,6 +45,16 @@ public:
         return m_mutex;
     }
 
+    void Lock()
+    {
+        SDL_LockMutex(m_mutex);
+    }
+
+    void Unlock()
+    {
+        SDL_UnlockMutex(m_mutex);
+    }
+
 private:
     SDL_mutex* m_mutex;
 };
diff --git a/src/common/thread/thread.h b/src/common/thread/thread.h
new file mode 100644
index 0000000..d0b6b87
--- /dev/null
+++ b/src/common/thread/thread.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the Colobot: Gold Edition source code
+ * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
+ * http://epsitec.ch; http://colobot.info; http://github.com/colobot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://gnu.org/licenses
+ */
+
+#pragma once
+
+#include "common/make_unique.h"
+
+#include "common/thread/resource_owning_thread.h"
+
+#include <functional>
+#include <string>
+#include <memory>
+
+/**
+ * \class CThread
+ * \brief Wrapper for using SDL_thread with std::function
+ */
+class CThread
+{
+public:
+    using ThreadFunctionPtr = std::function<void()>;
+
+private:
+    struct ThreadData
+    {
+        ThreadFunctionPtr func;
+    };
+
+public:
+    CThread(ThreadFunctionPtr func, std::string name = "")
+        : m_func(std::move(func))
+        , m_name(name)
+    {}
+
+    void Start()
+    {
+        std::unique_ptr<ThreadData> data = MakeUnique<ThreadData>();
+        data->func = m_func;
+        m_thread = MakeUnique<CResourceOwningThread<ThreadData>>(Run, std::move(data), m_name);
+        m_thread->Start();
+    }
+
+    void Join()
+    {
+        if (!m_thread) return;
+        m_thread->Join();
+    }
+
+    CThread(const CThread&) = delete;
+    CThread& operator=(const CThread&) = delete;
+
+private:
+    static void Run(std::unique_ptr<ThreadData> data)
+    {
+        data->func();
+    }
+
+    std::unique_ptr<CResourceOwningThread<ThreadData>> m_thread;
+    ThreadFunctionPtr m_func;
+    std::string m_name;
+};
diff --git a/src/common/thread/worker_thread.h b/src/common/thread/worker_thread.h
new file mode 100644
index 0000000..aa2ce3e
--- /dev/null
+++ b/src/common/thread/worker_thread.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Colobot: Gold Edition source code
+ * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
+ * http://epsitec.ch; http://colobot.info; http://github.com/colobot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://gnu.org/licenses
+ */
+
+#pragma once
+
+#include "common/make_unique.h"
+
+#include "common/thread/sdl_cond_wrapper.h"
+#include "common/thread/sdl_mutex_wrapper.h"
+#include "common/thread/thread.h"
+
+#include <functional>
+#include <string>
+#include <queue>
+
+/**
+ * \class CWorkerThread
+ * \brief Thread that runs functions, one at a time
+ */
+class CWorkerThread
+{
+public:
+    using ThreadFunctionPtr = std::function<void()>;
+
+public:
+    CWorkerThread(std::string name = "")
+        : m_thread(std::bind(&CWorkerThread::Run, this), name)
+    {
+        m_thread.Start();
+    }
+
+    ~CWorkerThread()
+    {
+        m_mutex.Lock();
+        m_running = false;
+        m_cond.Signal();
+        m_mutex.Unlock();
+        m_thread.Join();
+    }
+
+    void Start(ThreadFunctionPtr func)
+    {
+        m_mutex.Lock();
+        m_queue.push(func);
+        m_cond.Signal();
+        m_mutex.Unlock();
+    }
+
+    CWorkerThread(const CWorkerThread&) = delete;
+    CWorkerThread& operator=(const CWorkerThread&) = delete;
+
+private:
+    void Run()
+    {
+        m_mutex.Lock();
+        while (true)
+        {
+            while (m_queue.empty() && m_running)
+            {
+                m_cond.Wait(*m_mutex);
+            }
+            if (!m_running) break;
+
+            ThreadFunctionPtr func = m_queue.front();
+            m_queue.pop();
+            func();
+        }
+        m_mutex.Unlock();
+    }
+
+    CThread m_thread;
+    CSDLMutexWrapper m_mutex;
+    CSDLCondWrapper m_cond;
+    bool m_running = true;
+    std::queue<ThreadFunctionPtr> m_queue;
+};
diff --git a/src/sound/oalsound/alsound.cpp b/src/sound/oalsound/alsound.cpp
index cfa335d..f51887b 100644
--- a/src/sound/oalsound/alsound.cpp
+++ b/src/sound/oalsound/alsound.cpp
@@ -32,7 +32,8 @@ CALSound::CALSound()
       m_musicVolume(1.0f),
       m_channelsLimit(2048),
       m_device{},
-      m_context{}
+      m_context{},
+      m_thread("Music loading thread")
 {
 }
 
@@ -144,18 +145,19 @@ bool CALSound::Cache(SoundType sound, const std::string &filename)
     return false;
 }
 
-bool CALSound::CacheMusic(const std::string &filename)
+void CALSound::CacheMusic(const std::string &filename)
 {
-    if (m_music.find(filename) == m_music.end())
+    m_thread.Start([this, filename]()
     {
-        auto buffer = MakeUnique<CBuffer>();
-        if (buffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+        if (m_music.find(filename) == m_music.end())
         {
-            m_music[filename] = std::move(buffer);
-            return true;
+            auto buffer = MakeUnique<CBuffer>();
+            if (buffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+            {
+                m_music[filename] = std::move(buffer);
+            }
         }
-    }
-    return false;
+    });
 }
 
 bool CALSound::IsCached(SoundType sound)
@@ -572,53 +574,54 @@ void CALSound::SetListener(const Math::Vector &eye, const Math::Vector &lookat)
     alListenerfv(AL_ORIENTATION, orientation);
 }
 
-bool CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
+void CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
 {
     if (!m_enabled)
     {
-        return false;
+        return;
     }
 
-    CBuffer *buffer = nullptr;
-
-    // check if we have music in cache
-    if (m_music.find(filename) == m_music.end())
+    m_thread.Start([this, filename, repeat, fadeTime]()
     {
-        GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
+        CBuffer* buffer = nullptr;
 
-        auto newBuffer = MakeUnique<CBuffer>();
-        buffer = newBuffer.get();
-        if (!newBuffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+        // check if we have music in cache
+        if (m_music.find(filename) == m_music.end())
         {
-            return false;
-        }
-        m_music[filename] = std::move(newBuffer);
-    }
-    else
-    {
-        GetLogger()->Debug("Music loaded from cache\n");
-        buffer = m_music[filename].get();
-    }
+            GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
 
-    if (m_currentMusic)
-    {
-        OldMusic old;
-        old.music = std::move(m_currentMusic);
-        old.fadeTime = fadeTime;
-        old.currentTime = 0.0f;
-        m_oldMusic.push_back(std::move(old));
-    }
+            auto newBuffer = MakeUnique<CBuffer>();
+            buffer = newBuffer.get();
+            if (!newBuffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+            {
+                return;
+            }
+            m_music[filename] = std::move(newBuffer);
+        }
+        else
+        {
+            GetLogger()->Debug("Music loaded from cache\n");
+            buffer = m_music[filename].get();
+        }
 
-    m_currentMusic = MakeUnique<CChannel>();
-    m_currentMusic->SetBuffer(buffer);
-    m_currentMusic->SetVolume(m_musicVolume);
-    m_currentMusic->SetLoop(repeat);
-    m_currentMusic->Play();
+        if (m_currentMusic)
+        {
+            OldMusic old;
+            old.music = std::move(m_currentMusic);
+            old.fadeTime = fadeTime;
+            old.currentTime = 0.0f;
+            m_oldMusic.push_back(std::move(old));
+        }
 
-    return true;
+        m_currentMusic = MakeUnique<CChannel>();
+        m_currentMusic->SetBuffer(buffer);
+        m_currentMusic->SetVolume(m_musicVolume);
+        m_currentMusic->SetLoop(repeat);
+        m_currentMusic->Play();
+    });
 }
 
-bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
+void CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
 {
     if (m_previousMusic.fadeTime > 0.0f)
     {
@@ -640,7 +643,7 @@ bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
             m_previousMusic.currentTime = 0.0f;
         }
     }
-    return PlayMusic(filename, repeat);
+    PlayMusic(filename, repeat);
 }
 
 void CALSound::StopPauseMusic()
@@ -662,18 +665,6 @@ void CALSound::StopPauseMusic()
     }
 }
 
-bool CALSound::RestartMusic()
-{
-    if (!m_enabled || m_currentMusic == nullptr)
-    {
-        return false;
-    }
-
-    m_currentMusic->Stop();
-    m_currentMusic->Play();
-    return true;
-}
-
 void CALSound::StopMusic(float fadeTime)
 {
     if (!m_enabled || m_currentMusic == nullptr)
@@ -698,16 +689,6 @@ bool CALSound::IsPlayingMusic()
     return m_currentMusic->IsPlaying();
 }
 
-void CALSound::SuspendMusic()
-{
-    if (!m_enabled || m_currentMusic == nullptr)
-    {
-        return;
-    }
-
-    m_currentMusic->Stop();
-}
-
 bool CALSound::CheckChannel(int &channel)
 {
     int id = (channel >> 16) & 0xffff;
diff --git a/src/sound/oalsound/alsound.h b/src/sound/oalsound/alsound.h
index e4656e1..03d5bd4 100644
--- a/src/sound/oalsound/alsound.h
+++ b/src/sound/oalsound/alsound.h
@@ -26,6 +26,8 @@
 
 #include "sound/sound.h"
 
+#include "common/thread/worker_thread.h"
+
 #include "sound/oalsound/buffer.h"
 #include "sound/oalsound/channel.h"
 #include "sound/oalsound/check.h"
@@ -83,7 +85,7 @@ public:
 
     bool Create() override;
     bool Cache(SoundType, const std::string &) override;
-    bool CacheMusic(const std::string &) override;
+    void CacheMusic(const std::string &) override;
     bool IsCached(SoundType) override;
     bool IsCachedMusic(const std::string &) override;
 
@@ -106,12 +108,10 @@ public:
     bool StopAll() override;
     bool MuteAll(bool mute) override;
 
-    bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f) override;
-    bool RestartMusic() override;
-    void SuspendMusic() override;
+    void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f) override;
     void StopMusic(float fadeTime=2.0f) override;
     bool IsPlayingMusic() override;
-    bool PlayPauseMusic(const std::string &filename, bool repeat) override;
+    void PlayPauseMusic(const std::string &filename, bool repeat) override;
     void StopPauseMusic() override;
 
 private:
@@ -134,4 +134,5 @@ private:
     OldMusic m_previousMusic;
     Math::Vector m_eye;
     Math::Vector m_lookat;
+    CWorkerThread m_thread;
 };
diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp
index 3640c58..e5b2929 100644
--- a/src/sound/sound.cpp
+++ b/src/sound/sound.cpp
@@ -52,22 +52,13 @@ void CSoundInterface::CacheAll()
     }
 }
 
-void CSoundInterface::CacheCommonMusic()
-{
-    CacheMusic("music/Intro1.ogg");
-    CacheMusic("music/Intro2.ogg");
-    CacheMusic("music/music010.ogg");
-    CacheMusic("music/music011.ogg");
-}
-
 bool CSoundInterface::Cache(SoundType sound, const std::string &file)
 {
     return true;
 }
 
-bool CSoundInterface::CacheMusic(const std::string &file)
+void CSoundInterface::CacheMusic(const std::string &file)
 {
-    return true;
 }
 
 bool CSoundInterface::IsCached(SoundType sound)
@@ -156,17 +147,7 @@ bool CSoundInterface::MuteAll(bool mute)
     return true;
 }
 
-bool CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
-{
-    return true;
-}
-
-bool CSoundInterface::RestartMusic()
-{
-    return true;
-}
-
-void CSoundInterface::SuspendMusic()
+void CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
 {
 }
 
@@ -176,12 +157,11 @@ void CSoundInterface::StopMusic(float fadeTime)
 
 bool CSoundInterface::IsPlayingMusic()
 {
-    return true;
+    return false;
 }
 
-bool CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat)
+void CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat)
 {
-    return true;
 }
 
 void CSoundInterface::StopPauseMusic()
diff --git a/src/sound/sound.h b/src/sound/sound.h
index 3799d6f..a2dce31 100644
--- a/src/sound/sound.h
+++ b/src/sound/sound.h
@@ -72,9 +72,6 @@ public:
      */
     void CacheAll();
 
-    /** Function called to add all music files to list */
-    void CacheCommonMusic();
-
     /** Function called to cache sound effect file.
      *  This function is called by plugin interface for each file.
      * \param sound - id of a file, will be used to identify sound files
@@ -85,10 +82,10 @@ public:
 
     /** Function called to cache music file.
      *  This function is called by CRobotMain for each file used in the mission.
+     *  This function is executed asynchronously
      * \param file - file to load
-     * \return return true on success
      */
-    virtual bool CacheMusic(const std::string &file);
+    virtual void CacheMusic(const std::string &file);
 
     /** Function to check if sound effect file was cached.
      * \param sound - id of a sound effect file
@@ -205,22 +202,12 @@ public:
     virtual bool MuteAll(bool mute);
 
     /** Start playing music
+     * This function is executed asynchronously
      * \param filename - name of file to play
      * \param repeat - repeat playing
-     * \param fadeTime - time of transition between music
-     * \return return true on success
-     */
-    virtual bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f);
-
-    /** Restart music
-     * \return return true on success
-     */
-    virtual bool RestartMusic();
-
-    /** Susspend playing music
-     * \return nothing
+     * \param fadeTime - time of transition between music, 0 to disable
      */
-    virtual void SuspendMusic();
+    virtual void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f);
 
     /** Stop playing music
      * \return nothing
@@ -233,11 +220,12 @@ public:
     virtual bool IsPlayingMusic();
 
     /** Start playing pause music
+     * This function is executed asynchronously
      * \param filename - name of file to play
      * \param repeat - repeat playing
      * \return return true on success
      */
-     virtual bool PlayPauseMusic(const std::string &filename, bool repeat);
+     virtual void PlayPauseMusic(const std::string &filename, bool repeat);
 
      /** Stop playing pause music and return to the mission music
       * \return nothing
diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp
index f373d69..503cc49 100644
--- a/src/ui/mainui.cpp
+++ b/src/ui/mainui.cpp
@@ -213,9 +213,10 @@ void CMainUserInterface::ChangePhase(Phase phase)
 
     if ( IsMainMenuPhase(m_phase) )
     {
-        if (!m_sound->IsPlayingMusic() && m_sound->IsCachedMusic("music/Intro1.ogg"))
+        if (!m_sound->IsPlayingMusic())
         {
             m_sound->PlayMusic("music/Intro1.ogg", false);
+            m_sound->CacheMusic("music/Intro2.ogg");
         }
     }
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/colobot.git



More information about the Pkg-games-commits mailing list