From ef2900b535c33b3b8d640c3a184525dede138406 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 29 Sep 2023 17:08:48 -0700 Subject: [PATCH] fixed an issue that could cause jittery camera motion at extremely high frame rates --- .efrocachemap | 88 ++++++------ .idea/dictionaries/ericf.xml | 1 + CHANGELOG.md | 10 +- .../.idea/dictionaries/ericf.xml | 1 + src/assets/ba_data/python/baenv.py | 2 +- .../base/app_adapter/app_adapter.cc | 80 ++--------- src/ballistica/base/app_adapter/app_adapter.h | 15 +++ .../base/app_adapter/app_adapter_sdl.cc | 126 +++++++++--------- .../base/app_adapter/app_adapter_sdl.h | 3 +- .../base/app_adapter/app_adapter_vr.cc | 12 +- src/ballistica/base/assets/assets.cc | 5 +- src/ballistica/base/assets/texture_asset.cc | 5 +- src/ballistica/base/audio/audio_server.cc | 3 +- src/ballistica/base/base.cc | 9 -- src/ballistica/base/base.h | 1 - .../base/graphics/gl/framebuffer_object_gl.h | 7 +- .../graphics/gl/mesh/mesh_asset_data_gl.h | 5 +- .../base/graphics/gl/mesh/mesh_data_gl.h | 4 +- .../base/graphics/gl/program/program_gl.h | 9 +- .../base/graphics/gl/render_target_gl.h | 6 +- .../base/graphics/gl/renderer_gl.cc | 18 +-- .../base/graphics/gl/texture_data_gl.h | 7 +- src/ballistica/base/graphics/graphics.cc | 17 ++- src/ballistica/base/graphics/graphics.h | 3 +- .../base/graphics/graphics_server.cc | 106 +++++---------- .../base/graphics/graphics_server.h | 13 +- .../base/graphics/mesh/mesh_data.cc | 5 +- .../base/graphics/mesh/nine_patch_mesh.cc | 21 +-- .../base/graphics/mesh/nine_patch_mesh.h | 14 +- .../base/graphics/renderer/render_pass.cc | 5 +- .../base/graphics/renderer/render_target.cc | 3 +- .../base/graphics/renderer/renderer.cc | 12 +- .../base/graphics/support/frame_def.h | 10 +- src/ballistica/core/support/base_soft.h | 2 - src/ballistica/shared/ballistica.cc | 2 +- src/tools/pcommandbatch/pcommandbatch.c | 44 +++++- tools/efro/terminal.py | 13 +- tools/efrotools/pcommandbatch.py | 8 +- 38 files changed, 334 insertions(+), 361 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index bf905853..f5a2c4be 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4056,50 +4056,50 @@ "build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", - "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "3d62f18e70a8421820ed2ae8527d7d14", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "3241035738b539e94cf719674953e8fb", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "2229f735f006bef70ad1d2c08ae97fec", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "8895e3a5b262aef6102c16029359a587", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "c94700f8b6ad1ce6b5574372a289d6da", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "8e0b9a000dab93c61bd58180a1eb69db", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "d7c2c596d67d96386171cae1489d3ff8", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "f492626b4a61c4550f3a01bc8d8abd53", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "fae843a11f1f84a2bc4a266e5723adfd", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "d2724a5a94267a0d7546c8cf648e729c", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "02df04e605addd02bcfcfe64e01109e3", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "beaac64be0ae8023cce5ec0a1d4306f0", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "f1d3c2985d5f0760c72ecded41d91398", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "271b5fc80696ba6d67d02a089358b353", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "22ddbd257824f055627c16d1be1e575a", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "b6de42bf834a0b9cc25c6433d49d6dc2", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "febb196acecc041b07d0ad06430ec2d5", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "fafe18465a2dc39e6a3e2abae0557784", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "bbab03393707e902053429ce38f9986d", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "d68af9931e2adfac40e8bb147ba0383b", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "f3d305e647a7f77dd70a48f615cfd750", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "931ce8eab9859d20ad86c47d196ba62c", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "f3d305e647a7f77dd70a48f615cfd750", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "931ce8eab9859d20ad86c47d196ba62c", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "e80b5f536b30a23fe107852a0c2f7536", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "076df48013d64bc07aa59001819f8583", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "e80b5f536b30a23fe107852a0c2f7536", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "076df48013d64bc07aa59001819f8583", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "fcfdeb63ced9156995cf1b08ae5c861f", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d82ad28301dc8e5b0ca98cf1da5d907c", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "fcfdeb63ced9156995cf1b08ae5c861f", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d82ad28301dc8e5b0ca98cf1da5d907c", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "6db0247bf985f9d8c7ed85a5e5508a8b", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "097e17c460bf798edf61303789860596", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "14df40bc07bdde8184843d16d5ba7798", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "097e17c460bf798edf61303789860596", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "044195de59694aa90afb2db2a500c383", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "5669f2204fa5bf0bfac57c249dafc1d6", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "820b134e62b814066c40c9439587c945", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "76406fb75d1bc422accf3b8ad69d48e5", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "6b25bae7fd9eabaad90a2c3103bb6948", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "55bd741c876af2a7d9633dbc6a1bff45", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "188433602c168f76f83183b052852e43", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "1bf745b621bae741df16d9b604342e4b", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "f6aa1723e7bee0328bcd8de8eaf41844", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "6b8d5b78eae91ce978a21766f2f08829", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "3691b9d78d2b8251fc854121ed274fa7", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "0f96bd7efe76c573c52cc391824a5bdd", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "62a3f4260790c6e4e648380985b92c79", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4800d86542ac3db78c23984002fa4f63", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "64a094f2b2c8f82b3bb9c33077192f00", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "1f33c24237a6f3fc9ba5b04c2591d9c2", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "bd832c2b4071ad0531324190ab394d3a", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "fca25dc6756546cd5166c836f74b9ff4", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "3cb2ec9e30b8b6d52ab08aaf31cdcf9e", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "d4305ad198b3e9e26edc7bada23d2b73", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "b784af9a46351b20f03a1096d13e0243", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "561a56987aced8b326db9af825de90a5", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "0eee318bf2f64a15972bde46a33427d8", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "4464bd01ba4df385634b248a68633c4d", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "f76d925131181ed178baf38a5ffd15e0", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "9228141043bb88a32adc1c7a406dfbcc", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "413169acc3e3feaf17543be1f6e242ee", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e73509f5d2bc32ffb81800bf31f7e44b", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "6ce4983e76e1cc2d2803fe306d08ad58", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4ea0cf78901f994215f215aebb0af1dc", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "6ce4983e76e1cc2d2803fe306d08ad58", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "4ea0cf78901f994215f215aebb0af1dc", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "87f651fed518cfa5ee34f820c49af72b", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "5f28547e33fa30904dc989a39ac2d127", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "87f651fed518cfa5ee34f820c49af72b", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "5f28547e33fa30904dc989a39ac2d127", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "1168b2ba42e7120d9321a900c5712cf0", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d23c56d85e8ee9a07c9245e548daa39c", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "1168b2ba42e7120d9321a900c5712cf0", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d23c56d85e8ee9a07c9245e548daa39c", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "0c46358e1af4384af36367a3c0768605", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "de83e1b384abb333f457f2c9646f810f", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "0149ef8dae7720330416846919a834e7", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "de83e1b384abb333f457f2c9646f810f", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "e3ad15ecc9656a1a268582b14336bed7", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "fce3fd45ed78f84592ef914450817778", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "feeb14560de0fa18d8b7654aa43534ea", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "7896041f53b6f076d032388d661413b0", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a5b84aaf028c8b13d55d2d99f3853f5d", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "4a8387851909f5cedabb5972b397a7b1", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "53fa53d605a009a6ab592e30d45629a8", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2988d1791814f0ec0d64914d691a100e", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318", "src/ballistica/base/mgen/pyembed/binding_base.inc": "c81b2b1f3a14b4cd20a7b93416fe893a", diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index d90743d6..e14bdba4 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -2974,6 +2974,7 @@ templatefs tenum termcolors + termenv termios testbuffer testbuild diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad4a4af..9eaf9b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.28 (build 21397, api 8, 2023-09-28) +### 1.7.28 (build 21401, api 8, 2023-09-29) - 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 @@ -74,10 +74,10 @@ - Worked to improve sanity checking on C++ RenderComponents in debug builds to make it easier to use and avoid sending broken commands to the renderer. Some specifics follow. -- RenderComponents no longer need an explicit Submit() at the end; if one goes - out of scope not in the submitted state it will implicitly run a submit. - Hopefully this will encourage concise code where RenderComponents are defined - in tight scopes. +- RenderComponents (C++ layer) no longer need an explicit Submit() at the end; + if one goes out of scope not in the submitted state it will implicitly run a + submit. Hopefully this will encourage concise code where RenderComponents are + defined in tight scopes. - RenderComponents now have a ScopedTransform() call which can be used to push and pop the transform stack based on C++ scoping instead of the old PushTransform/PopTransform. This should make it harder to accidentally break diff --git a/ballisticakit-cmake/.idea/dictionaries/ericf.xml b/ballisticakit-cmake/.idea/dictionaries/ericf.xml index a90597dd..a7cc8acd 100644 --- a/ballisticakit-cmake/.idea/dictionaries/ericf.xml +++ b/ballisticakit-cmake/.idea/dictionaries/ericf.xml @@ -1754,6 +1754,7 @@ templatefs tempvec tenum + termenv testbuild testclinic testint diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index eee3d173..2e3ed75e 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21397 +TARGET_BALLISTICA_BUILD = 21401 TARGET_BALLISTICA_VERSION = '1.7.28' diff --git a/src/ballistica/base/app_adapter/app_adapter.cc b/src/ballistica/base/app_adapter/app_adapter.cc index db79fbba..0ca4d909 100644 --- a/src/ballistica/base/app_adapter/app_adapter.cc +++ b/src/ballistica/base/app_adapter/app_adapter.cc @@ -100,75 +100,6 @@ void AppAdapter::OnAppShutdownComplete() { assert(g_base->InLogicThread()); } void AppAdapter::OnScreenSizeChange() { assert(g_base->InLogicThread()); } void AppAdapter::DoApplyAppConfig() { assert(g_base->InLogicThread()); } -// void AppAdapter::DrawFrame(bool during_resize) { -// assert(g_base->InGraphicsThread()); - -// // It's possible to be asked to draw before we're ready. -// if (!g_base->graphics_server || !g_base->graphics_server->renderer()) { -// return; -// } - -// millisecs_t starttime = g_core->GetAppTimeMillisecs(); - -// // A resize-draw event means that we're drawing due to a window resize. -// // In this case we ignore regular draw events for a short while -// // afterwards which makes resizing smoother. -// // -// // FIXME: should figure out the *correct* way to handle this; I believe -// // the underlying cause here is some sort of context contention across -// // threads. -// if (during_resize) { -// last_resize_draw_event_time_ = starttime; -// } else { -// if (starttime - last_resize_draw_event_time_ < (1000 / 30)) { -// return; -// } -// } -// g_base->graphics_server->TryRender(); -// // RunRenderUpkeepCycle(); -// } - -// void AppAdapter::RunRenderUpkeepCycle() { -// // This should only be firing if the OS is handling the event loop. -// assert(!ManagesMainThreadEventLoop()); - -// // Pump the main event loop (when we're being driven by frame-draw -// // callbacks, this is the only place that gets done). -// g_core->main_event_loop()->RunSingleCycle(); - -// // Now do the general app event cycle for whoever needs to process things. -// // FIXME KILL THIS. -// RunEvents(); -// } - -// FIXME KILL THIS. -// void AppAdapter::RunEvents() { -// There's probably a better place for this. -// g_base->stress_test()->Update(); - -// Give platforms a chance to pump/handle their own events. -// -// FIXME: now that we have app class overrides, platform should really not -// be doing event handling. (need to fix Rift build in this regard). -// g_core->platform->RunEvents(); -// } - -// void AppAdapter::UpdatePauseResume_() { -// if (app_paused_) { -// // Unpause if no one wants pause. -// if (!app_pause_requested_) { -// OnAppResume_(); -// app_paused_ = false; -// } -// } else { -// // OnAppPause if anyone wants. -// if (app_pause_requested_) { -// OnAppPause_(); -// app_paused_ = true; -// } -// } -// } - void AppAdapter::OnAppPause_() { assert(g_core->InMainThread()); @@ -188,7 +119,6 @@ void AppAdapter::OnAppPause_() { void AppAdapter::OnAppResume_() { assert(g_core->InMainThread()); - // last_app_resume_time_ = g_core->GetAppTimeMillisecs(); // Spin all event-loops back up. EventLoop::SetEventLoopsPaused(false); @@ -234,6 +164,7 @@ void AppAdapter::PauseApp() { "PauseApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs())); // assert(!app_pause_requested_); // app_pause_requested_ = true; + app_paused_ = true; OnAppPause_(); // UpdatePauseResume_(); @@ -285,6 +216,7 @@ void AppAdapter::ResumeApp() { // assert(app_pause_requested_); // app_pause_requested_ = false; // UpdatePauseResume_(); + app_paused_ = false; OnAppResume_(); if (g_buildconfig.debug_build()) { Log(LogLevel::kDebug, @@ -309,4 +241,12 @@ auto AppAdapter::SupportsVSync() -> bool const { return false; } auto AppAdapter::SupportsMaxFPS() -> bool const { return false; } +/// As a default, allow graphics stuff in the main thread. +auto AppAdapter::InGraphicsContext() -> bool { return g_core->InMainThread(); } + +/// As a default, assume our main thread *is* our graphics context. +void AppAdapter::DoPushGraphicsContextRunnable(Runnable* runnable) { + DoPushMainThreadRunnable(runnable); +} + } // namespace ballistica::base diff --git a/src/ballistica/base/app_adapter/app_adapter.h b/src/ballistica/base/app_adapter/app_adapter.h index 2ef9880e..6290b897 100644 --- a/src/ballistica/base/app_adapter/app_adapter.h +++ b/src/ballistica/base/app_adapter/app_adapter.h @@ -52,6 +52,17 @@ class AppAdapter { DoPushMainThreadRunnable(NewLambdaRunnableUnmanaged(lambda)); } + /// Should return whether the current thread and/or context setup is the + /// one where graphics calls should be made. For the default + /// implementation, this simply returns true in the main thread. + virtual auto InGraphicsContext() -> bool; + + /// Push a call to be run in the app's graphics context. + template + void PushGraphicsContextCall(const F& lambda) { + DoPushGraphicsContextRunnable(NewLambdaRunnableUnmanaged(lambda)); + } + /// Put the app into a paused state. Should be called from the main /// thread. Pauses work, closes network sockets, etc. May correspond to /// being backgrounded on mobile, being minimized on desktop, etc. It is @@ -86,6 +97,10 @@ class AppAdapter { /// thread should call its RunAndLogErrors() method and then delete it. virtual void DoPushMainThreadRunnable(Runnable* runnable) = 0; + /// Push a raw pointer Runnable to be run in the platform's graphics + /// context. By default this is simply the main thread. + virtual void DoPushGraphicsContextRunnable(Runnable* runnable); + private: void OnAppPause_(); void OnAppResume_(); diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.cc b/src/ballistica/base/app_adapter/app_adapter_sdl.cc index 7bac9ec9..fba47fb7 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.cc +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.cc @@ -104,8 +104,6 @@ void AppAdapterSDL::DoApplyAppConfig() { void AppAdapterSDL::RunMainThreadEventLoopToCompletion() { assert(g_core->InMainThread()); - // float smoothed_fps{}; - // float fps_smoothing{0.1f}; while (!done_) { microsecs_t cycle_start_time = g_core->GetAppTimeMicrosecs(); @@ -120,67 +118,69 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() { SDL_GL_SwapWindow(sdl_window_); } - // Sleep until we should start our next cycle (based on - // max-frame-rate or other factors). - - // Special case which means no max. Farewell poor laptop battery. - if (max_fps_ == -1) { - continue; - } - microsecs_t now = g_core->GetAppTimeMicrosecs(); - auto used_max_fps = max_fps_; - millisecs_t millisecs_per_frame = 1000000 / used_max_fps; - // Special case: if we've got vsync enabled, let's tweak max-fps to be - // just a *tiny* bit higher than requested. This means if our max-fps - // matches the refresh rate we'll be trying to render just a bit faster - // than vsync which should push us up against the vsync wall and keep - // vsync doing most of the delay work. In that case the logging below - // should show mostly 'no sleep.'. Without this delay, our render - // kick-offs tend to drift around the middle of the vsync cycle and I - // worry there could be bad interference patterns in certain spots close - // to the edges. Note that we want this tweak to be small enough that it - // won't be noticable in situations where vsync and max-fps *don't* - // match. (for instance limiting to 60hz on a 120hz vsynced monitor). - if (vsync_actually_enabled_) { - millisecs_per_frame = 99 * millisecs_per_frame / 100; - } - microsecs_t target_time = - cycle_start_time + millisecs_per_frame - oversleep; - - // Set a minimum so we don't sleep if we're within a few millisecs of - // where we want to be. Sleep tends to run over by a bit so we'll - // probably render closer to our target time by just skipping the sleep. - // And the oversleep system will compensate just as it does if we sleep - // too long. - const microsecs_t min_sleep{2000}; - if (now + min_sleep >= target_time) { - if (debug_log_sdl_frame_timing_) { - Log(LogLevel::kDebug, "no sleep."); // 'till brooklyn! - } - } else { - if (debug_log_sdl_frame_timing_) { - char buf[256]; - snprintf(buf, sizeof(buf), "render %.1f sleep %.1f", - (now - cycle_start_time) / 1000.0f, - (target_time - now) / 1000.0f); - Log(LogLevel::kDebug, buf); - } - g_core->platform->SleepMicrosecs(target_time - now); - } - - // Maintain an 'oversleep' amount to compensate for the timer not being - // exact. This should keep us exactly at our target frame-rate in the - // end. - now = g_core->GetAppTimeMicrosecs(); - oversleep = now - target_time; - - // Prevent oversleep from compensating by more than a few millisecs per - // frame (not sure if this would ever be a problem but lets be safe). - oversleep = std::max(int64_t{-3000}, oversleep); - oversleep = std::min(int64_t{3000}, oversleep); + // In some cases, sleep until we should start our next cycle (depending + // on max-frame-rate or other factors). + SleepUntilNextEventCycle_(cycle_start_time); } } +void AppAdapterSDL::SleepUntilNextEventCycle_(microsecs_t cycle_start_time) { + // Special case which means no max. Farewell poor laptop battery. + if (max_fps_ == -1) { + return; + } + microsecs_t now = g_core->GetAppTimeMicrosecs(); + auto used_max_fps = max_fps_; + millisecs_t millisecs_per_frame = 1000000 / used_max_fps; + // Special case: if we've got vsync enabled, let's tweak max-fps to be + // just a *tiny* bit higher than requested. This means if our max-fps + // matches the refresh rate we'll be trying to render just a bit faster + // than vsync which should push us up against the vsync wall and keep + // vsync doing most of the delay work. In that case the logging below + // should show mostly 'no sleep.'. Without this delay, our render + // kick-offs tend to drift around the middle of the vsync cycle and I + // worry there could be bad interference patterns in certain spots close + // to the edges. Note that we want this tweak to be small enough that it + // won't be noticable in situations where vsync and max-fps *don't* + // match. (for instance limiting to 60hz on a 120hz vsynced monitor). + if (vsync_actually_enabled_) { + millisecs_per_frame = 99 * millisecs_per_frame / 100; + } + microsecs_t target_time = cycle_start_time + millisecs_per_frame - oversleep_; + + // Set a minimum so we don't sleep if we're within a few millisecs of + // where we want to be. Sleep tends to run over by a bit so we'll + // probably render closer to our target time by just skipping the sleep. + // And the oversleep system will compensate just as it does if we sleep + // too long. + const microsecs_t min_sleep{2000}; + if (now + min_sleep >= target_time) { + if (debug_log_sdl_frame_timing_) { + Log(LogLevel::kDebug, "no sleep."); // 'till brooklyn! + } + } else { + if (debug_log_sdl_frame_timing_) { + char buf[256]; + snprintf(buf, sizeof(buf), "render %.1f sleep %.1f", + (now - cycle_start_time) / 1000.0f, + (target_time - now) / 1000.0f); + Log(LogLevel::kDebug, buf); + } + g_core->platform->SleepMicrosecs(target_time - now); + } + + // Maintain an 'oversleep' amount to compensate for the timer not being + // exact. This should keep us exactly at our target frame-rate in the + // end. + now = g_core->GetAppTimeMicrosecs(); + oversleep_ = now - target_time; + + // Prevent oversleep from compensating by more than a few millisecs per + // frame (not sure if this would ever be a problem but lets be safe). + oversleep_ = std::max(int64_t{-3000}, oversleep_); + oversleep_ = std::min(int64_t{3000}, oversleep_); +} + void AppAdapterSDL::DoPushMainThreadRunnable(Runnable* runnable) { assert(sdl_runnable_event_id_ != 0); SDL_Event event; @@ -454,7 +454,7 @@ void AppAdapterSDL::SetScreen_( bool fullscreen, int max_fps, VSyncRequest vsync_requested, TextureQualityRequest texture_quality_requested, GraphicsQualityRequest graphics_quality_requested) { - assert(g_base->InGraphicsThread()); + assert(InGraphicsContext()); assert(!g_core->HeadlessMode()); // If we know what we support, filter our request types to what is @@ -554,7 +554,7 @@ void AppAdapterSDL::SetScreen_( void AppAdapterSDL::ReloadRenderer_( bool fullscreen, GraphicsQualityRequest graphics_quality_requested, TextureQualityRequest texture_quality_requested) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); auto* gs = g_base->graphics_server; @@ -635,7 +635,7 @@ void AppAdapterSDL::ReloadRenderer_( } void AppAdapterSDL::UpdateScreenSizes_() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Grab logical window dimensions (points?). // This is the coordinate space SDL's events deal in. diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.h b/src/ballistica/base/app_adapter/app_adapter_sdl.h index 3cb3e442..f41b5b86 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.h +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.h @@ -61,6 +61,7 @@ class AppAdapterSDL : public AppAdapter { // void UpdateAutoVSync_(int diff); void AddSDLInputDevice_(JoystickInput* input, int index); void RemoveSDLInputDevice_(int index); + void SleepUntilNextEventCycle_(microsecs_t cycle_start_time); // millisecs_t last_swap_time_{}; // millisecs_t swap_start_time_{}; // int too_slow_frame_count_{}; @@ -80,7 +81,7 @@ class AppAdapterSDL : public AppAdapter { bool fullscreen_{}; VSync vsync_{VSync::kUnset}; bool vsync_actually_enabled_{}; - microsecs_t oversleep{}; + microsecs_t oversleep_{}; int max_fps_{60}; bool debug_log_sdl_frame_timing_{}; // std::unique_ptr gl_context_; diff --git a/src/ballistica/base/app_adapter/app_adapter_vr.cc b/src/ballistica/base/app_adapter/app_adapter_vr.cc index 57ee5322..bfa614cd 100644 --- a/src/ballistica/base/app_adapter/app_adapter_vr.cc +++ b/src/ballistica/base/app_adapter/app_adapter_vr.cc @@ -17,7 +17,7 @@ auto AppAdapterVR::ManagesMainThreadEventLoop() const -> bool { return false; } void AppAdapterVR::PushVRSimpleRemoteStateCall( const VRSimpleRemoteState& state) { - g_base->app_adapter->PushMainThreadCall([this, state] { + g_base->app_adapter->PushGraphicsContextCall([this, state] { // Convert this to a full hands state, adding in some simple elbow // positioning of our own and left/right. VRHandsState s; @@ -48,7 +48,7 @@ void AppAdapterVR::VRPreDraw() { || !g_base->graphics_server->renderer()) { return; } - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // FIXME - this is internal graphics-server details that the render-server // should handle. Log(LogLevel::kWarning, "FIXME: Have GraphicsServer handle VR drawing."); @@ -65,7 +65,7 @@ void AppAdapterVR::VRPreDraw() { } void AppAdapterVR::VRPostDraw() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!g_base || !g_base->graphics_server || !g_base->graphics_server->renderer()) { return; @@ -80,14 +80,14 @@ void AppAdapterVR::VRPostDraw() { void AppAdapterVR::VRSetHead(float tx, float ty, float tz, float yaw, float pitch, float roll) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); Renderer* renderer = g_base->graphics_server->renderer(); if (renderer == nullptr) return; renderer->VRSetHead(tx, ty, tz, yaw, pitch, roll); } void AppAdapterVR::VRSetHands(const VRHandsState& state) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Pass this along to the renderer (in this same thread) for drawing (so // hands can be drawn at their absolute most up-to-date positions, etc). @@ -114,7 +114,7 @@ void AppAdapterVR::VRDrawEye(int eye, float yaw, float pitch, float roll, || !g_base->graphics_server->renderer()) { return; } - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (vr_render_frame_def_) { // Set up VR eye stuff. Renderer* renderer = g_base->graphics_server->renderer(); diff --git a/src/ballistica/base/assets/assets.cc b/src/ballistica/base/assets/assets.cc index 0d19e03e..db0b5bde 100644 --- a/src/ballistica/base/assets/assets.cc +++ b/src/ballistica/base/assets/assets.cc @@ -2,6 +2,7 @@ #include "ballistica/base/assets/assets.h" +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/app_mode/app_mode.h" #include "ballistica/base/assets/assets_server.h" #include "ballistica/base/assets/collision_mesh_asset.h" @@ -409,7 +410,7 @@ void Assets::MarkAllAssetsForLoad() { // Call this from the graphics thread to immediately unload all // assets used by it. (for when GL context gets lost, etc). void Assets::UnloadRendererBits(bool do_textures, bool do_meshes) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // need to keep lists locked while iterating over them.. AssetListLock m_lock; if (do_textures) { @@ -728,7 +729,7 @@ auto Assets::RunPendingAudioLoads() -> bool { // Runs the pending loads that need to run from the graphics thread. auto Assets::RunPendingGraphicsLoads() -> bool { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); return RunPendingLoadList(&pending_loads_graphics_); } diff --git a/src/ballistica/base/assets/texture_asset.cc b/src/ballistica/base/assets/texture_asset.cc index f71b3af6..e64b0665 100644 --- a/src/ballistica/base/assets/texture_asset.cc +++ b/src/ballistica/base/assets/texture_asset.cc @@ -2,6 +2,7 @@ #include "ballistica/base/assets/texture_asset.h" +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/assets/texture_asset_preload_data.h" #include "ballistica/base/assets/texture_asset_renderer_data.h" #include "ballistica/base/graphics/graphics.h" @@ -425,7 +426,7 @@ void TextureAsset::DoPreload() { } void TextureAsset::DoLoad() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); assert(!renderer_data_.Exists()); renderer_data_ = g_base->graphics_server->renderer()->NewTextureData(*this); assert(renderer_data_.Exists()); @@ -441,7 +442,7 @@ void TextureAsset::DoLoad() { } void TextureAsset::DoUnload() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); assert(valid_); assert(renderer_data_.Exists()); renderer_data_.Clear(); diff --git a/src/ballistica/base/audio/audio_server.cc b/src/ballistica/base/audio/audio_server.cc index aef706c9..b446f686 100644 --- a/src/ballistica/base/audio/audio_server.cc +++ b/src/ballistica/base/audio/audio_server.cc @@ -199,8 +199,7 @@ void AudioServer::OnAppStartInThread() { } #endif // BA_RIFT_BUILD - ALCdevice* device; - device = alcOpenDevice(al_device_name); + auto* device = alcOpenDevice(al_device_name); if (!device) { FatalError( "No audio devices found. Do you have speakers/headphones/etc. " diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index da18bcec..5bedf8d5 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -468,15 +468,6 @@ auto BaseFeatureSet::InLogicThread() const -> bool { return false; } -auto BaseFeatureSet::InGraphicsThread() const -> bool { - // FIXME. - return g_core->InMainThread(); - // if (auto* loop = graphics_server->event_loop()) { - // return loop->ThreadIsCurrent(); - // } - // return false; -} - auto BaseFeatureSet::InAudioThread() const -> bool { if (auto* loop = audio_server->event_loop()) { return loop->ThreadIsCurrent(); diff --git a/src/ballistica/base/base.h b/src/ballistica/base/base.h index 723b764d..97484e3e 100644 --- a/src/ballistica/base/base.h +++ b/src/ballistica/base/base.h @@ -682,7 +682,6 @@ class BaseFeatureSet : public FeatureSetNativeComponent, auto InAssetsThread() const -> bool override; auto InLogicThread() const -> bool override; - auto InGraphicsThread() const -> bool override; auto InAudioThread() const -> bool override; auto InBGDynamicsThread() const -> bool override; auto InNetworkWriteThread() const -> bool override; diff --git a/src/ballistica/base/graphics/gl/framebuffer_object_gl.h b/src/ballistica/base/graphics/gl/framebuffer_object_gl.h index fc767c51..7742ddd1 100644 --- a/src/ballistica/base/graphics/gl/framebuffer_object_gl.h +++ b/src/ballistica/base/graphics/gl/framebuffer_object_gl.h @@ -5,6 +5,7 @@ #if BA_ENABLE_OPENGL +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/graphics_server.h" @@ -46,7 +47,7 @@ class RendererGL::FramebufferObjectGL : public Framebuffer { void Load(bool force_low_quality = false) { if (loaded_) return; - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; GLenum status; BA_DEBUG_CHECK_GL_ERROR; @@ -246,7 +247,7 @@ class RendererGL::FramebufferObjectGL : public Framebuffer { } void Unload() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!loaded_) return; // If our textures are currently bound as anything, clear that out. @@ -288,7 +289,7 @@ class RendererGL::FramebufferObjectGL : public Framebuffer { } void Bind() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); renderer_->BindFramebuffer(framebuffer_); // if (time(nullptr)%2 == 0) { // glDisable(GL_FRAMEBUFFER_SRGB); diff --git a/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h b/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h index 5fd6936c..50624eac 100644 --- a/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h +++ b/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h @@ -5,6 +5,7 @@ #if BA_ENABLE_OPENGL +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/gl/gl_sys.h" #include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/graphics_server.h" @@ -23,7 +24,7 @@ class RendererGL::MeshAssetDataGL : public MeshAssetRendererData { name_ = model.GetName(); #endif - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; // Create our vertex array to hold all this state. @@ -98,7 +99,7 @@ class RendererGL::MeshAssetDataGL : public MeshAssetRendererData { } ~MeshAssetDataGL() override { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; // Unbind if we're bound; otherwise if a new vao pops up with our same diff --git a/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h b/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h index eb29ceae..b27129e0 100644 --- a/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h +++ b/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h @@ -27,7 +27,7 @@ class RendererGL::MeshDataGL : public MeshRendererData { : renderer_(renderer), uses_secondary_data_(static_cast(flags & kUsesSecondaryBuffer)), uses_index_data_(static_cast(flags & kUsesIndexBuffer)) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; // Create our vertex array to hold all this state. @@ -85,7 +85,7 @@ class RendererGL::MeshDataGL : public MeshRendererData { } ~MeshDataGL() override { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Unbind if we're bound; otherwise we might prevent a new vao that // reuses our ID from binding. if (vao_ == renderer_->current_vertex_array_) { diff --git a/src/ballistica/base/graphics/gl/program/program_gl.h b/src/ballistica/base/graphics/gl/program/program_gl.h index e7fea6bc..7bf60b8a 100644 --- a/src/ballistica/base/graphics/gl/program/program_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_gl.h @@ -5,6 +5,7 @@ #if BA_ENABLE_OPENGL +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/graphics_server.h" @@ -18,7 +19,7 @@ class RendererGL::ShaderGL : public Object { } ShaderGL(GLenum type_in, const std::string& src_in) : type_(type_in) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; assert(type_ == GL_FRAGMENT_SHADER || type_ == GL_VERTEX_SHADER); shader_ = glCreateShader(type_); @@ -73,7 +74,7 @@ class RendererGL::ShaderGL : public Object { } ~ShaderGL() override { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!g_base->graphics_server->renderer_context_lost()) { glDeleteShader(shader_); BA_DEBUG_CHECK_GL_ERROR; @@ -127,7 +128,7 @@ class RendererGL::ProgramGL { renderer_(renderer), pflags_(pflags), name_(std::move(name)) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; program_ = glCreateProgram(); BA_PRECONDITION(program_); @@ -209,7 +210,7 @@ class RendererGL::ProgramGL { } virtual ~ProgramGL() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!g_base->graphics_server->renderer_context_lost()) { glDetachShader(program_, fragment_shader_->shader()); glDetachShader(program_, vertex_shader_->shader()); diff --git a/src/ballistica/base/graphics/gl/render_target_gl.h b/src/ballistica/base/graphics/gl/render_target_gl.h index 935fdd17..d5ddf747 100644 --- a/src/ballistica/base/graphics/gl/render_target_gl.h +++ b/src/ballistica/base/graphics/gl/render_target_gl.h @@ -23,7 +23,7 @@ class RendererGL::RenderTargetGL : public RenderTarget { void DrawBegin(bool must_clear_color, float clear_r, float clear_g, float clear_b, float clear_a) override { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; Bind(); @@ -94,7 +94,7 @@ class RendererGL::RenderTargetGL : public RenderTarget { // Screen constructor. explicit RenderTargetGL(RendererGL* renderer) : RenderTarget(Type::kScreen), renderer_(renderer) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); depth_ = true; // This will update our width/height values. @@ -106,7 +106,7 @@ class RendererGL::RenderTargetGL : public RenderTarget { bool linear_interp, bool depth, bool texture, bool depth_texture, bool high_quality, bool msaa, bool alpha) : RenderTarget(Type::kFramebuffer), renderer_(renderer) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; framebuffer_ = Object::New( renderer, width, height, linear_interp, depth, texture, depth_texture, diff --git a/src/ballistica/base/graphics/gl/renderer_gl.cc b/src/ballistica/base/graphics/gl/renderer_gl.cc index 9fce3a61..9238619d 100644 --- a/src/ballistica/base/graphics/gl/renderer_gl.cc +++ b/src/ballistica/base/graphics/gl/renderer_gl.cc @@ -87,7 +87,7 @@ bool RendererGL::is_extra_speedy_android_device_{}; #endif RendererGL::RendererGL() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (explicit_bool(BA_FORCE_CHECK_GL_ERRORS)) { ScreenMessage("GL ERROR CHECKS ENABLED"); @@ -175,7 +175,7 @@ static auto CheckGLExtension(const std::vector& exts, void RendererGL::CheckGLCapabilities_() { BA_DEBUG_CHECK_GL_ERROR; - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); draws_shields_funny_set_ = true; @@ -653,7 +653,7 @@ auto RendererGL::DebugGLGetInt(GLenum name) -> int { // This is probably inefficient so make sure we don't leave it on in // release builds. assert(g_buildconfig.debug_build()); - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Clear any error coming in; don't want to die for something that's not // ours. @@ -874,7 +874,7 @@ void RendererGL::InvalidateFramebuffer(bool color, bool depth, } RendererGL::~RendererGL() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); printf("FIXME: need to unload renderer on destroy.\n"); // Unload(); BA_DEBUG_CHECK_GL_ERROR; @@ -2610,7 +2610,7 @@ auto RendererGL::GetFunkyDepthIssue_() -> bool { #if BA_OSTYPE_ANDROID std::string RendererGL::GetAutoAndroidRes() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); const char* renderer = (const char*)glGetString(GL_RENDERER); @@ -2636,7 +2636,7 @@ std::string RendererGL::GetAutoAndroidRes() { #endif // BA_OSTYPE_ANDROID auto RendererGL::GetAutoTextureQuality() -> TextureQuality { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); TextureQuality qual{TextureQuality::kHigh}; @@ -2668,7 +2668,7 @@ auto RendererGL::GetAutoTextureQuality() -> TextureQuality { } auto RendererGL::GetAutoGraphicsQuality() -> GraphicsQuality { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); GraphicsQuality q{GraphicsQuality::kMedium}; #if BA_OSTYPE_ANDROID // lets be cheaper in VR mode since we draw twice.. @@ -2691,7 +2691,7 @@ auto RendererGL::GetAutoGraphicsQuality() -> GraphicsQuality { void RendererGL::RetainShader_(ProgramGL* p) { shaders_.emplace_back(p); } void RendererGL::Load() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); assert(!data_loaded_); assert(g_base->graphics_server->graphics_quality_set()); BA_DEBUG_CHECK_GL_ERROR; @@ -2920,7 +2920,7 @@ void RendererGL::PostLoad() { } void RendererGL::Unload() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; assert(data_loaded_); Renderer::Unload(); diff --git a/src/ballistica/base/graphics/gl/texture_data_gl.h b/src/ballistica/base/graphics/gl/texture_data_gl.h index eb889d3a..71090184 100644 --- a/src/ballistica/base/graphics/gl/texture_data_gl.h +++ b/src/ballistica/base/graphics/gl/texture_data_gl.h @@ -5,6 +5,7 @@ #if BA_ENABLE_OPENGL +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/assets/texture_asset_preload_data.h" #include "ballistica/base/assets/texture_asset_renderer_data.h" #include "ballistica/base/graphics/gl/renderer_gl.h" @@ -16,14 +17,14 @@ class RendererGL::TextureDataGL : public TextureAssetRendererData { public: TextureDataGL(const TextureAsset& texture_in, RendererGL* renderer_in) : tex_media_(&texture_in), texture_(0), renderer_(renderer_in) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; glGenTextures(1, &texture_); BA_DEBUG_CHECK_GL_ERROR; } ~TextureDataGL() override { - if (!g_base->InGraphicsThread()) { + if (!g_base->app_adapter->InGraphicsContext()) { Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread."); } else { // If we're currently bound as anything, clear that out (otherwise a @@ -46,7 +47,7 @@ class RendererGL::TextureDataGL : public TextureAssetRendererData { auto GetTexture() const -> GLuint { return texture_; } void Load() override { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); BA_DEBUG_CHECK_GL_ERROR; if (tex_media_->texture_type() == TextureType::k2D) { diff --git a/src/ballistica/base/graphics/graphics.cc b/src/ballistica/base/graphics/graphics.cc index 818653d3..56e0c7c1 100644 --- a/src/ballistica/base/graphics/graphics.cc +++ b/src/ballistica/base/graphics/graphics.cc @@ -1121,14 +1121,24 @@ void Graphics::BuildAndPushFrameDef() { // Store how much time this frame_def represents. auto display_time_microsecs = g_base->logic->display_time_microsecs(); + auto display_time_millisecs = display_time_microsecs / 1000; // Clamp a frame-def's elapsed time to 1/10th of a second even if it has // been longer than that since the last. Don't want things like // motion-blur to get out of control. - millisecs_t elapsed_microsecs = + microsecs_t elapsed_microsecs = std::min(microsecs_t{100000}, - display_time_microsecs - last_create_frame_def_time_); - last_create_frame_def_time_ = display_time_microsecs; + display_time_microsecs - last_create_frame_def_time_microsecs_); + last_create_frame_def_time_microsecs_ = display_time_microsecs; + + // We need to do a separate elapsed calculation for milliseconds. It would + // seem that we could just calc this based on our elapsed microseconds, + // but the problem is that at very high frame rates we wind up always + // rounding down to 0. + millisecs_t elapsed_millisecs = + std::min(millisecs_t{100}, + display_time_millisecs - last_create_frame_def_time_millisecs_); + last_create_frame_def_time_millisecs_ = display_time_millisecs; frame_def_count_++; @@ -1153,6 +1163,7 @@ void Graphics::BuildAndPushFrameDef() { frame_def->set_display_time_microsecs( g_base->logic->display_time_microsecs()); frame_def->set_display_time_elapsed_microsecs(elapsed_microsecs); + frame_def->set_display_time_elapsed_millisecs(elapsed_millisecs); frame_def->set_frame_number(frame_def_count_); frame_def->set_frame_number_filtered(frame_def_count_filtered_); diff --git a/src/ballistica/base/graphics/graphics.h b/src/ballistica/base/graphics/graphics.h index 199b4f7d..92e6c18f 100644 --- a/src/ballistica/base/graphics/graphics.h +++ b/src/ballistica/base/graphics/graphics.h @@ -375,7 +375,8 @@ class Graphics { std::list> clean_frame_commands_; std::vector mesh_data_creates_; std::vector mesh_data_destroys_; - millisecs_t last_create_frame_def_time_{}; + microsecs_t last_create_frame_def_time_microsecs_{}; + millisecs_t last_create_frame_def_time_millisecs_{}; Vector3f shadow_offset_{0.0f, 0.0f, 0.0f}; Vector2f shadow_scale_{1.0f, 1.0f}; Vector3f tint_{1.0f, 1.0f, 1.0f}; diff --git a/src/ballistica/base/graphics/graphics_server.cc b/src/ballistica/base/graphics/graphics_server.cc index ce47f3b2..04a7b484 100644 --- a/src/ballistica/base/graphics/graphics_server.cc +++ b/src/ballistica/base/graphics/graphics_server.cc @@ -22,11 +22,11 @@ namespace ballistica::base { -GraphicsServer::GraphicsServer() {} +GraphicsServer::GraphicsServer() = default; GraphicsServer::~GraphicsServer() = default; void GraphicsServer::SetRenderHold() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); render_hold_++; } @@ -46,7 +46,7 @@ void GraphicsServer::EnqueueFrameDef(FrameDef* framedef) { } auto GraphicsServer::TryRender() -> bool { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); bool success{}; @@ -75,7 +75,7 @@ auto GraphicsServer::TryRender() -> bool { } auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); millisecs_t app_time = g_core->GetAppTimeMillisecs(); if (!renderer_) { @@ -127,13 +127,13 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* { } void GraphicsServer::ApplyFrameDefSettings(FrameDef* frame_def) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); tv_border_ = frame_def->tv_border(); } // Runs any mesh updates contained in the frame-def. void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Run any mesh-data creates/destroys included with this frame_def. for (auto&& i : frame_def->mesh_data_creates()) { @@ -151,7 +151,7 @@ void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) { // Renders shadow passes and other common parts of a frame_def. void GraphicsServer::PreprocessRenderFrameDef(FrameDef* frame_def) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Now let the renderer do any preprocess passes (shadows, etc). assert(renderer_); @@ -179,7 +179,7 @@ void GraphicsServer::FinishRenderFrameDef(FrameDef* frame_def) { // Reload all media (for debugging/benchmarking purposes). void GraphicsServer::ReloadMedia_() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Immediately unload all renderer data here in this thread. if (renderer_) { @@ -205,7 +205,7 @@ void GraphicsServer::ReloadMedia_() { // Call when a renderer context has been lost. void GraphicsServer::ReloadLostRenderer() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { Log(LogLevel::kError, "No renderer on GraphicsServer::ReloadLostRenderer."); @@ -274,14 +274,14 @@ void GraphicsServer::SetNullGraphics() { } void GraphicsServer::set_renderer(Renderer* renderer) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); assert(!renderer_loaded_); assert(!renderer_); renderer_ = renderer; } void GraphicsServer::LoadRenderer() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { Log(LogLevel::kError, "LoadRenderer() called with no renderer present."); return; @@ -358,15 +358,14 @@ void GraphicsServer::LoadRenderer() { renderer_loaded_ = true; - // Set a render-hold so we ignore all frame_defs up until the point at - // which we receive the corresponding remove-hold. (At which point - // subsequent frame-defs will be be progress-bar frame_defs so we won't - // hitch if we actually render them.) + // Set an immediate render-hold so we ignore all frame_defs up until the + // point at which we receive the corresponding remove-hold. (At which + // point subsequent frame-defs will be be progress-bar frame_defs so we + // won't hitch if we actually render them.) SetRenderHold(); // Now tell the logic thread to kick off loads for everything, flip on - // progress bar drawing, and then tell the graphics thread to stop - // ignoring frame-defs. + // progress bar drawing, and then ship a remove-hold call back to us. g_base->logic->event_loop()->PushCall([this] { g_base->assets->MarkAllAssetsForLoad(); g_base->graphics->set_internal_components_inited(false); @@ -376,7 +375,7 @@ void GraphicsServer::LoadRenderer() { } void GraphicsServer::UnloadRenderer() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { Log(LogLevel::kError, "UnloadRenderer() called with no renderer present."); return; @@ -417,7 +416,7 @@ void GraphicsServer::CalcVirtualRes_(float* x, float* y) { } void GraphicsServer::UpdateVirtualScreenRes_() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // In vr mode our virtual res is independent of our screen size. // (since it gets drawn to an overlay) @@ -432,7 +431,7 @@ void GraphicsServer::UpdateVirtualScreenRes_() { } void GraphicsServer::SetScreenResolution(float h, float v) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Ignore redundant sets. if (res_x_ == h && res_y_ == v) { @@ -477,50 +476,9 @@ void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) { } } -// void GraphicsServer::HandleFullscreenToggling(bool do_set_existing_fs, -// bool do_toggle_fs, -// bool fullscreen) { -// if (do_set_existing_fs) { -// #if BA_SDL2_BUILD -// bool rift_vr_mode = false; -// #if BA_RIFT_BUILD -// if (g_core->IsVRMode()) { -// rift_vr_mode = true; -// } -// #endif // BA_RIFT_BUILD -// if (explicit_bool(!rift_vr_mode)) { -// #if BA_OSTYPE_IOS_TVOS -// set_fullscreen_enabled(true); - -// #else // BA_OSTYPE_IOS_TVOS -// auto* app_adapter_sdl = AppAdapterSDL::Get(); -// uint32_t fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP; -// SDL_SetWindowFullscreen(app_adapter_sdl->sdl_window_, -// fullscreen ? fullscreen_flag : 0); - -// // Ideally this should be driven by OS events and not just explicitly -// by -// // us (so, for instance, if someone presses fullscreen on mac we'd know -// // we've gone into fullscreen). But this works for now. -// set_fullscreen_enabled(fullscreen); -// #endif // BA_OSTYPE_IOS_TVOS -// } -// #endif // BA_SDL2_BUILD -// } else if (do_toggle_fs) { -// // If we're doing a fullscreen-toggle, we need to do it after coming out -// of -// // sync mode (because the toggle triggers sync-mode itself). -// #if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_XCODE_NEW_PROJECT -// #if BA_ENABLE_OPENGL -// SDL_WM_ToggleFullScreen(gl_context_->sdl_screen_surface()); -// #endif -// #endif // macos && xcode_build -// } -// } - void GraphicsServer::SetTextureCompressionTypes( const std::list& types) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); texture_compression_types_ = 0; for (auto&& i : types) { texture_compression_types_ |= (0x01u << (static_cast(i))); @@ -531,7 +489,7 @@ void GraphicsServer::SetTextureCompressionTypes( void GraphicsServer::SetOrthoProjection(float left, float right, float bottom, float top, float nearval, float farval) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); float tx = -((right + left) / (right - left)); float ty = -((top + bottom) / (top - bottom)); float tz = -((farval + nearval) / (farval - nearval)); @@ -562,7 +520,7 @@ void GraphicsServer::SetOrthoProjection(float left, float right, float bottom, void GraphicsServer::SetCamera(const Vector3f& eye, const Vector3f& target, const Vector3f& up_vector) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // Reset the modelview stack. model_view_stack_.clear(); @@ -604,7 +562,7 @@ void GraphicsServer::SetCamera(const Vector3f& eye, const Vector3f& target, } void GraphicsServer::UpdateCamOrientMatrix_() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (cam_orient_matrix_dirty_) { cam_orient_matrix_ = kMatrix44fIdentity; Vector3f to_cam = cam_pos_ - cam_target_; @@ -631,12 +589,12 @@ void GraphicsServer::UpdateCamOrientMatrix_() { } void GraphicsServer::PushReloadMediaCall() { - g_base->app_adapter->PushMainThreadCall([this] { ReloadMedia_(); }); + g_base->app_adapter->PushGraphicsContextCall([this] { ReloadMedia_(); }); } void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { - g_base->app_adapter->PushMainThreadCall([this, pixel_scale] { - assert(g_base->InGraphicsThread()); + g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] { + assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { return; } @@ -646,8 +604,8 @@ void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { void GraphicsServer::PushComponentUnloadCall( const std::vector*>& components) { - g_base->app_adapter->PushMainThreadCall([components] { - assert(g_base->InGraphicsThread()); + g_base->app_adapter->PushGraphicsContextCall([components] { + assert(g_base->app_adapter->InGraphicsContext()); // Unload the components. for (auto&& i : components) { (**i).Unload(); @@ -662,8 +620,8 @@ void GraphicsServer::PushComponentUnloadCall( } void GraphicsServer::PushRemoveRenderHoldCall() { - g_base->app_adapter->PushMainThreadCall([this] { - assert(g_base->InGraphicsThread()); + g_base->app_adapter->PushGraphicsContextCall([this] { + assert(g_base->app_adapter->InGraphicsContext()); assert(render_hold_); render_hold_--; if (render_hold_ < 0) { @@ -673,4 +631,8 @@ void GraphicsServer::PushRemoveRenderHoldCall() { }); } +auto GraphicsServer::InGraphicsContext_() const -> bool { + return g_base->app_adapter->InGraphicsContext(); +} + } // namespace ballistica::base diff --git a/src/ballistica/base/graphics/graphics_server.h b/src/ballistica/base/graphics/graphics_server.h index 92c4a442..ff36d534 100644 --- a/src/ballistica/base/graphics/graphics_server.h +++ b/src/ballistica/base/graphics/graphics_server.h @@ -226,27 +226,27 @@ class GraphicsServer { } auto screen_pixel_width() const -> float { - assert(g_base->InGraphicsThread()); + assert(InGraphicsContext_()); return res_x_; } auto screen_pixel_height() const -> float { - assert(g_base->InGraphicsThread()); + assert(InGraphicsContext_()); return res_y_; } auto screen_virtual_width() const -> float { - assert(g_base->InGraphicsThread()); + assert(InGraphicsContext_()); return res_x_virtual_; } auto screen_virtual_height() const -> float { - assert(g_base->InGraphicsThread()); + assert(InGraphicsContext_()); return res_y_virtual_; } auto tv_border() const { - assert(g_base->InGraphicsThread()); + assert(InGraphicsContext_()); return tv_border_; } @@ -308,6 +308,9 @@ class GraphicsServer { // bool fullscreen); private: + // So we don't have to include app_adapter.h here for asserts. + auto InGraphicsContext_() const -> bool; + // Return the next frame_def to be rendered, waiting for it to arrive if // necessary. this can return nullptr if no frame_defs come in within a // reasonable amount of time. a frame_def here *must* be rendered and diff --git a/src/ballistica/base/graphics/mesh/mesh_data.cc b/src/ballistica/base/graphics/mesh/mesh_data.cc index 1d3f9c23..bac31391 100644 --- a/src/ballistica/base/graphics/mesh/mesh_data.cc +++ b/src/ballistica/base/graphics/mesh/mesh_data.cc @@ -2,19 +2,20 @@ #include "ballistica/base/graphics/mesh/mesh_data.h" +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/renderer/renderer.h" namespace ballistica::base { void MeshData::Load(Renderer* renderer) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_data_) { renderer_data_ = renderer->NewMeshData(type(), draw_type()); } } void MeshData::Unload(Renderer* renderer) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (renderer_data_) { renderer->DeleteMeshData(renderer_data_, type()); renderer_data_ = nullptr; diff --git a/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc b/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc index e48fc436..1e243551 100644 --- a/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc +++ b/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc @@ -6,18 +6,6 @@ namespace ballistica::base { -auto NinePatchMesh::BorderForRadius(float corner_radius, - float matching_dimension, - float other_dimension) -> float { - // Limit the radius to no more than half the shortest side. - corner_radius = std::min( - corner_radius, std::min(matching_dimension, other_dimension) * 0.5f); - if (corner_radius < 0.0001f) { - return 0.0f; - } - return corner_radius / matching_dimension; -} - NinePatchMesh::NinePatchMesh(float x, float y, float z, float width, float height, float border_left, float border_bottom, float border_right, @@ -35,6 +23,7 @@ NinePatchMesh::NinePatchMesh(float x, float y, float z, float width, VertexSimpleFull verts[16]; // 4 vertical * 4 horizontal slices. uint16_t indices[54]; // 9 patches * 2 triangles * 3 verts. + // Vertical slices. float y0 = y; float y1 = y + border_bottom * height; float y2 = y + (1.0 - border_top) * height; @@ -44,6 +33,7 @@ NinePatchMesh::NinePatchMesh(float x, float y, float z, float width, auto v2 = 32767; auto v3 = 0; + // Horizontal slices. float x0 = x; float x1 = x + border_left * width; float x2 = x + (1.0 - border_right) * width; @@ -53,9 +43,7 @@ NinePatchMesh::NinePatchMesh(float x, float y, float z, float width, auto u2 = 32767; auto u3 = 65535; - int icount{}; - - // Assign all 16 positions and uvs. + // Fill out all 16 verts. for (int yi = 0; yi < 4; ++yi) { for (int xi = 0; xi < 4; ++xi) { VertexSimpleFull* v = verts + yi * 4 + xi; @@ -108,18 +96,17 @@ NinePatchMesh::NinePatchMesh(float x, float y, float z, float width, } // Now add triangle draws for any of the 9 patches with width/height > 0. + int icount{}; for (int yi = 0; yi < 3; ++yi) { for (int xi = 0; xi < 3; ++xi) { VertexSimpleFull* v = verts + yi * 4 + xi; VertexSimpleFull* vright = v + 1; VertexSimpleFull* vtop = v + 4; - if (vright->position[0] > v->position[0] && vtop->position[1] > v->position[1]) { indices[icount++] = yi * 4 + xi; indices[icount++] = yi * 4 + xi + 1; indices[icount++] = (yi + 1) * 4 + xi + 1; - indices[icount++] = yi * 4 + xi; indices[icount++] = (yi + 1) * 4 + xi + 1; indices[icount++] = (yi + 1) * 4 + xi; diff --git a/src/ballistica/base/graphics/mesh/nine_patch_mesh.h b/src/ballistica/base/graphics/mesh/nine_patch_mesh.h index d71cf49f..fad02593 100644 --- a/src/ballistica/base/graphics/mesh/nine_patch_mesh.h +++ b/src/ballistica/base/graphics/mesh/nine_patch_mesh.h @@ -17,9 +17,19 @@ class NinePatchMesh : public MeshIndexedSimpleFull { float border_top); /// Calculate a border value for a NinePatchMesh based on dimensions and a - /// desired max corner radius. + /// desired max corner radius. For calculating left or right borders, + /// matching_dimension should be width and other_dimension should be + /// height. For top or bottom borders it is the opposite. static auto BorderForRadius(float corner_radius, float matching_dimension, - float other_dimension) -> float; + float other_dimension) -> float { + // Limit the radius to no more than half the shortest side. + corner_radius = std::min( + corner_radius, std::min(matching_dimension, other_dimension) * 0.5f); + if (matching_dimension <= 0.0f) { + return 0.0f; + } + return corner_radius / matching_dimension; + } }; } // namespace ballistica::base diff --git a/src/ballistica/base/graphics/renderer/render_pass.cc b/src/ballistica/base/graphics/renderer/render_pass.cc index b57d7a8c..966dbbda 100644 --- a/src/ballistica/base/graphics/renderer/render_pass.cc +++ b/src/ballistica/base/graphics/renderer/render_pass.cc @@ -2,6 +2,7 @@ #include "ballistica/base/graphics/renderer/render_pass.h" +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/renderer/renderer.h" @@ -36,7 +37,7 @@ RenderPass::RenderPass(RenderPass::Type type_in, FrameDef* frame_def_in) RenderPass::~RenderPass() = default; void RenderPass::Render(RenderTarget* render_target, bool transparent) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); if (explicit_bool(!DRAW_TRANSPARENT) && transparent) { return; @@ -460,7 +461,7 @@ void RenderPass::Reset() { } void RenderPass::SetFrustum(float near_val, float far_val) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // If we're using fov-tangents: if (cam_use_fov_tangents_) { float l = near_val * cam_fov_l_tan_; diff --git a/src/ballistica/base/graphics/renderer/render_target.cc b/src/ballistica/base/graphics/renderer/render_target.cc index a54ff1c3..10493961 100644 --- a/src/ballistica/base/graphics/renderer/render_target.cc +++ b/src/ballistica/base/graphics/renderer/render_target.cc @@ -2,12 +2,13 @@ #include "ballistica/base/graphics/renderer/render_target.h" +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/graphics_server.h" namespace ballistica::base { RenderTarget::RenderTarget(Type type) : type_(type) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); } RenderTarget::~RenderTarget() = default; diff --git a/src/ballistica/base/graphics/renderer/renderer.cc b/src/ballistica/base/graphics/renderer/renderer.cc index 1539550b..88f36c98 100644 --- a/src/ballistica/base/graphics/renderer/renderer.cc +++ b/src/ballistica/base/graphics/renderer/renderer.cc @@ -2,14 +2,10 @@ #include "ballistica/base/graphics/renderer/renderer.h" +#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/graphics/graphics_server.h" #include "ballistica/core/core.h" -// FIXME: Clear out conditional stuff. -// #if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD -// #include "ballistica/core/platform/support/min_sdl.h" -// #endif - #if BA_VR_BUILD #include "ballistica/base/graphics/graphics_vr.h" #endif @@ -35,7 +31,7 @@ Renderer::~Renderer() { } void Renderer::PreprocessFrameDef(FrameDef* frame_def) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // If this frame_def was made in a different quality mode than we're // currently in, don't attempt to render it. @@ -96,7 +92,7 @@ void Renderer::PreprocessFrameDef(FrameDef* frame_def) { // actually render one of these frame_def suckers... // (called within the graphics thread) void Renderer::RenderFrameDef(FrameDef* frame_def) { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // If preprocess decided not to render this. if (!frame_def->rendering()) return; @@ -756,7 +752,7 @@ void Renderer::UpdateDOFParams(FrameDef* frame_def) { } void Renderer::OnScreenSizeChange() { - assert(g_base->InGraphicsThread()); + assert(g_base->app_adapter->InGraphicsContext()); // We can actually get these events at times when we don't have a valid // gl context, so instead of doing any GL work here let's just make a note to diff --git a/src/ballistica/base/graphics/support/frame_def.h b/src/ballistica/base/graphics/support/frame_def.h index 77999caa..41341028 100644 --- a/src/ballistica/base/graphics/support/frame_def.h +++ b/src/ballistica/base/graphics/support/frame_def.h @@ -75,7 +75,11 @@ class FrameDef { // How much display time does this frame-def represent. auto display_time_elapsed_millisecs() const -> millisecs_t { - return display_time_elapsed_microsecs_ / 1000; + return display_time_elapsed_millisecs_; + } + + auto display_time_elapsed_microsecs() const -> microsecs_t { + return display_time_elapsed_microsecs_; } auto quality() const -> GraphicsQuality { return quality_; } @@ -135,6 +139,9 @@ class FrameDef { void set_display_time_elapsed_microsecs(microsecs_t val) { display_time_elapsed_microsecs_ = val; } + void set_display_time_elapsed_millisecs(millisecs_t val) { + display_time_elapsed_millisecs_ = val; + } // void set_app_time_millisecs(millisecs_t val) { app_time_millisecs_ = val; } void set_app_time_microsecs(microsecs_t val) { app_time_microsecs_ = val; } @@ -242,6 +249,7 @@ class FrameDef { microsecs_t app_time_microsecs_{}; microsecs_t display_time_microsecs_{}; microsecs_t display_time_elapsed_microsecs_{}; + microsecs_t display_time_elapsed_millisecs_{}; int64_t frame_number_{}; int64_t frame_number_filtered_{}; Vector3f shadow_offset_{0.0f, 0.0f, 0.0f}; diff --git a/src/ballistica/core/support/base_soft.h b/src/ballistica/core/support/base_soft.h index ca435aa4..4c7a7ac8 100644 --- a/src/ballistica/core/support/base_soft.h +++ b/src/ballistica/core/support/base_soft.h @@ -18,10 +18,8 @@ class BaseSoftInterface { virtual void StartApp() = 0; virtual auto AppManagesMainThreadEventLoop() -> bool = 0; virtual void RunAppToCompletion() = 0; - // virtual void PrimeAppMainThreadEventPump() = 0; virtual auto InAssetsThread() const -> bool = 0; virtual auto InLogicThread() const -> bool = 0; - virtual auto InGraphicsThread() const -> bool = 0; virtual auto InAudioThread() const -> bool = 0; virtual auto InBGDynamicsThread() const -> bool = 0; virtual auto InNetworkWriteThread() const -> bool = 0; diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index c42df09e..71b6cc9e 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // These are set automatically via script; don't modify them here. -const int kEngineBuildNumber = 21397; +const int kEngineBuildNumber = 21401; const char* kEngineVersion = "1.7.28"; const int kEngineApiVersion = 8; diff --git a/src/tools/pcommandbatch/pcommandbatch.c b/src/tools/pcommandbatch/pcommandbatch.c index 2cf91218..28fbb5c3 100644 --- a/src/tools/pcommandbatch/pcommandbatch.c +++ b/src/tools/pcommandbatch/pcommandbatch.c @@ -412,6 +412,41 @@ int calc_paths_(struct Context_* ctx) { return 0; } +int color_enabled() { + // This logic here should line up with how the 'color_enabled' val in + // efro.terminal is calculated. + + // Allow explict enabling/disabling via this env var. + const char* env = getenv("EFRO_TERMCOLORS"); + if (env && !strcmp(env, "1")) { + return 1; + } + if (env && !strcmp(env, "0")) { + return 0; + } + + env = getenv("TERM"); + + // If TERM is unset, don't attempt color (this is currently the case + // in xcode). + if (!env) { + return 0; + } + + // A common way to say the terminal can't do fancy stuff like color. + if (env && !(strcmp(env, "dumb"))) { + return 0; + } + + // If our stdout is not attached to a terminal, go with no-color. + if (!isatty(1)) { + return 0; + } + + // We seem to be a terminal with color support; let's do it! + return 1; +} + int send_command_(struct Context_* ctx, int argc, char** argv) { // Build a json array of our args. cJSON* req = cJSON_CreateObject(); @@ -420,10 +455,8 @@ int send_command_(struct Context_* ctx, int argc, char** argv) { cJSON_AddItemToArray(array, cJSON_CreateString(argv[i])); } cJSON_AddItemToObject(req, "a", array); - cJSON_AddItemToObject(req, "t", - isatty(1) ? cJSON_CreateTrue() : cJSON_CreateFalse()); - cJSON_AddItemToObject(req, "t", - isatty(1) ? cJSON_CreateTrue() : cJSON_CreateFalse()); + cJSON_AddItemToObject( + req, "c", color_enabled() ? cJSON_CreateTrue() : cJSON_CreateFalse()); char* json_out = cJSON_Print(req); // Send our command. @@ -466,7 +499,6 @@ int handle_response_(const struct Context_* ctx) { return -1; } - cJSON* result_dict = cJSON_Parse(inbuf); if (!result_dict) { @@ -474,7 +506,7 @@ int handle_response_(const struct Context_* ctx) { stderr, "Error: pcommandbatch client %s_%d (pid %d): failed to parse result " "value: %s\n", - ctx->instance_prefix, ctx->instance_num, ctx->pid, inbuf); + ctx->instance_prefix, ctx->instance_num, ctx->pid, inbuf); free(inbuf); return -1; } else { diff --git a/tools/efro/terminal.py b/tools/efro/terminal.py index 666adb32..7b84c658 100644 --- a/tools/efro/terminal.py +++ b/tools/efro/terminal.py @@ -71,12 +71,19 @@ def _default_color_enabled() -> bool: """Return whether we enable ANSI color codes by default.""" import platform - # If we're not attached to a terminal, go with no-color. + # If our stdout is not attached to a terminal, go with no-color. if not sys.__stdout__.isatty(): return False - # Another common way to say the terminal can't do fancy stuff like color: - if os.environ.get('TERM') == 'dumb': + termenv = os.environ.get('TERM') + + # If TERM is unset, don't attempt color (this is currently the case + # in xcode). + if termenv is None: + return False + + # A common way to say the terminal can't do fancy stuff like color. + if termenv == 'dumb': return False # On windows, try to enable ANSI color mode. diff --git a/tools/efrotools/pcommandbatch.py b/tools/efrotools/pcommandbatch.py index 88ca4076..51b727d1 100644 --- a/tools/efrotools/pcommandbatch.py +++ b/tools/efrotools/pcommandbatch.py @@ -331,8 +331,8 @@ class Server: argv: list[str] = reqdata['a'] assert isinstance(argv, list) assert all(isinstance(i, str) for i in argv) - isatty: bool = reqdata['t'] - assert isinstance(isatty, bool) + color_enabled: bool = reqdata['c'] + assert isinstance(color_enabled, bool) print( f'pcommandbatch server {self._instance} (pid {self._pid})' @@ -345,7 +345,9 @@ class Server: # client. ideally should expand the client-side logic to # exactly match what efro.terminal.Clr does locally. clr: type[efro.terminal.ClrBase] = ( - efro.terminal.ClrAlways if isatty else efro.terminal.ClrNever + efro.terminal.ClrAlways + if color_enabled + else efro.terminal.ClrNever ) try: