diff --git a/.efrocachemap b/.efrocachemap index 95f67d38..4f28e4fe 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -421,7 +421,7 @@ "build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55", - "build/assets/ba_data/data/langdata.json": "7a5f49ae1738b012a6d7c16740af80a0", + "build/assets/ba_data/data/langdata.json": "1a960da2db069ca3926b8ee6b8f82df7", "build/assets/ba_data/data/languages/arabic.json": "295c559911fa251f401f8cdcad91c226", "build/assets/ba_data/data/languages/belarussian.json": "e151808b6b4f6dc159cf55ee62adad3c", "build/assets/ba_data/data/languages/chinese.json": "b0d4e874ba8d22c8fd0d7a0eaaf96ac9", @@ -433,30 +433,30 @@ "build/assets/ba_data/data/languages/english.json": "e70277fc6325126d3d893524c8df03c9", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", "build/assets/ba_data/data/languages/filipino.json": "347f38524816691170d266708fe25894", - "build/assets/ba_data/data/languages/french.json": "d8527da977a563185de25ef02bacf826", + "build/assets/ba_data/data/languages/french.json": "4e218dcd488fa63e7db5b4da2261b9e1", "build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad", - "build/assets/ba_data/data/languages/gibberish.json": "7863ceeedb1e87eef46f7769bae5f842", - "build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a", - "build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a", + "build/assets/ba_data/data/languages/gibberish.json": "b8dfd407fb7fd9b268129c364b70ca54", + "build/assets/ba_data/data/languages/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3", + "build/assets/ba_data/data/languages/hindi.json": "8ea0c58a44a24edb131d0e53b074d1f6", "build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e", - "build/assets/ba_data/data/languages/indonesian.json": "bff88ce57744a639810b93a1d1dd79f4", + "build/assets/ba_data/data/languages/indonesian.json": "53961b1484a1831f32bec2cc2941e672", "build/assets/ba_data/data/languages/italian.json": "58ecf53a963dbeca1bbf3605e5ab6a2f", "build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2", "build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38", - "build/assets/ba_data/data/languages/persian.json": "71cc5b33abda0f285b970b8cc4a014a8", + "build/assets/ba_data/data/languages/persian.json": "a391d80ff58ea22926499e4b19d2c0d0", "build/assets/ba_data/data/languages/polish.json": "e1a1a801851924748ad38fa68216439a", "build/assets/ba_data/data/languages/portuguese.json": "9fcd6b4da9e5d0dc0e337ab00b5debe2", "build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826", "build/assets/ba_data/data/languages/russian.json": "70f79c606ccc5ec7bd6ce0303fdece70", "build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69", "build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef", - "build/assets/ba_data/data/languages/spanish.json": "6ccd728df4766be1969434d6f04c36d2", + "build/assets/ba_data/data/languages/spanish.json": "e72e394f94b99d3e838c1b81a9d17615", "build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18", "build/assets/ba_data/data/languages/tamil.json": "65ab7798d637fa62a703750179eeb723", "build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc", "build/assets/ba_data/data/languages/turkish.json": "42318070b817663f671d78a9c8f3019c", "build/assets/ba_data/data/languages/ukrainian.json": "f72eb51abfbbb56e27866895d7e947d2", - "build/assets/ba_data/data/languages/venetian.json": "88595b7ee696b4094d7874c3c4188852", + "build/assets/ba_data/data/languages/venetian.json": "9fe1a58d9e5dfb00f31ce3b2eb9993f4", "build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba", "build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054", "build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422", @@ -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": "ece14cc6d7a449f581c810a2d6d3449d", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "b0b75bde134af8c73aa1f7e239bd84dc", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "f0742e77993c006a5f2df3e9bee6732e", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b803f154b4bf2aeb908a603fa7888301", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "2be90b3e6fc6908448a7677dd3cfb594", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4d12d1887901f7c77b5df965bb0b4622", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "987f4e024c7ed08e58223369b40aa309", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "7dd6ce5ab63d9d255029fb907cf6fb63", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ad505c3ad979b2cf52c664ee79798575", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "3773aa5c6d396b4c38883321067f5523", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "748f3877c0ac40f48ebc5d8aab442173", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "2d059f03286603ac416718eb262241ab", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "3bd988564ed41c15b4d0f493eced88ef", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "f01539e046d72d86d63da0b4b6fc28df", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "d554e6d3ef9709ad7d7c848633901089", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "967375f76d43831afd7e10208502dcc1", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "7dba8e8a0b8ffbe7f8d73b33b0c41ed5", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "11ccabb65197c9f2e3059ac434888e11", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "8d79aece6620eb017896a7e816a78f0d", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "a3331c3d60962e7f0c2b62728bf7f43e", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "1636f9569ee8b8a6c0abed5c9e31e3f7", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "3fc153ee973090358916b90938429931", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "1636f9569ee8b8a6c0abed5c9e31e3f7", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "3fc153ee973090358916b90938429931", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1c7ed5b60c2961cf7d1a918157f90bce", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "0cbfd345b7e6a02d2a6bdfe7966d03d1", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1c7ed5b60c2961cf7d1a918157f90bce", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "0cbfd345b7e6a02d2a6bdfe7966d03d1", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "1360496e008c0d0fb631b2fde94e0d54", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "f64f8060f46a1f7088c7aadef33220dd", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "1360496e008c0d0fb631b2fde94e0d54", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "f64f8060f46a1f7088c7aadef33220dd", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "dc45874c7796f4fc740c224243efac28", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c7e7528347b1ec5bc37b13ed8ae88df1", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "abeca8c975a6cd5766fc90df99e8dcd1", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c7e7528347b1ec5bc37b13ed8ae88df1", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "7aa3fa305f66461ec5e5bbc550aa742d", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "89c02f2300860fded6b44855f9b8407f", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "69f97da125d43fc396eeaea8013cb133", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "9e56ac32e0cc2785811a162de68c69da", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a62570a46fed2002590be0bafe5055e8", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "b2a10b1eb917197da8f981d5a5daed44", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "c3af2f896ddb7a0b5f2ee2f35bac0318", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "30628de8aa6a7d9cfccf09f102ff9953", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "48b5a58b401a2b22b88491f7bcd0e22a", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "6d83849db3e1398503e2bb682eb4323e", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "b054c284b778dc77edc9c9b046303f46", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b16789743a603fac02763c09bbca446b", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "ab0a78d42cc511b4041478205e892897", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4bbb41936ffe72a7fe9bdc803761b937", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "a1427251545496f84c4d4e2d90e6e25a", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "729bec30bafe25cac07f920c0cc30ab8", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f8b086fdf6bca929ebc75b117b80f522", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "7d8f3ffe791e5a665ecbb2c517483814", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "5f4207138b152a110593c6c5ea8a9b32", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "4211f250197995e7df6942d32cffd202", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "40be1e38cbac3baf88dee161eeb912e1", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "d4aacb95a1855e969d1cc8db33732c40", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "570f49dc1de66e3fe75d76ee5f9306e0", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "ad319e6fb3cd7043a597f0780de42a98", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "4c15b26f43b4cb81f433beeb927c8aa6", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "a06aaa7a95abb56d49ba7925cc503a28", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "82619b88184faf3ef7ae4bf41ea282ce", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "61251e6fe58347224750fdf30d4bf8bc", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "bbd5b31cd9b4d30e48ce46e2cf968fcf", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "bbd5b31cd9b4d30e48ce46e2cf968fcf", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "b483573e1ef7e6be1090c67187e9d1d8", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "b483573e1ef7e6be1090c67187e9d1d8", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "cf4e9ef8006953c365b0928c68f5a21b", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "cf4e9ef8006953c365b0928c68f5a21b", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "840e96e79a56393c353184475cf28fbc", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "d0940a2237c33b922cf3747cbf3910ef", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "52ccfcbba95dcf3d06620748690446be", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "8369a217dc8cd95db308851de9f35d86", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6bc8e9e67a0cbe50ab2c6891d454570f", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "2ce9d1659647bca4725f404d192c3554", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "8fd2fd1ec12170942823e809332e8cb9", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "f40bd1a61620168791b88901975ea8ee", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4e3f244ac43cd400ffdbd2ac2e887399", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "a5e5e62c259e23429eca4af7054cc7cb", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101", "src/ballistica/base/mgen/pyembed/binding_base.inc": "bb96031e3f844704fcc9a0549a6d2c41", diff --git a/CHANGELOG.md b/CHANGELOG.md index 37afc519..eda3bcdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.28 (build 21479, api 8, 2023-10-17) +### 1.7.28 (build 21486, api 8, 2023-10-20) - 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 diff --git a/ballisticakit-cmake/CMakeLists.txt b/ballisticakit-cmake/CMakeLists.txt index b9050756..297cfe0b 100644 --- a/ballisticakit-cmake/CMakeLists.txt +++ b/ballisticakit-cmake/CMakeLists.txt @@ -353,6 +353,10 @@ set(BALLISTICA_SOURCES ${BA_SRC_ROOT}/ballistica/base/graphics/support/camera.h ${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.cc ${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.h + ${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_client_context.cc + ${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_client_context.h + ${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_settings.cc + ${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_settings.h ${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.cc ${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.h ${BA_SRC_ROOT}/ballistica/base/graphics/support/render_command_buffer.h @@ -688,6 +692,7 @@ set(BALLISTICA_SOURCES ${BA_SRC_ROOT}/ballistica/shared/generic/lambda_runnable.h ${BA_SRC_ROOT}/ballistica/shared/generic/runnable.cc ${BA_SRC_ROOT}/ballistica/shared/generic/runnable.h + ${BA_SRC_ROOT}/ballistica/shared/generic/snapshot.h ${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.cc ${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.h ${BA_SRC_ROOT}/ballistica/shared/generic/utf8.cc diff --git a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj index 3ec34dcc..affcd239 100644 --- a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj +++ b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj @@ -345,6 +345,10 @@ + + + + @@ -680,6 +684,7 @@ + diff --git a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters index f77535ec..b3e5ebdf 100644 --- a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters +++ b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters @@ -469,6 +469,18 @@ ballistica\base\graphics\support + + ballistica\base\graphics\support + + + ballistica\base\graphics\support + + + ballistica\base\graphics\support + + + ballistica\base\graphics\support + ballistica\base\graphics\support @@ -1474,6 +1486,9 @@ ballistica\shared\generic + + ballistica\shared\generic + ballistica\shared\generic diff --git a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj index 505b71c1..82d010b3 100644 --- a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj +++ b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj @@ -340,6 +340,10 @@ + + + + @@ -675,6 +679,7 @@ + diff --git a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters index f77535ec..b3e5ebdf 100644 --- a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters +++ b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters @@ -469,6 +469,18 @@ ballistica\base\graphics\support + + ballistica\base\graphics\support + + + ballistica\base\graphics\support + + + ballistica\base\graphics\support + + + ballistica\base\graphics\support + ballistica\base\graphics\support @@ -1474,6 +1486,9 @@ ballistica\shared\generic + + ballistica\shared\generic + ballistica\shared\generic diff --git a/src/assets/ba_data/python/babase/_env.py b/src/assets/ba_data/python/babase/_env.py index 1e386689..2af28c23 100644 --- a/src/assets/ba_data/python/babase/_env.py +++ b/src/assets/ba_data/python/babase/_env.py @@ -185,10 +185,8 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None: def _on_log(entry: LogEntry) -> None: # Forward this along to the engine to display in the in-app # console, in the Android log, etc. - _babase.display_log( - name=entry.name, - level=entry.level.name, - message=entry.message, + _babase.emit_log( + name=entry.name, level=entry.level.name, message=entry.message ) # We also want to feed some logs to the old v1-cloud-log system. diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index 9c68772d..b34bebc9 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21479 +TARGET_BALLISTICA_BUILD = 21486 TARGET_BALLISTICA_VERSION = '1.7.28' diff --git a/src/ballistica/base/app_adapter/app_adapter.cc b/src/ballistica/base/app_adapter/app_adapter.cc index 566383ac..a7f1cf89 100644 --- a/src/ballistica/base/app_adapter/app_adapter.cc +++ b/src/ballistica/base/app_adapter/app_adapter.cc @@ -2,7 +2,7 @@ #include "ballistica/base/app_adapter/app_adapter.h" -#if BA_OSTYPE_ANDROID +#if BA_OSTYPE_ANDROID // Remove conditional once android sources are public. #include "ballistica/base/app_adapter/app_adapter_android.h" #endif #include "ballistica/base/app_adapter/app_adapter_apple.h" @@ -305,4 +305,14 @@ void AppAdapter::DoSoftQuit() { FatalError("Fixme unimplemented."); } void AppAdapter::TerminateApp() { FatalError("Fixme unimplemented."); } auto AppAdapter::HasDirectKeyboardInput() -> bool { return false; } +void AppAdapter::ApplyGraphicsSettings(const GraphicsSettings* settings) {} + +auto AppAdapter::GetGraphicsSettings() -> GraphicsSettings* { + return new GraphicsSettings(); +} + +auto AppAdapter::GetGraphicsClientContext() -> GraphicsClientContext* { + return new GraphicsClientContext(); +} + } // namespace ballistica::base diff --git a/src/ballistica/base/app_adapter/app_adapter.h b/src/ballistica/base/app_adapter/app_adapter.h index 67879ca8..606b5113 100644 --- a/src/ballistica/base/app_adapter/app_adapter.h +++ b/src/ballistica/base/app_adapter/app_adapter.h @@ -30,6 +30,16 @@ class AppAdapter { virtual void OnScreenSizeChange(); virtual void DoApplyAppConfig(); + /// When called, should allocate an instance of a GraphicsSettings + /// subclass using 'new', fill it out, and return it. Runs in the logic + /// thread. + virtual auto GetGraphicsSettings() -> GraphicsSettings*; + + /// When called, should allocate an instance of a GraphicsClientContext + /// subclass using 'new', fill it out, and return it. Runs in the graphics + /// context. + virtual auto GetGraphicsClientContext() -> GraphicsClientContext*; + /// Return whether this class manages the main thread event loop itself. /// Default is true. If this is true, RunMainThreadEventLoopToCompletion() /// will be called to run the app. This should return false on builds @@ -181,6 +191,17 @@ class AppAdapter { /// should be callable from any thread. virtual auto HasDirectKeyboardInput() -> bool; + /// Called in the graphics context to apply new settings coming in from + /// the logic subsystem. This will be called initially to jump-start the + /// graphics system as well as before frame draws to update any new + /// settings coming in. + /// + /// Whenever graphics settings will result in a change to the graphics + /// context (ie: something visible to rendering code in the logic thread) + /// the adapter should call g_base->graphics_server->set_context() with + /// the updated context. + virtual void ApplyGraphicsSettings(const GraphicsSettings* settings); + protected: AppAdapter(); virtual ~AppAdapter(); diff --git a/src/ballistica/base/app_adapter/app_adapter_apple.cc b/src/ballistica/base/app_adapter/app_adapter_apple.cc index 53c4f771..69b36034 100644 --- a/src/ballistica/base/app_adapter/app_adapter_apple.cc +++ b/src/ballistica/base/app_adapter/app_adapter_apple.cc @@ -44,58 +44,27 @@ void AppAdapterApple::DoPushMainThreadRunnable(Runnable* runnable) { BallisticaKit::FromCppPushRawRunnableToMain(runnable); } -void AppAdapterApple::DoApplyAppConfig() { - assert(g_base->InLogicThread()); +void AppAdapterApple::DoApplyAppConfig() { assert(g_base->InLogicThread()); } - g_base->graphics_server->PushSetScreenPixelScaleCall( - g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale)); +void AppAdapterApple::ApplyGraphicsSettings(const GraphicsSettings* settings) { + auto* graphics_server = g_base->graphics_server; - auto graphics_quality_requested = - g_base->graphics->GraphicsQualityFromAppConfig(); - - auto texture_quality_requested = - g_base->graphics->TextureQualityFromAppConfig(); - - g_base->app_adapter->PushGraphicsContextCall([=] { - SetScreen_(texture_quality_requested, graphics_quality_requested); - }); -} - -void AppAdapterApple::SetScreen_( - TextureQualityRequest texture_quality_requested, - GraphicsQualityRequest graphics_quality_requested) { - // If we know what we support, filter our request types to what is - // supported. This will keep us from rebuilding contexts if request type - // is flipping between different types that we don't support. - if (g_base->graphics->has_supports_high_quality_graphics_value()) { - if (!g_base->graphics->supports_high_quality_graphics() - && graphics_quality_requested > GraphicsQualityRequest::kMedium) { - graphics_quality_requested = GraphicsQualityRequest::kMedium; - } - } - - auto* gs = g_base->graphics_server; + // We need a full renderer reload if quality values have changed + // or if we don't have a renderer yet. + bool need_full_reload = ((graphics_server->texture_quality_requested() + != settings->texture_quality) + || (graphics_server->graphics_quality_requested() + != settings->graphics_quality)); // We need a full renderer reload if quality values have changed or if we - // don't have one yet. - bool need_full_reload = - ((gs->texture_quality_requested() != texture_quality_requested) - || (gs->graphics_quality_requested() != graphics_quality_requested) - || !gs->texture_quality_set() || !gs->graphics_quality_set()); + // don't yet have a renderer. if (need_full_reload) { - ReloadRenderer_(graphics_quality_requested, texture_quality_requested); + ReloadRenderer_(settings); } - - // Let the logic thread know we've got a graphics system up and running. - // It may use this cue to kick off asset loads and other bootstrapping. - g_base->logic->event_loop()->PushCall( - [] { g_base->logic->OnGraphicsReady(); }); } -void AppAdapterApple::ReloadRenderer_( - GraphicsQualityRequest graphics_quality_requested, - TextureQualityRequest texture_quality_requested) { +void AppAdapterApple::ReloadRenderer_(const GraphicsSettings* settings) { auto* gs = g_base->graphics_server; if (gs->renderer() && gs->renderer_loaded()) { @@ -109,11 +78,11 @@ void AppAdapterApple::ReloadRenderer_( // along the latest real resolution just before each frame draw, but we // need *something* here or else we'll get errors due to framebuffers // getting made at size 0/etc. - g_base->graphics_server->SetScreenResolution(320.0, 240.0); + // g_base->graphics_server->SetScreenResolution(320.0, 240.0); // Update graphics quality based on request. - gs->set_graphics_quality_requested(graphics_quality_requested); - gs->set_texture_quality_requested(texture_quality_requested); + gs->set_graphics_quality_requested(settings->graphics_quality); + gs->set_texture_quality_requested(settings->texture_quality); // (Re)load stuff with these latest quality settings. gs->LoadRenderer(); @@ -123,12 +92,6 @@ void AppAdapterApple::UpdateScreenSizes_() { assert(g_base->app_adapter->InGraphicsContext()); } -void AppAdapterApple::SetScreenResolution(float pixel_width, - float pixel_height) { - auto allow = ScopedAllowGraphics_(this); - g_base->graphics_server->SetScreenResolution(pixel_width, pixel_height); -} - auto AppAdapterApple::TryRender() -> bool { auto allow = ScopedAllowGraphics_(this); @@ -146,10 +109,45 @@ auto AppAdapterApple::TryRender() -> bool { call->RunAndLogErrors(); delete call; } - // Lastly render. - return g_base->graphics_server->TryRender(); - return true; + // Lastly, render. + auto result = g_base->graphics_server->TryRender(); + + // A little trick to make mac resizing look a lot smoother. Because we + // render in a background thread, we often don't render at the most up to + // date window size during a window resize. Normally this makes our image + // jerk around in an ugly way, but if we just re-render once or twice in + // those cases we mostly always get the most up to date window size. + if (result && resize_friendly_frames_ > 0) { + // Leave this enabled for just a few frames every time it is set. + // (so just in case it breaks we won't draw each frame serveral times for + // eternity). + resize_friendly_frames_ -= 1; + + // Keep on drawing until the drawn window size + // matches what we have (or until we try for too long or fail at drawing). + seconds_t start_time = g_core->GetAppTimeSeconds(); + for (int i = 0; i < 5; ++i) { + if (((std::abs(resize_target_resolution_.x + - g_base->graphics_server->screen_pixel_width()) + > 0.01f) + || (std::abs(resize_target_resolution_.y + - g_base->graphics_server->screen_pixel_height()) + > 0.01f)) + && g_core->GetAppTimeSeconds() - start_time < 0.1 && result) { + result = g_base->graphics_server->TryRender(); + } else { + break; + } + } + } + + return result; +} + +void AppAdapterApple::EnableResizeFriendlyMode(int width, int height) { + resize_friendly_frames_ = 5; + resize_target_resolution_ = Vector2f(width, height); } auto AppAdapterApple::InGraphicsContext() -> bool { diff --git a/src/ballistica/base/app_adapter/app_adapter_apple.h b/src/ballistica/base/app_adapter/app_adapter_apple.h index 133966b1..d800759d 100644 --- a/src/ballistica/base/app_adapter/app_adapter_apple.h +++ b/src/ballistica/base/app_adapter/app_adapter_apple.h @@ -11,6 +11,7 @@ #include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/shared/generic/runnable.h" +#include "ballistica/shared/math/vector2f.h" namespace ballistica::base { @@ -32,7 +33,7 @@ class AppAdapterApple : public AppAdapter { auto TryRender() -> bool; /// Called by FromSwift. - void SetScreenResolution(float pixel_width, float pixel_height); + // void SetScreenResolution(float pixel_width, float pixel_height); auto FullscreenControlAvailable() const -> bool override; auto FullscreenControlGet() const -> bool override; @@ -42,6 +43,8 @@ class AppAdapterApple : public AppAdapter { auto HasDirectKeyboardInput() -> bool override; + void EnableResizeFriendlyMode(int width, int height); + protected: void DoPushMainThreadRunnable(Runnable* runnable) override; void DoPushGraphicsContextRunnable(Runnable* runnable) override; @@ -50,16 +53,18 @@ class AppAdapterApple : public AppAdapter { auto HasHardwareCursor() -> bool override; void SetHardwareCursorVisible(bool visible) override; void TerminateApp() override; + void ApplyGraphicsSettings(const GraphicsSettings* settings) override; private: - void UpdateScreenSizes_(); class ScopedAllowGraphics_; - void SetScreen_(TextureQualityRequest texture_quality_requested, - GraphicsQualityRequest graphics_quality_requested); - void ReloadRenderer_(GraphicsQualityRequest graphics_quality_requested, - TextureQualityRequest texture_quality_requested); + + void UpdateScreenSizes_(); + void ReloadRenderer_(const GraphicsSettings* settings); + std::thread::id graphics_thread_{}; - bool graphics_allowed_; + bool graphics_allowed_ : 1 {}; + uint8_t resize_friendly_frames_{}; + Vector2f resize_target_resolution_{-1.0f, -1.0f}; std::mutex graphics_calls_mutex_; std::vector graphics_calls_; }; diff --git a/src/ballistica/base/app_adapter/app_adapter_headless.cc b/src/ballistica/base/app_adapter/app_adapter_headless.cc index 2b8339bb..bc313f33 100644 --- a/src/ballistica/base/app_adapter/app_adapter_headless.cc +++ b/src/ballistica/base/app_adapter/app_adapter_headless.cc @@ -4,6 +4,7 @@ #include "ballistica/base/app_adapter/app_adapter_headless.h" #include "ballistica/base/graphics/graphics_server.h" +#include "ballistica/base/graphics/support/graphics_client_context.h" #include "ballistica/shared/ballistica.h" namespace ballistica::base { @@ -19,12 +20,7 @@ void AppAdapterHeadless::OnMainThreadStartApp() { new EventLoop(EventLoopID::kMain, ThreadSource::kWrapCurrent); } -void AppAdapterHeadless::DoApplyAppConfig() { - // Normal graphical app-adapters kick off screen creation here - // which then leads to remaining app bootstrapping. We create - // a 'null' screen purely for the same effect. - PushMainThreadCall([] { g_base->graphics_server->SetNullGraphics(); }); -} +void AppAdapterHeadless::DoApplyAppConfig() {} void AppAdapterHeadless::RunMainThreadEventLoopToCompletion() { assert(g_core->InMainThread()); @@ -40,6 +36,11 @@ void AppAdapterHeadless::DoExitMainThreadEventLoop() { main_event_loop_->Exit(); } +auto AppAdapterHeadless::GetGraphicsClientContext() -> GraphicsClientContext* { + // Special dummy form. + return new GraphicsClientContext(0); +} + } // namespace ballistica::base #endif // BA_HEADLESS_BUILD diff --git a/src/ballistica/base/app_adapter/app_adapter_headless.h b/src/ballistica/base/app_adapter/app_adapter_headless.h index 6d687bb2..2df675ea 100644 --- a/src/ballistica/base/app_adapter/app_adapter_headless.h +++ b/src/ballistica/base/app_adapter/app_adapter_headless.h @@ -17,6 +17,8 @@ class AppAdapterHeadless : public AppAdapter { void DoApplyAppConfig() override; + auto GetGraphicsClientContext() -> GraphicsClientContext* override; + protected: void DoPushMainThreadRunnable(Runnable* runnable) override; void RunMainThreadEventLoopToCompletion() override; diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.cc b/src/ballistica/base/app_adapter/app_adapter_sdl.cc index 51552e14..a77ee149 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.cc +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.cc @@ -104,32 +104,115 @@ void AppAdapterSDL::OnMainThreadStartApp() { SDL_ShowCursor(SDL_DISABLE); } +/// Our particular flavor of graphics settings. +struct AppAdapterSDL::GraphicsSettings_ : public GraphicsSettings { + bool fullscreen = g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen); + VSyncRequest vsync = g_base->graphics->VSyncFromAppConfig(); + int max_fps = g_base->app_config->Resolve(AppConfig::IntID::kMaxFPS); +}; + void AppAdapterSDL::DoApplyAppConfig() { assert(g_base->InLogicThread()); - g_base->graphics_server->PushSetScreenPixelScaleCall( - g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale)); - - auto graphics_quality_requested = - g_base->graphics->GraphicsQualityFromAppConfig(); - - auto texture_quality_requested = - g_base->graphics->TextureQualityFromAppConfig(); - // Android res string. // std::string android_res = // g_base->app_config->Resolve(AppConfig::StringID::kResolutionAndroid); +} - bool fullscreen = g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen); +auto AppAdapterSDL::GetGraphicsSettings() -> GraphicsSettings* { + assert(g_base->InLogicThread()); + return new GraphicsSettings_(); +} - auto vsync = g_base->graphics->VSyncFromAppConfig(); - int max_fps = g_base->app_config->Resolve(AppConfig::IntID::kMaxFPS); +void AppAdapterSDL::ApplyGraphicsSettings( + const GraphicsSettings* settings_base) { + assert(g_core->InMainThread()); + assert(!g_core->HeadlessMode()); - // Tell the main thread to set up the screen with these settings. - g_base->app_adapter->PushMainThreadCall([=] { - SetScreen_(fullscreen, max_fps, vsync, texture_quality_requested, - graphics_quality_requested); - }); + // In strict mode, allow graphics stuff while in here. + auto allow = ScopedAllowGraphics_(this); + + // Settings will always be our subclass (since we created it). + auto* settings = static_cast(settings_base); + + // Apply any changes. + bool do_toggle_fs{}; + bool do_set_existing_fullscreen{}; + + auto* graphics_server = g_base->graphics_server; + + // We need a full renderer reload if quality values have changed + // or if we don't have a renderer yet. + bool need_full_reload = ((sdl_window_ == nullptr + || graphics_server->texture_quality_requested() + != settings->texture_quality) + || (graphics_server->graphics_quality_requested() + != settings->graphics_quality)); + + if (need_full_reload) { + ReloadRenderer_(settings); + } else if (settings->fullscreen != fullscreen_) { + SDL_SetWindowFullscreen( + sdl_window_, settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + fullscreen_ = settings->fullscreen; + } + + // VSync always gets set independent of the screen (though we set it down + // here to make sure we have a screen when its set). + VSync vsync; + switch (settings->vsync) { + case VSyncRequest::kNever: + vsync = VSync::kNever; + break; + case VSyncRequest::kAlways: + vsync = VSync::kAlways; + break; + case VSyncRequest::kAuto: + vsync = VSync::kAdaptive; + break; + default: + vsync = VSync::kNever; + break; + } + if (vsync != vsync_) { + switch (vsync) { + case VSync::kUnset: + case VSync::kNever: { + SDL_GL_SetSwapInterval(0); + vsync_actually_enabled_ = false; + break; + } + case VSync::kAlways: { + SDL_GL_SetSwapInterval(1); + vsync_actually_enabled_ = true; + break; + } + case VSync::kAdaptive: { + // In this case, let's try setting to 'adaptive' and turn it off if + // that is unsupported. + auto result = SDL_GL_SetSwapInterval(-1); + if (result == 0) { + vsync_actually_enabled_ = true; + } else { + SDL_GL_SetSwapInterval(0); + vsync_actually_enabled_ = false; + } + break; + } + } + vsync_ = vsync; + } + + // This we can set anytime. Probably could have just set it from the logic + // thread where we read it, but let's be pedantic and keep everything to + // the main thread. + max_fps_ = settings->max_fps; + + // Take -1 to mean no max. Otherwise clamp to a reasonable range. + if (max_fps_ != -1) { + max_fps_ = std::max(10, max_fps_); + max_fps_ = std::min(99999, max_fps_); + } } void AppAdapterSDL::RunMainThreadEventLoopToCompletion() { @@ -158,7 +241,7 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() { auto AppAdapterSDL::TryRender() -> bool { if (strict_graphics_context_) { - // In strict mode, allow graphics stuff in here. Normally we allow it + // In strict mode, allow graphics stuff in here. Otherwise we allow it // anywhere in the main thread. auto allow = ScopedAllowGraphics_(this); @@ -179,7 +262,7 @@ auto AppAdapterSDL::TryRender() -> bool { // Lastly render. return g_base->graphics_server->TryRender(); } else { - // Simple path; just render. + // Simpler path; just render. return g_base->graphics_server->TryRender(); } } @@ -188,7 +271,7 @@ void AppAdapterSDL::SleepUntilNextEventCycle_(microsecs_t cycle_start_time) { // Special case: if we're hidden, we simply sleep for a long bit; no fancy // timing. if (hidden_) { - g_core->platform->SleepMillisecs(100); + g_core->platform->SleepSeconds(0.1); return; } @@ -359,7 +442,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) { break; case SDL_QUIT: - if (g_core->GetAppTimeMillisecs() - last_windowevent_close_time_ < 100) { + if (g_core->GetAppTimeSeconds() - last_windowevent_close_time_ < 0.1) { // If they hit the window close button, skip the confirm. g_base->QuitApp(false); } else { @@ -380,7 +463,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) { case SDL_WINDOWEVENT_CLOSE: { // Simply note that this happened. We use this to adjust our // SDL_QUIT behavior (quit is called right after this). - last_windowevent_close_time_ = g_core->GetAppTimeMillisecs(); + last_windowevent_close_time_ = g_core->GetAppTimeSeconds(); break; } @@ -546,114 +629,96 @@ auto AppAdapterSDL::GetSDLJoystickInput_(int sdl_joystick_id) const return nullptr; // Epic fail. } -void AppAdapterSDL::SetScreen_( - bool fullscreen, int max_fps, VSyncRequest vsync_requested, - TextureQualityRequest texture_quality_requested, - GraphicsQualityRequest graphics_quality_requested) { - assert(g_core->InMainThread()); - assert(!g_core->HeadlessMode()); +// void AppAdapterSDL::ApplyGraphicsSettings_(const GraphicsSettings_* settings) +// { +// assert(g_core->InMainThread()); +// assert(!g_core->HeadlessMode()); - // In strict mode, allow graphics stuff in here. - auto allow = ScopedAllowGraphics_(this); +// // In strict mode, allow graphics stuff while in here. +// auto allow = ScopedAllowGraphics_(this); - // If we know what we support, filter our request types to what is - // supported. This will keep us from rebuilding contexts if request type - // is flipping between different types that we don't support. - if (g_base->graphics->has_supports_high_quality_graphics_value()) { - if (!g_base->graphics->supports_high_quality_graphics() - && graphics_quality_requested > GraphicsQualityRequest::kMedium) { - graphics_quality_requested = GraphicsQualityRequest::kMedium; - } - } +// bool do_toggle_fs{}; +// bool do_set_existing_fullscreen{}; - bool do_toggle_fs{}; - bool do_set_existing_fullscreen{}; +// auto* graphics_server = g_base->graphics_server; - auto* gs = g_base->graphics_server; +// // We need a full renderer reload if quality values have changed +// // or if we don't have a renderer yet. +// bool need_full_reload = ((sdl_window_ == nullptr +// || graphics_server->texture_quality_requested() +// != settings->texture_quality) +// || (graphics_server->graphics_quality_requested() +// != settings->graphics_quality)); - // We need a full renderer reload if quality values have changed - // or if we don't have one yet. - bool need_full_reload = - ((sdl_window_ == nullptr - || gs->texture_quality_requested() != texture_quality_requested) - || (gs->graphics_quality_requested() != graphics_quality_requested) - || !gs->texture_quality_set() || !gs->graphics_quality_set()); +// if (need_full_reload) { +// ReloadRenderer_(settings->fullscreen, settings->graphics_quality, +// settings->texture_quality); +// } else if (settings->fullscreen != fullscreen_) { +// SDL_SetWindowFullscreen( +// sdl_window_, settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : +// 0); +// fullscreen_ = settings->fullscreen; +// } - if (need_full_reload) { - ReloadRenderer_(fullscreen, graphics_quality_requested, - texture_quality_requested); - } else if (fullscreen != fullscreen_) { - SDL_SetWindowFullscreen(sdl_window_, - fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - fullscreen_ = fullscreen; - } +// // VSync always gets set independent of the screen (though we set it down +// // here to make sure we have a screen when its set). +// VSync vsync; +// switch (settings->vsync) { +// case VSyncRequest::kNever: +// vsync = VSync::kNever; +// break; +// case VSyncRequest::kAlways: +// vsync = VSync::kAlways; +// break; +// case VSyncRequest::kAuto: +// vsync = VSync::kAdaptive; +// break; +// default: +// vsync = VSync::kNever; +// break; +// } +// if (vsync != vsync_) { +// switch (vsync) { +// case VSync::kUnset: +// case VSync::kNever: { +// SDL_GL_SetSwapInterval(0); +// vsync_actually_enabled_ = false; +// break; +// } +// case VSync::kAlways: { +// SDL_GL_SetSwapInterval(1); +// vsync_actually_enabled_ = true; +// break; +// } +// case VSync::kAdaptive: { +// // In this case, let's try setting to 'adaptive' and turn it off if +// // that is unsupported. +// auto result = SDL_GL_SetSwapInterval(-1); +// if (result == 0) { +// vsync_actually_enabled_ = true; +// } else { +// SDL_GL_SetSwapInterval(0); +// vsync_actually_enabled_ = false; +// } +// break; +// } +// } +// vsync_ = vsync; +// } - // VSync always gets set independent of the screen (though we set it down - // here to make sure we have a screen when its set). - VSync vsync; - switch (vsync_requested) { - case VSyncRequest::kNever: - vsync = VSync::kNever; - break; - case VSyncRequest::kAlways: - vsync = VSync::kAlways; - break; - case VSyncRequest::kAuto: - vsync = VSync::kAdaptive; - break; - default: - vsync = VSync::kNever; - break; - } - if (vsync != vsync_) { - switch (vsync) { - case VSync::kUnset: - case VSync::kNever: { - SDL_GL_SetSwapInterval(0); - vsync_actually_enabled_ = false; - break; - } - case VSync::kAlways: { - SDL_GL_SetSwapInterval(1); - vsync_actually_enabled_ = true; - break; - } - case VSync::kAdaptive: { - // In this case, let's try setting to 'adaptive' and turn it off if - // that is unsupported. - auto result = SDL_GL_SetSwapInterval(-1); - if (result == 0) { - vsync_actually_enabled_ = true; - } else { - SDL_GL_SetSwapInterval(0); - vsync_actually_enabled_ = false; - } - break; - } - } - vsync_ = vsync; - } +// // This we can set anytime. Probably could have just set it from the logic +// // thread where we read it, but let's be pedantic and keep everything to +// // the main thread. +// max_fps_ = settings->max_fps; - // This we can set anytime. Probably could have just set it from the logic - // thread where we read it, but let's be pedantic and keep everything to - // the main thread. - max_fps_ = max_fps; +// // Take -1 to mean no max. Otherwise clamp to a reasonable range. +// if (max_fps_ != -1) { +// max_fps_ = std::max(10, max_fps_); +// max_fps_ = std::min(99999, max_fps_); +// } +// } - // Take -1 to mean no max. Otherwise clamp to a reasonable range. - if (max_fps_ != -1) { - max_fps_ = std::max(10, max_fps_); - max_fps_ = std::min(99999, max_fps_); - } - - // Let the logic thread know we've got a graphics system up and running. - // It may use this cue to kick off asset loads and other bootstrapping. - g_base->logic->event_loop()->PushCall( - [] { g_base->logic->OnGraphicsReady(); }); -} - -void AppAdapterSDL::ReloadRenderer_( - bool fullscreen, GraphicsQualityRequest graphics_quality_requested, - TextureQualityRequest texture_quality_requested) { +void AppAdapterSDL::ReloadRenderer_(const GraphicsSettings_* settings) { assert(g_base->app_adapter->InGraphicsContext()); auto* gs = g_base->graphics_server; @@ -664,7 +729,7 @@ void AppAdapterSDL::ReloadRenderer_( // If we don't haven't yet, create our window and renderer. if (!sdl_window_) { - fullscreen_ = fullscreen; + fullscreen_ = settings->fullscreen; // A reasonable default window size. auto width = static_cast(kBaseVirtualResX * 0.8f); @@ -672,7 +737,7 @@ void AppAdapterSDL::ReloadRenderer_( uint32_t flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; - if (fullscreen) { + if (settings->fullscreen) { flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } @@ -729,15 +794,16 @@ void AppAdapterSDL::ReloadRenderer_( } } - // Update graphics quality based on request. - gs->set_graphics_quality_requested(graphics_quality_requested); - gs->set_texture_quality_requested(texture_quality_requested); + // Update graphics-server's qualities based on request. + gs->set_graphics_quality_requested(settings->graphics_quality); + gs->set_texture_quality_requested(settings->texture_quality); gs->LoadRenderer(); } void AppAdapterSDL::UpdateScreenSizes_() { - assert(g_base->app_adapter->InGraphicsContext()); + // This runs in the main thread in response to SDL events. + assert(g_core->InMainThread()); // Grab logical window dimensions (points?). This is the coordinate space // SDL's events deal in. @@ -749,8 +815,13 @@ void AppAdapterSDL::UpdateScreenSizes_() { // dimensions. int pixels_x, pixels_y; SDL_GL_GetDrawableSize(sdl_window_, &pixels_x, &pixels_y); - g_base->graphics_server->SetScreenResolution(static_cast(pixels_x), - static_cast(pixels_y)); + + // Push this over to the logic thread which owns the canonical value + // for this. + g_base->logic->event_loop()->PushCall([pixels_x, pixels_y] { + g_base->graphics->SetScreenResolution(static_cast(pixels_x), + static_cast(pixels_y)); + }); } auto AppAdapterSDL::InGraphicsContext() -> bool { diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.h b/src/ballistica/base/app_adapter/app_adapter_sdl.h index 9f57b127..585c29f1 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.h +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.h @@ -41,6 +41,9 @@ class AppAdapterSDL : public AppAdapter { auto SupportsMaxFPS() -> bool const override; auto HasDirectKeyboardInput() -> bool override; + void ApplyGraphicsSettings(const GraphicsSettings* settings) override; + + auto GetGraphicsSettings() -> GraphicsSettings* override; protected: void DoPushMainThreadRunnable(Runnable* runnable) override; @@ -52,14 +55,11 @@ class AppAdapterSDL : public AppAdapter { private: class ScopedAllowGraphics_; - void SetScreen_(bool fullscreen, int max_fps, VSyncRequest vsync_requested, - TextureQualityRequest texture_quality_requested, - GraphicsQualityRequest graphics_quality_requested); + struct GraphicsSettings_; + void HandleSDLEvent_(const SDL_Event& event); void UpdateScreenSizes_(); - void ReloadRenderer_(bool fullscreen, - GraphicsQualityRequest graphics_quality_requested, - TextureQualityRequest texture_quality_requested); + void ReloadRenderer_(const GraphicsSettings_* settings); void OnSDLJoystickAdded_(int index); void OnSDLJoystickRemoved_(int index); // Given an SDL joystick ID, returns our Ballistica input for it. @@ -70,6 +70,7 @@ class AppAdapterSDL : public AppAdapter { void RemoveSDLInputDevice_(int index); void SleepUntilNextEventCycle_(microsecs_t cycle_start_time); + int max_fps_{60}; bool done_ : 1 {}; bool fullscreen_ : 1 {}; bool vsync_actually_enabled_ : 1 {}; @@ -85,17 +86,16 @@ class AppAdapterSDL : public AppAdapter { /// that require such a setup. bool strict_graphics_context_ : 1 {}; bool strict_graphics_allowed_ : 1 {}; - std::mutex strict_graphics_calls_mutex_; - std::vector strict_graphics_calls_; VSync vsync_{VSync::kUnset}; uint32_t sdl_runnable_event_id_{}; - int max_fps_{60}; + std::mutex strict_graphics_calls_mutex_; + std::vector strict_graphics_calls_; microsecs_t oversleep_{}; std::vector sdl_joysticks_; Vector2f window_size_{1.0f, 1.0f}; SDL_Window* sdl_window_{}; void* sdl_gl_context_{}; - millisecs_t last_windowevent_close_time_{}; + seconds_t last_windowevent_close_time_{}; }; } // namespace ballistica::base diff --git a/src/ballistica/base/app_adapter/app_adapter_vr.cc b/src/ballistica/base/app_adapter/app_adapter_vr.cc index bfa614cd..703dbca2 100644 --- a/src/ballistica/base/app_adapter/app_adapter_vr.cc +++ b/src/ballistica/base/app_adapter/app_adapter_vr.cc @@ -40,7 +40,8 @@ void AppAdapterVR::PushVRSimpleRemoteStateCall( } void AppAdapterVR::VRSetDrawDimensions(int w, int h) { - g_base->graphics_server->SetScreenResolution(w, h); + FatalError("FIXME UPDATE SET-SCREEN-RESOLUTION"); + // g_base->graphics_server->SetScreenResolution(w, h); } void AppAdapterVR::VRPreDraw() { diff --git a/src/ballistica/base/app_mode/app_mode.cc b/src/ballistica/base/app_mode/app_mode.cc index 5953f720..e87ab5a6 100644 --- a/src/ballistica/base/app_mode/app_mode.cc +++ b/src/ballistica/base/app_mode/app_mode.cc @@ -36,8 +36,6 @@ void AppMode::HandleGameQuery(const char* buffer, size_t size, auto AppMode::DoesWorldFillScreen() -> bool { return false; } -void AppMode::GraphicsQualityChanged(GraphicsQuality quality) {} - void AppMode::DrawWorld(FrameDef* frame_def) {} void AppMode::ChangeGameSpeed(int offs) {} diff --git a/src/ballistica/base/app_mode/app_mode.h b/src/ballistica/base/app_mode/app_mode.h index 119f7659..5a12fa0d 100644 --- a/src/ballistica/base/app_mode/app_mode.h +++ b/src/ballistica/base/app_mode/app_mode.h @@ -62,8 +62,6 @@ class AppMode { virtual void DrawWorld(FrameDef* frame_def); - virtual void GraphicsQualityChanged(GraphicsQuality quality); - /// Called whenever screen size changes. virtual void OnScreenSizeChange(); diff --git a/src/ballistica/base/assets/assets.cc b/src/ballistica/base/assets/assets.cc index a8da1408..e00b84f3 100644 --- a/src/ballistica/base/assets/assets.cc +++ b/src/ballistica/base/assets/assets.cc @@ -82,8 +82,9 @@ void Assets::StartLoading() { assert(g_base); assert(g_base->audio_server && g_base->assets_server && g_base->graphics_server); - assert(g_base->graphics_server->texture_compression_types_are_set()); - assert(g_base->graphics_server->texture_quality_set()); + assert(g_base->graphics->has_client_context()); + // assert(g_base->graphics_server->texture_compression_types_are_set()); + // assert(g_base->graphics_server->texture_quality_set()); assert(!asset_loads_allowed_); // We should only be called once. asset_loads_allowed_ = true; @@ -1102,10 +1103,13 @@ auto Assets::FindAssetFile(FileType type, const std::string& name) } } - assert(g_base->graphics_server - && g_base->graphics_server->texture_compression_types_are_set()); - assert(g_base->graphics_server - && g_base->graphics_server->texture_quality_set()); + // Make sure we know what compression/quality to use. + assert(g_base->graphics->has_client_context()); + // assert(g_base->graphics_server + // && + // g_base->graphics_server->texture_compression_types_are_set()); + // assert(g_base->graphics_server + // && g_base->graphics_server->texture_quality_set()); prefix = "textures/"; #if BA_OSTYPE_ANDROID && !BA_ANDROID_DDS_BUILD diff --git a/src/ballistica/base/assets/assets.h b/src/ballistica/base/assets/assets.h index 6db00287..cbfa7ba1 100644 --- a/src/ballistica/base/assets/assets.h +++ b/src/ballistica/base/assets/assets.h @@ -136,20 +136,21 @@ class Assets { std::unordered_map >* c_list) -> Object::Ref; - std::vector asset_paths_; + int language_state_{}; bool have_pending_loads_[static_cast(AssetType::kLast)]{}; + + // Will be true while a AssetListLock exists. Good to debug-verify this + // during any asset list access. + bool asset_lists_locked_ : 1 {}; + bool asset_loads_allowed_ : 1 {}; + bool sys_assets_loaded_ : 1 {}; + + std::vector asset_paths_; std::unordered_map packages_; // For use by AssetListLock; don't manually acquire. std::mutex asset_lists_mutex_; - // Will be true while a AssetListLock exists. Good to debug-verify this - // during any asset list access. - bool asset_lists_locked_{}; - - // 'hard-wired' internal assets - bool asset_loads_allowed_{}; - bool sys_assets_loaded_{}; std::vector > system_textures_; std::vector > system_cube_map_textures_; std::vector > system_sounds_; @@ -177,7 +178,6 @@ class Assets { // Text & Language (need to mold this into more asset-like concepts). std::mutex language_mutex_; std::unordered_map language_; - int language_state_{}; std::mutex special_char_mutex_; std::unordered_map special_char_strings_; }; diff --git a/src/ballistica/base/assets/assets_server.cc b/src/ballistica/base/assets/assets_server.cc index e5352ddb..0fddc71f 100644 --- a/src/ballistica/base/assets/assets_server.cc +++ b/src/ballistica/base/assets/assets_server.cc @@ -4,6 +4,7 @@ #include "ballistica/base/assets/asset.h" #include "ballistica/base/assets/assets.h" +#include "ballistica/base/graphics/graphics.h" #include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/support/huffman.h" #include "ballistica/shared/foundation/event_loop.h" @@ -221,12 +222,18 @@ void AssetsServer::WriteReplayMessages() { void AssetsServer::Process() { // Make sure we don't do any loading until we know what kind/quality of // textures we'll be loading. - if (!g_base->assets || !g_base->graphics_server - || !g_base->graphics_server - ->texture_compression_types_are_set() // NOLINT - || !g_base->graphics_server->texture_quality_set()) { + + // FIXME - we'll need to revisit this when adding support for + // renderer switches, since this is not especially thread-safe. + + if (!g_base->graphics->has_client_context()) { return; } + // if (!g_base->assets || + // || !g_base->graphics->texture_compression_types_are_set() // NOLINT + // || !g_base->graphics_server->texture_quality_set()) { + // return; + // } // Process exactly 1 preload item. Empty out our non-audio list first // (audio is less likely to cause noticeable hitches if it needs to be loaded diff --git a/src/ballistica/base/assets/texture_asset.cc b/src/ballistica/base/assets/texture_asset.cc index b43c13bb..aa757cb4 100644 --- a/src/ballistica/base/assets/texture_asset.cc +++ b/src/ballistica/base/assets/texture_asset.cc @@ -93,11 +93,14 @@ auto TextureAsset::GetNameFull() const -> std::string { void TextureAsset::DoPreload() { assert(valid_); - assert(g_base->graphics_server - && g_base->graphics_server->texture_compression_types_are_set()); + // Make sure we're not loading without knowing what texture types we + // support. + // assert(g_base->graphics->has_client_context()); + // assert(g_base->graphics_server + // && g_base->graphics_server->texture_compression_types_are_set()); - // We figure out which LOD should be our base level based on quality. - TextureQuality texture_quality = g_base->graphics_server->texture_quality(); + // Figure out which LOD should be our base level based on texture quality. + auto texture_quality = g_base->graphics->placeholder_texture_quality(); // If we're a text-texture. if (packer_.Exists()) { @@ -218,12 +221,14 @@ void TextureAsset::DoPreload() { &preload_datas_[0].base_level); // We should only be loading this if we support etc1 in hardware. - assert(g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kETC1)); + assert(g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kETC1)); // Decompress dxt1/dxt5 ones if we don't natively support S3TC. - if (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kS3TC)) { + if (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kS3TC)) { if ((preload_datas_[0].formats[preload_datas_[0].base_level] == TextureFormat::kDXT5) || (preload_datas_[0].formats[preload_datas_[0].base_level] @@ -241,8 +246,9 @@ void TextureAsset::DoPreload() { &preload_datas_[0].base_level); // Decompress dxt1/dxt5 if we don't natively support it. - if (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kS3TC)) { + if (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kS3TC)) { preload_datas_[0].ConvertToUncompressed(this); } } else if (!strcmp(file_name_full_.c_str() + file_name_size - 4, @@ -264,16 +270,18 @@ void TextureAsset::DoPreload() { == TextureFormat::kETC2_RGB) || (preload_datas_[0].formats[preload_datas_[0].base_level] == TextureFormat::kETC2_RGBA)) - && (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kETC2))) { + && (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kETC2))) { preload_datas_[0].ConvertToUncompressed(this); } // Decompress etc1 if we don't natively support it. if ((preload_datas_[0].formats[preload_datas_[0].base_level] == TextureFormat::kETC1) - && (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kETC1))) { + && (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kETC1))) { preload_datas_[0].ConvertToUncompressed(this); } @@ -287,8 +295,9 @@ void TextureAsset::DoPreload() { &preload_datas_[0].base_level); // We should only be loading this if we support pvr in hardware. - assert(g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kPVR)); + assert( + g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType(TextureCompressionType::kPVR)); } else if (!strcmp(file_name_full_.c_str() + file_name_size - 4, ".nop")) { // Dummy path for headless; nothing to do here. @@ -342,12 +351,14 @@ void TextureAsset::DoPreload() { } // We should only be loading this if we support etc1 in hardware. - assert(g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kETC1)); + assert(g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kETC1)); // Decompress dxt1/dxt5 ones if we don't natively support S3TC. - if (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kS3TC)) { + if (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kS3TC)) { if ((preload_datas_[d].formats[preload_datas_[d].base_level] == TextureFormat::kDXT5) || (preload_datas_[d].formats[preload_datas_[d].base_level] @@ -365,8 +376,9 @@ void TextureAsset::DoPreload() { &preload_datas_[d].base_level); // Decompress dxt1/dxt5 if we don't natively support it. - if (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kS3TC)) { + if (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kS3TC)) { preload_datas_[d].ConvertToUncompressed(this); } } else if (!strcmp(file_name_full_.c_str() + file_name_size - 4, @@ -383,16 +395,18 @@ void TextureAsset::DoPreload() { == TextureFormat::kETC2_RGB) || (preload_datas_[d].formats[preload_datas_[d].base_level] == TextureFormat::kETC2_RGBA)) - && (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kETC2))) { + && (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kETC2))) { preload_datas_[d].ConvertToUncompressed(this); } // Decompress etc1 if we don't natively support it. if ((preload_datas_[d].formats[preload_datas_[d].base_level] == TextureFormat::kETC1) - && (!g_base->graphics_server->SupportsTextureCompressionType( - TextureCompressionType::kETC1))) { + && (!g_base->graphics->placeholder_client_context() + ->SupportsTextureCompressionType( + TextureCompressionType::kETC1))) { preload_datas_[d].ConvertToUncompressed(this); } diff --git a/src/ballistica/base/audio/audio.cc b/src/ballistica/base/audio/audio.cc index f7daba43..f1ddb876 100644 --- a/src/ballistica/base/audio/audio.cc +++ b/src/ballistica/base/audio/audio.cc @@ -5,6 +5,7 @@ #include "ballistica/base/assets/sound_asset.h" #include "ballistica/base/audio/audio_server.h" #include "ballistica/base/audio/audio_source.h" +#include "ballistica/base/graphics/graphics.h" #include "ballistica/base/support/app_config.h" #include "ballistica/shared/foundation/event_loop.h" @@ -12,6 +13,19 @@ namespace ballistica::base { Audio::Audio() = default; +auto Audio::UseLowQualityAudio() -> bool { + assert(g_base->InLogicThread()); + // Currently just piggybacking off graphics quality here. + if (g_core->HeadlessMode() || g_base->graphics->has_client_context()) { + return true; + } + // We don't have a frame-def to look at so need to calc this ourself; ugh. + auto quality = Graphics::GraphicsQualityFromRequest( + g_base->graphics->settings()->graphics_quality, + g_base->graphics->client_context()->auto_graphics_quality); + return quality < GraphicsQuality::kMedium; +} + void Audio::Reset() { assert(g_base->InLogicThread()); g_base->audio_server->PushResetCall(); diff --git a/src/ballistica/base/audio/audio.h b/src/ballistica/base/audio/audio.h index d3126565..9c87daec 100644 --- a/src/ballistica/base/audio/audio.h +++ b/src/ballistica/base/audio/audio.h @@ -29,36 +29,41 @@ class Audio { virtual void OnScreenSizeChange(); virtual void StepDisplayTime(); + /// Can be keyed off of to cut corners in audio (leaving sounds out, etc.) + /// Currently just piggybacks off graphics quality settings but this logic + /// may get fancier in the future. + auto UseLowQualityAudio() -> bool; + void SetVolumes(float music_volume, float sound_volume); void SetListenerPosition(const Vector3f& p); void SetListenerOrientation(const Vector3f& forward, const Vector3f& up); void SetSoundPitch(float pitch); - // Return a pointer to a locked sound source, or nullptr if they're all busy. - // The sound source will be reset to standard settings (no loop, fade 1, pos - // 0,0,0, etc.). - // Send the source any immediate commands and then unlock it. - // For later modifications, re-retrieve the sound with GetPlayingSound() + /// Return a pointer to a locked sound source, or nullptr if they're all busy. + /// The sound source will be reset to standard settings (no loop, fade 1, pos + /// 0,0,0, etc.). + /// Send the source any immediate commands and then unlock it. + /// For later modifications, re-retrieve the sound with GetPlayingSound() auto SourceBeginNew() -> AudioSource*; - // If a sound play id is playing, locks and returns its sound source. - // on success, you must unlock the source once done with it. + /// If a sound play id is playing, locks and returns its sound source. + /// on success, you must unlock the source once done with it. auto SourceBeginExisting(uint32_t play_id, int debug_id) -> AudioSource*; - // Return true if the sound id is currently valid. This is not guaranteed - // to be super accurate, but can be used to determine if a sound is still - // playing. + /// Return true if the sound id is currently valid. This is not guaranteed + /// to be super accurate, but can be used to determine if a sound is still + /// playing. auto IsSoundPlaying(uint32_t play_id) -> bool; - // Simple one-shot play functions. + /// Simple one-shot play functions. auto PlaySound(SoundAsset* s, float volume = 1.0f) -> std::optional; auto PlaySoundAtPosition(SoundAsset* sound, float volume, float x, float y, float z) -> std::optional; - // Call this if you want to prevent repeated plays of the same sound. It'll - // tell you if the sound has been played recently. The one-shot sound-play - // functions use this under the hood. (PlaySound, PlaySoundAtPosition). + /// Call this if you want to prevent repeated plays of the same sound. It'll + /// tell you if the sound has been played recently. The one-shot sound-play + /// functions use this under the hood. (PlaySound, PlaySoundAtPosition). auto ShouldPlay(SoundAsset* s) -> bool; // Hmm; shouldn't these be accessed through the Source class? @@ -73,15 +78,15 @@ class Audio { } private: - // Flat list of client sources indexed by id. + /// Flat list of client sources indexed by id. std::vector client_sources_; - // List of sources that are ready to use. - // This is kept filled by the audio thread - // and used by the client. + /// List of sources that are ready to use. + /// This is kept filled by the audio thread + /// and used by the client. std::vector available_sources_; - // This must be locked whenever accessing the availableSources list. + /// This must be locked whenever accessing the availableSources list. std::mutex available_sources_mutex_; }; diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index 822b02fd..c834d38a 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -420,6 +420,10 @@ auto BaseFeatureSet::IsUnmodifiedBlessedBuild() -> bool { return false; } +auto BaseFeatureSet::InMainThread() const -> bool { + return g_core->InMainThread(); +} + auto BaseFeatureSet::InAssetsThread() const -> bool { if (auto* loop = assets_server->event_loop()) { return loop->ThreadIsCurrent(); diff --git a/src/ballistica/base/base.h b/src/ballistica/base/base.h index c2bd3a43..fa413c36 100644 --- a/src/ballistica/base/base.h +++ b/src/ballistica/base/base.h @@ -59,6 +59,8 @@ class DataAsset; class FrameDef; class Graphics; class GraphicsServer; +struct GraphicsSettings; +struct GraphicsClientContext; class Huffman; class ImageMesh; class Input; @@ -662,6 +664,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent, /// allowing certain functionality before this time. auto IsBaseCompletelyImported() -> bool; + auto InMainThread() const -> bool; auto InAssetsThread() const -> bool override; auto InLogicThread() const -> bool override; auto InAudioThread() const -> bool override; diff --git a/src/ballistica/base/dynamics/bg/bg_dynamics.cc b/src/ballistica/base/dynamics/bg/bg_dynamics.cc index a1b82815..231a964b 100644 --- a/src/ballistica/base/dynamics/bg/bg_dynamics.cc +++ b/src/ballistica/base/dynamics/bg/bg_dynamics.cc @@ -39,10 +39,15 @@ void BGDynamics::Emit(const BGDynamicsEmission& e) { void BGDynamics::Step(const Vector3f& cam_pos, int step_millisecs) { assert(g_base->InLogicThread()); + // Don't actually start doing anything until there's a + // client-graphics-context. We need this to calculate qualities/etc. + if (!g_base->graphics->has_client_context()) { + return; + } + // The BG dynamics thread just processes steps as fast as it can; // we need to throttle what we send or tell it to cut back if its behind int step_count = g_base->bg_dynamics_server->step_count(); - // printf("STEP COUNT %d\n", step_count); // If we're really getting behind, start pruning stuff. if (step_count > 3) { @@ -62,6 +67,9 @@ void BGDynamics::Step(const Vector3f& cam_pos, int step_millisecs) { // Pass a newly allocated raw pointer to the bg-dynamics thread; it takes care // of disposing it when done. auto d = Object::NewDeferred(); + d->graphics_quality = Graphics::GraphicsQualityFromRequest( + g_base->graphics->settings()->graphics_quality, + g_base->graphics->client_context()->auto_graphics_quality); d->step_millisecs = step_millisecs; d->cam_pos = cam_pos; @@ -174,7 +182,7 @@ void BGDynamics::Draw(FrameDef* frame_def) { // In high-quality, we draw in the overlay pass so that we don't get wiped // out by depth-of-field. - bool draw_in_overlay = (frame_def->quality() >= GraphicsQuality::kHigh); + bool draw_in_overlay = frame_def->quality() >= GraphicsQuality::kHigh; SpriteComponent c(draw_in_overlay ? frame_def->overlay_3d_pass() : frame_def->beauty_pass()); c.SetCameraAligned(true); @@ -232,7 +240,7 @@ void BGDynamics::Draw(FrameDef* frame_def) { tendrils_mesh_->SetIndexData(ds->tendril_indices); tendrils_mesh_->SetData( Object::Ref>(ds->tendril_vertices)); - bool draw_in_overlay = (frame_def->quality() >= GraphicsQuality::kHigh); + bool draw_in_overlay = frame_def->quality() >= GraphicsQuality::kHigh; SmokeComponent c(draw_in_overlay ? frame_def->overlay_3d_pass() : frame_def->beauty_pass()); c.SetOverlay(draw_in_overlay); diff --git a/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc b/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc index 424e46c5..d0ff57bf 100644 --- a/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc +++ b/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc @@ -2282,7 +2282,8 @@ void BGDynamicsServer::Step(StepData* step_data) { auto ref(Object::CompleteDeferred(step_data)); // Keep our quality in sync with the graphics thread's. - graphics_quality_ = g_base->graphics_server->graphics_quality(); + graphics_quality_ = step_data->graphics_quality; + assert(graphics_quality_ != GraphicsQuality::kUnset); cam_pos_ = step_data->cam_pos; diff --git a/src/ballistica/base/dynamics/bg/bg_dynamics_server.h b/src/ballistica/base/dynamics/bg/bg_dynamics_server.h index 46918f45..f52694f0 100644 --- a/src/ballistica/base/dynamics/bg/bg_dynamics_server.h +++ b/src/ballistica/base/dynamics/bg/bg_dynamics_server.h @@ -73,6 +73,7 @@ class BGDynamicsServer { auto GetDefaultOwnerThread() const -> EventLoopID override { return EventLoopID::kBGDynamics; } + GraphicsQuality graphics_quality{}; int step_millisecs{}; Vector3f cam_pos{0.0f, 0.0f, 0.0f}; diff --git a/src/ballistica/base/graphics/gl/program/program_gl.h b/src/ballistica/base/graphics/gl/program/program_gl.h index 437d2723..84f1f57b 100644 --- a/src/ballistica/base/graphics/gl/program/program_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_gl.h @@ -238,10 +238,10 @@ class RendererGL::ProgramGL { // Update matrices as necessary. - uint32_t mvpState = + int mvp_state = g_base->graphics_server->GetModelViewProjectionMatrixState(); - if (mvpState != mvp_state_) { - mvp_state_ = mvpState; + if (mvp_state != mvp_state_) { + mvp_state_ = mvp_state; glUniformMatrix4fv( mvp_uniform_, 1, 0, g_base->graphics_server->GetModelViewProjectionMatrix().m); @@ -251,7 +251,7 @@ class RendererGL::ProgramGL { if (pflags_ & PFLAG_USES_MODEL_WORLD_MATRIX) { // With world space points this would be identity; don't waste time. assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS)); - uint32_t state = g_base->graphics_server->GetModelWorldMatrixState(); + int state = g_base->graphics_server->GetModelWorldMatrixState(); if (state != model_world_matrix_state_) { model_world_matrix_state_ = state; glUniformMatrix4fv(model_world_matrix_uniform_, 1, 0, @@ -264,8 +264,7 @@ class RendererGL::ProgramGL { // With world space points this would be identity; don't waste time. assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS)); // There's no state for just modelview but this works. - uint32_t state = - g_base->graphics_server->GetModelViewProjectionMatrixState(); + int state = g_base->graphics_server->GetModelViewProjectionMatrixState(); if (state != model_view_matrix_state_) { model_view_matrix_state_ = state; glUniformMatrix4fv(model_view_matrix_uniform_, 1, 0, @@ -275,7 +274,7 @@ class RendererGL::ProgramGL { BA_DEBUG_CHECK_GL_ERROR; if (pflags_ & PFLAG_USES_CAM_POS) { - uint32_t state = g_base->graphics_server->cam_pos_state(); + int state = g_base->graphics_server->cam_pos_state(); if (state != cam_pos_state_) { cam_pos_state_ = state; const Vector3f& p(g_base->graphics_server->cam_pos()); @@ -285,7 +284,7 @@ class RendererGL::ProgramGL { BA_DEBUG_CHECK_GL_ERROR; if (pflags_ & PFLAG_USES_CAM_ORIENT_MATRIX) { - uint32_t state = g_base->graphics_server->GetCamOrientMatrixState(); + int state = g_base->graphics_server->GetCamOrientMatrixState(); if (state != cam_orient_matrix_state_) { cam_orient_matrix_state_ = state; glUniformMatrix4fv(cam_orient_matrix_uniform_, 1, 0, @@ -295,7 +294,7 @@ class RendererGL::ProgramGL { BA_DEBUG_CHECK_GL_ERROR; if (pflags_ & PFLAG_USES_SHADOW_PROJECTION_MATRIX) { - uint32_t state = + int state = g_base->graphics_server->light_shadow_projection_matrix_state(); if (state != light_shadow_projection_matrix_state_) { light_shadow_projection_matrix_state_ = state; @@ -336,19 +335,19 @@ class RendererGL::ProgramGL { Object::Ref vertex_shader_; std::string name_; GLuint program_{}; - int pflags_{}; - uint32_t mvp_state_{}; GLint mvp_uniform_{}; GLint model_world_matrix_uniform_{}; GLint model_view_matrix_uniform_{}; GLint light_shadow_projection_matrix_uniform_{}; - uint32_t light_shadow_projection_matrix_state_{}; - uint32_t model_world_matrix_state_{}; - uint32_t model_view_matrix_state_{}; GLint cam_pos_uniform_{}; - uint32_t cam_pos_state_{}; GLint cam_orient_matrix_uniform_{}; - GLuint cam_orient_matrix_state_{}; + int cam_orient_matrix_state_{}; + int light_shadow_projection_matrix_state_{}; + int pflags_{}; + int mvp_state_{}; + int cam_pos_state_{}; + int model_world_matrix_state_{}; + int model_view_matrix_state_{}; BA_DISALLOW_CLASS_COPIES(ProgramGL); }; diff --git a/src/ballistica/base/graphics/gl/renderer_gl.cc b/src/ballistica/base/graphics/gl/renderer_gl.cc index ffa42e0f..adb60eae 100644 --- a/src/ballistica/base/graphics/gl/renderer_gl.cc +++ b/src/ballistica/base/graphics/gl/renderer_gl.cc @@ -366,7 +366,7 @@ void RendererGL::CheckGLCapabilities_() { // Both GL 3 and GL ES 3.0 support depth textures (and thus our high // quality mode) as a core feature. - g_base->graphics->SetSupportsHighQualityGraphics(true); + // g_base->graphics->SetSupportsHighQualityGraphics(true); // Store the tex-compression type we support. BA_DEBUG_CHECK_GL_ERROR; @@ -2598,7 +2598,8 @@ void RendererGL::RetainShader_(ProgramGL* p) { shaders_.emplace_back(p); } void RendererGL::Load() { assert(g_base->app_adapter->InGraphicsContext()); assert(!data_loaded_); - assert(g_base->graphics_server->graphics_quality_set()); + assert(g_base->graphics_server->graphics_quality() + != GraphicsQuality::kUnset); BA_DEBUG_CHECK_GL_ERROR; if (!got_screen_framebuffer_) { got_screen_framebuffer_ = true; diff --git a/src/ballistica/base/graphics/graphics.cc b/src/ballistica/base/graphics/graphics.cc index 5582a189..773e85dd 100644 --- a/src/ballistica/base/graphics/graphics.cc +++ b/src/ballistica/base/graphics/graphics.cc @@ -115,9 +115,14 @@ void Graphics::OnAppShutdownComplete() { assert(g_base->InLogicThread()); } void Graphics::DoApplyAppConfig() { assert(g_base->InLogicThread()); + // Any time we load the config we ship a new graphics-settings to + // the graphics server since something likely changed. + graphics_settings_dirty_ = true; + show_fps_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowFPS); show_ping_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowPing); - tv_border_ = g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder); + // tv_border_ = + // g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder); bool disable_camera_shake = g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraShake); @@ -126,6 +131,52 @@ void Graphics::DoApplyAppConfig() { bool disable_camera_gyro = g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraGyro); set_camera_gyro_explicitly_disabled(disable_camera_gyro); + + applied_app_config_ = true; + + // At this point we may want to send initial graphics settings to the + // graphics server if we haven't. + UpdateInitialGraphicsSettingsSend_(); +} + +void Graphics::UpdateInitialGraphicsSettingsSend_() { + assert(g_base->InLogicThread()); + if (sent_initial_graphics_settings_) { + return; + } + + // We need to send an initial graphics-settings to the server to kick + // things off, but we need a few things to be in place first. + auto app_config_ready = applied_app_config_; + // At some point we may want to wait to know our actual screen res before + // sending. This won't apply everywhere though since on some platforms the + // screen doesn't exist until we send this. + auto screen_resolution_ready = true; + + if (app_config_ready && screen_resolution_ready) { + // Update/grab the current settings snapshot. + auto* settings = GetGraphicsSettingsSnapshot(); + + // We need to explicitly push settings to the graphics server to kick + // things off. We need to keep this settings instance alive until + // handled by the graphics context (which might be in another thread + // where we're not allowed to muck with settings' refs from). So let's + // explicitly increment its refcount here in the logic thread now and + // then push a call back here to decrement it when we're done. + settings->ObjectIncrementStrongRefCount(); + // auto* s = settings_.Get(); + g_base->app_adapter->PushGraphicsContextCall([settings] { + assert(g_base->app_adapter->InGraphicsContext()); + g_base->graphics_server->ApplySettings(settings->Get()); + g_base->logic->event_loop()->PushCall([settings] { + // Release our strong ref back here in the logic thread. + assert(g_base->InLogicThread()); + settings->ObjectDecrementStrongRefCount(); + }); + }); + + sent_initial_graphics_settings_ = true; + } } void Graphics::StepDisplayTime() { assert(g_base->InLogicThread()); } @@ -976,6 +1027,20 @@ auto Graphics::GetEmptyFrameDef() -> FrameDef* { return frame_def; } +auto Graphics::GetGraphicsSettingsSnapshot() -> Snapshot* { + assert(g_base->InLogicThread()); + + // If need be, ask the app-adapter to build us a new settings instance. + if (graphics_settings_dirty_) { + auto* new_settings = g_base->app_adapter->GetGraphicsSettings(); + new_settings->index = next_settings_index_++; + settings_snapshot_ = Object::New>(new_settings); + graphics_settings_dirty_ = false; + } + assert(settings_snapshot_.Exists()); + return settings_snapshot_.Get(); +} + void Graphics::ClearFrameDefDeleteList() { assert(g_base->InLogicThread()); std::scoped_lock lock(frame_def_delete_list_mutex_); @@ -1120,6 +1185,8 @@ void Graphics::DrawDevUI(FrameDef* frame_def) { void Graphics::BuildAndPushFrameDef() { assert(g_base->InLogicThread()); + + BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete()); assert(camera_.Exists()); assert(!g_core->HeadlessMode()); @@ -1128,10 +1195,6 @@ void Graphics::BuildAndPushFrameDef() { assert(!building_frame_def_); building_frame_def_ = true; - // We should not be building/pushing any frames until the native - // layer is fully bootstrapped. - BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete()); - microsecs_t app_time_microsecs = g_core->GetAppTimeMicrosecs(); // Store how much time this frame_def represents. @@ -1187,13 +1250,6 @@ void Graphics::BuildAndPushFrameDef() { internal_components_inited_ = true; } - // If graphics quality has changed since our last draw, inform anyone who - // wants to know. - if (last_frame_def_graphics_quality_ != frame_def->quality()) { - last_frame_def_graphics_quality_ = frame_def->quality(); - g_base->app_mode()->GraphicsQualityChanged(frame_def->quality()); - } - ApplyCamera(frame_def); if (progress_bar_) { @@ -1254,7 +1310,7 @@ void Graphics::BuildAndPushFrameDef() { RunCleanFrameCommands(); } - frame_def->Finalize(); + frame_def->Complete(); // Include all mesh-data loads and unloads that have accumulated up to // this point the graphics thread will have to handle these before @@ -1555,11 +1611,6 @@ void Graphics::DrawBlotches(FrameDef* frame_def) { } } -void Graphics::SetSupportsHighQualityGraphics(bool s) { - supports_high_quality_graphics_ = s; - has_supports_high_quality_graphics_value_ = true; -} - void Graphics::ClearScreenMessageTranslations() { assert(g_base && g_base->InLogicThread()); for (auto&& i : screen_messages_) { @@ -1922,20 +1973,55 @@ auto Graphics::ScreenMessageEntry::GetText() -> TextGroup& { void Graphics::OnScreenSizeChange() {} -void Graphics::SetScreenSize(float virtual_width, float virtual_height, - float pixel_width, float pixel_height) { +void Graphics::CalcVirtualRes_(float* x, float* y) { + float x_in = *x; + float y_in = *y; + if (*x / *y > static_cast(kBaseVirtualResX) + / static_cast(kBaseVirtualResY)) { + *y = kBaseVirtualResY; + *x = *y * (x_in / y_in); + } else { + *x = kBaseVirtualResX; + *y = *x * (y_in / x_in); + } +} + +void Graphics::SetScreenResolution(float x, float y) { assert(g_base->InLogicThread()); - res_x_virtual_ = virtual_width; - res_y_virtual_ = virtual_height; - res_x_ = pixel_width; - res_y_ = pixel_height; + + // Ignore redundant sets. + if (res_x_ == x && res_y_ == y) { + return; + } + + // We'll need to ship a new settings to the server with this change. + graphics_settings_dirty_ = true; + + res_x_ = x; + res_y_ = y; + + // Calc virtual res. In vr mode our virtual res is independent of our + // screen size (since it gets drawn to an overlay). + if (g_core->IsVRMode()) { + res_x_virtual_ = kBaseVirtualResX; + res_y_virtual_ = kBaseVirtualResY; + } else { + res_x_virtual_ = res_x_; + res_y_virtual_ = res_y_; + CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_); + } // Need to rebuild internal components (some are sized to the screen). internal_components_inited_ = false; - // This will inform all applicable logic thread subsystems. - g_base->logic->OnScreenSizeChange(virtual_width, virtual_height, pixel_width, - pixel_height); + // Inform all our logic thread buddies of this change. + g_base->logic->OnScreenSizeChange(res_x_virtual_, res_y_virtual_, res_x_, + res_y_); + + // This may trigger us sending initial graphics settings to the + // graphics-server to kick off drawing. + got_screen_resolution_ = true; + UpdateInitialGraphicsSettingsSend_(); } void Graphics::ScreenMessageEntry::UpdateTranslation() { @@ -2030,4 +2116,61 @@ void Graphics::LanguageChanged() { ClearScreenMessageTranslations(); } +auto Graphics::GraphicsQualityFromRequest(GraphicsQualityRequest request, + GraphicsQuality auto_val) + -> GraphicsQuality { + switch (request) { + case GraphicsQualityRequest::kLow: + return GraphicsQuality::kLow; + case GraphicsQualityRequest::kMedium: + return GraphicsQuality::kMedium; + case GraphicsQualityRequest::kHigh: + return GraphicsQuality::kHigh; + case GraphicsQualityRequest::kHigher: + return GraphicsQuality::kHigher; + case GraphicsQualityRequest::kAuto: + return auto_val; + default: + Log(LogLevel::kError, "Unhandled GraphicsQualityRequest value: " + + std::to_string(static_cast(request))); + return GraphicsQuality::kLow; + } +} +auto Graphics::TextureQualityFromRequest(TextureQualityRequest request, + TextureQuality auto_val) + -> TextureQuality { + switch (request) { + case TextureQualityRequest::kLow: + return TextureQuality::kLow; + case TextureQualityRequest::kMedium: + return TextureQuality::kMedium; + case TextureQualityRequest::kHigh: + return TextureQuality::kHigh; + case TextureQualityRequest::kAuto: + return auto_val; + default: + Log(LogLevel::kError, "Unhandled TextureQualityRequest value: " + + std::to_string(static_cast(request))); + return TextureQuality::kLow; + } +} + +void Graphics::set_client_context(Snapshot* context) { + assert(g_base->InLogicThread()); + + // Currently we only expect this to be set once. That will change + // once we support renderer swapping/etc. + assert(!g_base->logic->graphics_ready()); + assert(!client_context_snapshot_.Exists()); + client_context_snapshot_ = context; + + // Update our static placeholder value (we don't want to calc it dynamically + // since it can be accessed from other threads). + texture_quality_placeholder_ = TextureQualityFromRequest( + settings()->texture_quality, client_context()->auto_texture_quality); + + // Let the logic system know its free to proceed beyond bootstrapping. + g_base->logic->OnGraphicsReady(); +} + } // namespace ballistica::base diff --git a/src/ballistica/base/graphics/graphics.h b/src/ballistica/base/graphics/graphics.h index 92e6c18f..022e58d8 100644 --- a/src/ballistica/base/graphics/graphics.h +++ b/src/ballistica/base/graphics/graphics.h @@ -6,13 +6,15 @@ #include #include #include -#include #include #include #include "ballistica/base/base.h" +#include "ballistica/base/graphics/support/graphics_client_context.h" +#include "ballistica/base/graphics/support/graphics_settings.h" #include "ballistica/shared/foundation/object.h" #include "ballistica/shared/foundation/types.h" +#include "ballistica/shared/generic/snapshot.h" #include "ballistica/shared/math/matrix44f.h" #include "ballistica/shared/math/rect.h" #include "ballistica/shared/math/vector2f.h" @@ -62,10 +64,10 @@ class Graphics { void OnScreenSizeChange(); void DoApplyAppConfig(); - /// Called by the graphics server to keep us up to date in the logic - /// thread. Dispatches the news to all logic subsystems that care. - void SetScreenSize(float virtual_width, float virtual_height, - float physical_width, float physical_height); + /// Should be called by the app-adapter to keep the engine informed + /// on the drawable area it has to work with (in pixels). + void SetScreenResolution(float x, float y); + void StepDisplayTime(); auto TextureQualityFromAppConfig() -> TextureQualityRequest; @@ -97,13 +99,25 @@ class Graphics { // Called when the GraphicsServer has sent us a frame-def for deletion. void ReturnCompletedFrameDef(FrameDef* frame_def); - auto screen_pixel_width() const { return res_x_; } - auto screen_pixel_height() const { return res_y_; } + auto screen_pixel_width() const { + assert(g_base->InLogicThread()); + return res_x_; + } + auto screen_pixel_height() const { + assert(g_base->InLogicThread()); + return res_y_; + } - // Return the size of the virtual screen. This value should always + // Return the current size of the virtual screen. This value should always // be used for interface positioning, etc. - auto screen_virtual_width() const { return res_x_virtual_; } - auto screen_virtual_height() const { return res_y_virtual_; } + auto screen_virtual_width() const { + assert(g_base->InLogicThread()); + return res_x_virtual_; + } + auto screen_virtual_height() const { + assert(g_base->InLogicThread()); + return res_y_virtual_; + } void ClearScreenMessageTranslations(); @@ -226,10 +240,10 @@ class Graphics { float upper_top); void ReleaseFadeEndCommand(); - auto tv_border() const { - assert(g_base->InLogicThread()); - return tv_border_; - } + // auto tv_border() const { + // assert(g_base->InLogicThread()); + // return tv_border_; + // } // Nodes that draw flat stuff into the overlay pass should query this z value // for where to draw in z. @@ -270,17 +284,6 @@ class Graphics { return y * (res_y_virtual_ / res_y_); } - // FIXME: This should probably move to Renderer or AppAdapter once we - // support switching renderers. - auto supports_high_quality_graphics() const { - assert(has_supports_high_quality_graphics_value_); - return supports_high_quality_graphics_; - } - void SetSupportsHighQualityGraphics(bool s); - auto has_supports_high_quality_graphics_value() const { - return has_supports_high_quality_graphics_value_; - } - void set_internal_components_inited(bool val) { internal_components_inited_ = val; } @@ -322,19 +325,65 @@ class Graphics { camera_gyro_explicitly_disabled_ = disabled; } + auto* settings() const { + assert(g_base->InLogicThread()); + assert(settings_snapshot_.Exists()); + return settings_snapshot_.Get()->Get(); + } + + auto GetGraphicsSettingsSnapshot() -> Snapshot*; + + /// Called by the graphics-server when a new client context is ready. + void set_client_context(Snapshot* context); + + auto has_client_context() -> bool { + return client_context_snapshot_.Exists(); + } + + auto client_context() const -> const GraphicsClientContext* { + assert(g_base->InLogicThread()); + assert(client_context_snapshot_.Exists()); + return client_context_snapshot_.Get()->Get(); + } + + static auto GraphicsQualityFromRequest(GraphicsQualityRequest request, + GraphicsQuality auto_val) + -> GraphicsQuality; + static auto TextureQualityFromRequest(TextureQualityRequest request, + TextureQuality auto_val) + -> TextureQuality; + + /// For temporary use from arbitrary threads. This should be removed when + /// possible and replaced with proper safe thread-specific access patterns + /// (so we can support switching renderers/etc.). + auto placeholder_texture_quality() const { + assert(client_context_snapshot_.Exists()); + return texture_quality_placeholder_; + } + + /// For temporary use in arbitrary threads. This should be removed when + /// possible and replaced with proper safe thread-specific access + /// patterns (so we can support switching renderers/etc.). + auto placeholder_client_context() const -> const GraphicsClientContext* { + // Using this from arbitrary threads is currently ok currently since + // context never changes once set. Will need to kill this call once that + // can happen though. + assert(client_context_snapshot_.Exists()); + return client_context_snapshot_.Get()->Get(); + } + protected: + class ScreenMessageEntry; + Graphics(); virtual ~Graphics(); virtual void DoDrawFade(FrameDef* frame_def, float amt); - - private: - class ScreenMessageEntry; + static void CalcVirtualRes_(float* x, float* y); void DrawBoxingGlovesTest(FrameDef* frame_def); void DrawBlotches(FrameDef* frame_def); void DrawCursor(FrameDef* frame_def); void DrawFades(FrameDef* frame_def); void DrawDebugBuffers(RenderPass* pass); - void UpdateAndDrawProgressBar(FrameDef* frame_def); void DoDrawBlotch(std::vector* indices, std::vector* verts, const Vector3f& pos, @@ -347,44 +396,47 @@ class Graphics { void DrawProgressBar(RenderPass* pass, float opacity); void UpdateProgressBarProgress(float target); void UpdateGyro(microsecs_t time, microsecs_t elapsed); + void UpdateInitialGraphicsSettingsSend_(); - bool drawing_transparent_only_{}; - bool drawing_opaque_only_{}; - bool has_supports_high_quality_graphics_value_{}; - bool supports_high_quality_graphics_{}; - bool internal_components_inited_{}; - bool fade_out_{true}; - bool progress_bar_{}; - bool progress_bar_fade_in_{}; - bool debug_draw_{}; - bool network_debug_display_enabled_{}; - bool hardware_cursor_visible_{}; - bool camera_shake_disabled_{}; - bool camera_gyro_explicitly_disabled_{}; - bool gyro_enabled_{true}; - bool show_fps_{}; - bool show_ping_{}; - bool show_net_info_{}; - bool tv_border_{}; - bool floor_reflection_{}; - bool building_frame_def_{}; - bool shadow_ortho_{}; - bool fetched_overlay_node_z_depth_{}; - bool gyro_broken_{}; - GraphicsQuality last_frame_def_graphics_quality_{GraphicsQuality::kUnset}; - std::list> clean_frame_commands_; - std::vector mesh_data_creates_; - std::vector mesh_data_destroys_; - microsecs_t last_create_frame_def_time_microsecs_{}; - millisecs_t last_create_frame_def_time_millisecs_{}; + int last_total_frames_rendered_{}; + int last_fps_{}; + int progress_bar_loads_{}; + int frame_def_count_{}; + int frame_def_count_filtered_{}; + int next_settings_index_{}; + TextureQuality texture_quality_placeholder_{}; + bool drawing_transparent_only_ : 1 {}; + bool drawing_opaque_only_ : 1 {}; + bool internal_components_inited_ : 1 {}; + bool fade_out_ : 1 {true}; + bool progress_bar_ : 1 {}; + bool progress_bar_fade_in_ : 1 {}; + bool debug_draw_ : 1 {}; + bool network_debug_display_enabled_ : 1 {}; + bool hardware_cursor_visible_ : 1 {}; + bool camera_shake_disabled_ : 1 {}; + bool camera_gyro_explicitly_disabled_ : 1 {}; + bool gyro_enabled_ : 1 {true}; + bool show_fps_ : 1 {}; + bool show_ping_ : 1 {}; + bool show_net_info_ : 1 {}; + bool tv_border_ : 1 {}; + bool floor_reflection_ : 1 {}; + bool building_frame_def_ : 1 {}; + bool shadow_ortho_ : 1 {}; + bool fetched_overlay_node_z_depth_ : 1 {}; + bool gyro_broken_ : 1 {}; + bool set_fade_start_on_next_draw_ : 1 {}; + bool graphics_settings_dirty_ : 1 {true}; + bool applied_app_config_ : 1 {}; + bool sent_initial_graphics_settings_ : 1 {}; + bool got_screen_resolution_ : 1 {}; Vector3f shadow_offset_{0.0f, 0.0f, 0.0f}; Vector2f shadow_scale_{1.0f, 1.0f}; Vector3f tint_{1.0f, 1.0f, 1.0f}; Vector3f ambient_color_{1.0f, 1.0f, 1.0f}; Vector3f vignette_outer_{0.0f, 0.0f, 0.0f}; Vector3f vignette_inner_{1.0f, 1.0f, 1.0f}; - std::vector recycle_frame_defs_; - millisecs_t last_jitter_update_time_{}; Vector3f jitter_{0.0f, 0.0f, 0.0f}; Vector3f accel_smoothed_{0.0f, 0.0f, 0.0f}; Vector3f accel_smoothed2_{0.0f, 0.0f, 0.0f}; @@ -394,8 +446,50 @@ class Graphics { Vector3f tilt_smoothed_{0.0f, 0.0f, 0.0f}; Vector3f tilt_vel_{0.0f, 0.0f, 0.0f}; Vector3f tilt_pos_{0.0f, 0.0f, 0.0f}; + Vector3f gyro_vals_{0.0f, 0.0, 0.0f}; + std::string fps_string_; + std::string ping_string_; + std::string net_info_string_; + std::map> debug_graphs_; + std::mutex frame_def_delete_list_mutex_; + std::list> clean_frame_commands_; + std::list screen_messages_; + std::list screen_messages_top_; + std::vector recycle_frame_defs_; + std::vector blotch_indices_; + std::vector blotch_verts_; + std::vector blotch_soft_indices_; + std::vector blotch_soft_verts_; + std::vector blotch_soft_obj_indices_; + std::vector blotch_soft_obj_verts_; + std::vector frame_def_delete_list_; + std::vector mesh_data_creates_; + std::vector mesh_data_destroys_; + float fade_{}; + float res_x_{256.0f}; + float res_y_{256.0f}; + float res_x_virtual_{256.0f}; + float res_y_virtual_{256.0f}; float gyro_mag_test_{}; float overlay_node_z_depth_{}; + float progress_bar_progress_{}; + float screen_gamma_{1.0f}; + float shadow_lower_bottom_{-4.0f}; + float shadow_lower_top_{4.0f}; + float shadow_upper_bottom_{30.0f}; + float shadow_upper_top_{40.0f}; + millisecs_t fade_start_{}; + millisecs_t fade_time_{}; + millisecs_t next_stat_update_time_{}; + millisecs_t progress_bar_end_time_{-9999}; + millisecs_t last_progress_bar_draw_time_{}; + millisecs_t last_progress_bar_start_time_{}; + microsecs_t last_suppress_gyro_time_{}; + millisecs_t last_cursor_visibility_event_time_{}; + microsecs_t next_frame_number_filtered_increment_time_{}; + microsecs_t last_create_frame_def_time_microsecs_{}; + millisecs_t last_create_frame_def_time_millisecs_{}; + millisecs_t last_jitter_update_time_{}; Object::Ref screen_mesh_; Object::Ref progress_bar_bottom_mesh_; Object::Ref progress_bar_top_mesh_; @@ -406,49 +500,10 @@ class Graphics { Object::Ref shadow_blotch_mesh_; Object::Ref shadow_blotch_soft_mesh_; Object::Ref shadow_blotch_soft_obj_mesh_; - std::string fps_string_; - std::string ping_string_; - std::string net_info_string_; - std::vector blotch_indices_; - std::vector blotch_verts_; - std::vector blotch_soft_indices_; - std::vector blotch_soft_verts_; - std::vector blotch_soft_obj_indices_; - std::vector blotch_soft_obj_verts_; - std::map> debug_graphs_; - std::mutex frame_def_delete_list_mutex_; - std::vector frame_def_delete_list_; Object::Ref camera_; - millisecs_t next_stat_update_time_{}; - int last_total_frames_rendered_{}; - int last_fps_{}; - std::list screen_messages_; - std::list screen_messages_top_; - bool set_fade_start_on_next_draw_{}; - millisecs_t fade_start_{}; - millisecs_t fade_time_{}; - float fade_{}; - Vector3f gyro_vals_{0.0f, 0.0, 0.0f}; - float res_x_{100}; - float res_y_{100}; - float res_x_virtual_{100}; - float res_y_virtual_{100}; - int progress_bar_loads_{}; - millisecs_t progress_bar_end_time_{-9999}; - millisecs_t last_progress_bar_draw_time_{}; - millisecs_t last_progress_bar_start_time_{}; - float progress_bar_progress_{}; - float screen_gamma_{1.0f}; - float shadow_lower_bottom_{-4.0f}; - float shadow_lower_top_{4.0f}; - float shadow_upper_bottom_{30.0f}; - float shadow_upper_top_{40.0f}; - millisecs_t last_cursor_visibility_event_time_{}; - microsecs_t next_frame_number_filtered_increment_time_{}; - int64_t frame_def_count_{}; - int64_t frame_def_count_filtered_{}; - microsecs_t last_suppress_gyro_time_{}; Object::Ref fade_end_call_; + Object::Ref> settings_snapshot_; + Object::Ref> client_context_snapshot_; }; } // namespace ballistica::base diff --git a/src/ballistica/base/graphics/graphics_server.cc b/src/ballistica/base/graphics/graphics_server.cc index 8a89c655..33025a94 100644 --- a/src/ballistica/base/graphics/graphics_server.cc +++ b/src/ballistica/base/graphics/graphics_server.cc @@ -32,15 +32,88 @@ void GraphicsServer::EnqueueFrameDef(FrameDef* framedef) { } } +void GraphicsServer::ApplySettings(const GraphicsSettings* settings) { + assert(g_base->InGraphicsContext()); + + // Only push each unique settings instance through once. + if (settings->index == settings_index_) { + return; + } + settings_index_ = settings->index; + + assert(settings->resolution.x >= 0.0f && settings->resolution.y >= 0.0f + && settings->resolution_virtual.x >= 0.0f + && settings->resolution_virtual.y >= 0.0f); + + // Pull a few things out ourself such as screen resolution. + tv_border_ = settings->tv_border; + if (renderer_) { + renderer_->set_pixel_scale(settings->pixel_scale); + } + // Note: not checking virtual res here; assuming it only changes when + // actual res changes. + if (res_x_ != settings->resolution.x || res_y_ != settings->resolution.y) { + res_x_ = settings->resolution.x; + res_y_ = settings->resolution.y; + res_x_virtual_ = settings->resolution_virtual.x; + res_y_virtual_ = settings->resolution_virtual.y; + if (renderer_) { + renderer_->OnScreenSizeChange(); + } + } + + // Kick this over to the app-adapter to apply whatever settings they + // gathered for themself. + g_base->app_adapter->ApplyGraphicsSettings(settings); + + // Lastly, if we've not yet sent a context to the client, do so. + if (client_context_ == nullptr) { + set_client_context(g_base->app_adapter->GetGraphicsClientContext()); + } +} + +void GraphicsServer::set_client_context(GraphicsClientContext* context) { + assert(g_base->InGraphicsContext()); + + // We have to do a bit of a song and dance with these context pointers. + // We wrap the context in an immutable object wrapper which is owned by + // the logic thread and that takes care of killing it when no longer + // used there, but we also need to keep it alive here in our thread. + // (which may not be the logic thread). So to accomplish that, we + // immediately ship a refcount increment over to the logic thread, and + // once we're done with an obj we ship a decrement. + + auto* old_wrapper = client_context_; + auto* new_wrapper = + Object::NewDeferred>(context); + + client_context_ = new_wrapper; + + g_base->logic->event_loop()->PushCall([old_wrapper, new_wrapper] { + // (This has to happen in logic thread). + auto ref = Object::CompleteDeferred(new_wrapper); + + // Free the old one which the graphics server doesn't need anymore. + if (old_wrapper) { + old_wrapper->ObjectDecrementStrongRefCount(); + } + + // Keep the new one alive for the graphics server. + ref->ObjectIncrementStrongRefCount(); + + // Plug the new one in for logic to start using. + g_base->graphics->set_client_context(new_wrapper); + }); +} + auto GraphicsServer::TryRender() -> bool { assert(g_base->app_adapter->InGraphicsContext()); bool success{}; if (FrameDef* frame_def = WaitForRenderFrameDef_()) { - // Apply settings such as tv-mode that were passed along via the - // frame-def. - ApplyFrameDefSettings(frame_def); + // Apply any new graphics settings passed along via the frame-def. + ApplySettings(frame_def->settings()); // Note: we run mesh-updates on each frame-def that comes through even // if we don't actually render the frame. @@ -58,6 +131,7 @@ auto GraphicsServer::TryRender() -> bool { // Send this frame_def back to the logic thread for deletion or recycling. g_base->graphics->ReturnCompletedFrameDef(frame_def); } + return success; } @@ -113,11 +187,6 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* { return nullptr; } -void GraphicsServer::ApplyFrameDefSettings(FrameDef* frame_def) { - 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->app_adapter->InGraphicsContext()); @@ -250,14 +319,15 @@ void GraphicsServer::SetNullGraphics() { SetTextureCompressionTypes(c_types); graphics_quality_requested_ = GraphicsQualityRequest::kLow; graphics_quality_ = GraphicsQuality::kLow; - graphics_quality_set_ = true; + // graphics_quality_set_ = true; texture_quality_requested_ = TextureQualityRequest::kLow; texture_quality_ = TextureQuality::kLow; - texture_quality_set_ = true; + // texture_quality_set_ = true; + FatalError("FIXME REWORK THIS"); // Let the logic thread know screen creation is done (or lack thereof). - g_base->logic->event_loop()->PushCall( - [] { g_base->logic->OnGraphicsReady(); }); + // g_base->logic->event_loop()->PushCall( + // [] { g_base->logic->OnGraphicsReady(); }); } void GraphicsServer::set_renderer(Renderer* renderer) { @@ -279,59 +349,43 @@ void GraphicsServer::LoadRenderer() { return; } - switch (graphics_quality_requested_) { - case GraphicsQualityRequest::kLow: - graphics_quality_ = GraphicsQuality::kLow; - break; - case GraphicsQualityRequest::kMedium: - graphics_quality_ = GraphicsQuality::kMedium; - break; - case GraphicsQualityRequest::kHigh: - graphics_quality_ = GraphicsQuality::kHigh; - break; - case GraphicsQualityRequest::kHigher: - graphics_quality_ = GraphicsQuality::kHigher; - break; - case GraphicsQualityRequest::kAuto: - graphics_quality_ = renderer_->GetAutoGraphicsQuality(); - break; - default: - Log(LogLevel::kError, - "Unhandled GraphicsQualityRequest value: " - + std::to_string(static_cast(graphics_quality_requested_))); - graphics_quality_ = GraphicsQuality::kLow; - } + graphics_quality_ = Graphics::GraphicsQualityFromRequest( + graphics_quality_requested_, renderer_->GetAutoGraphicsQuality()); + + texture_quality_ = Graphics::TextureQualityFromRequest( + texture_quality_requested_, renderer_->GetAutoTextureQuality()); // If we don't support high quality graphics, make sure we're no higher than // medium. - BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value()); - if (!g_base->graphics->supports_high_quality_graphics() - && graphics_quality_ > GraphicsQuality::kMedium) { - graphics_quality_ = GraphicsQuality::kMedium; - } - graphics_quality_set_ = true; + // BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value()); + // if (!g_base->graphics->supports_high_quality_graphics() + // && graphics_quality_ > GraphicsQuality::kMedium) { + // graphics_quality_ = GraphicsQuality::kMedium; + // } + // graphics_quality_set_ = true; // Update texture quality based on request. - switch (texture_quality_requested_) { - case TextureQualityRequest::kLow: - texture_quality_ = TextureQuality::kLow; - break; - case TextureQualityRequest::kMedium: - texture_quality_ = TextureQuality::kMedium; - break; - case TextureQualityRequest::kHigh: - texture_quality_ = TextureQuality::kHigh; - break; - case TextureQualityRequest::kAuto: - texture_quality_ = renderer_->GetAutoTextureQuality(); - break; - default: - Log(LogLevel::kError, - "Unhandled TextureQualityRequest value: " - + std::to_string(static_cast(texture_quality_requested_))); - texture_quality_ = TextureQuality::kLow; - } - texture_quality_set_ = true; + // switch (texture_quality_requested_) { + // case TextureQualityRequest::kLow: + // texture_quality_ = TextureQuality::kLow; + // break; + // case TextureQualityRequest::kMedium: + // texture_quality_ = TextureQuality::kMedium; + // break; + // case TextureQualityRequest::kHigh: + // texture_quality_ = TextureQuality::kHigh; + // break; + // case TextureQualityRequest::kAuto: + // texture_quality_ = renderer_->GetAutoTextureQuality(); + // break; + // default: + // Log(LogLevel::kError, + // "Unhandled TextureQualityRequest value: " + // + + // std::to_string(static_cast(texture_quality_requested_))); + // texture_quality_ = TextureQuality::kLow; + // } + // texture_quality_set_ = true; // Ok we've got our qualities figured out; now load/update the renderer. renderer_->Load(); @@ -389,56 +443,56 @@ void GraphicsServer::UnloadRenderer() { } // Given physical res, calculate virtual res. -void GraphicsServer::CalcVirtualRes_(float* x, float* y) { - float x_in = *x; - float y_in = *y; - if (*x / *y > static_cast(kBaseVirtualResX) - / static_cast(kBaseVirtualResY)) { - *y = kBaseVirtualResY; - *x = *y * (x_in / y_in); - } else { - *x = kBaseVirtualResX; - *y = *x * (y_in / x_in); - } -} +// void GraphicsServer::CalcVirtualRes_(float* x, float* y) { +// float x_in = *x; +// float y_in = *y; +// if (*x / *y > static_cast(kBaseVirtualResX) +// / static_cast(kBaseVirtualResY)) { +// *y = kBaseVirtualResY; +// *x = *y * (x_in / y_in); +// } else { +// *x = kBaseVirtualResX; +// *y = *x * (y_in / x_in); +// } +// } -void GraphicsServer::UpdateVirtualScreenRes_() { - assert(g_base->app_adapter->InGraphicsContext()); +// void GraphicsServer::UpdateVirtualScreenRes_() { +// 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) - if (g_core->IsVRMode()) { - res_x_virtual_ = kBaseVirtualResX; - res_y_virtual_ = kBaseVirtualResY; - } else { - res_x_virtual_ = res_x_; - res_y_virtual_ = res_y_; - CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_); - } -} +// // In vr mode our virtual res is independent of our screen size. +// // (since it gets drawn to an overlay) +// if (g_core->IsVRMode()) { +// res_x_virtual_ = kBaseVirtualResX; +// res_y_virtual_ = kBaseVirtualResY; +// } else { +// res_x_virtual_ = res_x_; +// res_y_virtual_ = res_y_; +// CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_); +// } +// } -void GraphicsServer::SetScreenResolution(float h, float v) { - assert(g_base->app_adapter->InGraphicsContext()); +// void GraphicsServer::SetScreenResolution(float h, float v) { +// assert(g_base->app_adapter->InGraphicsContext()); - // Ignore redundant sets. - if (res_x_ == h && res_y_ == v) { - return; - } - res_x_ = h; - res_y_ = v; - UpdateVirtualScreenRes_(); +// // Ignore redundant sets. +// if (res_x_ == h && res_y_ == v) { +// return; +// } +// res_x_ = h; +// res_y_ = v; +// // UpdateVirtualScreenRes_(); - // Inform renderer of the change. - if (renderer_) { - renderer_->OnScreenSizeChange(); - } +// // Inform renderer of the change. +// if (renderer_) { +// renderer_->OnScreenSizeChange(); +// } - // Inform all logic thread bits of this change. - g_base->logic->event_loop()->PushCall( - [vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] { - g_base->graphics->SetScreenSize(vx, vy, x, y); - }); -} +// // Inform all logic thread bits of this change. +// g_base->logic->event_loop()->PushCall( +// [vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] { +// g_base->graphics->SetScreenSize(vx, vy, x, y); +// }); +// } // FIXME: Shouldn't have android-specific code in here. void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) { @@ -579,15 +633,15 @@ void GraphicsServer::PushReloadMediaCall() { g_base->app_adapter->PushGraphicsContextCall([this] { ReloadMedia_(); }); } -void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { - g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] { - assert(g_base->app_adapter->InGraphicsContext()); - if (!renderer_) { - return; - } - renderer_->set_pixel_scale(pixel_scale); - }); -} +// void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { +// g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] { +// assert(g_base->app_adapter->InGraphicsContext()); +// if (!renderer_) { +// return; +// } +// renderer_->set_pixel_scale(pixel_scale); +// }); +// } void GraphicsServer::PushComponentUnloadCall( const std::vector*>& components) { diff --git a/src/ballistica/base/graphics/graphics_server.h b/src/ballistica/base/graphics/graphics_server.h index 40309f8d..abc333f2 100644 --- a/src/ballistica/base/graphics/graphics_server.h +++ b/src/ballistica/base/graphics/graphics_server.h @@ -11,6 +11,7 @@ #include "ballistica/base/base.h" #include "ballistica/shared/foundation/object.h" +#include "ballistica/shared/generic/snapshot.h" #include "ballistica/shared/math/matrix44f.h" namespace ballistica::base { @@ -51,16 +52,18 @@ class GraphicsServer { return renderer_loaded_; } + void ApplySettings(const GraphicsSettings* settings); + /// The AppAdapter should call this to inform the engine of screen size /// changes. Changes will be applied to the server and then sent to the /// logic thread to apply to various app systems (ui, etc.). - void SetScreenResolution(float h, float v); + // void SetScreenResolution(float h, float v); /// Used by headless builds to init the graphics-server into a /// non-functional state. void SetNullGraphics(); - void PushSetScreenPixelScaleCall(float pixel_scale); + // void PushSetScreenPixelScaleCall(float pixel_scale); void PushReloadMediaCall(); void PushRemoveRenderHoldCall(); void PushComponentUnloadCall( @@ -71,8 +74,6 @@ class GraphicsServer { /// rendering. void EnqueueFrameDef(FrameDef* framedef); - void ApplyFrameDefSettings(FrameDef* frame_def); - void RunFrameDefMeshUpdates(FrameDef* frame_def); // Renders shadow passes and other common parts of a frame_def. @@ -108,9 +109,7 @@ class GraphicsServer { projection_matrix_state_++; } - auto projection_matrix_state() -> uint32_t { - return projection_matrix_state_; - } + auto projection_matrix_state() { return projection_matrix_state_; } void SetLightShadowProjectionMatrix(const Matrix44f& p) { // This will generally get repeatedly set to the same value @@ -121,61 +120,57 @@ class GraphicsServer { } } - auto light_shadow_projection_matrix_state() const -> uint32_t { + auto light_shadow_projection_matrix_state() const { return light_shadow_projection_matrix_state_; } - auto light_shadow_projection_matrix() const -> const Matrix44f& { + const auto& light_shadow_projection_matrix() const { return light_shadow_projection_matrix_; } // Return the modelview * projection matrix. - auto GetModelViewProjectionMatrix() -> const Matrix44f& { + const auto& GetModelViewProjectionMatrix() { UpdateModelViewProjectionMatrix_(); return model_view_projection_matrix_; } - auto GetModelViewProjectionMatrixState() -> uint32_t { + auto GetModelViewProjectionMatrixState() { UpdateModelViewProjectionMatrix_(); return model_view_projection_matrix_state_; } - auto GetModelWorldMatrix() -> const Matrix44f& { + const auto& GetModelWorldMatrix() { UpdateModelWorldMatrix_(); return model_world_matrix_; } - auto GetModelWorldMatrixState() -> uint32_t { + auto GetModelWorldMatrixState() { UpdateModelWorldMatrix_(); return model_world_matrix_state_; } - auto cam_pos() -> const Vector3f& { return cam_pos_; } + const auto& cam_pos() { return cam_pos_; } - auto cam_pos_state() -> uint32_t { return cam_pos_state_; } + auto cam_pos_state() { return cam_pos_state_; } - auto GetCamOrientMatrix() -> const Matrix44f& { + const auto& GetCamOrientMatrix() { UpdateCamOrientMatrix_(); return cam_orient_matrix_; } - auto GetCamOrientMatrixState() -> uint32_t { + auto GetCamOrientMatrixState() { UpdateCamOrientMatrix_(); return cam_orient_matrix_state_; } - auto model_view_matrix() const -> const Matrix44f& { - return model_view_matrix_; - } + const auto& model_view_matrix() const { return model_view_matrix_; } void SetModelViewMatrix(const Matrix44f& m) { model_view_matrix_ = m; model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true; } - auto projection_matrix() const -> const Matrix44f& { - return projection_matrix_; - } + const auto& projection_matrix() const { return projection_matrix_; } void PushTransform() { model_view_stack_.push_back(model_view_matrix_); @@ -209,32 +204,34 @@ class GraphicsServer { model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true; } - auto quality() const -> GraphicsQuality { - assert(graphics_quality_set_); + auto quality() const { + assert(InGraphicsContext_()); + assert(graphics_quality_ != GraphicsQuality::kUnset); return graphics_quality_; } - auto texture_quality() const -> TextureQuality { - assert(texture_quality_set_); + auto texture_quality() const { + assert(InGraphicsContext_()); + assert(texture_quality_ != TextureQuality::kUnset); return texture_quality_; } - auto screen_pixel_width() const -> float { + auto screen_pixel_width() const { assert(InGraphicsContext_()); return res_x_; } - auto screen_pixel_height() const -> float { + auto screen_pixel_height() const { assert(InGraphicsContext_()); return res_y_; } - auto screen_virtual_width() const -> float { + auto screen_virtual_width() const { assert(InGraphicsContext_()); return res_x_virtual_; } - auto screen_virtual_height() const -> float { + auto screen_virtual_height() const { assert(InGraphicsContext_()); return res_y_virtual_; } @@ -244,45 +241,69 @@ class GraphicsServer { return tv_border_; } - auto graphics_quality_set() const { return graphics_quality_set_; } + // auto graphics_quality_set() const { + // return graphics_quality_ != GraphicsQuality::kUnset; + // } - auto texture_quality_set() const { return texture_quality_set_; } + // auto texture_quality_set() const { + // return texture_quality_ != TextureQuality::kUnset; + // } auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool { + assert(InGraphicsContext_()); assert(texture_compression_types_set_); return ((texture_compression_types_ & (0x01u << static_cast(t))) != 0u); } + void SetTextureCompressionTypes( const std::list& types); - auto texture_compression_types_are_set() const { - return texture_compression_types_set_; - } + // auto texture_compression_types_are_set() const { + // return texture_compression_types_set_; + // } void set_renderer_context_lost(bool lost) { renderer_context_lost_ = lost; } auto renderer_context_lost() const { return renderer_context_lost_; } auto graphics_quality_requested() const { + assert(InGraphicsContext_()); return graphics_quality_requested_; } void set_graphics_quality_requested(GraphicsQualityRequest val) { + assert(InGraphicsContext_()); graphics_quality_requested_ = val; } void set_texture_quality_requested(TextureQualityRequest val) { + assert(InGraphicsContext_()); texture_quality_requested_ = val; } - auto graphics_quality() const { return graphics_quality_; } + auto graphics_quality() const { + assert(InGraphicsContext_()); + return graphics_quality_; + } - auto texture_quality_requested() const { return texture_quality_requested_; } + auto texture_quality_requested() const { + assert(InGraphicsContext_()); + return texture_quality_requested_; + } void HandlePushAndroidRes(const std::string& android_res); + auto texture_compression_types() const { + assert(texture_compression_types_set_); + return texture_compression_types_; + } + private: + /// Pass a freshly allocated GraphicsContext instance, which the graphics + /// system will take ownership of. + void set_client_context(GraphicsClientContext* context); + // So we don't have to include app_adapter.h here for asserts. auto InGraphicsContext_() const -> bool; @@ -293,8 +314,8 @@ class GraphicsServer { auto WaitForRenderFrameDef_() -> FrameDef*; // Update virtual screen dimensions based on the current physical ones. - static void CalcVirtualRes_(float* x, float* y); - void UpdateVirtualScreenRes_(); + // static void CalcVirtualRes_(float* x, float* y); + // void UpdateVirtualScreenRes_(); void UpdateCamOrientMatrix_(); void ReloadMedia_(); void UpdateModelViewProjectionMatrix_() { @@ -314,23 +335,17 @@ class GraphicsServer { } bool renderer_loaded_ : 1 {}; - bool v_sync_ : 1 {}; - bool auto_vsync_ : 1 {}; bool model_view_projection_matrix_dirty_ : 1 {true}; bool model_world_matrix_dirty_ : 1 {true}; - bool graphics_quality_set_ : 1 {}; - bool texture_quality_set_ : 1 {}; bool tv_border_ : 1 {}; bool renderer_context_lost_ : 1 {}; bool texture_compression_types_set_ : 1 {}; bool cam_orient_matrix_dirty_ : 1 {true}; - TextureQualityRequest texture_quality_requested_{ - TextureQualityRequest::kUnset}; - TextureQuality texture_quality_{TextureQuality::kLow}; - GraphicsQualityRequest graphics_quality_requested_{ - GraphicsQualityRequest::kUnset}; - GraphicsQuality graphics_quality_{GraphicsQuality::kUnset}; - int render_hold_{}; + Snapshot* client_context_{}; + TextureQualityRequest texture_quality_requested_{}; + TextureQuality texture_quality_{}; + GraphicsQualityRequest graphics_quality_requested_{}; + GraphicsQuality graphics_quality_{}; float res_x_{}; float res_y_{}; float res_x_virtual_{}; @@ -340,20 +355,21 @@ class GraphicsServer { Matrix44f projection_matrix_{kMatrix44fIdentity}; Matrix44f model_view_projection_matrix_{kMatrix44fIdentity}; Matrix44f model_world_matrix_{kMatrix44fIdentity}; - std::vector model_view_stack_; uint32_t texture_compression_types_{}; - uint32_t projection_matrix_state_{1}; - uint32_t model_view_projection_matrix_state_{1}; - uint32_t model_world_matrix_state_{1}; - uint32_t light_shadow_projection_matrix_state_{1}; - uint32_t cam_pos_state_{1}; - uint32_t cam_orient_matrix_state_{1}; + int render_hold_{}; + int projection_matrix_state_{}; + int model_view_projection_matrix_state_{}; + int model_world_matrix_state_{}; + int light_shadow_projection_matrix_state_{}; + int cam_pos_state_{}; + int cam_orient_matrix_state_{}; + int settings_index_{-1}; Vector3f cam_pos_{0.0f, 0.0f, 0.0f}; Vector3f cam_target_{0.0f, 0.0f, 0.0f}; Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity}; Matrix44f cam_orient_matrix_ = kMatrix44fIdentity; + std::vector model_view_stack_; std::list mesh_datas_; - Timer* render_timer_{}; Renderer* renderer_{}; FrameDef* frame_def_{}; std::mutex frame_def_mutex_{}; diff --git a/src/ballistica/base/graphics/renderer/render_pass.cc b/src/ballistica/base/graphics/renderer/render_pass.cc index 966dbbda..39a265b1 100644 --- a/src/ballistica/base/graphics/renderer/render_pass.cc +++ b/src/ballistica/base/graphics/renderer/render_pass.cc @@ -487,7 +487,7 @@ void RenderPass::SetFrustum(float near_val, float far_val) { g_base->graphics_server->SetProjectionMatrix(projection_matrix_); } -void RenderPass::Finalize() { +void RenderPass::Complete() { if (UsesWorldLists()) { for (auto& command : commands_) { command->Finalize(); diff --git a/src/ballistica/base/graphics/renderer/render_pass.h b/src/ballistica/base/graphics/renderer/render_pass.h index d52f3817..90abb2b7 100644 --- a/src/ballistica/base/graphics/renderer/render_pass.h +++ b/src/ballistica/base/graphics/renderer/render_pass.h @@ -112,7 +112,7 @@ class RenderPass { return model_view_projection_matrix_; } auto HasDrawCommands() const -> bool; - void Finalize(); + void Complete(); void Reset(); // Whether this pass draws stuff from the per-shader command lists diff --git a/src/ballistica/base/graphics/renderer/renderer.cc b/src/ballistica/base/graphics/renderer/renderer.cc index 88f36c98..9aeae8f0 100644 --- a/src/ballistica/base/graphics/renderer/renderer.cc +++ b/src/ballistica/base/graphics/renderer/renderer.cc @@ -35,10 +35,12 @@ void Renderer::PreprocessFrameDef(FrameDef* frame_def) { // If this frame_def was made in a different quality mode than we're // currently in, don't attempt to render it. - if (frame_def->quality() != g_base->graphics_server->quality()) { - frame_def->set_rendering(false); - return; - } + // UPDATE - scratch that; we now set our quality FROM the frame def. + // if (frame_def->quality() != g_base->graphics_server->quality()) { + // frame_def->set_rendering(false); + // return; + // } + frame_def->set_rendering(true); // Some VR environments muck with render states before/after diff --git a/src/ballistica/base/graphics/renderer/renderer.h b/src/ballistica/base/graphics/renderer/renderer.h index 9f5198a0..0ab47c42 100644 --- a/src/ballistica/base/graphics/renderer/renderer.h +++ b/src/ballistica/base/graphics/renderer/renderer.h @@ -82,7 +82,6 @@ class Renderer { auto light_pitch() const -> float { return light_pitch_; } auto light_heading() const -> float { return light_heading_; } void set_pixel_scale(float s) { pixel_scale_requested_ = s; } - // void set_screen_gamma(float val) { screen_gamma_requested_ = val; } void set_debug_draw_mode(bool debugModeIn) { debug_draw_mode_ = debugModeIn; } auto debug_draw_mode() -> bool { return debug_draw_mode_; } @@ -269,7 +268,6 @@ class Renderer { Vector3f vignette_outer_{0.0f, 0.0f, 0.0f}; Vector3f vignette_inner_{1.0f, 1.0f, 1.0f}; int shadow_res_{-1}; - // float screen_gamma_requested_{1.0f}; float screen_gamma_{1.0f}; float pixel_scale_requested_{1.0f}; float pixel_scale_{1.0f}; diff --git a/src/ballistica/base/graphics/support/camera.cc b/src/ballistica/base/graphics/support/camera.cc index aa0554df..d12ad130 100644 --- a/src/ballistica/base/graphics/support/camera.cc +++ b/src/ballistica/base/graphics/support/camera.cc @@ -1013,7 +1013,7 @@ void Camera::ApplyToFrameDef(FrameDef* frame_def) { up_, 4, 1000.0f, -1.0f, // Auto x fov. final_fov_y - * (g_base->graphics->tv_border() ? (1.0f + kTVBorder) : 1.0f), + * (frame_def->settings()->tv_border ? (1.0f + kTVBorder) : 1.0f), false, 0, 0, 0, 0, // Not using tangent fovs. area_of_interest_points_); } diff --git a/src/ballistica/base/graphics/support/frame_def.cc b/src/ballistica/base/graphics/support/frame_def.cc index 06def221..52c2ed80 100644 --- a/src/ballistica/base/graphics/support/frame_def.cc +++ b/src/ballistica/base/graphics/support/frame_def.cc @@ -49,6 +49,13 @@ auto FrameDef::GetOverlayFlatPass() -> RenderPass* { void FrameDef::Reset() { assert(g_base->InLogicThread()); + + // Update & grab the current settings. + settings_snapshot_ = g_base->graphics->GetGraphicsSettingsSnapshot(); + + auto* settings = settings_snapshot_->Get(); + auto* client_context = g_base->graphics->client_context(); + app_time_microsecs_ = 0; display_time_microsecs_ = 0; display_time_elapsed_microsecs_ = 0; @@ -68,11 +75,17 @@ void FrameDef::Reset() { mesh_index_sizes_.clear(); mesh_buffers_.clear(); - quality_ = g_base->graphics_server->quality(); + quality_ = Graphics::GraphicsQualityFromRequest( + settings->graphics_quality, client_context->auto_graphics_quality); - assert(g_base->graphics->has_supports_high_quality_graphics_value()); + texture_quality_ = Graphics::TextureQualityFromRequest( + settings->texture_quality, client_context->auto_texture_quality); + + // pixel_scale_ = g_base->graphics->settings()->pixel_scale; + + // assert(g_base->graphics->has_supports_high_quality_graphics_value()); orbiting_ = (g_base->graphics->camera()->mode() == CameraMode::kOrbit); - tv_border_ = g_base->graphics->tv_border(); + // tv_border_ = g_base->graphics->tv_border(); shadow_offset_ = g_base->graphics->shadow_offset(); shadow_scale_ = g_base->graphics->shadow_scale(); @@ -99,21 +112,21 @@ void FrameDef::Reset() { beauty_pass_->set_floor_reflection(g_base->graphics->floor_reflection()); } -void FrameDef::Finalize() { +void FrameDef::Complete() { assert(!defining_component_); - light_pass_->Finalize(); - light_shadow_pass_->Finalize(); - beauty_pass_->Finalize(); - beauty_pass_bg_->Finalize(); - overlay_pass_->Finalize(); - overlay_front_pass_->Finalize(); + light_pass_->Complete(); + light_shadow_pass_->Complete(); + beauty_pass_->Complete(); + beauty_pass_bg_->Complete(); + overlay_pass_->Complete(); + overlay_front_pass_->Complete(); if (g_core->IsVRMode()) { - overlay_fixed_pass_->Finalize(); - overlay_flat_pass_->Finalize(); - vr_cover_pass_->Finalize(); + overlay_fixed_pass_->Complete(); + overlay_flat_pass_->Complete(); + vr_cover_pass_->Complete(); } - overlay_3d_pass_->Finalize(); - blit_pass_->Finalize(); + overlay_3d_pass_->Complete(); + blit_pass_->Complete(); } void FrameDef::AddMesh(Mesh* mesh) { diff --git a/src/ballistica/base/graphics/support/frame_def.h b/src/ballistica/base/graphics/support/frame_def.h index 41341028..d839c52e 100644 --- a/src/ballistica/base/graphics/support/frame_def.h +++ b/src/ballistica/base/graphics/support/frame_def.h @@ -7,13 +7,14 @@ #include #include "ballistica/base/assets/asset.h" +#include "ballistica/shared/generic/snapshot.h" #include "ballistica/shared/math/matrix44f.h" #include "ballistica/shared/math/vector2f.h" namespace ballistica::base { /// A flattened representation of a frame; generated by the logic thread and -/// sent to the graphics thread to render. +/// sent to the graphics server to render. class FrameDef { public: auto light_pass() -> RenderPass* { return light_pass_.get(); } @@ -49,15 +50,15 @@ class FrameDef { // A number incremented for each frame renderered. Note that graphics code // should not plug this directly into things like flash calculations since - // frame-rates will vary a lot these days. A 30hz flash will look a lot + // frame-rates vary a lot these days. A 30hz flash will look a lot // different than a 240hz flash. Use frame_number_filtered() for such // purposes. auto frame_number() const { return frame_number_; } // A number incremented for each frame rendered, but a maximum of 60 times - // per second. Code for drawing flashes or other exact effects should use - // this value instead of regular frame_number so that things don't turn - // muddy at extremely high frame rates. + // per second. Code for drawing flashes or other crisp blink-y effects + // should use this value instead of regular frame_number so that things + // don't turn muddy at extremely high frame rates. auto frame_number_filtered() const { return frame_number_filtered_; } // Returns the display-time this frame-def was created at (tries to match @@ -66,14 +67,19 @@ class FrameDef { auto display_time_millisecs() const -> millisecs_t { return display_time_microsecs_ / 1000; } + auto display_time_microsecs() const -> microsecs_t { return display_time_microsecs_; } - auto display_time() const -> double { - return static_cast(display_time_microsecs_) / 1000000.0; + + auto display_time() const -> seconds_t { + return static_cast(display_time_microsecs_) / 1000000.0; + } + + auto display_time_elapsed() const -> seconds_t { + return static_cast(display_time_elapsed_microsecs_) / 1000000.0; } - // How much display time does this frame-def represent. auto display_time_elapsed_millisecs() const -> millisecs_t { return display_time_elapsed_millisecs_; } @@ -82,7 +88,9 @@ class FrameDef { return display_time_elapsed_microsecs_; } - auto quality() const -> GraphicsQuality { return quality_; } + auto quality() const { return quality_; } + auto texture_quality() const { return texture_quality_; } + auto orbiting() const -> bool { return orbiting_; } auto shadow_offset() const -> const Vector3f& { return shadow_offset_; } auto shadow_scale() const -> const Vector2f& { return shadow_scale_; } @@ -115,11 +123,12 @@ class FrameDef { vr_overlay_screen_matrix_fixed_ = mat; } - // Effects requiring availability of a depth texture should - // check this to determine whether they should draw. - auto has_depth_texture() const -> bool { + // Effects requiring availability of a depth texture should check this to + // determine whether they should draw. + auto HasDepthTexture() const -> bool { return (quality_ >= GraphicsQuality::kHigh); } + void AddComponent(const Object::Ref& component) { // Add a reference to this component only if we havn't yet. if (component->last_frame_def_num() != frame_number_) { @@ -134,7 +143,7 @@ class FrameDef { FrameDef(); ~FrameDef(); void Reset(); - void Finalize(); + void Complete(); void set_display_time_elapsed_microsecs(microsecs_t val) { display_time_elapsed_microsecs_ = val; @@ -187,7 +196,7 @@ class FrameDef { auto media_components() const -> const std::vector>& { return media_components_; } - auto tv_border() const { return tv_border_; } + // auto tv_border() const { return tv_border_; } void set_camera_mode(CameraMode val) { camera_mode_ = val; } void set_rendering(bool val) { rendering_ = val; } @@ -204,18 +213,27 @@ class FrameDef { } #endif + // auto pixel_scale() const { return pixel_scale_; } + + auto* settings() const { + assert(settings_snapshot_.Exists()); + return settings_snapshot_->Get(); + } + private: - bool needs_clear_{}; - bool rendering_{}; - bool orbiting_{}; - bool tv_border_{}; - bool shadow_ortho_{}; + Object::Ref> settings_snapshot_; + bool needs_clear_ : 1 {}; + bool rendering_ : 1 {}; + bool orbiting_ : 1 {}; + // bool tv_border_ : 1 {}; + bool shadow_ortho_ : 1 {}; BenchmarkType benchmark_type_{BenchmarkType::kNone}; CameraMode camera_mode_{CameraMode::kFollow}; Vector3f cam_original_{0.0f, 0.0f, 0.0f}; Vector3f cam_target_original_{0.0f, 0.0f, 0.0f}; Vector3f shake_original_{0.0f, 0.0f, 0.0f}; float vr_near_clip_{}; + // float pixel_scale_{}; Matrix44f vr_overlay_screen_matrix_ = kMatrix44fIdentity; Matrix44f vr_overlay_screen_matrix_fixed_ = kMatrix44fIdentity; std::vector mesh_data_creates_; @@ -245,7 +263,8 @@ class FrameDef { std::unique_ptr vr_cover_pass_; std::unique_ptr overlay_3d_pass_; std::unique_ptr blit_pass_; - GraphicsQuality quality_{GraphicsQuality::kLow}; + GraphicsQuality quality_{}; + TextureQuality texture_quality_{}; microsecs_t app_time_microsecs_{}; microsecs_t display_time_microsecs_{}; microsecs_t display_time_elapsed_microsecs_{}; diff --git a/src/ballistica/base/graphics/support/graphics_client_context.cc b/src/ballistica/base/graphics/support/graphics_client_context.cc new file mode 100644 index 00000000..5201cdc9 --- /dev/null +++ b/src/ballistica/base/graphics/support/graphics_client_context.cc @@ -0,0 +1,23 @@ +// Released under the MIT License. See LICENSE for details. + +#include "ballistica/base/graphics/support/graphics_client_context.h" + +#include "ballistica/base/graphics/graphics_server.h" +#include "ballistica/base/graphics/renderer/renderer.h" + +namespace ballistica::base { + +GraphicsClientContext::GraphicsClientContext() + : auto_graphics_quality{g_base->graphics_server->renderer() + ->GetAutoGraphicsQuality()}, + auto_texture_quality{ + g_base->graphics_server->renderer()->GetAutoTextureQuality()}, + texture_compression_types{ + g_base->graphics_server->texture_compression_types()} {} + +GraphicsClientContext::GraphicsClientContext(int dummy) + : auto_graphics_quality{GraphicsQuality::kLow}, + auto_texture_quality{TextureQuality::kLow}, + texture_compression_types{0} {} + +} // namespace ballistica::base diff --git a/src/ballistica/base/graphics/support/graphics_client_context.h b/src/ballistica/base/graphics/support/graphics_client_context.h new file mode 100644 index 00000000..c130413d --- /dev/null +++ b/src/ballistica/base/graphics/support/graphics_client_context.h @@ -0,0 +1,31 @@ +// Released under the MIT License. See LICENSE for details. + +#ifndef BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_CLIENT_CONTEXT_H_ +#define BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_CLIENT_CONTEXT_H_ + +#include "ballistica/base/base.h" + +namespace ballistica::base { + +/// Represents a valid graphics setup delivered by the graphics server to +/// the logic thread. It contains various info about concrete graphics +/// settings and capabilities. +struct GraphicsClientContext { + GraphicsClientContext(); + + /// Special constructor to create a dummy context (used by headless builds). + explicit GraphicsClientContext(int dummy); + + auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool { + return ((texture_compression_types & (0x01u << static_cast(t))) + != 0u); + } + + GraphicsQuality auto_graphics_quality; + TextureQuality auto_texture_quality; + uint32_t texture_compression_types; +}; + +} // namespace ballistica::base + +#endif // BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_CLIENT_CONTEXT_H_ diff --git a/src/ballistica/base/graphics/support/graphics_settings.cc b/src/ballistica/base/graphics/support/graphics_settings.cc new file mode 100644 index 00000000..a132fe88 --- /dev/null +++ b/src/ballistica/base/graphics/support/graphics_settings.cc @@ -0,0 +1,27 @@ +// Released under the MIT License. See LICENSE for details. + +#include "ballistica/base/graphics/support/graphics_settings.h" + +#include + +#include "ballistica/base/graphics/graphics.h" +#include "ballistica/base/support/app_config.h" +#include "ballistica/shared/foundation/object.h" + +namespace ballistica::base { + +GraphicsSettings::GraphicsSettings() + + : resolution{g_base->graphics->screen_pixel_width(), + g_base->graphics->screen_pixel_height()}, + resolution_virtual{g_base->graphics->screen_virtual_width(), + g_base->graphics->screen_virtual_height()}, + pixel_scale{std::clamp( + g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale), + 0.1f, 1.0f)}, + graphics_quality{g_base->graphics->GraphicsQualityFromAppConfig()}, + texture_quality{g_base->graphics->TextureQualityFromAppConfig()}, + tv_border{ + g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder)} {} + +} // namespace ballistica::base diff --git a/src/ballistica/base/graphics/support/graphics_settings.h b/src/ballistica/base/graphics/support/graphics_settings.h new file mode 100644 index 00000000..89ce31ea --- /dev/null +++ b/src/ballistica/base/graphics/support/graphics_settings.h @@ -0,0 +1,33 @@ +// Released under the MIT License. See LICENSE for details. + +#ifndef BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_SETTINGS_H_ +#define BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_SETTINGS_H_ + +#include "ballistica/base/base.h" +#include "ballistica/shared/math/vector2f.h" + +namespace ballistica::base { + +/// A set of settings for graphics, covering things like screen +/// resolution, texture quality, etc. These are filled out by the +/// AppAdapter in the logic thread and passed up to the GraphicsServer +/// either through standalone calls or attached to a FrameDef. Generally +/// AppAdapters define their own subclass of this containing additional +/// settings specific to themselves or the renderer(s) they use. +struct GraphicsSettings { + GraphicsSettings(); + // Each new settings instance will be assigned a unique incrementing index. + int index{-1}; + + // Some standard settings used by most renderers. + Vector2f resolution; + Vector2f resolution_virtual; + float pixel_scale; + GraphicsQualityRequest graphics_quality; + TextureQualityRequest texture_quality; + bool tv_border; +}; + +} // namespace ballistica::base + +#endif // BALLISTICA_BASE_GRAPHICS_SUPPORT_GRAPHICS_SETTINGS_H_ diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc index 736ecc37..551b499c 100644 --- a/src/ballistica/base/input/input.cc +++ b/src/ballistica/base/input/input.cc @@ -159,16 +159,16 @@ void Input::AnnounceConnects_() { // For the first announcement just say "X controllers detected" and don't // have a sound. - if (first_print && g_core->GetAppTimeMillisecs() < 10000) { + if (first_print && g_core->GetAppTimeSeconds() < 5.0) { first_print = false; - // Disabling this completely for now; being more lenient with devices - // allowed on Android means this will often come back with large - // numbers. - bool do_print{false}; + // Disabling this completely on Android for now; we often get large + // numbers of devices there that aren't actually devices. + + bool do_print_initial_counts{!g_buildconfig.ostype_android()}; // If there's been several connected, just give a number. - if (explicit_bool(do_print)) { + if (explicit_bool(do_print_initial_counts)) { if (newly_connected_controllers_.size() > 1) { std::string s = g_base->assets->GetResourceString("controllersDetectedText"); diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index f752c635..67a05cf2 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -76,11 +76,11 @@ void Logic::OnAppStart() { void Logic::OnGraphicsReady() { assert(g_base->InLogicThread()); - if (on_initial_screen_creation_complete_called_) { + if (graphics_ready_) { // Only want to fire this logic the first time. return; } - on_initial_screen_creation_complete_called_ = true; + graphics_ready_ = true; // Ok; graphics-server is telling us we've got a screen (or no screen in // the case of headless-mode). We use this as a cue to kick off our diff --git a/src/ballistica/base/logic/logic.h b/src/ballistica/base/logic/logic.h index 18852d62..b215be32 100644 --- a/src/ballistica/base/logic/logic.h +++ b/src/ballistica/base/logic/logic.h @@ -4,7 +4,6 @@ #define BALLISTICA_BASE_LOGIC_LOGIC_H_ #include -#include #include #include "ballistica/shared/foundation/object.h" @@ -118,6 +117,8 @@ class Logic { auto shutting_down() const { return shutting_down_; } auto shutdown_completed() const { return shutdown_completed_; } + auto graphics_ready() const { return graphics_ready_; } + private: void UpdateDisplayTimeForFrameDraw_(); void UpdateDisplayTimeForHeadlessMode_(); @@ -127,31 +128,31 @@ class Logic { void UpdatePendingWorkTimer_(); void StepDisplayTime_(); - double display_time_{}; - double display_time_increment_{1.0 / 60.0}; + seconds_t display_time_{}; + seconds_t display_time_increment_{1.0 / 60.0}; microsecs_t display_time_microsecs_{}; microsecs_t display_time_increment_microsecs_{1000000 / 60}; - // GUI scheduling. - double last_display_time_update_app_time_{-1.0}; - double recent_display_time_increments_[kDisplayTimeSampleCount]{}; - int recent_display_time_increments_index_{-1}; - // Headless scheduling. Timer* headless_display_time_step_timer_{}; - Timer* process_pending_work_timer_{}; - Timer* asset_prune_timer_{}; - Timer* debug_timer_{}; - EventLoop* event_loop_{}; - std::unique_ptr display_timers_; + // GUI scheduling. + seconds_t last_display_time_update_app_time_{-1.0}; + seconds_t recent_display_time_increments_[kDisplayTimeSampleCount]{}; + int recent_display_time_increments_index_{-1}; + bool app_bootstrapping_complete_ : 1 {}; bool have_pending_loads_ : 1 {}; bool debug_log_display_time_ : 1 {}; bool applied_app_config_ : 1 {}; bool shutting_down_ : 1 {}; bool shutdown_completed_ : 1 {}; - bool on_initial_screen_creation_complete_called_ : 1 {}; + bool graphics_ready_ : 1 {}; + Timer* process_pending_work_timer_{}; + Timer* asset_prune_timer_{}; + Timer* debug_timer_{}; + EventLoop* event_loop_{}; + std::unique_ptr display_timers_; }; } // namespace ballistica::base diff --git a/src/ballistica/base/platform/apple/base_platform_apple.cc b/src/ballistica/base/platform/apple/base_platform_apple.cc index ef83cd71..fc18f79c 100644 --- a/src/ballistica/base/platform/apple/base_platform_apple.cc +++ b/src/ballistica/base/platform/apple/base_platform_apple.cc @@ -4,6 +4,7 @@ #include "ballistica/base/platform/apple/base_platform_apple.h" #if BA_XCODE_BUILD +#include #include #endif #include @@ -53,9 +54,14 @@ void BasePlatformApple::PurchaseAck(const std::string& purchase, void BasePlatformApple::DoOpenURL(const std::string& url) { #if BA_XCODE_BUILD +#if BA_OSTYPE_MACOS + BallisticaKit::CocoaFromCppOpenURL(url); +#else + BallisticaKit::UIKitFromCppOpenURL(url); +#endif // Go ahead and do this ourself. Though perhaps the default // Python path would be fine. - AppleUtils::OpenURL(url.c_str()); + // AppleUtils::OpenURL(url.c_str()); #else // Otherwise go with the default (Python webbrowser module). BasePlatform::DoOpenURL(url); diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc index 94792ad4..befd17d3 100644 --- a/src/ballistica/base/python/methods/python_methods_app.cc +++ b/src/ballistica/base/python/methods/python_methods_app.cc @@ -810,9 +810,9 @@ static PyMethodDef PySetStressTestingDef = { "(internal)", }; -// ------------------------------ display_log ---------------------------------- +// -------------------------------- emit_log ----------------------------------- -static auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds) +static auto PyEmitLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; static const char* kwlist[] = {"name", "level", "message", nullptr}; @@ -839,25 +839,25 @@ static auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds) level = LogLevel::kCritical; } else { // Assume we should avoid Log() calls here since it could infinite loop. - fprintf(stderr, "Invalid log level to display_log(): %s\n", levelstr); + fprintf(stderr, "Invalid log level to emit_log(): %s\n", levelstr); level = LogLevel::kInfo; } - Logging::DisplayLog(name, level, message); + Logging::EmitLog(name, level, message); Py_RETURN_NONE; BA_PYTHON_CATCH; } -static PyMethodDef PyDisplayLogDef = { - "display_log", // name - (PyCFunction)PyDisplayLog, // method +static PyMethodDef PyEmitLogDef = { + "emit_log", // name + (PyCFunction)PyEmitLog, // method METH_VARARGS | METH_KEYWORDS, // flags - "display_log(name: str, level: str, message: str) -> None\n" + "emit_log(name: str, level: str, message: str) -> None\n" "\n" "(internal)\n" "\n" - "Sends a log message to the in-game console and any per-platform\n" + "Sends a log message to the in-app console and any per-platform\n" "log destinations (Android log, etc.). This generally is not called\n" "directly and should instead be fed Python logging output.", }; @@ -1654,7 +1654,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector { PyAppNameUpperDef, PyIsXCodeBuildDef, PyCanDisplayFullUnicodeDef, - PyDisplayLogDef, + PyEmitLogDef, PyV1CloudLogDef, PySetStressTestingDef, PyEnvDef, diff --git a/src/ballistica/base/python/methods/python_methods_graphics.cc b/src/ballistica/base/python/methods/python_methods_graphics.cc index 0ebf464b..63eac46f 100644 --- a/src/ballistica/base/python/methods/python_methods_graphics.cc +++ b/src/ballistica/base/python/methods/python_methods_graphics.cc @@ -348,13 +348,14 @@ static PyMethodDef PySafeColorDef = { static auto PyGetMaxGraphicsQuality(PyObject* self) -> PyObject* { BA_PYTHON_TRY; - if (g_base->graphics - && g_base->graphics->has_supports_high_quality_graphics_value() - && g_base->graphics->supports_high_quality_graphics()) { - return Py_BuildValue("s", "High"); - } else { - return Py_BuildValue("s", "Medium"); - } + // if (g_base->graphics + // && g_base->graphics->has_supports_high_quality_graphics_value() + // && g_base->graphics->supports_high_quality_graphics()) { + // return Py_BuildValue("s", "High"); + // } else { + // return Py_BuildValue("s", "Medium"); + // } + return Py_BuildValue("s", "Higher"); BA_PYTHON_CATCH; } diff --git a/src/ballistica/core/platform/apple/core_platform_apple.cc b/src/ballistica/core/platform/apple/core_platform_apple.cc index b227b817..04d79880 100644 --- a/src/ballistica/core/platform/apple/core_platform_apple.cc +++ b/src/ballistica/core/platform/apple/core_platform_apple.cc @@ -147,17 +147,17 @@ auto CorePlatformApple::IsRunningOnDesktop() -> bool { #endif } -void CorePlatformApple::DisplayLog(const std::string& name, LogLevel level, - const std::string& msg) { +void CorePlatformApple::EmitPlatformLog(const std::string& name, LogLevel level, + const std::string& msg) { #if BA_XCODE_BUILD && !BA_HEADLESS_BUILD // HMM: do we want to use proper logging APIs here or simple printing? // base::AppleUtils::NSLogStr(msg); - CorePlatform::DisplayLog(name, level, msg); + CorePlatform::EmitPlatformLog(name, level, msg); #else // Fall back to default handler... - CorePlatform::DisplayLog(name, level, msg); + CorePlatform::EmitPlatformLog(name, level, msg); #endif } diff --git a/src/ballistica/core/platform/apple/core_platform_apple.h b/src/ballistica/core/platform/apple/core_platform_apple.h index 4198f7e8..6419215d 100644 --- a/src/ballistica/core/platform/apple/core_platform_apple.h +++ b/src/ballistica/core/platform/apple/core_platform_apple.h @@ -26,8 +26,8 @@ class CorePlatformApple : public CorePlatform { auto DoHasTouchScreen() -> bool override; auto GetDefaultUIScale() -> UIScale override; auto IsRunningOnDesktop() -> bool override; - void DisplayLog(const std::string& name, LogLevel level, - const std::string& msg) override; + void EmitPlatformLog(const std::string& name, LogLevel level, + const std::string& msg) override; void GetTextBoundsAndWidth(const std::string& text, Rect* r, float* width) override; void FreeTextTexture(void* tex) override; diff --git a/src/ballistica/core/platform/core_platform.cc b/src/ballistica/core/platform/core_platform.cc index 338d451d..35a9e4e1 100644 --- a/src/ballistica/core/platform/core_platform.cc +++ b/src/ballistica/core/platform/core_platform.cc @@ -465,12 +465,17 @@ auto CorePlatform::IsRunningOnDesktop() -> bool { return true; } -void CorePlatform::SleepMillisecs(millisecs_t ms) { - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +void CorePlatform::SleepSeconds(seconds_t duration) { + std::this_thread::sleep_for( + std::chrono::microseconds(static_cast(duration * 1000000))); } -void CorePlatform::SleepMicrosecs(millisecs_t ms) { - std::this_thread::sleep_for(std::chrono::microseconds(ms)); +void CorePlatform::SleepMillisecs(millisecs_t duration) { + std::this_thread::sleep_for(std::chrono::milliseconds(duration)); +} + +void CorePlatform::SleepMicrosecs(millisecs_t duration) { + std::this_thread::sleep_for(std::chrono::microseconds(duration)); } #pragma clang diagnostic push @@ -481,8 +486,8 @@ auto CorePlatform::GetDefaultUIScale() -> UIScale { return UIScale::kLarge; } -void CorePlatform::DisplayLog(const std::string& name, LogLevel level, - const std::string& msg) { +void CorePlatform::EmitPlatformLog(const std::string& name, LogLevel level, + const std::string& msg) { // Do nothing by default. } diff --git a/src/ballistica/core/platform/core_platform.h b/src/ballistica/core/platform/core_platform.h index df05291a..23fd4fae 100644 --- a/src/ballistica/core/platform/core_platform.h +++ b/src/ballistica/core/platform/core_platform.h @@ -99,8 +99,8 @@ class CorePlatform { /// Display a message to any default log for the platform (android log, /// etc.) Note that this can be called from any thread. Default /// implementation does nothing. - virtual void DisplayLog(const std::string& name, LogLevel level, - const std::string& msg); + virtual void EmitPlatformLog(const std::string& name, LogLevel level, + const std::string& msg); #pragma mark ENVIRONMENT ------------------------------------------------------- @@ -381,9 +381,9 @@ class CorePlatform { /// to not go backwards. static auto GetCurrentWholeSeconds() -> int64_t; - static void SleepMillisecs(millisecs_t ms); - - static void SleepMicrosecs(microsecs_t ms); + static void SleepSeconds(seconds_t duration); + static void SleepMillisecs(millisecs_t duration); + static void SleepMicrosecs(microsecs_t duration); /// Given a C++ symbol, attempt to return a pretty one. virtual auto DemangleCXXSymbol(const std::string& s) -> std::string; diff --git a/src/ballistica/core/platform/windows/core_platform_windows.cc b/src/ballistica/core/platform/windows/core_platform_windows.cc index f7e08276..8a44a78e 100644 --- a/src/ballistica/core/platform/windows/core_platform_windows.cc +++ b/src/ballistica/core/platform/windows/core_platform_windows.cc @@ -827,11 +827,12 @@ std::string CorePlatformWindows::DoGetDeviceName() { bool CorePlatformWindows::DoHasTouchScreen() { return false; } -void CorePlatformWindows::DisplayLog(const std::string& name, LogLevel level, - const std::string& msg) { +void CorePlatformWindows::EmitPlatformLog(const std::string& name, + LogLevel level, + const std::string& msg) { // if (have_stdin_stdout_) { // // On headless builds we use default handler (simple stdout). - // return CorePlatform::DisplayLog(msg); + // return CorePlatform::EmitPlatformLog(msg); // } // Also spit this out as a debug-string for when running from msvc. diff --git a/src/ballistica/core/platform/windows/core_platform_windows.h b/src/ballistica/core/platform/windows/core_platform_windows.h index ddc1054a..fe77ae71 100644 --- a/src/ballistica/core/platform/windows/core_platform_windows.h +++ b/src/ballistica/core/platform/windows/core_platform_windows.h @@ -41,8 +41,8 @@ class CorePlatformWindows : public CorePlatform { auto GetLocale() -> std::string override; auto DoGetDeviceName() -> std::string override; auto DoHasTouchScreen() -> bool override; - void DisplayLog(const std::string& name, LogLevel level, - const std::string& msg) override; + void EmitPlatformLog(const std::string& name, LogLevel level, + const std::string& msg) override; void SetEnv(const std::string& name, const std::string& value) override; auto GetEnv(const std::string& name) -> std::optional override; auto GetIsStdinATerminal() -> bool override; diff --git a/src/ballistica/core/python/core_python.cc b/src/ballistica/core/python/core_python.cc index 1794ae97..dcebb4a6 100644 --- a/src/ballistica/core/python/core_python.cc +++ b/src/ballistica/core/python/core_python.cc @@ -307,8 +307,8 @@ void CorePython::LoggingCall(LogLevel loglevel, const std::string& msg) { "CorePython::LoggingCall() called before Python" " logging available."}; if (g_core->platform) { - g_core->platform->DisplayLog("root", LogLevel::kError, errmsg); - g_core->platform->DisplayLog("root", loglevel, msg); + g_core->platform->EmitPlatformLog("root", LogLevel::kError, errmsg); + g_core->platform->EmitPlatformLog("root", loglevel, msg); } fprintf(stderr, "%s\n%s\n", errmsg, msg.c_str()); } diff --git a/src/ballistica/core/python/core_python.h b/src/ballistica/core/python/core_python.h index 2aa49e42..fef471f4 100644 --- a/src/ballistica/core/python/core_python.h +++ b/src/ballistica/core/python/core_python.h @@ -45,7 +45,7 @@ class CorePython { /// Calls Python logging function (logging.error, logging.warning, etc.) /// Can be called from any thread at any time. If called before Python - /// logging is available, logs locally using Logging::DisplayLog() + /// logging is available, logs locally using Logging::EmitPlatformLog() /// (with an added warning). void LoggingCall(LogLevel loglevel, const std::string& msg); void ImportPythonObjs(); diff --git a/src/ballistica/scene_v1/dynamics/collision.h b/src/ballistica/scene_v1/dynamics/collision.h index 65add53b..242ca422 100644 --- a/src/ballistica/scene_v1/dynamics/collision.h +++ b/src/ballistica/scene_v1/dynamics/collision.h @@ -12,11 +12,12 @@ namespace ballistica::scene_v1 { -// Stores info about an occurring collision. -// Note than just because a collision exists between two parts doesn't mean -// they're physically colliding in the simulation. It is just a shortcut to -// determine what behavior, if any, exists between two parts which are currently -// overlapping in the simulation. +/// Stores info about an occurring collision. +/// +/// Note than just because a collision exists between two parts doesn't mean +/// they're physically colliding in the simulation. It is just a shortcut to +/// determine what behavior, if any, exists between two parts which are +/// currently overlapping in the simulation. class Collision : public Object { public: explicit Collision(Scene* scene) : src_context(scene), dst_context(scene) {} diff --git a/src/ballistica/scene_v1/dynamics/dynamics.cc b/src/ballistica/scene_v1/dynamics/dynamics.cc index 4403f25c..ba6e5564 100644 --- a/src/ballistica/scene_v1/dynamics/dynamics.cc +++ b/src/ballistica/scene_v1/dynamics/dynamics.cc @@ -66,77 +66,77 @@ void do_dBodyGetLocalFeedback(dBodyID b, dReal px, dReal py, dReal pz, // Stores info about a collision needing a reset // (used when parts change materials). -class Dynamics::CollisionReset { +class Dynamics::CollisionReset_ { public: int node1; int node2; int part1; int part2; - CollisionReset(int node1_in, int part1_in, int node2_in, int part2_in) + CollisionReset_(int node1_in, int part1_in, int node2_in, int part2_in) : node1(node1_in), node2(node2_in), part1(part1_in), part2(part2_in) {} }; -class Dynamics::CollisionEvent { +class Dynamics::CollisionEvent_ { public: Object::Ref action; Object::Ref collision; Object::WeakRef node1; // first event node Object::WeakRef node2; // second event node - CollisionEvent(Node* node1_in, Node* node2_in, - const Object::Ref& action_in, - const Object::Ref& collision_in) + CollisionEvent_(Node* node1_in, Node* node2_in, + const Object::Ref& action_in, + const Object::Ref& collision_in) : node1(node1_in), node2(node2_in), action(action_in), collision(collision_in) {} }; -class Dynamics::SrcPartCollideMap { +class Dynamics::SrcPartCollideMap_ { public: std::unordered_map > dst_part_collisions; }; -class Dynamics::DstNodeCollideMap { +class Dynamics::DstNodeCollideMap_ { public: - std::unordered_map src_parts; + std::unordered_map src_parts; int collideDisabled; - DstNodeCollideMap() : collideDisabled(0) {} - ~DstNodeCollideMap() = default; + DstNodeCollideMap_() : collideDisabled(0) {} + ~DstNodeCollideMap_() = default; }; -class Dynamics::SrcNodeCollideMap { +class Dynamics::SrcNodeCollideMap_ { public: - std::unordered_map dst_nodes; + std::unordered_map dst_nodes; }; -class Dynamics::Impl { +class Dynamics::Impl_ { public: - explicit Impl(Dynamics* dynamics) : dynamics_(dynamics) {} + explicit Impl_(Dynamics* dynamics) : dynamics_(dynamics) {} // NOTE: we need to implement this here in an Impl class because // gcc currently chokes on unordered_maps with forward-declared types, // so we can't have this in our header without pushing all our map/collision // types there too. void HandleDisconnect( - const std::unordered_map::iterator& + const std::unordered_map::iterator& i, - const std::unordered_map::iterator& + const std::unordered_map::iterator& j, - const std::unordered_map::iterator& k, + const std::unordered_map::iterator& k, const std::unordered_map >::iterator& l); private: Dynamics* dynamics_{}; // Contains in-progress collisions for current nodes. - std::unordered_map node_collisions_; + std::unordered_map node_collisions_; friend class Dynamics; }; Dynamics::Dynamics(Scene* scene_in) : scene_(scene_in), collision_cache_(new base::CollisionCache()), - impl_(std::make_unique(this)) { - ResetODE(); + impl_(std::make_unique(this)) { + ResetODE_(); } Dynamics::~Dynamics() { @@ -145,7 +145,7 @@ Dynamics::~Dynamics() { "Dynamics going down within Process() call;" " should not happen."); } - ShutdownODE(); + ShutdownODE_(); } void Dynamics::Draw(base::FrameDef* frame_def) { @@ -203,7 +203,7 @@ void Dynamics::RemoveTrimesh(dGeomID g) { throw Exception("trimesh not found"); } -auto Dynamics::AreColliding(const Part& p1_in, const Part& p2_in) -> bool { +auto Dynamics::AreColliding_(const Part& p1_in, const Part& p2_in) -> bool { const Part* p1; const Part* p2; if (IsInStoreOrder(p1_in.node()->id(), p1_in.id(), p2_in.node()->id(), @@ -279,7 +279,7 @@ auto Dynamics::GetCollision(Part* p1_in, Part* p2_in, MaterialContext** cc1, p2->ApplyMaterials(*cc2, p2, p1); // If either disabled collisions between these two nodes, store that. - DstNodeCollideMap* dncm = + DstNodeCollideMap_* dncm = &impl_->node_collisions_[p1->node()->id()].dst_nodes[p2->node()->id()]; if (!(*cc1)->node_collide || !(*cc2)->node_collide) { dncm->collideDisabled = true; @@ -319,10 +319,12 @@ auto Dynamics::GetCollision(Part* p1_in, Part* p2_in, MaterialContext** cc1, return &(*(i.first->second)); } -void Dynamics::Impl::HandleDisconnect( - const std::unordered_map::iterator& i, - const std::unordered_map::iterator& j, - const std::unordered_map::iterator& k, +void Dynamics::Impl_::HandleDisconnect( + const std::unordered_map::iterator& + i, + const std::unordered_map::iterator& + j, + const std::unordered_map::iterator& k, const std::unordered_map >::iterator& l) { // Handle disconnect equivalents if they were colliding. if (l->second->collide) { @@ -367,7 +369,7 @@ void Dynamics::Impl::HandleDisconnect( k->second.dst_part_collisions.erase(l); } -void Dynamics::ProcessCollisions() { +void Dynamics::ProcessCollision_() { processing_collisions_ = true; collision_count_ = 0; @@ -441,10 +443,10 @@ void Dynamics::ProcessCollisions() { // Process all standard collisions. This will trigger our callback which // do the real work (add collisions to list, store commands to be // called, etc). - dSpaceCollide(ode_space_, this, &DoCollideCallback); + dSpaceCollide(ode_space_, this, &DoCollideCallback_); // Collide our trimeshes against everything. - collision_cache_->CollideAgainstSpace(ode_space_, this, &DoCollideCallback); + collision_cache_->CollideAgainstSpace(ode_space_, this, &DoCollideCallback_); // Do a bit of precalc each cycle. collision_cache_->Precalc(); @@ -453,9 +455,9 @@ void Dynamics::ProcessCollisions() { // setting parts' currently-colliding-with lists // based on current info, // removing unclaimed collisions and empty groups. - std::unordered_map::iterator i_next; - std::unordered_map::iterator j_next; - std::unordered_map::iterator k_next; + std::unordered_map::iterator i_next; + std::unordered_map::iterator j_next; + std::unordered_map::iterator k_next; std::unordered_map >::iterator l_next; for (auto i = impl_->node_collisions_.begin(); i != impl_->node_collisions_.end(); i = i_next) { @@ -507,26 +509,26 @@ void Dynamics::ProcessCollisions() { collision_events_.clear(); } -void Dynamics::process() { +void Dynamics::Process() { in_process_ = true; // Update this once so we can recycle results. real_time_ = g_core->GetAppTimeMillisecs(); - ProcessCollisions(); + ProcessCollision_(); dWorldQuickStep(ode_world_, kGameStepSeconds); dJointGroupEmpty(ode_contact_group_); in_process_ = false; } -void Dynamics::DoCollideCallback(void* data, dGeomID o1, dGeomID o2) { +void Dynamics::DoCollideCallback_(void* data, dGeomID o1, dGeomID o2) { auto* d = static_cast(data); - d->CollideCallback(o1, o2); + d->CollideCallback_(o1, o2); } // Run collisions for everything. Store any callbacks that will need to be made // and run them after all collision constraints are made. // This way we know all bodies and their associated nodes, etc are valid // throughout collision processing. -void Dynamics::CollideCallback(dGeomID o1, dGeomID o2) { +void Dynamics::CollideCallback_(dGeomID o1, dGeomID o2) { dBodyID b1 = dGeomGetBody(o1); dBodyID b2 = dGeomGetBody(o2); @@ -1103,7 +1105,7 @@ void Dynamics::CollideCallback(dGeomID o1, dGeomID o2) { } } -void Dynamics::ShutdownODE() { +void Dynamics::ShutdownODE_() { if (ode_space_) { dSpaceDestroy(ode_space_); ode_space_ = nullptr; @@ -1118,8 +1120,8 @@ void Dynamics::ShutdownODE() { } } -void Dynamics::ResetODE() { - ShutdownODE(); +void Dynamics::ResetODE_() { + ShutdownODE_(); ode_world_ = dWorldCreate(); assert(ode_world_); dWorldSetGravity(ode_world_, 0, -20, 0); diff --git a/src/ballistica/scene_v1/dynamics/dynamics.h b/src/ballistica/scene_v1/dynamics/dynamics.h index 9e29780c..413b3b2b 100644 --- a/src/ballistica/scene_v1/dynamics/dynamics.h +++ b/src/ballistica/scene_v1/dynamics/dynamics.h @@ -4,7 +4,6 @@ #define BALLISTICA_SCENE_V1_DYNAMICS_DYNAMICS_H_ #include -#include #include #include "ballistica/base/base.h" @@ -16,12 +15,12 @@ namespace ballistica::scene_v1 { class Dynamics : public Object { public: - explicit Dynamics(Scene* scene_in); + explicit Dynamics(Scene* scene); ~Dynamics() override; void Draw(base::FrameDef* frame_def); // Draw any debug stuff, etc. auto ode_world() -> dWorldID { return ode_world_; } - auto getContactGroup() -> dJointGroupID { return ode_contact_group_; } - auto space() -> dSpaceID { return ode_space_; } + auto ode_contact_group() -> dJointGroupID { return ode_contact_group_; } + auto ode_space() -> dSpaceID { return ode_space_; } // Discontinues a collision. Used by parts when changing materials // so that new collisions may enter effect. @@ -37,6 +36,7 @@ class Dynamics : public Object { : active_collide_src_node_) .Get(); } + // Used by collision callbacks - internal. auto GetActiveCollideDstNode() -> Node* { assert(active_collision_); @@ -49,19 +49,19 @@ class Dynamics : public Object { } // Used by collide message handlers. - void set_collide_message_state(bool inCollideMessageIn, - bool target_other_in = false) { - in_collide_message_ = inCollideMessageIn; - collide_message_reverse_order_ = target_other_in; + void set_collide_message_state(bool in_collide_message, + bool target_other = false) { + in_collide_message_ = in_collide_message; + collide_message_reverse_order_ = target_other; } - auto in_collide_message() const -> bool { return in_collide_message_; } - void process(); - void increment_skid_sound_count() { skid_sound_count_++; } - void decrement_skid_sound_count() { skid_sound_count_--; } - auto skid_sound_count() const -> int { return skid_sound_count_; } - void incrementRollSoundCount() { roll_sound_count_++; } - void decrement_roll_sound_count() { roll_sound_count_--; } - auto getRollSoundCount() const -> int { return roll_sound_count_; } + auto in_collide_message() const { return in_collide_message_; } + void Process(); + void IncrementSkidSoundCount() { skid_sound_count_++; } + void DecrementSkidSoundCount() { skid_sound_count_--; } + auto skid_sound_count() const { return skid_sound_count_; } + void IncrementRollSoundCount() { roll_sound_count_++; } + void DecrementRollSoundCount() { roll_sound_count_--; } + auto roll_sound_count() const { return roll_sound_count_; } // We do some fancy collision testing stuff for trimeshes instead // of going through regular ODE space collision testing.. so we have @@ -69,55 +69,52 @@ class Dynamics : public Object { void AddTrimesh(dGeomID g); void RemoveTrimesh(dGeomID g); - auto collision_count() const -> int { return collision_count_; } - auto process_real_time() const -> millisecs_t { return real_time_; } - auto last_impact_sound_time() const -> millisecs_t { - return last_impact_sound_time_; - } - auto in_process() const -> bool { return in_process_; } + auto collision_count() const { return collision_count_; } + auto process_real_time() const { return real_time_; } + auto last_impact_sound_time() const { return last_impact_sound_time_; } + auto in_process() const { return in_process_; } private: - auto AreColliding(const Part& p1, const Part& p2) -> bool; - class SrcNodeCollideMap; - class DstNodeCollideMap; - class SrcPartCollideMap; - class CollisionEvent; - class CollisionReset; - class Impl; - std::vector collision_resets_; + auto AreColliding_(const Part& p1, const Part& p2) -> bool; + class SrcNodeCollideMap_; + class DstNodeCollideMap_; + class SrcPartCollideMap_; + class CollisionEvent_; + class CollisionReset_; + class Impl_; + std::vector collision_resets_; // Return a collision object between these two parts, // creating a new one if need be. auto GetCollision(Part* p1, Part* p2, MaterialContext** cc1, MaterialContext** cc2) -> Collision*; - std::vector collision_events_; - void ResetODE(); - void ShutdownODE(); - static void DoCollideCallback(void* data, dGeomID o1, dGeomID o2); - void CollideCallback(dGeomID o1, dGeomID o2); - void ProcessCollisions(); + std::vector collision_events_; + void ResetODE_(); + void ShutdownODE_(); + static void DoCollideCallback_(void* data, dGeomID o1, dGeomID o2); + void CollideCallback_(dGeomID o1, dGeomID o2); + void ProcessCollision_(); - std::unique_ptr impl_; - bool processing_collisions_{}; + int skid_sound_count_{}; + int roll_sound_count_{}; + int collision_count_{}; + bool in_process_ : 1 {}; + bool in_collide_message_ : 1 {}; + bool collide_message_reverse_order_ : 1 {}; + bool processing_collisions_ : 1 {}; dWorldID ode_world_{}; dJointGroupID ode_contact_group_{}; dSpaceID ode_space_{}; millisecs_t real_time_{}; - bool in_process_{}; - std::vector trimeshes_; millisecs_t last_impact_sound_time_{}; - int skid_sound_count_{}; - int roll_sound_count_{}; - int collision_count_{}; Scene* scene_{}; - bool in_collide_message_{}; - bool collide_message_reverse_order_{}; Collision* active_collision_{}; Object::WeakRef active_collide_src_node_; Object::WeakRef active_collide_dst_node_; + std::vector trimeshes_; + std::unique_ptr impl_; std::unique_ptr collision_cache_; - friend class Impl; }; } // namespace ballistica::scene_v1 diff --git a/src/ballistica/scene_v1/dynamics/material/impact_sound_material_action.cc b/src/ballistica/scene_v1/dynamics/material/impact_sound_material_action.cc index baae54f2..667a88af 100644 --- a/src/ballistica/scene_v1/dynamics/material/impact_sound_material_action.cc +++ b/src/ballistica/scene_v1/dynamics/material/impact_sound_material_action.cc @@ -2,7 +2,7 @@ #include "ballistica/scene_v1/dynamics/material/impact_sound_material_action.h" -#include "ballistica/base/graphics/graphics_server.h" +#include "ballistica/base/audio/audio.h" #include "ballistica/scene_v1/dynamics/dynamics.h" #include "ballistica/scene_v1/dynamics/material/material_context.h" #include "ballistica/scene_v1/support/client_session.h" @@ -50,10 +50,8 @@ void ImpactSoundMaterialAction::Apply(MaterialContext* context, assert(context->dynamics.Exists()); assert(context->dynamics->in_process()); - // For now lets avoid this in low-quality graphics mode (should we make - // a low-quality sound mode?) - if (g_base->graphics_server - && g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) { + // Avoid this if we're cutting corners. + if (g_base->audio->UseLowQualityAudio()) { return; } diff --git a/src/ballistica/scene_v1/dynamics/material/material_context.cc b/src/ballistica/scene_v1/dynamics/material/material_context.cc index 42ea5284..eab72da9 100644 --- a/src/ballistica/scene_v1/dynamics/material/material_context.cc +++ b/src/ballistica/scene_v1/dynamics/material/material_context.cc @@ -42,7 +42,7 @@ MaterialContext::SkidSoundEntry::SkidSoundEntry( assert(context->dynamics.Exists()); #endif assert(context->dynamics->in_process()); - context->dynamics->increment_skid_sound_count(); + context->dynamics->IncrementSkidSoundCount(); } MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in, @@ -57,13 +57,13 @@ MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in, assert(context); assert(context->dynamics.Exists()); assert(context->dynamics->in_process()); - context->dynamics->increment_skid_sound_count(); + context->dynamics->IncrementSkidSoundCount(); } MaterialContext::SkidSoundEntry::~SkidSoundEntry() { assert(context); assert(context->dynamics.Exists()); - context->dynamics->decrement_skid_sound_count(); + context->dynamics->DecrementSkidSoundCount(); if (playing) { g_base->audio->PushSourceFadeOutCall(play_id, 200); } @@ -81,7 +81,7 @@ MaterialContext::RollSoundEntry::RollSoundEntry(MaterialContext* context_in, assert(context); assert(context->dynamics.Exists()); assert(context->dynamics->in_process()); - context->dynamics->incrementRollSoundCount(); + context->dynamics->IncrementRollSoundCount(); } MaterialContext::RollSoundEntry::RollSoundEntry( @@ -90,13 +90,13 @@ MaterialContext::RollSoundEntry::RollSoundEntry( assert(context); assert(context->dynamics.Exists()); assert(context->dynamics->in_process()); - context->dynamics->incrementRollSoundCount(); + context->dynamics->IncrementRollSoundCount(); } MaterialContext::RollSoundEntry::~RollSoundEntry() { assert(context); assert(context->dynamics.Exists()); - context->dynamics->decrement_roll_sound_count(); + context->dynamics->DecrementRollSoundCount(); if (playing) { g_base->audio->PushSourceFadeOutCall(play_id, 200); } diff --git a/src/ballistica/scene_v1/dynamics/material/roll_sound_material_action.cc b/src/ballistica/scene_v1/dynamics/material/roll_sound_material_action.cc index 763dc28b..e6ea2b64 100644 --- a/src/ballistica/scene_v1/dynamics/material/roll_sound_material_action.cc +++ b/src/ballistica/scene_v1/dynamics/material/roll_sound_material_action.cc @@ -2,7 +2,7 @@ #include "ballistica/scene_v1/dynamics/material/roll_sound_material_action.h" -#include "ballistica/base/graphics/graphics_server.h" +#include "ballistica/base/audio/audio.h" #include "ballistica/scene_v1/dynamics/dynamics.h" #include "ballistica/scene_v1/dynamics/material/material_context.h" #include "ballistica/scene_v1/support/client_session.h" @@ -34,16 +34,14 @@ void RollSoundMaterialAction::Apply(MaterialContext* context, assert(context->dynamics.Exists()); assert(context->dynamics->in_process()); - // For now lets avoid this in low-quality graphics mode - // (should we make a low-quality sound mode?) - if (g_base->graphics - && g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) { + // Avoid this if we're cutting corners. + if (g_base->audio->UseLowQualityAudio()) { return; } // Let's limit the amount of skid-sounds we spawn, otherwise we'll // start using up all our sound resources on skids when things get messy - if (context->dynamics->getRollSoundCount() < 2) { + if (context->dynamics->roll_sound_count() < 2) { context->roll_sounds.emplace_back(context, sound.Get(), target_impulse, volume); context->complex_sound = true; diff --git a/src/ballistica/scene_v1/dynamics/material/skid_sound_material_action.cc b/src/ballistica/scene_v1/dynamics/material/skid_sound_material_action.cc index a8c8f381..3aa71a6c 100644 --- a/src/ballistica/scene_v1/dynamics/material/skid_sound_material_action.cc +++ b/src/ballistica/scene_v1/dynamics/material/skid_sound_material_action.cc @@ -2,7 +2,7 @@ #include "ballistica/scene_v1/dynamics/material/skid_sound_material_action.h" -#include "ballistica/base/graphics/graphics_server.h" +#include "ballistica/base/audio/audio.h" #include "ballistica/scene_v1/dynamics/dynamics.h" #include "ballistica/scene_v1/dynamics/material/material_context.h" #include "ballistica/scene_v1/support/client_session.h" @@ -34,10 +34,8 @@ void SkidSoundMaterialAction::Apply(MaterialContext* context, assert(context->dynamics.Exists()); assert(context->dynamics->in_process()); - // For now lets avoid this in low-quality graphics mode - // (should we make a low-quality sound mode?). - if (g_base->graphics_server - && g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) { + // Avoid this if we're cutting corners. + if (g_base->audio->UseLowQualityAudio()) { return; } diff --git a/src/ballistica/scene_v1/dynamics/rigid_body.cc b/src/ballistica/scene_v1/dynamics/rigid_body.cc index 84588e9a..e0b76192 100644 --- a/src/ballistica/scene_v1/dynamics/rigid_body.cc +++ b/src/ballistica/scene_v1/dynamics/rigid_body.cc @@ -65,23 +65,23 @@ RigidBody::RigidBody(int id_in, Part* part_in, Type type_in, Shape shape_in, case Shape::kSphere: { dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.3f; geoms_.resize(1); - geoms_[0] = dCreateSphere(dynamics_->space(), dimensions_[0]); + geoms_[0] = dCreateSphere(dynamics_->ode_space(), dimensions_[0]); break; } case Shape::kBox: { dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.6f; geoms_.resize(1); - geoms_[0] = dCreateBox(dynamics_->space(), dimensions_[0], dimensions_[1], - dimensions_[2]); + geoms_[0] = dCreateBox(dynamics_->ode_space(), dimensions_[0], + dimensions_[1], dimensions_[2]); break; } case Shape::kCapsule: { dimensions_[0] = dimensions_[1] = 0.3f; geoms_.resize(1); - geoms_[0] = - dCreateCCylinder(dynamics_->space(), dimensions_[0], dimensions_[1]); + geoms_[0] = dCreateCCylinder(dynamics_->ode_space(), dimensions_[0], + dimensions_[1]); break; } @@ -98,14 +98,15 @@ RigidBody::RigidBody(int id_in, Part* part_in, Type type_in, Shape shape_in, Vector3f p = Matrix44fRotate(Vector3f(0, 1, 0), static_cast(i) * inc) * Vector3f(offset, 0, 0); - geoms_[i * 2] = dCreateGeomTransform(dynamics_->space()); + geoms_[i * 2] = dCreateGeomTransform(dynamics_->ode_space()); geoms_[i * 2 + 1] = dCreateSphere(nullptr, sub_rad); dGeomTransformSetGeom(geoms_[i * 2], geoms_[i * 2 + 1]); dGeomSetPosition(geoms_[i * 2 + 1], p.v[0], p.v[1], p.v[2]); } // One last center sphere to keep stuff from getting stuck in our middle. - geoms_[geoms_.size() - 1] = dCreateSphere(dynamics_->space(), sub_rad); + geoms_[geoms_.size() - 1] = + dCreateSphere(dynamics_->ode_space(), sub_rad); break; } diff --git a/src/ballistica/scene_v1/node/flag_node.cc b/src/ballistica/scene_v1/node/flag_node.cc index 202b3ecf..d0bae293 100644 --- a/src/ballistica/scene_v1/node/flag_node.cc +++ b/src/ballistica/scene_v1/node/flag_node.cc @@ -129,9 +129,6 @@ FlagNode::FlagNode(Scene* scene) : Node(scene, node_type), part_(this) { mesh_.SetIndexData(indices); mesh_.SetStaticData(v_static); - - // Create our shadow set. - UpdateForGraphicsQuality(g_base->graphics_server->quality()); } auto FlagNode::getPosition() const -> std::vector { @@ -257,6 +254,11 @@ void FlagNode::HandleMessage(const char* data_in) { } void FlagNode::Draw(base::FrameDef* frame_def) { + if (graphics_quality_ != frame_def->quality()) { + graphics_quality_ = frame_def->quality(); + UpdateForGraphicsQuality(graphics_quality_); + } + // Flag cloth. { // Update the dynamic portion of our mesh data. @@ -311,45 +313,60 @@ void FlagNode::Draw(base::FrameDef* frame_def) { FullShadowSet* full_shadows = full_shadow_set_.Get(); - if (full_shadows) { - // Pole bottom. - { - full_shadows->shadow_pole_bottom_.GetValues(&s_scale, &s_density); - const Vector3f& p(full_shadows->shadow_pole_bottom_.GetPosition()); - g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, - s_density * 0.25f); - } + // Update our shadow objects. + if (!g_core->HeadlessMode()) { + dBodyID b = body_->body(); + assert(b); + dVector3 p; + if (FullShadowSet* full_shadows = full_shadow_set_.Get()) { + full_shadows->shadow_flag_.SetPosition( + flag_points_[kFlagSizeX * (kFlagSizeY / 2) + (kFlagSizeX / 2)]); + dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.4f, p); + full_shadows->shadow_pole_bottom_.SetPosition(Vector3f(p)); + full_shadows->shadow_pole_middle_.SetPosition( + Vector3f(dBodyGetPosition(b))); + dBodyGetRelPointPos(b, 0, 0, kFlagHeight * 0.4f, p); + full_shadows->shadow_pole_top_.SetPosition(Vector3f(p)); + // Pole bottom. + { + full_shadows->shadow_pole_bottom_.GetValues(&s_scale, &s_density); + const Vector3f& p(full_shadows->shadow_pole_bottom_.GetPosition()); + g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, + s_density * 0.25f); + } - // Pole middle. - { - full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density); - const Vector3f& p(full_shadows->shadow_pole_middle_.GetPosition()); - g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, - s_density * 0.25f); - } + // Pole middle. + { + full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density); + const Vector3f& p(full_shadows->shadow_pole_middle_.GetPosition()); + g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, + s_density * 0.25f); + } - // Pole top. - { - full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density); - const Vector3f& p(full_shadows->shadow_pole_top_.GetPosition()); - g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, - s_density * 0.25f); - } + // Pole top. + { + full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density); + const Vector3f& p(full_shadows->shadow_pole_top_.GetPosition()); + g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, + s_density * 0.25f); + } - // Flag center. - { - full_shadows->shadow_flag_.GetValues(&s_scale, &s_density); - const Vector3f& p(full_shadows->shadow_flag_.GetPosition()); + // Flag center. + { + full_shadows->shadow_flag_.GetValues(&s_scale, &s_density); + const Vector3f& p(full_shadows->shadow_flag_.GetPosition()); + g_base->graphics->DrawBlotch(p, 0.8f * s_scale, 0, 0, 0, + s_density * 0.3f); + } + + } else if (SimpleShadowSet* simple_shadows = simple_shadow_set_.Get()) { + dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.3f, p); + simple_shadows->shadow_.SetPosition(Vector3f(p)); + simple_shadows->shadow_.GetValues(&s_scale, &s_density); + const Vector3f& p(simple_shadows->shadow_.GetPosition()); g_base->graphics->DrawBlotch(p, 0.8f * s_scale, 0, 0, 0, - s_density * 0.3f); + s_density * 0.5f); } - } else { - SimpleShadowSet* simple_shadows = simple_shadow_set_.Get(); - assert(simple_shadows); - simple_shadows->shadow_.GetValues(&s_scale, &s_density); - const Vector3f& p(simple_shadows->shadow_.GetPosition()); - g_base->graphics->DrawBlotch(p, 0.8f * s_scale, 0, 0, 0, - s_density * 0.5f); } c.Submit(); } @@ -398,30 +415,6 @@ void FlagNode::Step() { // FIXME: This should probably happen for RBDs automatically? body_->UpdateBlending(); - // Update our shadow objects. - dBodyID b = body_->body(); - assert(b); - - if (!g_core->HeadlessMode()) { - dVector3 p; - FullShadowSet* full_shadows = full_shadow_set_.Get(); - if (full_shadows) { - full_shadows->shadow_flag_.SetPosition( - flag_points_[kFlagSizeX * (kFlagSizeY / 2) + (kFlagSizeX / 2)]); - dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.4f, p); - full_shadows->shadow_pole_bottom_.SetPosition(Vector3f(p)); - full_shadows->shadow_pole_middle_.SetPosition( - Vector3f(dBodyGetPosition(b))); - dBodyGetRelPointPos(b, 0, 0, kFlagHeight * 0.4f, p); - full_shadows->shadow_pole_top_.SetPosition(Vector3f(p)); - } else { - SimpleShadowSet* simple_shadows = simple_shadow_set_.Get(); - assert(simple_shadows); - dBodyGetRelPointPos(b, 0, 0, kFlagHeight * -0.3f, p); - simple_shadows->shadow_.SetPosition(Vector3f(p)); - } - } - if (dBodyIsEnabled(body_->body())) { // Try to keep upright by pushing the top of the // flag to be above the bottom. @@ -675,10 +668,6 @@ void FlagNode::GetRigidBodyPickupLocations(int id, float* obj, float* character, hand1[2] = -0.05f; } -void FlagNode::OnGraphicsQualityChanged(base::GraphicsQuality q) { - UpdateForGraphicsQuality(q); -} - void FlagNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) { if (!g_core->HeadlessMode()) { if (quality >= base::GraphicsQuality::kMedium) { diff --git a/src/ballistica/scene_v1/node/flag_node.h b/src/ballistica/scene_v1/node/flag_node.h index 717338cb..3dbd9a52 100644 --- a/src/ballistica/scene_v1/node/flag_node.h +++ b/src/ballistica/scene_v1/node/flag_node.h @@ -44,10 +44,13 @@ class FlagNode : public Node { void UpdateDimensions(); void ResetFlagMesh(); void UpdateFlagMesh(); - void OnGraphicsQualityChanged(base::GraphicsQuality q) override; void UpdateForGraphicsQuality(base::GraphicsQuality q); void UpdateSpringPoint(int p1, int p2, float rest_length); - base::AreaOfInterest* area_of_interest_ = nullptr; + + base::GraphicsQuality graphics_quality_{}; + bool light_weight_ : 1 {}; + bool have_flag_impulse_ : 1 {}; + base::AreaOfInterest* area_of_interest_{}; Part part_; std::vector color_ = {1.0f, 1.0f, 1.0f}; Object::Ref body_; @@ -56,15 +59,13 @@ class FlagNode : public Node { Object::Ref full_shadow_set_; Object::Ref simple_shadow_set_; int wind_rand_{}; + int footing_{}; float wind_rand_x_{}; float wind_rand_y_{}; float wind_rand_z_{}; float flag_impulse_add_x_{}; float flag_impulse_add_y_{}; float flag_impulse_add_z_{}; - bool have_flag_impulse_{}; - int footing_{}; - bool light_weight_{}; Vector3f flag_points_[25]{}; Vector3f flag_normals_[25]{}; Vector3f flag_velocities_[25]{}; diff --git a/src/ballistica/scene_v1/node/node.h b/src/ballistica/scene_v1/node/node.h index 83078d3f..bd68613c 100644 --- a/src/ballistica/scene_v1/node/node.h +++ b/src/ballistica/scene_v1/node/node.h @@ -40,7 +40,6 @@ class Node : public Object { /// Called when the language changes. virtual void OnLanguageChange() {} - virtual void OnGraphicsQualityChanged(base::GraphicsQuality q) {} /// The node can rule out collisions between particular bodies using this. virtual auto PreFilterCollision(RigidBody* b1, RigidBody* r2) -> bool { diff --git a/src/ballistica/scene_v1/node/prop_node.cc b/src/ballistica/scene_v1/node/prop_node.cc index 4f48492c..ed94268a 100644 --- a/src/ballistica/scene_v1/node/prop_node.cc +++ b/src/ballistica/scene_v1/node/prop_node.cc @@ -576,7 +576,7 @@ auto PropNode::CollideCallback(dContact* c, int count, dBodyGetMass(b2, &m); dJointID j = dJointCreateFixed(scene()->dynamics()->ode_world(), - scene()->dynamics()->getContactGroup()); + scene()->dynamics()->ode_contact_group()); dJointAttach(j, b1, b2); dJointSetFixed(j); dJointSetFixedSpringMode(j, 1, 1, false); diff --git a/src/ballistica/scene_v1/node/shield_node.cc b/src/ballistica/scene_v1/node/shield_node.cc index f1e73e39..df28bee8 100644 --- a/src/ballistica/scene_v1/node/shield_node.cc +++ b/src/ballistica/scene_v1/node/shield_node.cc @@ -260,7 +260,7 @@ void ShieldNode::Draw(base::FrameDef* frame_def) { c.Submit(); // Nifty intersection effects in fancy graphics mode. - if (frame_def->has_depth_texture()) { + if (frame_def->HasDepthTexture()) { base::ShieldComponent c2(frame_def->overlay_3d_pass()); { auto xf = c2.ScopedTransform(); @@ -273,7 +273,7 @@ void ShieldNode::Draw(base::FrameDef* frame_def) { } c2.Submit(); } - if (frame_def->has_depth_texture()) { + if (frame_def->HasDepthTexture()) { base::PostProcessComponent c2(frame_def->blit_pass()); c2.SetNormalDistort(distort); { diff --git a/src/ballistica/scene_v1/node/spaz_node.cc b/src/ballistica/scene_v1/node/spaz_node.cc index d89f7618..b316888c 100644 --- a/src/ballistica/scene_v1/node/spaz_node.cc +++ b/src/ballistica/scene_v1/node/spaz_node.cc @@ -916,9 +916,6 @@ SpazNode::SpazNode(Scene* scene) // Give joints initial vals. UpdateJoints(); - // FIXME: should do this on draw. - UpdateForGraphicsQuality(g_base->graphics_server->quality()); - // We want to have an area of interest by default. SetIsAreaOfInterest(true); @@ -2079,40 +2076,6 @@ void SpazNode::Step() { } } - // Update shadows. -#if !BA_HEADLESS_BUILD - FullShadowSet* full_shadows = full_shadow_set_.Get(); - if (full_shadows) { - full_shadows->torso_shadow_.SetPosition( - Vector3f(dBodyGetPosition(body_torso_->body()))); - full_shadows->head_shadow_.SetPosition( - Vector3f(dBodyGetPosition(body_head_->body()))); - full_shadows->pelvis_shadow_.SetPosition( - Vector3f(dBodyGetPosition(body_pelvis_->body()))); - full_shadows->lower_left_leg_shadow_.SetPosition( - Vector3f(dBodyGetPosition(lower_left_leg_body_->body()))); - full_shadows->lower_right_leg_shadow_.SetPosition( - Vector3f(dBodyGetPosition(lower_right_leg_body_->body()))); - full_shadows->upper_left_leg_shadow_.SetPosition( - Vector3f(dBodyGetPosition(upper_left_leg_body_->body()))); - full_shadows->upper_right_leg_shadow_.SetPosition( - Vector3f(dBodyGetPosition(upper_right_leg_body_->body()))); - full_shadows->lower_right_arm_shadow_.SetPosition( - Vector3f(dBodyGetPosition(lower_right_arm_body_->body()))); - full_shadows->upper_right_arm_shadow_.SetPosition( - Vector3f(dBodyGetPosition(upper_right_arm_body_->body()))); - full_shadows->lower_left_arm_shadow_.SetPosition( - Vector3f(dBodyGetPosition(lower_left_arm_body_->body()))); - full_shadows->upper_left_arm_shadow_.SetPosition( - Vector3f(dBodyGetPosition(upper_left_arm_body_->body()))); - } else { - SimpleShadowSet* simple_shadows = simple_shadow_set_.Get(); - assert(simple_shadows); - simple_shadows->shadow_.SetPosition( - Vector3f(dBodyGetPosition(body_pelvis_->body()))); - } -#endif // !BA_HEADLESS_BUILD - // Update wings if we've got 'em. if (wings_) { float maxDist = 0.8f; @@ -4547,6 +4510,11 @@ static void DrawRadialMeter(base::MeshIndexedSimpleFull* m, void SpazNode::Draw(base::FrameDef* frame_def) { #if !BA_HEADLESS_BUILD + if (graphics_quality_ != frame_def->quality()) { + graphics_quality_ = frame_def->quality(); + UpdateForGraphicsQuality(graphics_quality_); + } + #if BA_OSTYPE_MACOS if (g_base->graphics_server->renderer()->debug_draw_mode()) { base::SimpleComponent c(frame_def->overlay_3d_pass()); @@ -5356,49 +5324,68 @@ void SpazNode::Draw(base::FrameDef* frame_def) { sc[2] = weight * freeze_color[2] + (1.0f - weight) * sc[2]; } - FullShadowSet* full_shadows = full_shadow_set_.Get(); - if (full_shadows) { - DrawBrightSpot(full_shadows->lower_left_leg_shadow_, 0.3f * death_scale, - death_fade * (frozen_ ? 0.3f : 0.2f), sc); - DrawBrightSpot(full_shadows->lower_right_leg_shadow_, 0.3f * death_scale, - death_fade * (frozen_ ? 0.3f : 0.2f), sc); - DrawBrightSpot(full_shadows->head_shadow_, 0.45f * death_scale, - death_fade * (frozen_ ? 0.8f : 0.14f), sc); - } + // Update and draw shadows. + if (!g_core->HeadlessMode()) { + if (FullShadowSet* full_shadows = full_shadow_set_.Get()) { + full_shadows->torso_shadow_.SetPosition( + Vector3f(dBodyGetPosition(body_torso_->body()))); + full_shadows->head_shadow_.SetPosition( + Vector3f(dBodyGetPosition(body_head_->body()))); + full_shadows->pelvis_shadow_.SetPosition( + Vector3f(dBodyGetPosition(body_pelvis_->body()))); + full_shadows->lower_left_leg_shadow_.SetPosition( + Vector3f(dBodyGetPosition(lower_left_leg_body_->body()))); + full_shadows->lower_right_leg_shadow_.SetPosition( + Vector3f(dBodyGetPosition(lower_right_leg_body_->body()))); + full_shadows->upper_left_leg_shadow_.SetPosition( + Vector3f(dBodyGetPosition(upper_left_leg_body_->body()))); + full_shadows->upper_right_leg_shadow_.SetPosition( + Vector3f(dBodyGetPosition(upper_right_leg_body_->body()))); + full_shadows->lower_right_arm_shadow_.SetPosition( + Vector3f(dBodyGetPosition(lower_right_arm_body_->body()))); + full_shadows->upper_right_arm_shadow_.SetPosition( + Vector3f(dBodyGetPosition(upper_right_arm_body_->body()))); + full_shadows->lower_left_arm_shadow_.SetPosition( + Vector3f(dBodyGetPosition(lower_left_arm_body_->body()))); + full_shadows->upper_left_arm_shadow_.SetPosition( + Vector3f(dBodyGetPosition(upper_left_arm_body_->body()))); - if (full_shadows) { - DrawShadow(full_shadows->torso_shadow_, 0.19f * death_scale, 0.9f, sc); - DrawShadow(full_shadows->head_shadow_, 0.15f * death_scale, 0.7f, sc); - DrawShadow(full_shadows->pelvis_shadow_, 0.15f * death_scale, 0.7f, sc); - DrawShadow(full_shadows->lower_left_leg_shadow_, 0.08f * death_scale, - 1.0f, sc); - DrawShadow(full_shadows->lower_right_leg_shadow_, 0.08f * death_scale, - 1.0f, sc); - DrawShadow(full_shadows->upper_left_leg_shadow_, 0.08f * death_scale, - 1.0f, sc); - DrawShadow(full_shadows->upper_right_leg_shadow_, 0.08f * death_scale, - 1.0f, sc); - DrawShadow(full_shadows->upper_left_arm_shadow_, 0.08f * death_scale, - 0.5f, sc); - DrawShadow(full_shadows->lower_left_arm_shadow_, 0.08f * death_scale, - 0.3f, sc); - DrawShadow(full_shadows->lower_right_arm_shadow_, 0.08f * death_scale, - 0.3f, sc); - DrawShadow(full_shadows->upper_right_arm_shadow_, 0.08f * death_scale, - 0.5f, sc); - } else { - SimpleShadowSet* simple_shadows = simple_shadow_set_.Get(); - assert(simple_shadows); - DrawShadow(simple_shadows->shadow_, 0.2f * death_scale, 2.0f, sc); + DrawBrightSpot(full_shadows->lower_left_leg_shadow_, 0.3f * death_scale, + death_fade * (frozen_ ? 0.3f : 0.2f), sc); + DrawBrightSpot(full_shadows->lower_right_leg_shadow_, + 0.3f * death_scale, death_fade * (frozen_ ? 0.3f : 0.2f), + sc); + DrawBrightSpot(full_shadows->head_shadow_, 0.45f * death_scale, + death_fade * (frozen_ ? 0.8f : 0.14f), sc); + DrawShadow(full_shadows->torso_shadow_, 0.19f * death_scale, 0.9f, sc); + DrawShadow(full_shadows->head_shadow_, 0.15f * death_scale, 0.7f, sc); + DrawShadow(full_shadows->pelvis_shadow_, 0.15f * death_scale, 0.7f, sc); + DrawShadow(full_shadows->lower_left_leg_shadow_, 0.08f * death_scale, + 1.0f, sc); + DrawShadow(full_shadows->lower_right_leg_shadow_, 0.08f * death_scale, + 1.0f, sc); + DrawShadow(full_shadows->upper_left_leg_shadow_, 0.08f * death_scale, + 1.0f, sc); + DrawShadow(full_shadows->upper_right_leg_shadow_, 0.08f * death_scale, + 1.0f, sc); + DrawShadow(full_shadows->upper_left_arm_shadow_, 0.08f * death_scale, + 0.5f, sc); + DrawShadow(full_shadows->lower_left_arm_shadow_, 0.08f * death_scale, + 0.3f, sc); + DrawShadow(full_shadows->lower_right_arm_shadow_, 0.08f * death_scale, + 0.3f, sc); + DrawShadow(full_shadows->upper_right_arm_shadow_, 0.08f * death_scale, + 0.5f, sc); + } else if (SimpleShadowSet* simple_shadows = simple_shadow_set_.Get()) { + simple_shadows->shadow_.SetPosition( + Vector3f(dBodyGetPosition(body_pelvis_->body()))); + DrawShadow(simple_shadows->shadow_, 0.2f * death_scale, 2.0f, sc); + } } } #endif // !BA_HEADLESS_BUILD } // NOLINT (yes i know this is too big) -void SpazNode::OnGraphicsQualityChanged(base::GraphicsQuality q) { - UpdateForGraphicsQuality(q); -} - void SpazNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) { #if !BA_HEADLESS_BUILD if (quality >= base::GraphicsQuality::kMedium) { diff --git a/src/ballistica/scene_v1/node/spaz_node.h b/src/ballistica/scene_v1/node/spaz_node.h index 26b71a3a..3440ef59 100644 --- a/src/ballistica/scene_v1/node/spaz_node.h +++ b/src/ballistica/scene_v1/node/spaz_node.h @@ -277,7 +277,6 @@ class SpazNode : public Node { // Reset to a standing, non-moving state at the given point. void Stand(float x, float y, float z, float angle); - void OnGraphicsQualityChanged(base::GraphicsQuality q) override; void UpdateForGraphicsQuality(base::GraphicsQuality q); void UpdateAreaOfInterest(); auto CollideCallback(dContact* c, int count, RigidBody* colliding_body, @@ -515,6 +514,7 @@ class SpazNode : public Node { bool has_eyelids_{true}; bool running_{}; bool billboard_cross_out_{}; + base::GraphicsQuality graphics_quality_{}; Object::Ref hair_front_right_body_; JointFixedEF* hair_front_right_joint_{}; Object::Ref hair_front_left_body_; diff --git a/src/ballistica/scene_v1/support/host_activity.cc b/src/ballistica/scene_v1/support/host_activity.cc index 5fc1b899..b357fbb7 100644 --- a/src/ballistica/scene_v1/support/host_activity.cc +++ b/src/ballistica/scene_v1/support/host_activity.cc @@ -483,9 +483,6 @@ void HostActivity::PruneSessionBaseTimers() { void HostActivity::OnScreenSizeChange() { scene()->OnScreenSizeChange(); } void HostActivity::LanguageChanged() { scene()->LanguageChanged(); } void HostActivity::DebugSpeedMultChanged() { UpdateStepTimerLength(); } -void HostActivity::GraphicsQualityChanged(base::GraphicsQuality q) { - scene()->GraphicsQualityChanged(q); -} void HostActivity::Draw(base::FrameDef* frame_def) { if (!started_) { diff --git a/src/ballistica/scene_v1/support/host_activity.h b/src/ballistica/scene_v1/support/host_activity.h index ad4d998d..5f583ff9 100644 --- a/src/ballistica/scene_v1/support/host_activity.h +++ b/src/ballistica/scene_v1/support/host_activity.h @@ -57,7 +57,6 @@ class HostActivity : public SceneV1Context { void OnScreenSizeChange(); void LanguageChanged(); void DebugSpeedMultChanged(); - void GraphicsQualityChanged(base::GraphicsQuality q); // Used to register python calls created in this context so we can make sure // they got properly cleaned up. diff --git a/src/ballistica/scene_v1/support/host_session.cc b/src/ballistica/scene_v1/support/host_session.cc index 0601c0bb..a5d04506 100644 --- a/src/ballistica/scene_v1/support/host_session.cc +++ b/src/ballistica/scene_v1/support/host_session.cc @@ -154,16 +154,6 @@ void HostSession::LanguageChanged() { } } -void HostSession::GraphicsQualityChanged(base::GraphicsQuality q) { - // Let our internal scene know. - scene()->GraphicsQualityChanged(q); - - // Let all our activities know. - for (auto&& i : host_activities_) { - i->GraphicsQualityChanged(q); - } -} - auto HostSession::DoesFillScreen() const -> bool { // FIXME not necessarily the case. return true; diff --git a/src/ballistica/scene_v1/support/host_session.h b/src/ballistica/scene_v1/support/host_session.h index 40b370f4..d17de58d 100644 --- a/src/ballistica/scene_v1/support/host_session.h +++ b/src/ballistica/scene_v1/support/host_session.h @@ -70,7 +70,6 @@ class HostSession : public Session { void Draw(base::FrameDef* f) override; void OnScreenSizeChange() override; void LanguageChanged() override; - void GraphicsQualityChanged(base::GraphicsQuality q) override; void DebugSpeedMultChanged() override; auto GetHostSession() -> HostSession* override; auto GetMutableScene() -> Scene* override; diff --git a/src/ballistica/scene_v1/support/scene.cc b/src/ballistica/scene_v1/support/scene.cc index a8b3b162..f45b5de3 100644 --- a/src/ballistica/scene_v1/support/scene.cc +++ b/src/ballistica/scene_v1/support/scene.cc @@ -166,7 +166,7 @@ void Scene::Step() { } // Lastly step our sim. - dynamics_->process(); + dynamics_->Process(); time_ += kGameStepMilliseconds; stepnum_++; @@ -227,13 +227,6 @@ void Scene::DeleteNode(Node* node) { } } -void Scene::GraphicsQualityChanged(base::GraphicsQuality q) { - assert(g_base->InLogicThread()); - for (auto&& i : nodes_) { - i->OnGraphicsQualityChanged(q); - } -} - void Scene::OnScreenSizeChange() { assert(g_base->InLogicThread()); for (auto&& i : nodes_) { diff --git a/src/ballistica/scene_v1/support/scene.h b/src/ballistica/scene_v1/support/scene.h index e7780c2c..6cdf1891 100644 --- a/src/ballistica/scene_v1/support/scene.h +++ b/src/ballistica/scene_v1/support/scene.h @@ -43,7 +43,6 @@ class Scene : public Object { void SetMapBounds(float x, float y, float z, float X, float Y, float Z); void OnScreenSizeChange(); void LanguageChanged(); - void GraphicsQualityChanged(base::GraphicsQuality q); auto out_of_bounds_nodes() -> const std::vector >& { return out_of_bounds_nodes_; } diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc index 4b3d3c01..9fec9239 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc @@ -1312,15 +1312,6 @@ void SceneV1AppMode::SetPublicPartyStatsURL(const std::string& url) { } } -void SceneV1AppMode::GraphicsQualityChanged(base::GraphicsQuality quality) { - for (auto&& i : sessions_) { - if (!i.Exists()) { - continue; - } - i->GraphicsQualityChanged(quality); - } -} - void SceneV1AppMode::SetPublicPartyPlayerCount(int count) { assert(g_base->InLogicThread()); if (count == public_party_player_count_) { diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.h b/src/ballistica/scene_v1/support/scene_v1_app_mode.h index 769c7081..7c98470c 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.h +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.h @@ -172,7 +172,6 @@ class SceneV1AppMode : public base::AppMode { sockaddr_storage* from) override; void DrawWorld(base::FrameDef* frame_def) override; auto DoesWorldFillScreen() -> bool override; - void GraphicsQualityChanged(base::GraphicsQuality quality) override; void RunMainMenu(); auto dynamics_sync_time() const { return dynamics_sync_time_; } diff --git a/src/ballistica/scene_v1/support/session.cc b/src/ballistica/scene_v1/support/session.cc index feceb6d0..5ddde60e 100644 --- a/src/ballistica/scene_v1/support/session.cc +++ b/src/ballistica/scene_v1/support/session.cc @@ -33,8 +33,6 @@ void Session::OnScreenSizeChange() {} void Session::LanguageChanged() {} -void Session::GraphicsQualityChanged(base::GraphicsQuality q) {} - void Session::DebugSpeedMultChanged() {} void Session::DumpFullState(SessionStream* out) { diff --git a/src/ballistica/scene_v1/support/session.h b/src/ballistica/scene_v1/support/session.h index 3892e2a5..5e76d009 100644 --- a/src/ballistica/scene_v1/support/session.h +++ b/src/ballistica/scene_v1/support/session.h @@ -34,7 +34,6 @@ class Session : public SceneV1Context { virtual auto GetForegroundContext() -> base::ContextRef; virtual void OnScreenSizeChange(); virtual void LanguageChanged(); - virtual void GraphicsQualityChanged(base::GraphicsQuality q); virtual void DebugSpeedMultChanged(); auto benchmark_type() const -> base::BenchmarkType { return benchmark_type_; } void set_benchmark_type(base::BenchmarkType val) { benchmark_type_ = val; } diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index ff991ab1..0be21980 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // These are set automatically via script; don't modify them here. -const int kEngineBuildNumber = 21479; +const int kEngineBuildNumber = 21486; const char* kEngineVersion = "1.7.28"; const int kEngineApiVersion = 8; diff --git a/src/ballistica/shared/foundation/fatal_error.cc b/src/ballistica/shared/foundation/fatal_error.cc index 901394cf..71bac44c 100644 --- a/src/ballistica/shared/foundation/fatal_error.cc +++ b/src/ballistica/shared/foundation/fatal_error.cc @@ -92,7 +92,7 @@ void FatalError::ReportFatalError(const std::string& message, // momentarily, and also go to platform-specific logging and good ol' // stderr. Logging::V1CloudLog(logmsg); - Logging::DisplayLog("root", LogLevel::kCritical, logmsg); + Logging::EmitLog("root", LogLevel::kCritical, logmsg); fprintf(stderr, "%s\n", logmsg.c_str()); std::string prefix = "FATAL-ERROR-LOG:"; @@ -175,10 +175,10 @@ auto FatalError::HandleFatalError(bool exit_cleanly, // bring the app down ourself. if (!in_top_level_exception_handler) { if (exit_cleanly) { - Logging::DisplayLog("root", LogLevel::kCritical, "Calling exit(1)..."); + Logging::EmitLog("root", LogLevel::kCritical, "Calling exit(1)..."); exit(1); } else { - Logging::DisplayLog("root", LogLevel::kCritical, "Calling abort()..."); + Logging::EmitLog("root", LogLevel::kCritical, "Calling abort()..."); abort(); } } diff --git a/src/ballistica/shared/foundation/logging.cc b/src/ballistica/shared/foundation/logging.cc index e8fc9f33..9e84b713 100644 --- a/src/ballistica/shared/foundation/logging.cc +++ b/src/ballistica/shared/foundation/logging.cc @@ -20,16 +20,16 @@ void Logging::Log(LogLevel level, const std::string& msg) { g_core->python->LoggingCall(level, msg); } -void Logging::DisplayLog(const std::string& name, LogLevel level, - const std::string& msg) { - // Print to the in-app console (with a newline added). +void Logging::EmitLog(const std::string& name, LogLevel level, + const std::string& msg) { + // Print to the dev console. if (g_base_soft) { g_base_soft->PushDevConsolePrintCall(msg + "\n"); } // Ship to platform-specific display mechanisms (android log, etc). if (g_core) { - g_core->platform->DisplayLog(name, level, msg); + g_core->platform->EmitPlatformLog(name, level, msg); } } diff --git a/src/ballistica/shared/foundation/logging.h b/src/ballistica/shared/foundation/logging.h index 792665d2..d08b56e0 100644 --- a/src/ballistica/shared/foundation/logging.h +++ b/src/ballistica/shared/foundation/logging.h @@ -29,12 +29,11 @@ class Logging { /// babase is imported may not be visible in the app for that same reason. static void Log(LogLevel level, const std::string& msg); - /// Immediately display a log message in the in-app console, - /// platform-specific logs, etc. This generally should not be called - /// directly but instead wired up to display messages coming from the - /// Python logging system. - static void DisplayLog(const std::string& name, LogLevel level, - const std::string& msg); + /// Send a log message to the in-app console, platform-specific logs, etc. + /// This generally should not be called directly but instead wired up to + /// log messages coming through the Python logging system. + static void EmitLog(const std::string& name, LogLevel level, + const std::string& msg); /// Write a message to the v1 cloud log. This is considered legacy and /// will be phased out eventually. diff --git a/src/ballistica/shared/foundation/object.cc b/src/ballistica/shared/foundation/object.cc index a24dd221..1324e8d5 100644 --- a/src/ballistica/shared/foundation/object.cc +++ b/src/ballistica/shared/foundation/object.cc @@ -20,7 +20,6 @@ using core::g_core; Object::Object() { #if BA_DEBUG_BUILD // Mark when we were born. - // NOTE: Using core's internal globals here; don't do this normally. assert(g_core); object_birth_time_ = g_core->GetAppTimeMillisecs(); @@ -41,7 +40,6 @@ Object::Object() { Object::~Object() { #if BA_DEBUG_BUILD { - // NOTE: using core's internal globals here; don't do this normally! assert(g_core); // Pull ourself from the global obj list. std::scoped_lock lock(g_core->object_list_mutex); @@ -70,6 +68,7 @@ Object::~Object() { #endif // BA_DEBUG_BUILD // Invalidate all our weak refs. + // // We could call Release() on each but we'd have to deactivate the // thread-check since virtual functions won't work as expected in a // destructor. Also we can take a few shortcuts here since we know @@ -85,7 +84,6 @@ Object::~Object() { auto Object::GetObjectTypeName() const -> std::string { // Default implementation just returns type name. - // Note: using core's globals directly; don't normally do this! if (g_core) { return g_core->platform->DemangleCXXSymbol(typeid(*this).name()); } @@ -112,7 +110,6 @@ auto Object::GetThreadOwnership() const -> Object::ThreadOwnership { void Object::LsObjects() { #if BA_DEBUG_BUILD - // Note: using core's internal globals here; don't normally do this. assert(g_core); std::string s; { @@ -192,12 +189,12 @@ void Object::ObjectUpdateForAcquire() { } } -void Object::ObjectThreadCheck() { +void Object::ObjectThreadCheck() const { if (!thread_checks_enabled_) { return; } - ThreadOwnership thread_ownership = GetThreadOwnership(); + auto thread_ownership = GetThreadOwnership(); // Special case; graphics context (not simply a thread so have to handle // specially). diff --git a/src/ballistica/shared/foundation/object.h b/src/ballistica/shared/foundation/object.h index 91c38c46..c4f39b5d 100644 --- a/src/ballistica/shared/foundation/object.h +++ b/src/ballistica/shared/foundation/object.h @@ -12,23 +12,26 @@ namespace ballistica { /// Objects supporting strong and weak referencing and thread enforcement. -/// A rule or two for for Objects: -/// Don't throw exceptions out of object destructors; -/// This will break references to that object and lead to crashes if/when they -/// are used. class Object { - public: + protected: + /// Our base constructor is marked protected because we *require* Objects + /// to be dynamically allocated. This allows us extra measures of control + /// over their construction and destruction, and it does not seem that + /// there is a pressing use case for a statically allocated Object that + /// would justify diluting that control. Object(); + + public: virtual ~Object(); - // Object classes can provide descriptive names for themselves; - // these are used for debugging and other purposes. - // The default is to use the C++ symbol name, demangling it when possible. - // IMPORTANT: Do not rely on this being consistent across builds/platforms. + // Object classes can provide descriptive names for themselves; these are + // used for debugging and other purposes. The default is to use the C++ + // symbol name, demangling it when possible. IMPORTANT: Do not rely on + // this being consistent across builds/platforms. virtual auto GetObjectTypeName() const -> std::string; - // Provide a brief description of this particular object; by default returns - // type-name plus address. + // Provide a brief description of this particular object; by default + // returns type-name plus address. virtual auto GetObjectDescription() const -> std::string; enum class ThreadOwnership { @@ -42,25 +45,26 @@ class Object { #if BA_DEBUG_BUILD - /// This is called when adding or removing a reference to an Object; - /// it can perform sanity-tests to make sure references are not being - /// added at incorrect times or from incorrect threads. - /// The default implementation uses the per-object - /// ThreadOwnership/EventLoopID values accessible below. NOTE: this - /// check runs only in the debug build so don't add any logical side-effects! - virtual void ObjectThreadCheck(); + /// This is called when adding or removing a reference to an Object; it + /// can perform sanity-tests to make sure references are not being added + /// at incorrect times or from incorrect threads. The default + /// implementation uses the per-object ThreadOwnership/EventLoopID values + /// accessible below. + /// + /// NOTE: This check runs *only* in the debug build so don't include any + /// logical side-effects in these checks! + void ObjectThreadCheck() const; #endif - /// Called by the default ObjectThreadCheck() to determine ThreadOwnership - /// for an Object. The default uses the object's individual value - /// (which defaults to ThreadOwnership::kClassDefault and can be set via - /// SetThreadOwnership()) + /// Called by the default ObjectThreadCheck() to determine ownership for + /// an Object. By default, an object is owned by a specific thread, + /// defaulting to the logic thread. virtual auto GetThreadOwnership() const -> ThreadOwnership; - /// Return the exact thread to check for with ThreadOwnership::kClassDefault - /// (in the default ObjectThreadCheck implementation at least). - /// Default returns EventLoopID::kLogic + /// Return the exact thread to check for with + /// ThreadOwnership::kClassDefault (in the default ObjectThreadCheck + /// implementation at least). Default returns EventLoopID::kLogic virtual auto GetDefaultOwnerThread() const -> EventLoopID; /// Set thread ownership for an individual object. @@ -73,12 +77,12 @@ class Object { #endif } - // Return true if the provided obj ptr is not null, is ref-counted, and has at - // least 1 strong ref. This is generally a good thing for calls accepting - // object ptrs to check. It is considered bad practice to perform operations - // with not-yet-reffed objects. Note that in some cases this may return - // false positives, so only use this as a sanity check and only take action - // for a negative result. + // Return true if the provided obj ptr is not null, is ref-counted, and + // has at least 1 strong ref. This is generally a good thing for calls + // accepting object ptrs to check. It is considered bad practice to + // perform operations with not-yet-reffed objects. Note that in some cases + // this may return false positives, so only use this as a sanity check and + // only take action for a negative result. static auto IsValidManagedObject(Object* obj) -> bool { if (!obj) { return false; @@ -91,11 +95,11 @@ class Object { return (obj->object_strong_ref_count_ > 0); } - // Return true if the object seems to be valid and was allocated as unmanaged. - // Code that plans to explicitly delete raw passed pointers can check this - // for peace of mind. Note that for some build types this will return false - // positives, so only use this as a sanity check and only take action for - // negative results. + // Return true if the object seems to be valid and was allocated as + // unmanaged. Code that plans to explicitly delete raw passed pointers can + // check this for peace of mind. Note that for some build types this will + // return false positives, so only use this as a sanity check and only + // take action for negative results. static auto IsValidUnmanagedObject(Object* obj) -> bool { if (!obj) { return false; @@ -108,12 +112,53 @@ class Object { return false; } #endif - // We don't store specifics in release builds; assume everything is peachy. + // We don't store specifics in release builds; assume everything is + // peachy. return true; } + auto object_strong_ref_count() const -> int { return object_strong_ref_count_; } + + /// Increment the strong reference count for an Object. In most cases you + /// should let Ref objects handle this for you and not call this directly. + void ObjectIncrementStrongRefCount() { +#if BA_DEBUG_BUILD + ObjectUpdateForAcquire(); + ObjectThreadCheck(); + + // Obvs shouldn't be referencing dead stuff. + assert(!object_is_dead_); + + // Complain if trying ot create a ref to a non-ref-counted obj. + if (!object_is_ref_counted_) { + FatalError("Attempting to create a strong-ref to non-refcounted obj: " + + GetObjectDescription()); + } + object_has_been_strong_reffed_ = true; +#endif // BA_DEBUG_BUILD + + object_strong_ref_count_++; + } + + /// Decrement the strong reference count for the Object, deleting it if it + /// hits zero. In most cases you should let Ref objects handle this for + /// you and not call this directly. + void ObjectDecrementStrongRefCount() { +#if BA_DEBUG_BUILD + ObjectThreadCheck(); +#endif + assert(object_strong_ref_count_ > 0); + object_strong_ref_count_--; + if (object_strong_ref_count_ == 0) { +#if BA_DEBUG_BUILD + object_is_dead_ = true; +#endif + delete this; + } + } + template class Ref; template @@ -193,9 +238,8 @@ class Object { PyExcType::kReference); } - // Yes, reinterpret_cast is evil, but we make sure - // we only operate on cases where this is valid - // (see Acquire()). + // Yes, reinterpret_cast is evil, but we make sure we only operate on + // cases where this is valid (see Acquire()). return *reinterpret_cast(obj_); } @@ -207,8 +251,8 @@ class Object { PyExcType::kReference); } - // Yes, reinterpret_cast is evil, but we make sure we only operate - // on cases where this is valid (see Acquire()). + // Yes, reinterpret_cast is evil, but we make sure we only operate on + // cases where this is valid (see Acquire()). return reinterpret_cast(obj_); } @@ -263,8 +307,8 @@ class Object { auto operator=(U* ptr) -> WeakRef& { Release(); - // Go through our template type instead of assigning directly - // to our Object* so we catch invalid assigns at compile-time. + // Go through our template type instead of assigning directly to our + // Object* so we catch invalid assigns at compile-time. T* tmp = ptr; if (tmp) Acquire(tmp); @@ -295,13 +339,13 @@ class Object { // Default constructor. WeakRef() = default; - /// Copy constructor. Note that, by making this explicit, we require code - /// to be a bit more verbose. For example, we can't just do 'return - /// some_ref;' from a function and instead have to do 'return - /// Object::WeakRef(some_ref)'. However I feel this extra - /// verbosity is good; we're tossing around a mix of pointers and - /// strong-refs and weak-refs so it's good to be aware exactly where refs - /// are being added/etc. + /// Copy constructor. Note that, by making this explicit, we require + /// code to be a bit more verbose. For example, we can't just do 'return + /// some_ref;' from a function that returns a WeakRef and instead have + /// to do 'return Object::WeakRef(some_ref)'. However I feel + /// this extra verbosity is good; we're tossing around a mix of pointers + /// and strong-refs and weak-refs so it's good to be aware exactly where + /// refs are being added/etc. explicit WeakRef(const WeakRef& ref) { *this = ref.Get(); } /// Create from a pointer of any compatible type. @@ -469,13 +513,13 @@ class Object { /// Default constructor. Ref() = default; - /// Copy constructor. Note that, by making this explicit, we require code - /// to be a bit more verbose. For example, we can't just do 'return - /// some_ref;' from a function and instead have to do 'return - /// Object::Ref(some_ref)'. However I feel this extra verbosity - /// is good; we're tossing around a mix of pointers and strong-refs and - /// weak-refs so it's good to be aware exactly where refs are being - /// added/etc. + /// Copy constructor. Note that, by making this explicit, we require + /// code to be a bit more verbose. For example, we can't just do 'return + /// some_ref;' from a function that returns a Ref and instead have to do + /// 'return Object::Ref(some_ref)'. However I feel this extra + /// verbosity is good; we're tossing around a mix of pointers and + /// strong-refs and weak-refs so it's good to be aware exactly where + /// refs are being added/etc. explicit Ref(const Ref& ref) { *this = ref.Get(); } /// Create from a compatible pointer. @@ -505,42 +549,16 @@ class Object { PyExcType::kReference); } -#if BA_DEBUG_BUILD - obj->ObjectUpdateForAcquire(); - obj->ObjectThreadCheck(); - - // Obvs shouldn't be referencing dead stuff. - assert(!obj->object_is_dead_); - - // Complain if trying ot create a ref to a non-ref-counted obj. - if (!obj->object_is_ref_counted_) { - FatalError("Attempting to create a strong-ref to non-refcounted obj: " - + obj->GetObjectDescription()); - } - obj->object_has_been_strong_reffed_ = true; -#endif // BA_DEBUG_BUILD - - obj->object_strong_ref_count_++; + obj->ObjectIncrementStrongRefCount(); obj_ = obj; } void Release() { if (obj_ != nullptr) { -#if BA_DEBUG_BUILD - obj_->ObjectThreadCheck(); -#endif - assert(obj_->object_strong_ref_count_ > 0); - obj_->object_strong_ref_count_--; - T* tmp = obj_; - - // Invalidate ref *before* delete to avoid potential double-release. + auto* obj = obj_; + // Invalidate ref *before* to avoid potential recursive-release. obj_ = nullptr; - if (tmp->object_strong_ref_count_ == 0) { -#if BA_DEBUG_BUILD - tmp->object_is_dead_ = true; -#endif - delete tmp; - } + obj->ObjectDecrementStrongRefCount(); } } T* obj_{}; @@ -549,10 +567,10 @@ class Object { /// Object::New(): The preferred way to create ref-counted Objects. /// Allocates a new Object with the provided args and returns a strong /// reference to it. - /// Generally you pass a single type to be instantiated and returned, - /// but you can optionally specify the two separately. - /// (for instance you may want to create a Button but return - /// a Ref to a Widget) + /// + /// Generally you pass a single type to be instantiated and returned, but + /// you can optionally specify the two separately. For example, you may + /// want to create a Button but return a Ref to a Widget. template [[nodiscard]] static auto New(ARGS&&... args) -> Object::Ref { auto* ptr = new TALLOC(std::forward(args)...); @@ -642,17 +660,19 @@ class Object { private: #if BA_DEBUG_BUILD - // Making operator new private here to help ensure all of our dynamic - // allocation/deallocation goes through our special functions (New(), - // NewDeferred(), etc.). However, sticking with original new for release - // builds since it may handle corner cases that this does not. + // Making operator new private here purely to help enforce all of our + // dynamic allocation/deallocation going through our special functions + // (New(), NewDeferred(), etc.). However, sticking with original new for + // release builds since we don't actually intend to muck with its runtime + // behavior and the default might be somehow smarter than ours here. auto operator new(size_t size) -> void* { return new char[size]; } void ObjectUpdateForAcquire(); - bool object_has_been_strong_reffed_{}; - bool object_is_ref_counted_{}; - bool object_is_pending_deferred_{}; - bool object_is_unmanaged_{}; - bool object_is_dead_{}; + + bool object_has_been_strong_reffed_ : 1 {}; + bool object_is_ref_counted_ : 1 {}; + bool object_is_pending_deferred_ : 1 {}; + bool object_is_unmanaged_ : 1 {}; + bool object_is_dead_ : 1 {}; Object* object_next_{}; Object* object_prev_{}; ThreadOwnership thread_ownership_{ThreadOwnership::kClassDefault}; @@ -661,8 +681,8 @@ class Object { millisecs_t object_birth_time_{}; bool object_printed_warning_{}; #endif - int object_strong_ref_count_{}; WeakRefBase* object_weak_refs_{}; + int object_strong_ref_count_{}; BA_DISALLOW_CLASS_COPIES(Object); }; // Object diff --git a/src/ballistica/shared/generic/snapshot.h b/src/ballistica/shared/generic/snapshot.h new file mode 100644 index 00000000..c5591c92 --- /dev/null +++ b/src/ballistica/shared/generic/snapshot.h @@ -0,0 +1,26 @@ +// Released under the MIT License. See LICENSE for details. + +#ifndef BALLISTICA_SHARED_GENERIC_SNAPSHOT_H_ +#define BALLISTICA_SHARED_GENERIC_SNAPSHOT_H_ + +#include "ballistica/shared/foundation/object.h" + +namespace ballistica { + +/// Wraps a const instance of some type in a logic-thread-owned Object. To +/// use this, allocate some object using new(), fill it out, and pass it to +/// this which will take ownership and expose it as a const. +template +class Snapshot : public Object { + public: + explicit Snapshot(T* data) : data_{data} { assert(data); } + ~Snapshot() { delete data_; } + auto* Get() const { return data_; } + + private: + const T* data_; +}; + +} // namespace ballistica + +#endif // BALLISTICA_SHARED_GENERIC_SNAPSHOT_H_ diff --git a/src/tools/pcommandbatch/pcommandbatch.c b/src/tools/pcommandbatch/pcommandbatch.c index 28fbb5c3..1137ac10 100644 --- a/src/tools/pcommandbatch/pcommandbatch.c +++ b/src/tools/pcommandbatch/pcommandbatch.c @@ -307,7 +307,7 @@ int establish_connection_(const struct Context_* ctx) { retry_attempt + 1); } } else if (errno == ECONNREFUSED) { - // Am seeing this very rarely on random one-off commands. I'm + // Am seeing these very rarely on random one-off commands. I'm // guessing there's some race condition at the OS level where the // port-file write goes through before the socket is actually truly // accepting connections. A retry should succeed. @@ -318,6 +318,15 @@ int establish_connection_(const struct Context_* ctx) { ctx->instance_prefix, ctx->instance_num, ctx->pid, retry_attempt + 1); } + } else if (errno == EINVAL) { + // Saw this randomly once on Mac. Not sure what could have led to it. + if (ctx->verbose) { + fprintf(stderr, + "pcommandbatch client %s_%d (pid %d): got EINVAL" + " on connect attempt %d.\n", + ctx->instance_prefix, ctx->instance_num, ctx->pid, + retry_attempt + 1); + } } else { // Currently not retrying on other errors. fprintf(stderr, diff --git a/tools/efrotools/xcodebuild.py b/tools/efrotools/xcodebuild.py index 7933d72d..718a4dbd 100644 --- a/tools/efrotools/xcodebuild.py +++ b/tools/efrotools/xcodebuild.py @@ -983,8 +983,8 @@ class XCodeBuild: # on to clarify in that case (unless requested not to). assert self._section is not None sys.stdout.write( - f'{Clr.YLW}Unexpected {self._section.value}' - f' Output:{Clr.RST} {line}' + f'{Clr.YLW}Unfiltered Output (Section {self._section.value}):' + f'{Clr.RST} {line}' ) else: sys.stdout.write(line)