cleaning up audio shutdown

This commit is contained in:
Eric 2023-10-14 23:00:26 -07:00
parent baa09a775d
commit c42318cccc
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
25 changed files with 596 additions and 480 deletions

56
.efrocachemap generated
View File

@ -4056,26 +4056,26 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "af600306cf8085f909e40a7d2129a73b", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "8ea626f6dd70d998ee77c58fffc51545",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "54f7cdbdbc16601ff1f841e48cecf05c", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "d7ad10904fe7c4d4555366ccb1feedcb",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cc6387a5d3a8f36b7bf667f9b7e719df", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "a9eea92d521e97b1772b5e44b402ce8a",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "0de15cf59051ba17ee91e3d3ac25be93", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "57043f40971d800d27ee6d646aae8d61",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "345c4646377145cf88b30fcba122f42b", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3e4009d7fa4b90abc526f56361ecab05",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4dae703893e19bed1e8a16029a646104", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "334cdc0688e66a1bc75cd05bae1729c7",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "573adb2913309a6c375a6d3b92a20208", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "95278d80378be5b61026253492cbfa70",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "3db3c7e41182fd05f5f9731fee3002d0", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "0a3da8e5264a7b733960e83a0e8c4bba",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "1e3e5ee34645928ec3bdc3c52de98839", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "a089d4aaa39e18553cf0a70a77b4cfcd",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "41354efa8cdbb83a3b2e4cd7fce6b308", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "e66c4eceb79710b8fda2bfea781e241a",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "a304c1a0d2d2f5bdbc31a27ec899a95b", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "6b3021b9a7584da86bbb95324e81e851",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "6b236e3246ded0460fcc7ea50fc51b36", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "6fe90d50f905a0da9fa52c39a458d1e3",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "e26b4e3b5e2bca5606d2ac674fcc7496", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "afc7ed826486aec82613832865177570",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ae954a12775213c64d45b535b289ebff", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ae8b6dad770793188aad8e2966189bb3",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "960c3e2fd9c3e19de75fd92546447890", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "7622dde1a021152cac42e7db3e803392",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "e0c05531b48ec5e36da10783280957fe", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8a4b1e521bf668cc6ec6a65519defb12",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e1c137fdefcf34a642c0962999973eff", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "9a69c0d4c9ae319595843b16f14795fc",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "bdf75b68e6b0e1c8dcfa76ac04306fbb", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "89c02260fb4781f5e293658cecbb363f",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "5f01ab596c3d389c95761db531b1766b", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "3165d4230069c22300abfff8abf1d714",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "4826ca9c757dbf38749710bc94844aca", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "41e46dfdbb542e3e823f6aee87e93ac9",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f",
@ -4092,14 +4092,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819", "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "49775819d4ba9af15061080d17377a18", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "49775819d4ba9af15061080d17377a18",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "99e44969ef0f9421f21cb32554464bc7", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "750c2964308cd3f3e5986fcda9a25706",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "a14441d4ff9adae6e015a9f0d107a61a", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "772af7da6a115f53b0b3f6a4afd3baec",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "90b5c353d5296be7e3b06e8c823564bb", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "178c1a53a7ad50297aed68d0ca3a1476",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "84409ce44260a641295c1459e4d3af8f", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "b011bc2b6437995f2d33f5215b4ffa36",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "35c58dd0a7482bb5f1ef5d6edb647c46", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a758a4f98336208381b093aacb735878",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "a7c2895f59f75b767258277c766e9ed4", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "6c86545fab2327105114676a20ca5e68",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "e0430eeeb324ccfca8ace41d743e069c", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "f75525fdc9f7db4a81ca9bae6a79add5",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "d594057005fc56c8576f555f892b3dc4", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2e297069baec404d43ccdb18abeef658",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101", "src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2", "src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2",

View File

@ -1,4 +1,4 @@
### 1.7.28 (build 21453, api 8, 2023-10-13) ### 1.7.28 (build 21465, api 8, 2023-10-14)
- Massively cleaned up code related to rendering and window systems (OpenGL, - Massively cleaned up code related to rendering and window systems (OpenGL,
SDL, etc). This code had been growing into a nasty tangle for 15 years SDL, etc). This code had been growing into a nasty tangle for 15 years
@ -144,6 +144,8 @@
that you will be cutting off support for older clients if you use 35. that you will be cutting off support for older clients if you use 35.
- Fixed a bug with screen-messages animating off screen too fast when frame - Fixed a bug with screen-messages animating off screen too fast when frame
rates are high. rates are high.
- Added a proper graceful shutdown process for the audio server. This should
result in fewer ugly pops and warning messages when the app is quit.
### 1.7.27 (build 21282, api 8, 2023-08-30) ### 1.7.27 (build 21282, api 8, 2023-08-30)

View File

@ -14,7 +14,6 @@
"src/ballistica/core/platform/android/utf8/checked.h", "src/ballistica/core/platform/android/utf8/checked.h",
"src/ballistica/core/platform/android/utf8/unchecked.h", "src/ballistica/core/platform/android/utf8/unchecked.h",
"src/ballistica/core/platform/android/utf8/core.h", "src/ballistica/core/platform/android/utf8/core.h",
"src/ballistica/base/platform/apple/sdl_main_mac.h",
"src/ballistica/base/platform/oculus/main_rift.cc", "src/ballistica/base/platform/oculus/main_rift.cc",
"src/ballistica/core/platform/android/android_gl3.c" "src/ballistica/core/platform/android/android_gl3.c"
], ],

View File

@ -210,7 +210,8 @@ class App:
self._shutdown_task: asyncio.Task[None] | None = None self._shutdown_task: asyncio.Task[None] | None = None
self._shutdown_tasks: list[Coroutine[None, None, None]] = [ self._shutdown_tasks: list[Coroutine[None, None, None]] = [
self._wait_for_shutdown_suppressions(), self._wait_for_shutdown_suppressions(),
self._fade_for_shutdown(), self._fade_and_shutdown_graphics(),
self._fade_and_shutdown_audio(),
] ]
self._pool_thread_count = 0 self._pool_thread_count = 0
@ -798,6 +799,7 @@ class App:
async def _shutdown(self) -> None: async def _shutdown(self) -> None:
import asyncio import asyncio
_babase.lock_all_input()
try: try:
async with asyncio.TaskGroup() as task_group: async with asyncio.TaskGroup() as task_group:
for task_coro in self._shutdown_tasks: for task_coro in self._shutdown_tasks:
@ -898,18 +900,26 @@ class App:
await asyncio.sleep(0.001) await asyncio.sleep(0.001)
_babase.lifecyclelog('shutdown-suppress wait end') _babase.lifecyclelog('shutdown-suppress wait end')
async def _fade_for_shutdown(self) -> None: async def _fade_and_shutdown_graphics(self) -> None:
import asyncio import asyncio
# Kick off a fade, block input, and wait for a short bit. # Kick off a short fade and give it time to complete.
# Ideally most shutdown activity completes during the fade so _babase.lifecyclelog('fade-and-shutdown-graphics begin')
# there's no tangible wait.
_babase.lifecyclelog('fade-for-shutdown begin')
_babase.fade_screen(False, time=0.15) _babase.fade_screen(False, time=0.15)
_babase.lock_all_input()
# _babase.getsimplesound('swish2').play()
await asyncio.sleep(0.15) await asyncio.sleep(0.15)
_babase.lifecyclelog('fade-for-shutdown end') _babase.lifecyclelog('fade-and-shutdown-graphics end')
async def _fade_and_shutdown_audio(self) -> None:
import asyncio
# Tell the audio system to go down and give it a bit of
# time to do so gracefully.
_babase.lifecyclelog('fade-and-shutdown-audio begin')
_babase.audio_shutdown_begin()
await asyncio.sleep(0.15)
while not _babase.audio_shutdown_is_complete():
await asyncio.sleep(0.01)
_babase.lifecyclelog('fade-and-shutdown-audio end')
def _threadpool_no_wait_done(self, fut: Future) -> None: def _threadpool_no_wait_done(self, fut: Future) -> None:
try: try:

View File

@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21453 TARGET_BALLISTICA_BUILD = 21465
TARGET_BALLISTICA_VERSION = '1.7.28' TARGET_BALLISTICA_VERSION = '1.7.28'

View File

@ -190,13 +190,13 @@ void AppAdapterApple::SetHardwareCursorVisible(bool visible) {
assert(g_core->InMainThread()); assert(g_core->InMainThread());
#if BA_OSTYPE_MACOS #if BA_OSTYPE_MACOS
BallisticaKit::CocoaSupportSetCursorVisible(visible); BallisticaKit::CocoaFromCppSetCursorVisible(visible);
#endif #endif
} }
void AppAdapterApple::TerminateApp() { void AppAdapterApple::TerminateApp() {
#if BA_OSTYPE_MACOS #if BA_OSTYPE_MACOS
BallisticaKit::CocoaSupportTerminateApp(); BallisticaKit::CocoaFromCppTerminateApp();
#else #else
AppAdapter::TerminateApp(); AppAdapter::TerminateApp();
#endif #endif

View File

@ -101,7 +101,7 @@ auto Audio::SourceBeginNew() -> AudioSource* {
#pragma clang diagnostic pop #pragma clang diagnostic pop
auto Audio::IsSoundPlaying(uint32_t play_id) -> bool { auto Audio::IsSoundPlaying(uint32_t play_id) -> bool {
uint32_t source_id = AudioServer::source_id_from_play_id(play_id); uint32_t source_id = AudioServer::SourceIdFromPlayId(play_id);
assert(client_sources_.size() > source_id); assert(client_sources_.size() > source_id);
client_sources_[source_id]->Lock(2); client_sources_[source_id]->Lock(2);
bool result = (client_sources_[source_id]->play_id() == play_id); bool result = (client_sources_[source_id]->play_id() == play_id);
@ -112,7 +112,7 @@ auto Audio::IsSoundPlaying(uint32_t play_id) -> bool {
auto Audio::SourceBeginExisting(uint32_t play_id, int debug_id) auto Audio::SourceBeginExisting(uint32_t play_id, int debug_id)
-> AudioSource* { -> AudioSource* {
BA_DEBUG_FUNCTION_TIMER_BEGIN(); BA_DEBUG_FUNCTION_TIMER_BEGIN();
uint32_t source_id = AudioServer::source_id_from_play_id(play_id); uint32_t source_id = AudioServer::SourceIdFromPlayId(play_id);
// Ok, the audio thread fills in this source list, // Ok, the audio thread fills in this source list,
// so theoretically a client could call this before the audio thread // so theoretically a client could call this before the audio thread

File diff suppressed because it is too large Load Diff

View File

@ -12,14 +12,14 @@
namespace ballistica::base { namespace ballistica::base {
/// A module that handles audio processing. /// Wrangles audio off in its own thread.
class AudioServer { class AudioServer {
public: public:
static auto source_id_from_play_id(uint32_t play_id) -> uint32_t { static auto SourceIdFromPlayId(uint32_t play_id) -> uint32_t {
return play_id & 0xFFFFu; return play_id & 0xFFFFu;
} }
static auto play_count_from_play_id(uint32_t play_id) -> uint32_t { static auto PlayCountFromPlayId(uint32_t play_id) -> uint32_t {
return play_id >> 16u; return play_id >> 16u;
} }
@ -28,10 +28,9 @@ class AudioServer {
void PushSetVolumesCall(float music_volume, float sound_volume); void PushSetVolumesCall(float music_volume, float sound_volume);
void PushSetSoundPitchCall(float val); void PushSetSoundPitchCall(float val);
void PushSetSuspendedCall(bool pause);
static void BeginInterruption(); // static void BeginInterruption();
static void EndInterruption(); // static void EndInterruption();
void PushSetListenerPositionCall(const Vector3f& p); void PushSetListenerPositionCall(const Vector3f& p);
void PushSetListenerOrientationCall(const Vector3f& forward, void PushSetListenerOrientationCall(const Vector3f& forward,
@ -41,10 +40,12 @@ class AudioServer {
void PushComponentUnloadCall( void PushComponentUnloadCall(
const std::vector<Object::Ref<Asset>*>& components); const std::vector<Object::Ref<Asset>*>& components);
/// For use by g_logic_module().
void ClearSoundRefDeleteList(); void ClearSoundRefDeleteList();
auto paused() const -> bool { return paused_; } auto paused() const -> bool { return suspended_; }
void Shutdown();
auto shutdown_completed() const { return shutdown_completed_; }
// Client sources use these to pass settings to the server. // Client sources use these to pass settings to the server.
void PushSourceSetIsMusicCall(uint32_t play_id, bool val); void PushSourceSetIsMusicCall(uint32_t play_id, bool val);
@ -67,37 +68,36 @@ class AudioServer {
auto event_loop() const -> EventLoop* { return event_loop_; } auto event_loop() const -> EventLoop* { return event_loop_; }
private: private:
class ThreadSource; class ThreadSource_;
struct Impl; struct Impl_;
void OnAppStartInThread(); void OnAppStartInThread_();
~AudioServer(); ~AudioServer();
void OnThreadPause(); void OnThreadSuspend_();
void OnThreadResume(); void OnThreadUnsuspend_();
void SetPaused(bool paused); void SetSuspended_(bool suspended);
void SetMusicVolume(float volume); void SetMusicVolume_(float volume);
void SetSoundVolume(float volume); void SetSoundVolume_(float volume);
void SetSoundPitch(float pitch); void SetSoundPitch_(float pitch);
auto music_volume() -> float { return music_volume_; }
auto sound_volume() -> float { return sound_volume_; } void CompleteShutdown_();
auto sound_pitch() -> float { return sound_pitch_; }
/// If a sound play id is currently playing, return the sound. /// If a sound play id is currently playing, return the sound.
auto GetPlayingSound(uint32_t play_id) -> ThreadSource*; auto GetPlayingSound_(uint32_t play_id) -> ThreadSource_*;
void Reset(); void Reset_();
void Process(); void Process_();
/// Send a component to the audio thread to delete. /// Send a component to the audio thread to delete.
void DeleteAssetComponent(Asset* c); // void DeleteAssetComponent_(Asset* c);
void UpdateTimerInterval(); void UpdateTimerInterval_();
void UpdateAvailableSources(); void UpdateAvailableSources_();
void UpdateMusicPlayState(); void UpdateMusicPlayState_();
void ProcessSoundFades(); void ProcessSoundFades_();
// Some threads such as audio hold onto allocated Media-Component-Refs to keep // Some threads such as audio hold onto allocated Media-Component-Refs to keep
// media components alive that they need. Media-Component-Refs, however, must // media components alive that they need. Media-Component-Refs, however, must
@ -107,32 +107,36 @@ class AudioServer {
// Note: should use unique_ptr for this, but build fails on raspberry pi // Note: should use unique_ptr for this, but build fails on raspberry pi
// (gcc 8.3.0). Works on Ubuntu 9.3 so should try again later. // (gcc 8.3.0). Works on Ubuntu 9.3 so should try again later.
// std::unique_ptr<Impl> impl_{}; std::unique_ptr<Impl_> impl_{};
Impl* impl_{}; // Impl* impl_{};
EventLoop* event_loop_{}; EventLoop* event_loop_{};
Timer* process_timer_{}; Timer* process_timer_{};
bool have_pending_loads_{};
bool paused_{};
millisecs_t last_sound_fade_process_time_{};
float sound_volume_{1.0f}; float sound_volume_{1.0f};
float sound_pitch_{1.0f}; float sound_pitch_{1.0f};
float music_volume_{1.0f}; float music_volume_{1.0f};
bool have_pending_loads_ : 1 {};
bool suspended_ : 1 {};
bool shutdown_completed_ : 1 {};
bool shutting_down_ : 1 {};
seconds_t shutdown_start_time_{};
millisecs_t last_sound_fade_process_time_{};
/// Indexed list of sources. /// Indexed list of sources.
std::vector<ThreadSource*> sources_; std::vector<ThreadSource_*> sources_;
std::vector<ThreadSource*> streaming_sources_; std::vector<ThreadSource_*> streaming_sources_;
millisecs_t last_stream_process_time_{}; millisecs_t last_stream_process_time_{};
millisecs_t last_sanity_check_time_{};
// Holds refs to all sources. // Holds refs to all sources.
// Use sources, not this, for faster iterating. // Use sources, not this, for faster iterating.
std::vector<Object::Ref<ThreadSource> > sound_source_refs_; std::vector<Object::Ref<ThreadSource_>> sound_source_refs_;
struct SoundFadeNode; struct SoundFadeNode_;
// NOTE: would use unordered_map here but gcc doesn't seem to allow // NOTE: would use unordered_map here but gcc doesn't seem to allow
// forward-declared template params with them. // forward-declared template params with them.
std::map<int, SoundFadeNode> sound_fade_nodes_; std::map<int, SoundFadeNode_> sound_fade_nodes_;
// This mutex controls access to our list of media component shared ptrs to // This mutex controls access to our list of media component shared ptrs to
// delete in the main thread. // delete in the main thread.
@ -141,9 +145,7 @@ class AudioServer {
// Our list of sound media components to delete via the main thread. // Our list of sound media components to delete via the main thread.
std::vector<const Object::Ref<SoundAsset>*> sound_ref_delete_list_; std::vector<const Object::Ref<SoundAsset>*> sound_ref_delete_list_;
millisecs_t last_sanity_check_time_{}; int al_source_count_{};
static int al_source_count_;
}; };
} // namespace ballistica::base } // namespace ballistica::base

View File

@ -15,7 +15,7 @@ AudioSource::AudioSource(int id_in) : id_(id_in) {}
AudioSource::~AudioSource() { assert(client_queue_size_ == 0); } AudioSource::~AudioSource() { assert(client_queue_size_ == 0); }
void AudioSource::MakeAvailable(uint32_t play_id_new) { void AudioSource::MakeAvailable(uint32_t play_id_new) {
assert(AudioServer::source_id_from_play_id(play_id_new) == id_); assert(AudioServer::SourceIdFromPlayId(play_id_new) == id_);
assert(client_queue_size_ == 0); assert(client_queue_size_ == 0);
assert(locked()); assert(locked());
play_id_ = play_id_new; play_id_ = play_id_new;

View File

@ -68,7 +68,9 @@ void AudioStreamer::Stop() {
} }
void AudioStreamer::Update() { void AudioStreamer::Update() {
if (eof_) return; if (eof_) {
return;
}
CHECK_AL_ERROR; CHECK_AL_ERROR;

View File

@ -23,17 +23,18 @@ class AudioStreamer : public Object {
auto Play() -> bool; auto Play() -> bool;
void Stop(); void Stop();
void Update(); void Update();
enum Format { INVALID_FORMAT, MONO16_FORMAT, STEREO16_FORMAT }; enum class Format : uint8_t { kInvalid, kMono16, kStereo16 };
auto al_format() const -> ALenum { auto al_format() const -> ALenum {
switch (format_) { switch (format_) {
case MONO16_FORMAT: case Format::kMono16:
return AL_FORMAT_MONO16; return AL_FORMAT_MONO16;
case STEREO16_FORMAT: case Format::kStereo16:
return AL_FORMAT_STEREO16; return AL_FORMAT_STEREO16;
default: default:
break; break;
} }
return INVALID_FORMAT; FatalError("Invalid AL format.");
return AL_FORMAT_MONO16;
} }
auto loops() const -> bool { return loops_; } auto loops() const -> bool { return loops_; }
auto file_name() const -> const std::string& { return file_name_; } auto file_name() const -> const std::string& { return file_name_; }
@ -46,13 +47,13 @@ class AudioStreamer : public Object {
void set_format(Format format) { format_ = format; } void set_format(Format format) { format_ = format; }
private: private:
Format format_ = INVALID_FORMAT; Format format_{Format::kInvalid};
bool playing_ = false; bool playing_ : 1 {};
bool loops_ : 1 {};
bool eof_ : 1 {};
ALuint buffers_[kAudioStreamBufferCount]{}; ALuint buffers_[kAudioStreamBufferCount]{};
ALuint source_ = 0; ALuint source_{};
std::string file_name_; std::string file_name_;
bool loops_ = false;
bool eof_ = false;
}; };
#endif // BA_ENABLE_AUDIO #endif // BA_ENABLE_AUDIO

View File

@ -53,9 +53,9 @@ OggStream::OggStream(const char* file_name, ALuint source, bool loop)
vorbis_info_ = ov_info(&ogg_file_, -1); vorbis_info_ = ov_info(&ogg_file_, -1);
if (vorbis_info_->channels == 1) { if (vorbis_info_->channels == 1) {
set_format(MONO16_FORMAT); set_format(Format::kMono16);
} else { } else {
set_format(STEREO16_FORMAT); set_format(Format::kStereo16);
} }
} }

View File

@ -4,6 +4,7 @@
#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/app_mode/app_mode_empty.h" #include "ballistica/base/app_mode/app_mode_empty.h"
#include "ballistica/base/audio/audio_server.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/logic/logic.h" #include "ballistica/base/logic/logic.h"
#include "ballistica/base/python/base_python.h" #include "ballistica/base/python/base_python.h"
@ -729,11 +730,8 @@ static auto PyEnv(PyObject* self) -> PyObject* {
"ss" // ui_scale "ss" // ui_scale
"sO" // on_tv "sO" // on_tv
"sO" // vr_mode "sO" // vr_mode
// "sO" // toolbar_test
"sO" // demo_mode "sO" // demo_mode
"sO" // arcade_mode "sO" // arcade_mode
// "sO" // iircade_mode
// "si" // protocol_version
"sO" // headless_mode "sO" // headless_mode
"sO" // python_directory_app_site "sO" // python_directory_app_site
"ss" // device_name "ss" // device_name
@ -757,8 +755,6 @@ static auto PyEnv(PyObject* self) -> PyObject* {
"vr_mode", g_core->IsVRMode() ? Py_True : Py_False, "vr_mode", g_core->IsVRMode() ? Py_True : Py_False,
"demo_mode", g_buildconfig.demo_build() ? Py_True : Py_False, "demo_mode", g_buildconfig.demo_build() ? Py_True : Py_False,
"arcade_mode", g_buildconfig.arcade_build() ? Py_True : Py_False, "arcade_mode", g_buildconfig.arcade_build() ? Py_True : Py_False,
// "iircade_mode", g_buildconfig.iircade_build() ? Py_True: Py_False,
// "protocol_version", kProtocolVersion,
"headless_mode", g_core->HeadlessMode() ? Py_True : Py_False, "headless_mode", g_core->HeadlessMode() ? Py_True : Py_False,
"python_directory_app_site", "python_directory_app_site",
site_py_dir ? *PythonRef::FromString(*site_py_dir) : Py_None, site_py_dir ? *PythonRef::FromString(*site_py_dir) : Py_None,
@ -1603,6 +1599,52 @@ static PyMethodDef PyDevConsoleInputAdapterFinishDef = {
"(internal)\n", "(internal)\n",
}; };
// -------------------------- audio_shutdown_begin -----------------------------
static auto PyAudioShutdownBegin(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
auto* audio_event_loop = g_base->audio_server->event_loop();
BA_PRECONDITION(audio_event_loop);
audio_event_loop->PushCall([] { g_base->audio_server->Shutdown(); });
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyAudioShutdownBeginDef = {
"audio_shutdown_begin", // name
(PyCFunction)PyAudioShutdownBegin, // method
METH_NOARGS, // flags
"audio_shutdown_begin() -> None\n"
"\n"
"(internal)\n",
};
// ----------------------- audio_shutdown_is_complete --------------------------
static auto PyAudioShutdownIsComplete(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
if (g_base->audio_server->shutdown_completed()) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyAudioShutdownIsCompleteDef = {
"audio_shutdown_is_complete", // name
(PyCFunction)PyAudioShutdownIsComplete, // method
METH_NOARGS, // flags
"audio_shutdown_is_complete() -> bool\n"
"\n"
"(internal)\n",
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> { auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
@ -1658,6 +1700,8 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyGetDevConsoleInputTextDef, PyGetDevConsoleInputTextDef,
PySetDevConsoleInputTextDef, PySetDevConsoleInputTextDef,
PyDevConsoleInputAdapterFinishDef, PyDevConsoleInputAdapterFinishDef,
PyAudioShutdownBeginDef,
PyAudioShutdownIsCompleteDef,
}; };
} }

View File

@ -42,7 +42,7 @@ UI::UI() {
// VR and TV modes always use medium. // VR and TV modes always use medium.
scale_ = UIScale::kMedium; scale_ = UIScale::kMedium;
} else { } else {
scale_ = g_core->platform->GetUIScale(); scale_ = g_core->platform->GetDefaultUIScale();
} }
} }
} }

View File

@ -4,8 +4,10 @@
#include "ballistica/core/platform/apple/core_platform_apple.h" #include "ballistica/core/platform/apple/core_platform_apple.h"
#if BA_XCODE_BUILD #if BA_XCODE_BUILD
#include <BallisticaKit-Swift.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <uuid/uuid.h> #include <uuid/uuid.h>
#if BA_XCODE_BUILD #if BA_XCODE_BUILD
@ -102,7 +104,7 @@ auto CorePlatformApple::DoGetConfigDirectoryMonolithicDefault()
auto CorePlatformApple::GetLocale() -> std::string { auto CorePlatformApple::GetLocale() -> std::string {
#if BA_XCODE_BUILD #if BA_XCODE_BUILD
return base::AppleUtils::GetLocaleString(); return BallisticaKit::FromCppGetLocaleString();
#else #else
return CorePlatform::GetLocale(); return CorePlatform::GetLocale();
#endif #endif
@ -124,7 +126,7 @@ auto CorePlatformApple::DoHasTouchScreen() -> bool {
#endif #endif
} }
auto CorePlatformApple::GetUIScale() -> UIScale { auto CorePlatformApple::GetDefaultUIScale() -> UIScale {
#if BA_OSTYPE_IOS #if BA_OSTYPE_IOS
if (base::AppleUtils::IsTablet()) { if (base::AppleUtils::IsTablet()) {
return UIScale::kMedium; return UIScale::kMedium;
@ -132,8 +134,8 @@ auto CorePlatformApple::GetUIScale() -> UIScale {
return UIScale::kSmall; return UIScale::kSmall;
} }
#else #else
// default case handles mac/tvos // Default case handles mac & tvos.
return CorePlatform::GetUIScale(); return CorePlatform::GetDefaultUIScale();
#endif #endif
} }
@ -160,9 +162,8 @@ void CorePlatformApple::DisplayLog(const std::string& name, LogLevel level,
} }
auto CorePlatformApple::DoGetDataDirectoryMonolithicDefault() -> std::string { auto CorePlatformApple::DoGetDataDirectoryMonolithicDefault() -> std::string {
#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD #if BA_XCODE_BUILD
// On Apple package-y builds use our resources dir. return BallisticaKit::FromCppGetResourcesPath();
return base::AppleUtils::GetResourcesPath();
#else #else
// Fall back to default. // Fall back to default.
return CorePlatform::DoGetDataDirectoryMonolithicDefault(); return CorePlatform::DoGetDataDirectoryMonolithicDefault();
@ -292,13 +293,8 @@ void CorePlatformApple::OpenFileExternally(const std::string& path) {
} }
void CorePlatformApple::OpenDirExternally(const std::string& path) { void CorePlatformApple::OpenDirExternally(const std::string& path) {
#if BA_OSTYPE_MACOS #if BA_OSTYPE_MACOS && BA_XCODE_BUILD
std::string cmd = std::string("open \"") + path + "\""; BallisticaKit::CocoaFromCppOpenDirExternally(path);
int result = system(cmd.c_str());
if (result != 0) {
Log(LogLevel::kError, "Got return value " + std::to_string(result)
+ " on open cmd '" + cmd + "'");
}
#else #else
CorePlatform::OpenDirExternally(path); CorePlatform::OpenDirExternally(path);
#endif #endif
@ -380,7 +376,7 @@ auto CorePlatformApple::DoClipboardIsSupported() -> bool {
return base::AppleUtils::ClipboardIsSupported(); return base::AppleUtils::ClipboardIsSupported();
#else #else
return CorePlatform::DoClipboardIsSupported(); return CorePlatform::DoClipboardIsSupported();
#endif // BA_XCODE_BUILD #endif
} }
auto CorePlatformApple::DoClipboardHasText() -> bool { auto CorePlatformApple::DoClipboardHasText() -> bool {
@ -388,7 +384,7 @@ auto CorePlatformApple::DoClipboardHasText() -> bool {
return base::AppleUtils::ClipboardHasText(); return base::AppleUtils::ClipboardHasText();
#else #else
return CorePlatform::DoClipboardHasText(); return CorePlatform::DoClipboardHasText();
#endif // BA_XCODE_BUILD #endif
} }
void CorePlatformApple::DoClipboardSetText(const std::string& text) { void CorePlatformApple::DoClipboardSetText(const std::string& text) {
@ -396,7 +392,7 @@ void CorePlatformApple::DoClipboardSetText(const std::string& text) {
base::AppleUtils::ClipboardSetText(text); base::AppleUtils::ClipboardSetText(text);
#else #else
CorePlatform::DoClipboardSetText(text); CorePlatform::DoClipboardSetText(text);
#endif // BA_XCODE_BUILD #endif
} }
auto CorePlatformApple::DoClipboardGetText() -> std::string { auto CorePlatformApple::DoClipboardGetText() -> std::string {
@ -404,7 +400,7 @@ auto CorePlatformApple::DoClipboardGetText() -> std::string {
return base::AppleUtils::ClipboardGetText(); return base::AppleUtils::ClipboardGetText();
#else #else
return CorePlatform::DoClipboardGetText(); return CorePlatform::DoClipboardGetText();
#endif // BA_XCODE_BUILD #endif
} }
} // namespace ballistica::core } // namespace ballistica::core

View File

@ -24,7 +24,7 @@ class CorePlatformApple : public CorePlatform {
auto GetLocale() -> std::string override; auto GetLocale() -> std::string override;
auto DoGetDeviceName() -> std::string override; auto DoGetDeviceName() -> std::string override;
auto DoHasTouchScreen() -> bool override; auto DoHasTouchScreen() -> bool override;
auto GetUIScale() -> UIScale override; auto GetDefaultUIScale() -> UIScale override;
auto IsRunningOnDesktop() -> bool override; auto IsRunningOnDesktop() -> bool override;
void DisplayLog(const std::string& name, LogLevel level, void DisplayLog(const std::string& name, LogLevel level,
const std::string& msg) override; const std::string& msg) override;

View File

@ -476,7 +476,7 @@ void CorePlatform::SleepMicrosecs(millisecs_t ms) {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma ide diagnostic ignored "NullDereferences" #pragma ide diagnostic ignored "NullDereferences"
auto CorePlatform::GetUIScale() -> UIScale { auto CorePlatform::GetDefaultUIScale() -> UIScale {
// Handles mac/pc/linux cases. // Handles mac/pc/linux cases.
return UIScale::kLarge; return UIScale::kLarge;
} }

View File

@ -97,26 +97,22 @@ class CorePlatform {
#pragma mark PRINTING/LOGGING -------------------------------------------------- #pragma mark PRINTING/LOGGING --------------------------------------------------
/// Display a message to any default log for the platform (android log, /// Display a message to any default log for the platform (android log,
/// etc.) Note that this can be called from any thread. /// etc.) Note that this can be called from any thread. Default
/// implementation does nothing.
virtual void DisplayLog(const std::string& name, LogLevel level, virtual void DisplayLog(const std::string& name, LogLevel level,
const std::string& msg); const std::string& msg);
#pragma mark ENVIRONMENT ------------------------------------------------------- #pragma mark ENVIRONMENT -------------------------------------------------------
// Return a simple name for the platform: 'mac', 'windows', 'linux', etc. /// Return a simple name for the platform: 'mac', 'windows', 'linux', etc.
virtual auto GetPlatformName() -> std::string; virtual auto GetPlatformName() -> std::string;
// Return a simple name for the subplatform: 'amazon', 'google', etc. /// Return a simple name for the subplatform: 'amazon', 'google', etc.
virtual auto GetSubplatformName() -> std::string; virtual auto GetSubplatformName() -> std::string;
// Are we running in event-push-mode? With this on, we return from Main()
// and the system handles the event loop. With it off, we loop in Main()
// ourself.
// virtual auto IsEventPushMode() -> bool;
/// Return the interface type based on the environment (phone, tablet, /// Return the interface type based on the environment (phone, tablet,
/// etc). /// etc).
virtual auto GetUIScale() -> UIScale; virtual auto GetDefaultUIScale() -> UIScale;
/// Return default DataDirectory value for monolithic builds. /// Return default DataDirectory value for monolithic builds.
auto GetDataDirectoryMonolithicDefault() -> std::string; auto GetDataDirectoryMonolithicDefault() -> std::string;
@ -387,7 +383,7 @@ class CorePlatform {
static void SleepMillisecs(millisecs_t ms); static void SleepMillisecs(millisecs_t ms);
static void SleepMicrosecs(millisecs_t ms); static void SleepMicrosecs(microsecs_t ms);
/// Given a C++ symbol, attempt to return a pretty one. /// Given a C++ symbol, attempt to return a pretty one.
virtual auto DemangleCXXSymbol(const std::string& s) -> std::string; virtual auto DemangleCXXSymbol(const std::string& s) -> std::string;

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21453; const int kEngineBuildNumber = 21465;
const char* kEngineVersion = "1.7.28"; const char* kEngineVersion = "1.7.28";
const int kEngineApiVersion = 8; const int kEngineApiVersion = 8;

View File

@ -248,24 +248,27 @@ void EventLoop::WaitForNextEvent_(bool single_cycle) {
} }
} }
// void EventLoop::LoopUpkeep_(bool single_cycle) { // Note to self (Oct '23): can probably kill this at some point,
// assert(g_core); // but am still using some non-ARC objc stuff from logic thread
// // Keep our autorelease pool clean on mac/ios // so should keep it around just a bit longer just in case.
// // FIXME: Should define a CorePlatform::ThreadHelper or something void EventLoop::LoopUpkeep_(bool single_cycle) {
// // so we don't have platform-specific code here. assert(g_core);
// #if BA_XCODE_BUILD // Keep our autorelease pool clean on mac/ios
// // Let's not do autorelease pools when being called ad-hoc, // FIXME: Should define a CorePlatform::ThreadHelper or something
// // since in that case we're part of another run loop // so we don't have platform-specific code here.
// // (and its crashing on drain for some reason) #if BA_XCODE_BUILD
// if (!single_cycle) { // Let's not do autorelease pools when being called ad-hoc,
// if (auto_release_pool_) { // since in that case we're part of another run loop
// g_core->platform->DrainAutoReleasePool(auto_release_pool_); // (and its crashing on drain for some reason)
// auto_release_pool_ = nullptr; if (!single_cycle) {
// } if (auto_release_pool_) {
// auto_release_pool_ = g_core->platform->NewAutoReleasePool(); g_core->platform->DrainAutoReleasePool(auto_release_pool_);
// } auto_release_pool_ = nullptr;
// #endif }
// } auto_release_pool_ = g_core->platform->NewAutoReleasePool();
}
#endif
}
void EventLoop::RunToCompletion() { Run_(false); } void EventLoop::RunToCompletion() { Run_(false); }
void EventLoop::RunSingleCycle() { Run_(true); } void EventLoop::RunSingleCycle() { Run_(true); }
@ -273,7 +276,7 @@ void EventLoop::RunSingleCycle() { Run_(true); }
void EventLoop::Run_(bool single_cycle) { void EventLoop::Run_(bool single_cycle) {
assert(g_core); assert(g_core);
while (true) { while (true) {
// LoopUpkeep_(single_cycle); LoopUpkeep_(single_cycle);
WaitForNextEvent_(single_cycle); WaitForNextEvent_(single_cycle);

View File

@ -118,7 +118,6 @@ class EventLoop {
auto CheckPushRunnableSafety_() -> bool; auto CheckPushRunnableSafety_() -> bool;
void SetInternalThreadName_(const std::string& name); void SetInternalThreadName_(const std::string& name);
void WaitForNextEvent_(bool single_cycle); void WaitForNextEvent_(bool single_cycle);
// void LoopUpkeep_(bool single_cycle);
void LogThreadMessageTally_( void LogThreadMessageTally_(
std::vector<std::pair<LogLevel, std::string>>* log_entries); std::vector<std::pair<LogLevel, std::string>>* log_entries);
void PushLocalRunnable_(Runnable* runnable, bool* completion_flag); void PushLocalRunnable_(Runnable* runnable, bool* completion_flag);
@ -155,6 +154,13 @@ class EventLoop {
void BootstrapThread_(); void BootstrapThread_();
void LoopUpkeep_(bool single_cycle);
// FIXME: Should generalize this to some sort of PlatformThreadData class.
#if BA_XCODE_BUILD
void* auto_release_pool_{};
#endif
bool bootstrapped_{}; bool bootstrapped_{};
bool writing_tally_{}; bool writing_tally_{};
bool suspended_{}; bool suspended_{};

View File

@ -88,8 +88,7 @@ template <typename OUT_TYPE, typename IN_TYPE>
auto static_cast_check_type(IN_TYPE in) -> OUT_TYPE { auto static_cast_check_type(IN_TYPE in) -> OUT_TYPE {
auto out_static = static_cast<OUT_TYPE>(in); auto out_static = static_cast<OUT_TYPE>(in);
if (g_buildconfig.debug_build()) { if (g_buildconfig.debug_build()) {
auto out_dynamic = dynamic_cast<OUT_TYPE>(in); assert(out_static == dynamic_cast<OUT_TYPE>(in));
assert(out_static == out_dynamic);
} }
return out_static; return out_static;
} }

View File

@ -30,6 +30,7 @@ typedef struct _SDL_Joystick SDL_Joystick;
namespace ballistica { namespace ballistica {
// Used internally for time values. // Used internally for time values.
typedef double seconds_t;
typedef int64_t millisecs_t; typedef int64_t millisecs_t;
typedef int64_t microsecs_t; typedef int64_t microsecs_t;

View File

@ -23,10 +23,6 @@
#include "ballistica/ui_v1/widget/row_widget.h" #include "ballistica/ui_v1/widget/row_widget.h"
#include "ballistica/ui_v1/widget/scroll_widget.h" #include "ballistica/ui_v1/widget/scroll_widget.h"
// #if !BA_HEADLESS_BUILD && !BA_XCODE_NEW_PROJECT
// extern "C" void SDL_ericf_focus(void);
// #endif
namespace ballistica::ui_v1 { namespace ballistica::ui_v1 {
// Ignore signed bitwise stuff; python macros do it quite a bit. // Ignore signed bitwise stuff; python macros do it quite a bit.