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/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "3d62f18e70a8421820ed2ae8527d7d14", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "f6aa1723e7bee0328bcd8de8eaf41844",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "3241035738b539e94cf719674953e8fb", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "6b8d5b78eae91ce978a21766f2f08829",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "2229f735f006bef70ad1d2c08ae97fec", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "3691b9d78d2b8251fc854121ed274fa7",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "8895e3a5b262aef6102c16029359a587", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "0f96bd7efe76c573c52cc391824a5bdd",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "c94700f8b6ad1ce6b5574372a289d6da", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "62a3f4260790c6e4e648380985b92c79",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "8e0b9a000dab93c61bd58180a1eb69db", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4800d86542ac3db78c23984002fa4f63",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "d7c2c596d67d96386171cae1489d3ff8", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "64a094f2b2c8f82b3bb9c33077192f00",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "f492626b4a61c4550f3a01bc8d8abd53", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "1f33c24237a6f3fc9ba5b04c2591d9c2",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "fae843a11f1f84a2bc4a266e5723adfd", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "bd832c2b4071ad0531324190ab394d3a",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "d2724a5a94267a0d7546c8cf648e729c", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "fca25dc6756546cd5166c836f74b9ff4",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "02df04e605addd02bcfcfe64e01109e3", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "3cb2ec9e30b8b6d52ab08aaf31cdcf9e",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "beaac64be0ae8023cce5ec0a1d4306f0", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "d4305ad198b3e9e26edc7bada23d2b73",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "f1d3c2985d5f0760c72ecded41d91398", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "b784af9a46351b20f03a1096d13e0243",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "271b5fc80696ba6d67d02a089358b353", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "561a56987aced8b326db9af825de90a5",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "22ddbd257824f055627c16d1be1e575a", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "0eee318bf2f64a15972bde46a33427d8",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "b6de42bf834a0b9cc25c6433d49d6dc2", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "4464bd01ba4df385634b248a68633c4d",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "febb196acecc041b07d0ad06430ec2d5", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "f76d925131181ed178baf38a5ffd15e0",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "fafe18465a2dc39e6a3e2abae0557784", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "9228141043bb88a32adc1c7a406dfbcc",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "bbab03393707e902053429ce38f9986d", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "413169acc3e3feaf17543be1f6e242ee",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "d68af9931e2adfac40e8bb147ba0383b", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e73509f5d2bc32ffb81800bf31f7e44b",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "f3d305e647a7f77dd70a48f615cfd750", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "6ce4983e76e1cc2d2803fe306d08ad58",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "931ce8eab9859d20ad86c47d196ba62c", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4ea0cf78901f994215f215aebb0af1dc",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "f3d305e647a7f77dd70a48f615cfd750", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "6ce4983e76e1cc2d2803fe306d08ad58",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "931ce8eab9859d20ad86c47d196ba62c", "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "4ea0cf78901f994215f215aebb0af1dc",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "e80b5f536b30a23fe107852a0c2f7536", "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "87f651fed518cfa5ee34f820c49af72b",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "076df48013d64bc07aa59001819f8583", "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "5f28547e33fa30904dc989a39ac2d127",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "e80b5f536b30a23fe107852a0c2f7536", "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "87f651fed518cfa5ee34f820c49af72b",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "076df48013d64bc07aa59001819f8583", "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "5f28547e33fa30904dc989a39ac2d127",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "fcfdeb63ced9156995cf1b08ae5c861f", "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "1168b2ba42e7120d9321a900c5712cf0",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d82ad28301dc8e5b0ca98cf1da5d907c", "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d23c56d85e8ee9a07c9245e548daa39c",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "fcfdeb63ced9156995cf1b08ae5c861f", "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "1168b2ba42e7120d9321a900c5712cf0",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d82ad28301dc8e5b0ca98cf1da5d907c", "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d23c56d85e8ee9a07c9245e548daa39c",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "6db0247bf985f9d8c7ed85a5e5508a8b", "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "0c46358e1af4384af36367a3c0768605",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "097e17c460bf798edf61303789860596", "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "de83e1b384abb333f457f2c9646f810f",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "14df40bc07bdde8184843d16d5ba7798", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "0149ef8dae7720330416846919a834e7",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "097e17c460bf798edf61303789860596", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "de83e1b384abb333f457f2c9646f810f",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "044195de59694aa90afb2db2a500c383", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "e3ad15ecc9656a1a268582b14336bed7",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "5669f2204fa5bf0bfac57c249dafc1d6", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "fce3fd45ed78f84592ef914450817778",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "820b134e62b814066c40c9439587c945", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "feeb14560de0fa18d8b7654aa43534ea",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "76406fb75d1bc422accf3b8ad69d48e5", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "7896041f53b6f076d032388d661413b0",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "6b25bae7fd9eabaad90a2c3103bb6948", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a5b84aaf028c8b13d55d2d99f3853f5d",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "55bd741c876af2a7d9633dbc6a1bff45", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "4a8387851909f5cedabb5972b397a7b1",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "188433602c168f76f83183b052852e43", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "53fa53d605a009a6ab592e30d45629a8",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "1bf745b621bae741df16d9b604342e4b", "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/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318", "src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "c81b2b1f3a14b4cd20a7b93416fe893a", "src/ballistica/base/mgen/pyembed/binding_base.inc": "c81b2b1f3a14b4cd20a7b93416fe893a",

View File

@ -2974,6 +2974,7 @@
<w>templatefs</w> <w>templatefs</w>
<w>tenum</w> <w>tenum</w>
<w>termcolors</w> <w>termcolors</w>
<w>termenv</w>
<w>termios</w> <w>termios</w>
<w>testbuffer</w> <w>testbuffer</w>
<w>testbuild</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, - Massively cleaned up code related to rendering and window systems (OpenGL,
SDL, etc). This code had been growing into a nasty tangle for 15 years SDL, etc). This code had been growing into a nasty tangle for 15 years
@ -74,10 +74,10 @@
- Worked to improve sanity checking on C++ RenderComponents in debug builds to - 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 make it easier to use and avoid sending broken commands to the renderer. Some
specifics follow. specifics follow.
- RenderComponents no longer need an explicit Submit() at the end; if one goes - RenderComponents (C++ layer) no longer need an explicit Submit() at the end;
out of scope not in the submitted state it will implicitly run a submit. if one goes out of scope not in the submitted state it will implicitly run a
Hopefully this will encourage concise code where RenderComponents are defined submit. Hopefully this will encourage concise code where RenderComponents are
in tight scopes. defined in tight scopes.
- RenderComponents now have a ScopedTransform() call which can be used to push - 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 and pop the transform stack based on C++ scoping instead of the old
PushTransform/PopTransform. This should make it harder to accidentally break PushTransform/PopTransform. This should make it harder to accidentally break

View File

@ -1754,6 +1754,7 @@
<w>templatefs</w> <w>templatefs</w>
<w>tempvec</w> <w>tempvec</w>
<w>tenum</w> <w>tenum</w>
<w>termenv</w>
<w>testbuild</w> <w>testbuild</w>
<w>testclinic</w> <w>testclinic</w>
<w>testint</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 # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21397 TARGET_BALLISTICA_BUILD = 21401
TARGET_BALLISTICA_VERSION = '1.7.28' 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::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
void AppAdapter::DoApplyAppConfig() { 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_() { void AppAdapter::OnAppPause_() {
assert(g_core->InMainThread()); assert(g_core->InMainThread());
@ -188,7 +119,6 @@ void AppAdapter::OnAppPause_() {
void AppAdapter::OnAppResume_() { void AppAdapter::OnAppResume_() {
assert(g_core->InMainThread()); assert(g_core->InMainThread());
// last_app_resume_time_ = g_core->GetAppTimeMillisecs();
// Spin all event-loops back up. // Spin all event-loops back up.
EventLoop::SetEventLoopsPaused(false); EventLoop::SetEventLoopsPaused(false);
@ -234,6 +164,7 @@ void AppAdapter::PauseApp() {
"PauseApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs())); "PauseApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs()));
// assert(!app_pause_requested_); // assert(!app_pause_requested_);
// app_pause_requested_ = true; // app_pause_requested_ = true;
app_paused_ = true;
OnAppPause_(); OnAppPause_();
// UpdatePauseResume_(); // UpdatePauseResume_();
@ -285,6 +216,7 @@ void AppAdapter::ResumeApp() {
// assert(app_pause_requested_); // assert(app_pause_requested_);
// app_pause_requested_ = false; // app_pause_requested_ = false;
// UpdatePauseResume_(); // UpdatePauseResume_();
app_paused_ = false;
OnAppResume_(); OnAppResume_();
if (g_buildconfig.debug_build()) { if (g_buildconfig.debug_build()) {
Log(LogLevel::kDebug, Log(LogLevel::kDebug,
@ -309,4 +241,12 @@ auto AppAdapter::SupportsVSync() -> bool const { return false; }
auto AppAdapter::SupportsMaxFPS() -> 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 } // namespace ballistica::base

View File

@ -52,6 +52,17 @@ class AppAdapter {
DoPushMainThreadRunnable(NewLambdaRunnableUnmanaged(lambda)); 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 /// Put the app into a paused state. Should be called from the main
/// thread. Pauses work, closes network sockets, etc. May correspond to /// thread. Pauses work, closes network sockets, etc. May correspond to
/// being backgrounded on mobile, being minimized on desktop, etc. It is /// 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. /// thread should call its RunAndLogErrors() method and then delete it.
virtual void DoPushMainThreadRunnable(Runnable* runnable) = 0; 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: private:
void OnAppPause_(); void OnAppPause_();
void OnAppResume_(); void OnAppResume_();

View File

@ -104,8 +104,6 @@ void AppAdapterSDL::DoApplyAppConfig() {
void AppAdapterSDL::RunMainThreadEventLoopToCompletion() { void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
assert(g_core->InMainThread()); assert(g_core->InMainThread());
// float smoothed_fps{};
// float fps_smoothing{0.1f};
while (!done_) { while (!done_) {
microsecs_t cycle_start_time = g_core->GetAppTimeMicrosecs(); microsecs_t cycle_start_time = g_core->GetAppTimeMicrosecs();
@ -120,67 +118,69 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
SDL_GL_SwapWindow(sdl_window_); SDL_GL_SwapWindow(sdl_window_);
} }
// Sleep until we should start our next cycle (based on // In some cases, sleep until we should start our next cycle (depending
// max-frame-rate or other factors). // on max-frame-rate or other factors).
SleepUntilNextEventCycle_(cycle_start_time);
// 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);
} }
} }
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) { void AppAdapterSDL::DoPushMainThreadRunnable(Runnable* runnable) {
assert(sdl_runnable_event_id_ != 0); assert(sdl_runnable_event_id_ != 0);
SDL_Event event; SDL_Event event;
@ -454,7 +454,7 @@ void AppAdapterSDL::SetScreen_(
bool fullscreen, int max_fps, VSyncRequest vsync_requested, bool fullscreen, int max_fps, VSyncRequest vsync_requested,
TextureQualityRequest texture_quality_requested, TextureQualityRequest texture_quality_requested,
GraphicsQualityRequest graphics_quality_requested) { GraphicsQualityRequest graphics_quality_requested) {
assert(g_base->InGraphicsThread()); assert(InGraphicsContext());
assert(!g_core->HeadlessMode()); assert(!g_core->HeadlessMode());
// If we know what we support, filter our request types to what is // If we know what we support, filter our request types to what is
@ -554,7 +554,7 @@ void AppAdapterSDL::SetScreen_(
void AppAdapterSDL::ReloadRenderer_( void AppAdapterSDL::ReloadRenderer_(
bool fullscreen, GraphicsQualityRequest graphics_quality_requested, bool fullscreen, GraphicsQualityRequest graphics_quality_requested,
TextureQualityRequest texture_quality_requested) { TextureQualityRequest texture_quality_requested) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
auto* gs = g_base->graphics_server; auto* gs = g_base->graphics_server;
@ -635,7 +635,7 @@ void AppAdapterSDL::ReloadRenderer_(
} }
void AppAdapterSDL::UpdateScreenSizes_() { void AppAdapterSDL::UpdateScreenSizes_() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// Grab logical window dimensions (points?). // Grab logical window dimensions (points?).
// This is the coordinate space SDL's events deal in. // 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 UpdateAutoVSync_(int diff);
void AddSDLInputDevice_(JoystickInput* input, int index); void AddSDLInputDevice_(JoystickInput* input, int index);
void RemoveSDLInputDevice_(int index); void RemoveSDLInputDevice_(int index);
void SleepUntilNextEventCycle_(microsecs_t cycle_start_time);
// millisecs_t last_swap_time_{}; // millisecs_t last_swap_time_{};
// millisecs_t swap_start_time_{}; // millisecs_t swap_start_time_{};
// int too_slow_frame_count_{}; // int too_slow_frame_count_{};
@ -80,7 +81,7 @@ class AppAdapterSDL : public AppAdapter {
bool fullscreen_{}; bool fullscreen_{};
VSync vsync_{VSync::kUnset}; VSync vsync_{VSync::kUnset};
bool vsync_actually_enabled_{}; bool vsync_actually_enabled_{};
microsecs_t oversleep{}; microsecs_t oversleep_{};
int max_fps_{60}; int max_fps_{60};
bool debug_log_sdl_frame_timing_{}; bool debug_log_sdl_frame_timing_{};
// std::unique_ptr<GLContext> gl_context_; // std::unique_ptr<GLContext> gl_context_;

View File

@ -17,7 +17,7 @@ auto AppAdapterVR::ManagesMainThreadEventLoop() const -> bool { return false; }
void AppAdapterVR::PushVRSimpleRemoteStateCall( void AppAdapterVR::PushVRSimpleRemoteStateCall(
const VRSimpleRemoteState& state) { 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 // Convert this to a full hands state, adding in some simple elbow
// positioning of our own and left/right. // positioning of our own and left/right.
VRHandsState s; VRHandsState s;
@ -48,7 +48,7 @@ void AppAdapterVR::VRPreDraw() {
|| !g_base->graphics_server->renderer()) { || !g_base->graphics_server->renderer()) {
return; return;
} }
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// FIXME - this is internal graphics-server details that the render-server // FIXME - this is internal graphics-server details that the render-server
// should handle. // should handle.
Log(LogLevel::kWarning, "FIXME: Have GraphicsServer handle VR drawing."); Log(LogLevel::kWarning, "FIXME: Have GraphicsServer handle VR drawing.");
@ -65,7 +65,7 @@ void AppAdapterVR::VRPreDraw() {
} }
void AppAdapterVR::VRPostDraw() { void AppAdapterVR::VRPostDraw() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!g_base || !g_base->graphics_server if (!g_base || !g_base->graphics_server
|| !g_base->graphics_server->renderer()) { || !g_base->graphics_server->renderer()) {
return; return;
@ -80,14 +80,14 @@ void AppAdapterVR::VRPostDraw() {
void AppAdapterVR::VRSetHead(float tx, float ty, float tz, float yaw, void AppAdapterVR::VRSetHead(float tx, float ty, float tz, float yaw,
float pitch, float roll) { float pitch, float roll) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
Renderer* renderer = g_base->graphics_server->renderer(); Renderer* renderer = g_base->graphics_server->renderer();
if (renderer == nullptr) return; if (renderer == nullptr) return;
renderer->VRSetHead(tx, ty, tz, yaw, pitch, roll); renderer->VRSetHead(tx, ty, tz, yaw, pitch, roll);
} }
void AppAdapterVR::VRSetHands(const VRHandsState& state) { 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 // 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). // 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()) { || !g_base->graphics_server->renderer()) {
return; return;
} }
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (vr_render_frame_def_) { if (vr_render_frame_def_) {
// Set up VR eye stuff. // Set up VR eye stuff.
Renderer* renderer = g_base->graphics_server->renderer(); Renderer* renderer = g_base->graphics_server->renderer();

View File

@ -2,6 +2,7 @@
#include "ballistica/base/assets/assets.h" #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/app_mode/app_mode.h"
#include "ballistica/base/assets/assets_server.h" #include "ballistica/base/assets/assets_server.h"
#include "ballistica/base/assets/collision_mesh_asset.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 // Call this from the graphics thread to immediately unload all
// assets used by it. (for when GL context gets lost, etc). // assets used by it. (for when GL context gets lost, etc).
void Assets::UnloadRendererBits(bool do_textures, bool do_meshes) { 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.. // need to keep lists locked while iterating over them..
AssetListLock m_lock; AssetListLock m_lock;
if (do_textures) { if (do_textures) {
@ -728,7 +729,7 @@ auto Assets::RunPendingAudioLoads() -> bool {
// Runs the pending loads that need to run from the graphics thread. // Runs the pending loads that need to run from the graphics thread.
auto Assets::RunPendingGraphicsLoads() -> bool { auto Assets::RunPendingGraphicsLoads() -> bool {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
return RunPendingLoadList(&pending_loads_graphics_); return RunPendingLoadList(&pending_loads_graphics_);
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/base/assets/texture_asset.h" #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_preload_data.h"
#include "ballistica/base/assets/texture_asset_renderer_data.h" #include "ballistica/base/assets/texture_asset_renderer_data.h"
#include "ballistica/base/graphics/graphics.h" #include "ballistica/base/graphics/graphics.h"
@ -425,7 +426,7 @@ void TextureAsset::DoPreload() {
} }
void TextureAsset::DoLoad() { void TextureAsset::DoLoad() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
assert(!renderer_data_.Exists()); assert(!renderer_data_.Exists());
renderer_data_ = g_base->graphics_server->renderer()->NewTextureData(*this); renderer_data_ = g_base->graphics_server->renderer()->NewTextureData(*this);
assert(renderer_data_.Exists()); assert(renderer_data_.Exists());
@ -441,7 +442,7 @@ void TextureAsset::DoLoad() {
} }
void TextureAsset::DoUnload() { void TextureAsset::DoUnload() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
assert(valid_); assert(valid_);
assert(renderer_data_.Exists()); assert(renderer_data_.Exists());
renderer_data_.Clear(); renderer_data_.Clear();

View File

@ -199,8 +199,7 @@ void AudioServer::OnAppStartInThread() {
} }
#endif // BA_RIFT_BUILD #endif // BA_RIFT_BUILD
ALCdevice* device; auto* device = alcOpenDevice(al_device_name);
device = alcOpenDevice(al_device_name);
if (!device) { if (!device) {
FatalError( FatalError(
"No audio devices found. Do you have speakers/headphones/etc. " "No audio devices found. Do you have speakers/headphones/etc. "

View File

@ -468,15 +468,6 @@ auto BaseFeatureSet::InLogicThread() const -> bool {
return false; 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 { auto BaseFeatureSet::InAudioThread() const -> bool {
if (auto* loop = audio_server->event_loop()) { if (auto* loop = audio_server->event_loop()) {
return loop->ThreadIsCurrent(); return loop->ThreadIsCurrent();

View File

@ -682,7 +682,6 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
auto InAssetsThread() const -> bool override; auto InAssetsThread() const -> bool override;
auto InLogicThread() const -> bool override; auto InLogicThread() const -> bool override;
auto InGraphicsThread() const -> bool override;
auto InAudioThread() const -> bool override; auto InAudioThread() const -> bool override;
auto InBGDynamicsThread() const -> bool override; auto InBGDynamicsThread() const -> bool override;
auto InNetworkWriteThread() const -> bool override; auto InNetworkWriteThread() const -> bool override;

View File

@ -5,6 +5,7 @@
#if BA_ENABLE_OPENGL #if BA_ENABLE_OPENGL
#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
@ -46,7 +47,7 @@ class RendererGL::FramebufferObjectGL : public Framebuffer {
void Load(bool force_low_quality = false) { void Load(bool force_low_quality = false) {
if (loaded_) return; if (loaded_) return;
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
GLenum status; GLenum status;
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
@ -246,7 +247,7 @@ class RendererGL::FramebufferObjectGL : public Framebuffer {
} }
void Unload() { void Unload() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!loaded_) return; if (!loaded_) return;
// If our textures are currently bound as anything, clear that out. // If our textures are currently bound as anything, clear that out.
@ -288,7 +289,7 @@ class RendererGL::FramebufferObjectGL : public Framebuffer {
} }
void Bind() { void Bind() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
renderer_->BindFramebuffer(framebuffer_); renderer_->BindFramebuffer(framebuffer_);
// if (time(nullptr)%2 == 0) { // if (time(nullptr)%2 == 0) {
// glDisable(GL_FRAMEBUFFER_SRGB); // glDisable(GL_FRAMEBUFFER_SRGB);

View File

@ -5,6 +5,7 @@
#if BA_ENABLE_OPENGL #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/gl_sys.h"
#include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
@ -23,7 +24,7 @@ class RendererGL::MeshAssetDataGL : public MeshAssetRendererData {
name_ = model.GetName(); name_ = model.GetName();
#endif #endif
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
// Create our vertex array to hold all this state. // Create our vertex array to hold all this state.
@ -98,7 +99,7 @@ class RendererGL::MeshAssetDataGL : public MeshAssetRendererData {
} }
~MeshAssetDataGL() override { ~MeshAssetDataGL() override {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
// Unbind if we're bound; otherwise if a new vao pops up with our same // 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), : renderer_(renderer),
uses_secondary_data_(static_cast<bool>(flags & kUsesSecondaryBuffer)), uses_secondary_data_(static_cast<bool>(flags & kUsesSecondaryBuffer)),
uses_index_data_(static_cast<bool>(flags & kUsesIndexBuffer)) { uses_index_data_(static_cast<bool>(flags & kUsesIndexBuffer)) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
// Create our vertex array to hold all this state. // Create our vertex array to hold all this state.
@ -85,7 +85,7 @@ class RendererGL::MeshDataGL : public MeshRendererData {
} }
~MeshDataGL() override { ~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 // Unbind if we're bound; otherwise we might prevent a new vao that
// reuses our ID from binding. // reuses our ID from binding.
if (vao_ == renderer_->current_vertex_array_) { if (vao_ == renderer_->current_vertex_array_) {

View File

@ -5,6 +5,7 @@
#if BA_ENABLE_OPENGL #if BA_ENABLE_OPENGL
#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.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) { 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; BA_DEBUG_CHECK_GL_ERROR;
assert(type_ == GL_FRAGMENT_SHADER || type_ == GL_VERTEX_SHADER); assert(type_ == GL_FRAGMENT_SHADER || type_ == GL_VERTEX_SHADER);
shader_ = glCreateShader(type_); shader_ = glCreateShader(type_);
@ -73,7 +74,7 @@ class RendererGL::ShaderGL : public Object {
} }
~ShaderGL() override { ~ShaderGL() override {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!g_base->graphics_server->renderer_context_lost()) { if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteShader(shader_); glDeleteShader(shader_);
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
@ -127,7 +128,7 @@ class RendererGL::ProgramGL {
renderer_(renderer), renderer_(renderer),
pflags_(pflags), pflags_(pflags),
name_(std::move(name)) { name_(std::move(name)) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
program_ = glCreateProgram(); program_ = glCreateProgram();
BA_PRECONDITION(program_); BA_PRECONDITION(program_);
@ -209,7 +210,7 @@ class RendererGL::ProgramGL {
} }
virtual ~ProgramGL() { virtual ~ProgramGL() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!g_base->graphics_server->renderer_context_lost()) { if (!g_base->graphics_server->renderer_context_lost()) {
glDetachShader(program_, fragment_shader_->shader()); glDetachShader(program_, fragment_shader_->shader());
glDetachShader(program_, vertex_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, void DrawBegin(bool must_clear_color, float clear_r, float clear_g,
float clear_b, float clear_a) override { float clear_b, float clear_a) override {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
Bind(); Bind();
@ -94,7 +94,7 @@ class RendererGL::RenderTargetGL : public RenderTarget {
// Screen constructor. // Screen constructor.
explicit RenderTargetGL(RendererGL* renderer) explicit RenderTargetGL(RendererGL* renderer)
: RenderTarget(Type::kScreen), renderer_(renderer) { : RenderTarget(Type::kScreen), renderer_(renderer) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
depth_ = true; depth_ = true;
// This will update our width/height values. // This will update our width/height values.
@ -106,7 +106,7 @@ class RendererGL::RenderTargetGL : public RenderTarget {
bool linear_interp, bool depth, bool texture, bool linear_interp, bool depth, bool texture,
bool depth_texture, bool high_quality, bool msaa, bool alpha) bool depth_texture, bool high_quality, bool msaa, bool alpha)
: RenderTarget(Type::kFramebuffer), renderer_(renderer) { : RenderTarget(Type::kFramebuffer), renderer_(renderer) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
framebuffer_ = Object::New<FramebufferObjectGL>( framebuffer_ = Object::New<FramebufferObjectGL>(
renderer, width, height, linear_interp, depth, texture, depth_texture, renderer, width, height, linear_interp, depth, texture, depth_texture,

View File

@ -87,7 +87,7 @@ bool RendererGL::is_extra_speedy_android_device_{};
#endif #endif
RendererGL::RendererGL() { RendererGL::RendererGL() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (explicit_bool(BA_FORCE_CHECK_GL_ERRORS)) { if (explicit_bool(BA_FORCE_CHECK_GL_ERRORS)) {
ScreenMessage("GL ERROR CHECKS ENABLED"); ScreenMessage("GL ERROR CHECKS ENABLED");
@ -175,7 +175,7 @@ static auto CheckGLExtension(const std::vector<std::string>& exts,
void RendererGL::CheckGLCapabilities_() { void RendererGL::CheckGLCapabilities_() {
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
draws_shields_funny_set_ = true; 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 // This is probably inefficient so make sure we don't leave it on in
// release builds. // release builds.
assert(g_buildconfig.debug_build()); 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 // Clear any error coming in; don't want to die for something that's not
// ours. // ours.
@ -874,7 +874,7 @@ void RendererGL::InvalidateFramebuffer(bool color, bool depth,
} }
RendererGL::~RendererGL() { RendererGL::~RendererGL() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
printf("FIXME: need to unload renderer on destroy.\n"); printf("FIXME: need to unload renderer on destroy.\n");
// Unload(); // Unload();
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
@ -2610,7 +2610,7 @@ auto RendererGL::GetFunkyDepthIssue_() -> bool {
#if BA_OSTYPE_ANDROID #if BA_OSTYPE_ANDROID
std::string RendererGL::GetAutoAndroidRes() { std::string RendererGL::GetAutoAndroidRes() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
const char* renderer = (const char*)glGetString(GL_RENDERER); const char* renderer = (const char*)glGetString(GL_RENDERER);
@ -2636,7 +2636,7 @@ std::string RendererGL::GetAutoAndroidRes() {
#endif // BA_OSTYPE_ANDROID #endif // BA_OSTYPE_ANDROID
auto RendererGL::GetAutoTextureQuality() -> TextureQuality { auto RendererGL::GetAutoTextureQuality() -> TextureQuality {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
TextureQuality qual{TextureQuality::kHigh}; TextureQuality qual{TextureQuality::kHigh};
@ -2668,7 +2668,7 @@ auto RendererGL::GetAutoTextureQuality() -> TextureQuality {
} }
auto RendererGL::GetAutoGraphicsQuality() -> GraphicsQuality { auto RendererGL::GetAutoGraphicsQuality() -> GraphicsQuality {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
GraphicsQuality q{GraphicsQuality::kMedium}; GraphicsQuality q{GraphicsQuality::kMedium};
#if BA_OSTYPE_ANDROID #if BA_OSTYPE_ANDROID
// lets be cheaper in VR mode since we draw twice.. // 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::RetainShader_(ProgramGL* p) { shaders_.emplace_back(p); }
void RendererGL::Load() { void RendererGL::Load() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
assert(!data_loaded_); assert(!data_loaded_);
assert(g_base->graphics_server->graphics_quality_set()); assert(g_base->graphics_server->graphics_quality_set());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
@ -2920,7 +2920,7 @@ void RendererGL::PostLoad() {
} }
void RendererGL::Unload() { void RendererGL::Unload() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
assert(data_loaded_); assert(data_loaded_);
Renderer::Unload(); Renderer::Unload();

View File

@ -5,6 +5,7 @@
#if BA_ENABLE_OPENGL #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_preload_data.h"
#include "ballistica/base/assets/texture_asset_renderer_data.h" #include "ballistica/base/assets/texture_asset_renderer_data.h"
#include "ballistica/base/graphics/gl/renderer_gl.h" #include "ballistica/base/graphics/gl/renderer_gl.h"
@ -16,14 +17,14 @@ class RendererGL::TextureDataGL : public TextureAssetRendererData {
public: public:
TextureDataGL(const TextureAsset& texture_in, RendererGL* renderer_in) TextureDataGL(const TextureAsset& texture_in, RendererGL* renderer_in)
: tex_media_(&texture_in), texture_(0), renderer_(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; BA_DEBUG_CHECK_GL_ERROR;
glGenTextures(1, &texture_); glGenTextures(1, &texture_);
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
} }
~TextureDataGL() override { ~TextureDataGL() override {
if (!g_base->InGraphicsThread()) { if (!g_base->app_adapter->InGraphicsContext()) {
Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread."); Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread.");
} else { } else {
// If we're currently bound as anything, clear that out (otherwise a // 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_; } auto GetTexture() const -> GLuint { return texture_; }
void Load() override { void Load() override {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
if (tex_media_->texture_type() == TextureType::k2D) { if (tex_media_->texture_type() == TextureType::k2D) {

View File

@ -1121,14 +1121,24 @@ void Graphics::BuildAndPushFrameDef() {
// Store how much time this frame_def represents. // Store how much time this frame_def represents.
auto display_time_microsecs = g_base->logic->display_time_microsecs(); 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 // 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 // been longer than that since the last. Don't want things like
// motion-blur to get out of control. // motion-blur to get out of control.
millisecs_t elapsed_microsecs = microsecs_t elapsed_microsecs =
std::min(microsecs_t{100000}, std::min(microsecs_t{100000},
display_time_microsecs - last_create_frame_def_time_); display_time_microsecs - last_create_frame_def_time_microsecs_);
last_create_frame_def_time_ = display_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_++; frame_def_count_++;
@ -1153,6 +1163,7 @@ void Graphics::BuildAndPushFrameDef() {
frame_def->set_display_time_microsecs( frame_def->set_display_time_microsecs(
g_base->logic->display_time_microsecs()); g_base->logic->display_time_microsecs());
frame_def->set_display_time_elapsed_microsecs(elapsed_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(frame_def_count_);
frame_def->set_frame_number_filtered(frame_def_count_filtered_); 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::list<Object::Ref<PythonContextCall>> clean_frame_commands_;
std::vector<MeshData*> mesh_data_creates_; std::vector<MeshData*> mesh_data_creates_;
std::vector<MeshData*> mesh_data_destroys_; 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}; Vector3f shadow_offset_{0.0f, 0.0f, 0.0f};
Vector2f shadow_scale_{1.0f, 1.0f}; Vector2f shadow_scale_{1.0f, 1.0f};
Vector3f tint_{1.0f, 1.0f, 1.0f}; Vector3f tint_{1.0f, 1.0f, 1.0f};

View File

@ -22,11 +22,11 @@
namespace ballistica::base { namespace ballistica::base {
GraphicsServer::GraphicsServer() {} GraphicsServer::GraphicsServer() = default;
GraphicsServer::~GraphicsServer() = default; GraphicsServer::~GraphicsServer() = default;
void GraphicsServer::SetRenderHold() { void GraphicsServer::SetRenderHold() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
render_hold_++; render_hold_++;
} }
@ -46,7 +46,7 @@ void GraphicsServer::EnqueueFrameDef(FrameDef* framedef) {
} }
auto GraphicsServer::TryRender() -> bool { auto GraphicsServer::TryRender() -> bool {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
bool success{}; bool success{};
@ -75,7 +75,7 @@ auto GraphicsServer::TryRender() -> bool {
} }
auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* { auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
millisecs_t app_time = g_core->GetAppTimeMillisecs(); millisecs_t app_time = g_core->GetAppTimeMillisecs();
if (!renderer_) { if (!renderer_) {
@ -127,13 +127,13 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
} }
void GraphicsServer::ApplyFrameDefSettings(FrameDef* frame_def) { void GraphicsServer::ApplyFrameDefSettings(FrameDef* frame_def) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
tv_border_ = frame_def->tv_border(); tv_border_ = frame_def->tv_border();
} }
// Runs any mesh updates contained in the frame-def. // Runs any mesh updates contained in the frame-def.
void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* 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. // Run any mesh-data creates/destroys included with this frame_def.
for (auto&& i : frame_def->mesh_data_creates()) { 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. // Renders shadow passes and other common parts of a frame_def.
void GraphicsServer::PreprocessRenderFrameDef(FrameDef* 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). // Now let the renderer do any preprocess passes (shadows, etc).
assert(renderer_); assert(renderer_);
@ -179,7 +179,7 @@ void GraphicsServer::FinishRenderFrameDef(FrameDef* frame_def) {
// Reload all media (for debugging/benchmarking purposes). // Reload all media (for debugging/benchmarking purposes).
void GraphicsServer::ReloadMedia_() { void GraphicsServer::ReloadMedia_() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// Immediately unload all renderer data here in this thread. // Immediately unload all renderer data here in this thread.
if (renderer_) { if (renderer_) {
@ -205,7 +205,7 @@ void GraphicsServer::ReloadMedia_() {
// Call when a renderer context has been lost. // Call when a renderer context has been lost.
void GraphicsServer::ReloadLostRenderer() { void GraphicsServer::ReloadLostRenderer() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!renderer_) { if (!renderer_) {
Log(LogLevel::kError, "No renderer on GraphicsServer::ReloadLostRenderer."); Log(LogLevel::kError, "No renderer on GraphicsServer::ReloadLostRenderer.");
@ -274,14 +274,14 @@ void GraphicsServer::SetNullGraphics() {
} }
void GraphicsServer::set_renderer(Renderer* renderer) { void GraphicsServer::set_renderer(Renderer* renderer) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
assert(!renderer_loaded_); assert(!renderer_loaded_);
assert(!renderer_); assert(!renderer_);
renderer_ = renderer; renderer_ = renderer;
} }
void GraphicsServer::LoadRenderer() { void GraphicsServer::LoadRenderer() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!renderer_) { if (!renderer_) {
Log(LogLevel::kError, "LoadRenderer() called with no renderer present."); Log(LogLevel::kError, "LoadRenderer() called with no renderer present.");
return; return;
@ -358,15 +358,14 @@ void GraphicsServer::LoadRenderer() {
renderer_loaded_ = true; renderer_loaded_ = true;
// Set a render-hold so we ignore all frame_defs up until the point at // Set an immediate render-hold so we ignore all frame_defs up until the
// which we receive the corresponding remove-hold. (At which point // point at which we receive the corresponding remove-hold. (At which
// subsequent frame-defs will be be progress-bar frame_defs so we won't // point subsequent frame-defs will be be progress-bar frame_defs so we
// hitch if we actually render them.) // won't hitch if we actually render them.)
SetRenderHold(); SetRenderHold();
// Now tell the logic thread to kick off loads for everything, flip on // Now tell the logic thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop // progress bar drawing, and then ship a remove-hold call back to us.
// ignoring frame-defs.
g_base->logic->event_loop()->PushCall([this] { g_base->logic->event_loop()->PushCall([this] {
g_base->assets->MarkAllAssetsForLoad(); g_base->assets->MarkAllAssetsForLoad();
g_base->graphics->set_internal_components_inited(false); g_base->graphics->set_internal_components_inited(false);
@ -376,7 +375,7 @@ void GraphicsServer::LoadRenderer() {
} }
void GraphicsServer::UnloadRenderer() { void GraphicsServer::UnloadRenderer() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!renderer_) { if (!renderer_) {
Log(LogLevel::kError, "UnloadRenderer() called with no renderer present."); Log(LogLevel::kError, "UnloadRenderer() called with no renderer present.");
return; return;
@ -417,7 +416,7 @@ void GraphicsServer::CalcVirtualRes_(float* x, float* y) {
} }
void GraphicsServer::UpdateVirtualScreenRes_() { 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. // In vr mode our virtual res is independent of our screen size.
// (since it gets drawn to an overlay) // (since it gets drawn to an overlay)
@ -432,7 +431,7 @@ void GraphicsServer::UpdateVirtualScreenRes_() {
} }
void GraphicsServer::SetScreenResolution(float h, float v) { void GraphicsServer::SetScreenResolution(float h, float v) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// Ignore redundant sets. // Ignore redundant sets.
if (res_x_ == h && res_y_ == v) { 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( void GraphicsServer::SetTextureCompressionTypes(
const std::list<TextureCompressionType>& types) { const std::list<TextureCompressionType>& types) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
texture_compression_types_ = 0; texture_compression_types_ = 0;
for (auto&& i : types) { for (auto&& i : types) {
texture_compression_types_ |= (0x01u << (static_cast<uint32_t>(i))); 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, void GraphicsServer::SetOrthoProjection(float left, float right, float bottom,
float top, float nearval, float top, float nearval,
float farval) { float farval) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
float tx = -((right + left) / (right - left)); float tx = -((right + left) / (right - left));
float ty = -((top + bottom) / (top - bottom)); float ty = -((top + bottom) / (top - bottom));
float tz = -((farval + nearval) / (farval - nearval)); 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, void GraphicsServer::SetCamera(const Vector3f& eye, const Vector3f& target,
const Vector3f& up_vector) { const Vector3f& up_vector) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// Reset the modelview stack. // Reset the modelview stack.
model_view_stack_.clear(); model_view_stack_.clear();
@ -604,7 +562,7 @@ void GraphicsServer::SetCamera(const Vector3f& eye, const Vector3f& target,
} }
void GraphicsServer::UpdateCamOrientMatrix_() { void GraphicsServer::UpdateCamOrientMatrix_() {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (cam_orient_matrix_dirty_) { if (cam_orient_matrix_dirty_) {
cam_orient_matrix_ = kMatrix44fIdentity; cam_orient_matrix_ = kMatrix44fIdentity;
Vector3f to_cam = cam_pos_ - cam_target_; Vector3f to_cam = cam_pos_ - cam_target_;
@ -631,12 +589,12 @@ void GraphicsServer::UpdateCamOrientMatrix_() {
} }
void GraphicsServer::PushReloadMediaCall() { void GraphicsServer::PushReloadMediaCall() {
g_base->app_adapter->PushMainThreadCall([this] { ReloadMedia_(); }); g_base->app_adapter->PushGraphicsContextCall([this] { ReloadMedia_(); });
} }
void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
g_base->app_adapter->PushMainThreadCall([this, pixel_scale] { g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!renderer_) { if (!renderer_) {
return; return;
} }
@ -646,8 +604,8 @@ void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
void GraphicsServer::PushComponentUnloadCall( void GraphicsServer::PushComponentUnloadCall(
const std::vector<Object::Ref<Asset>*>& components) { const std::vector<Object::Ref<Asset>*>& components) {
g_base->app_adapter->PushMainThreadCall([components] { g_base->app_adapter->PushGraphicsContextCall([components] {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// Unload the components. // Unload the components.
for (auto&& i : components) { for (auto&& i : components) {
(**i).Unload(); (**i).Unload();
@ -662,8 +620,8 @@ void GraphicsServer::PushComponentUnloadCall(
} }
void GraphicsServer::PushRemoveRenderHoldCall() { void GraphicsServer::PushRemoveRenderHoldCall() {
g_base->app_adapter->PushMainThreadCall([this] { g_base->app_adapter->PushGraphicsContextCall([this] {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
assert(render_hold_); assert(render_hold_);
render_hold_--; render_hold_--;
if (render_hold_ < 0) { 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 } // namespace ballistica::base

View File

@ -226,27 +226,27 @@ class GraphicsServer {
} }
auto screen_pixel_width() const -> float { auto screen_pixel_width() const -> float {
assert(g_base->InGraphicsThread()); assert(InGraphicsContext_());
return res_x_; return res_x_;
} }
auto screen_pixel_height() const -> float { auto screen_pixel_height() const -> float {
assert(g_base->InGraphicsThread()); assert(InGraphicsContext_());
return res_y_; return res_y_;
} }
auto screen_virtual_width() const -> float { auto screen_virtual_width() const -> float {
assert(g_base->InGraphicsThread()); assert(InGraphicsContext_());
return res_x_virtual_; return res_x_virtual_;
} }
auto screen_virtual_height() const -> float { auto screen_virtual_height() const -> float {
assert(g_base->InGraphicsThread()); assert(InGraphicsContext_());
return res_y_virtual_; return res_y_virtual_;
} }
auto tv_border() const { auto tv_border() const {
assert(g_base->InGraphicsThread()); assert(InGraphicsContext_());
return tv_border_; return tv_border_;
} }
@ -308,6 +308,9 @@ class GraphicsServer {
// bool fullscreen); // bool fullscreen);
private: 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 // 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 // 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 // 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/graphics/mesh/mesh_data.h"
#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/graphics/renderer/renderer.h" #include "ballistica/base/graphics/renderer/renderer.h"
namespace ballistica::base { namespace ballistica::base {
void MeshData::Load(Renderer* renderer) { void MeshData::Load(Renderer* renderer) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (!renderer_data_) { if (!renderer_data_) {
renderer_data_ = renderer->NewMeshData(type(), draw_type()); renderer_data_ = renderer->NewMeshData(type(), draw_type());
} }
} }
void MeshData::Unload(Renderer* renderer) { void MeshData::Unload(Renderer* renderer) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (renderer_data_) { if (renderer_data_) {
renderer->DeleteMeshData(renderer_data_, type()); renderer->DeleteMeshData(renderer_data_, type());
renderer_data_ = nullptr; renderer_data_ = nullptr;

View File

@ -6,18 +6,6 @@
namespace ballistica::base { 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, NinePatchMesh::NinePatchMesh(float x, float y, float z, float width,
float height, float border_left, float height, float border_left,
float border_bottom, float border_right, 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. VertexSimpleFull verts[16]; // 4 vertical * 4 horizontal slices.
uint16_t indices[54]; // 9 patches * 2 triangles * 3 verts. uint16_t indices[54]; // 9 patches * 2 triangles * 3 verts.
// Vertical slices.
float y0 = y; float y0 = y;
float y1 = y + border_bottom * height; float y1 = y + border_bottom * height;
float y2 = y + (1.0 - border_top) * 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 v2 = 32767;
auto v3 = 0; auto v3 = 0;
// Horizontal slices.
float x0 = x; float x0 = x;
float x1 = x + border_left * width; float x1 = x + border_left * width;
float x2 = x + (1.0 - border_right) * 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 u2 = 32767;
auto u3 = 65535; auto u3 = 65535;
int icount{}; // Fill out all 16 verts.
// Assign all 16 positions and uvs.
for (int yi = 0; yi < 4; ++yi) { for (int yi = 0; yi < 4; ++yi) {
for (int xi = 0; xi < 4; ++xi) { for (int xi = 0; xi < 4; ++xi) {
VertexSimpleFull* v = verts + yi * 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. // 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 yi = 0; yi < 3; ++yi) {
for (int xi = 0; xi < 3; ++xi) { for (int xi = 0; xi < 3; ++xi) {
VertexSimpleFull* v = verts + yi * 4 + xi; VertexSimpleFull* v = verts + yi * 4 + xi;
VertexSimpleFull* vright = v + 1; VertexSimpleFull* vright = v + 1;
VertexSimpleFull* vtop = v + 4; VertexSimpleFull* vtop = v + 4;
if (vright->position[0] > v->position[0] if (vright->position[0] > v->position[0]
&& vtop->position[1] > v->position[1]) { && vtop->position[1] > v->position[1]) {
indices[icount++] = yi * 4 + xi; indices[icount++] = yi * 4 + xi;
indices[icount++] = yi * 4 + xi + 1; indices[icount++] = yi * 4 + xi + 1;
indices[icount++] = (yi + 1) * 4 + xi + 1; indices[icount++] = (yi + 1) * 4 + xi + 1;
indices[icount++] = yi * 4 + xi; indices[icount++] = yi * 4 + xi;
indices[icount++] = (yi + 1) * 4 + xi + 1; indices[icount++] = (yi + 1) * 4 + xi + 1;
indices[icount++] = (yi + 1) * 4 + xi; indices[icount++] = (yi + 1) * 4 + xi;

View File

@ -17,9 +17,19 @@ class NinePatchMesh : public MeshIndexedSimpleFull {
float border_top); float border_top);
/// Calculate a border value for a NinePatchMesh based on dimensions and a /// 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, 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 } // namespace ballistica::base

View File

@ -2,6 +2,7 @@
#include "ballistica/base/graphics/renderer/render_pass.h" #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/graphics_server.h"
#include "ballistica/base/graphics/renderer/renderer.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; RenderPass::~RenderPass() = default;
void RenderPass::Render(RenderTarget* render_target, bool transparent) { void RenderPass::Render(RenderTarget* render_target, bool transparent) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
if (explicit_bool(!DRAW_TRANSPARENT) && transparent) { if (explicit_bool(!DRAW_TRANSPARENT) && transparent) {
return; return;
@ -460,7 +461,7 @@ void RenderPass::Reset() {
} }
void RenderPass::SetFrustum(float near_val, float far_val) { 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 we're using fov-tangents:
if (cam_use_fov_tangents_) { if (cam_use_fov_tangents_) {
float l = near_val * cam_fov_l_tan_; 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/graphics/renderer/render_target.h"
#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base { namespace ballistica::base {
RenderTarget::RenderTarget(Type type) : type_(type) { RenderTarget::RenderTarget(Type type) : type_(type) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
} }
RenderTarget::~RenderTarget() = default; RenderTarget::~RenderTarget() = default;

View File

@ -2,14 +2,10 @@
#include "ballistica/base/graphics/renderer/renderer.h" #include "ballistica/base/graphics/renderer/renderer.h"
#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/core/core.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 #if BA_VR_BUILD
#include "ballistica/base/graphics/graphics_vr.h" #include "ballistica/base/graphics/graphics_vr.h"
#endif #endif
@ -35,7 +31,7 @@ Renderer::~Renderer() {
} }
void Renderer::PreprocessFrameDef(FrameDef* frame_def) { 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 // If this frame_def was made in a different quality mode than we're
// currently in, don't attempt to render it. // 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... // actually render one of these frame_def suckers...
// (called within the graphics thread) // (called within the graphics thread)
void Renderer::RenderFrameDef(FrameDef* frame_def) { void Renderer::RenderFrameDef(FrameDef* frame_def) {
assert(g_base->InGraphicsThread()); assert(g_base->app_adapter->InGraphicsContext());
// If preprocess decided not to render this. // If preprocess decided not to render this.
if (!frame_def->rendering()) return; if (!frame_def->rendering()) return;
@ -756,7 +752,7 @@ void Renderer::UpdateDOFParams(FrameDef* frame_def) {
} }
void Renderer::OnScreenSizeChange() { 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 // 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 // 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. // How much display time does this frame-def represent.
auto display_time_elapsed_millisecs() const -> millisecs_t { 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_; } auto quality() const -> GraphicsQuality { return quality_; }
@ -135,6 +139,9 @@ class FrameDef {
void set_display_time_elapsed_microsecs(microsecs_t val) { void set_display_time_elapsed_microsecs(microsecs_t val) {
display_time_elapsed_microsecs_ = 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_millisecs(millisecs_t val) { app_time_millisecs_ = val; }
void set_app_time_microsecs(microsecs_t val) { app_time_microsecs_ = 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 app_time_microsecs_{};
microsecs_t display_time_microsecs_{}; microsecs_t display_time_microsecs_{};
microsecs_t display_time_elapsed_microsecs_{}; microsecs_t display_time_elapsed_microsecs_{};
microsecs_t display_time_elapsed_millisecs_{};
int64_t frame_number_{}; int64_t frame_number_{};
int64_t frame_number_filtered_{}; int64_t frame_number_filtered_{};
Vector3f shadow_offset_{0.0f, 0.0f, 0.0f}; Vector3f shadow_offset_{0.0f, 0.0f, 0.0f};

View File

@ -18,10 +18,8 @@ class BaseSoftInterface {
virtual void StartApp() = 0; virtual void StartApp() = 0;
virtual auto AppManagesMainThreadEventLoop() -> bool = 0; virtual auto AppManagesMainThreadEventLoop() -> bool = 0;
virtual void RunAppToCompletion() = 0; virtual void RunAppToCompletion() = 0;
// virtual void PrimeAppMainThreadEventPump() = 0;
virtual auto InAssetsThread() const -> bool = 0; virtual auto InAssetsThread() const -> bool = 0;
virtual auto InLogicThread() const -> bool = 0; virtual auto InLogicThread() const -> bool = 0;
virtual auto InGraphicsThread() const -> bool = 0;
virtual auto InAudioThread() const -> bool = 0; virtual auto InAudioThread() const -> bool = 0;
virtual auto InBGDynamicsThread() const -> bool = 0; virtual auto InBGDynamicsThread() const -> bool = 0;
virtual auto InNetworkWriteThread() const -> bool = 0; virtual auto InNetworkWriteThread() const -> bool = 0;

View File

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

View File

@ -412,6 +412,41 @@ int calc_paths_(struct Context_* ctx) {
return 0; 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) { int send_command_(struct Context_* ctx, int argc, char** argv) {
// Build a json array of our args. // Build a json array of our args.
cJSON* req = cJSON_CreateObject(); 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_AddItemToArray(array, cJSON_CreateString(argv[i]));
} }
cJSON_AddItemToObject(req, "a", array); cJSON_AddItemToObject(req, "a", array);
cJSON_AddItemToObject(req, "t", cJSON_AddItemToObject(
isatty(1) ? cJSON_CreateTrue() : cJSON_CreateFalse()); req, "c", color_enabled() ? cJSON_CreateTrue() : cJSON_CreateFalse());
cJSON_AddItemToObject(req, "t",
isatty(1) ? cJSON_CreateTrue() : cJSON_CreateFalse());
char* json_out = cJSON_Print(req); char* json_out = cJSON_Print(req);
// Send our command. // Send our command.
@ -466,7 +499,6 @@ int handle_response_(const struct Context_* ctx) {
return -1; return -1;
} }
cJSON* result_dict = cJSON_Parse(inbuf); cJSON* result_dict = cJSON_Parse(inbuf);
if (!result_dict) { if (!result_dict) {
@ -474,7 +506,7 @@ int handle_response_(const struct Context_* ctx) {
stderr, stderr,
"Error: pcommandbatch client %s_%d (pid %d): failed to parse result " "Error: pcommandbatch client %s_%d (pid %d): failed to parse result "
"value: %s\n", "value: %s\n",
ctx->instance_prefix, ctx->instance_num, ctx->pid, inbuf); ctx->instance_prefix, ctx->instance_num, ctx->pid, inbuf);
free(inbuf); free(inbuf);
return -1; return -1;
} else { } else {

View File

@ -71,12 +71,19 @@ def _default_color_enabled() -> bool:
"""Return whether we enable ANSI color codes by default.""" """Return whether we enable ANSI color codes by default."""
import platform 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(): if not sys.__stdout__.isatty():
return False return False
# Another common way to say the terminal can't do fancy stuff like color: termenv = os.environ.get('TERM')
if os.environ.get('TERM') == 'dumb':
# 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 return False
# On windows, try to enable ANSI color mode. # On windows, try to enable ANSI color mode.

View File

@ -331,8 +331,8 @@ class Server:
argv: list[str] = reqdata['a'] argv: list[str] = reqdata['a']
assert isinstance(argv, list) assert isinstance(argv, list)
assert all(isinstance(i, str) for i in argv) assert all(isinstance(i, str) for i in argv)
isatty: bool = reqdata['t'] color_enabled: bool = reqdata['c']
assert isinstance(isatty, bool) assert isinstance(color_enabled, bool)
print( print(
f'pcommandbatch server {self._instance} (pid {self._pid})' f'pcommandbatch server {self._instance} (pid {self._pid})'
@ -345,7 +345,9 @@ class Server:
# client. ideally should expand the client-side logic to # client. ideally should expand the client-side logic to
# exactly match what efro.terminal.Clr does locally. # exactly match what efro.terminal.Clr does locally.
clr: type[efro.terminal.ClrBase] = ( 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: try: