fixed an issue that could cause jittery camera motion at extremely high frame rates

This commit is contained in:
Eric 2023-09-29 17:08:48 -07:00
parent 1df74d75d5
commit ef2900b535
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
38 changed files with 334 additions and 361 deletions

88
.efrocachemap generated
View File

@ -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",

View File

@ -2974,6 +2974,7 @@
<w>templatefs</w>
<w>tenum</w>
<w>termcolors</w>
<w>termenv</w>
<w>termios</w>
<w>testbuffer</w>
<w>testbuild</w>

View File

@ -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

View File

@ -1754,6 +1754,7 @@
<w>templatefs</w>
<w>tempvec</w>
<w>tenum</w>
<w>termenv</w>
<w>testbuild</w>
<w>testclinic</w>
<w>testint</w>

View File

@ -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'

View File

@ -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

View File

@ -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 <typename F>
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_();

View File

@ -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.

View File

@ -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<GLContext> gl_context_;

View File

@ -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();

View File

@ -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_);
}

View File

@ -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();

View File

@ -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. "

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -27,7 +27,7 @@ class RendererGL::MeshDataGL : public MeshRendererData {
: renderer_(renderer),
uses_secondary_data_(static_cast<bool>(flags & kUsesSecondaryBuffer)),
uses_index_data_(static_cast<bool>(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_) {

View File

@ -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());

View File

@ -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<FramebufferObjectGL>(
renderer, width, height, linear_interp, depth, texture, depth_texture,

View File

@ -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<std::string>& 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();

View File

@ -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) {

View File

@ -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_);

View File

@ -375,7 +375,8 @@ class Graphics {
std::list<Object::Ref<PythonContextCall>> clean_frame_commands_;
std::vector<MeshData*> mesh_data_creates_;
std::vector<MeshData*> 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};

View File

@ -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<TextureCompressionType>& 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<uint32_t>(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<Object::Ref<Asset>*>& 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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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_;

View File

@ -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;

View File

@ -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

View File

@ -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};

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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.

View File

@ -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: