cleaned up interaction between graphics server and client which will make alternate renderers more doable

This commit is contained in:
Eric 2023-10-20 18:07:15 -07:00
parent a3b51abb03
commit b5b34b4455
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
96 changed files with 1823 additions and 1200 deletions

106
.efrocachemap generated
View File

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

View File

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

View File

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

View File

@ -345,6 +345,10 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\net_graph.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h" />
@ -680,6 +684,7 @@
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" />
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />

View File

@ -469,6 +469,18 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h">
<Filter>ballistica\base\graphics\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc">
<Filter>ballistica\base\graphics\support</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h">
<Filter>ballistica\base\graphics\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc">
<Filter>ballistica\base\graphics\support</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h">
<Filter>ballistica\base\graphics\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc">
<Filter>ballistica\base\graphics\support</Filter>
</ClCompile>
@ -1474,6 +1486,9 @@
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h">
<Filter>ballistica\shared\generic</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h">
<Filter>ballistica\shared\generic</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc">
<Filter>ballistica\shared\generic</Filter>
</ClCompile>

View File

@ -340,6 +340,10 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\net_graph.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h" />
@ -675,6 +679,7 @@
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" />
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />

View File

@ -469,6 +469,18 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h">
<Filter>ballistica\base\graphics\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc">
<Filter>ballistica\base\graphics\support</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h">
<Filter>ballistica\base\graphics\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc">
<Filter>ballistica\base\graphics\support</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h">
<Filter>ballistica\base\graphics\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc">
<Filter>ballistica\base\graphics\support</Filter>
</ClCompile>
@ -1474,6 +1486,9 @@
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h">
<Filter>ballistica\shared\generic</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h">
<Filter>ballistica\shared\generic</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc">
<Filter>ballistica\shared\generic</Filter>
</ClCompile>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Runnable*> graphics_calls_;
};

View File

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

View File

@ -17,6 +17,8 @@ class AppAdapterHeadless : public AppAdapter {
void DoApplyAppConfig() override;
auto GetGraphicsClientContext() -> GraphicsClientContext* override;
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
void RunMainThreadEventLoopToCompletion() override;

View File

@ -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<const GraphicsSettings_*>(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<int>(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<float>(pixels_x),
static_cast<float>(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<float>(pixels_x),
static_cast<float>(pixels_y));
});
}
auto AppAdapterSDL::InGraphicsContext() -> bool {

View File

@ -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<Runnable*> 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<Runnable*> strict_graphics_calls_;
microsecs_t oversleep_{};
std::vector<JoystickInput*> 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

View File

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

View File

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

View File

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

View File

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

View File

@ -136,20 +136,21 @@ class Assets {
std::unordered_map<std::string, Object::Ref<T> >* c_list)
-> Object::Ref<T>;
std::vector<std::string> asset_paths_;
int language_state_{};
bool have_pending_loads_[static_cast<int>(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<std::string> asset_paths_;
std::unordered_map<std::string, std::string> 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<Object::Ref<TextureAsset> > system_textures_;
std::vector<Object::Ref<TextureAsset> > system_cube_map_textures_;
std::vector<Object::Ref<SoundAsset> > 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<std::string, std::string> language_;
int language_state_{};
std::mutex special_char_mutex_;
std::unordered_map<SpecialChar, std::string> special_char_strings_;
};

View File

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

View File

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

View File

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

View File

@ -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<uint32_t>;
auto PlaySoundAtPosition(SoundAsset* sound, float volume, float x, float y,
float z) -> std::optional<uint32_t>;
// 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<AudioSource*> 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<AudioSource*> 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_;
};

View File

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

View File

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

View File

@ -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<BGDynamicsServer::StepData>();
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<MeshBuffer<VertexSmokeFull>>(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);

View File

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

View File

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

View File

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

View File

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

View File

@ -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<GraphicsSettings>* {
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<Snapshot<GraphicsSettings>>(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<float>(kBaseVirtualResX)
/ static_cast<float>(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<int>(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<int>(request)));
return TextureQuality::kLow;
}
}
void Graphics::set_client_context(Snapshot<GraphicsClientContext>* 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

View File

@ -6,13 +6,15 @@
#include <list>
#include <map>
#include <mutex>
#include <set>
#include <string>
#include <vector>
#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<GraphicsSettings>*;
/// Called by the graphics-server when a new client context is ready.
void set_client_context(Snapshot<GraphicsClientContext>* 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<uint16_t>* indices,
std::vector<VertexSprite>* 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<Object::Ref<PythonContextCall>> clean_frame_commands_;
std::vector<MeshData*> mesh_data_creates_;
std::vector<MeshData*> 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<FrameDef*> 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<std::string, Object::Ref<NetGraph>> debug_graphs_;
std::mutex frame_def_delete_list_mutex_;
std::list<Object::Ref<PythonContextCall>> clean_frame_commands_;
std::list<ScreenMessageEntry> screen_messages_;
std::list<ScreenMessageEntry> screen_messages_top_;
std::vector<FrameDef*> recycle_frame_defs_;
std::vector<uint16_t> blotch_indices_;
std::vector<VertexSprite> blotch_verts_;
std::vector<uint16_t> blotch_soft_indices_;
std::vector<VertexSprite> blotch_soft_verts_;
std::vector<uint16_t> blotch_soft_obj_indices_;
std::vector<VertexSprite> blotch_soft_obj_verts_;
std::vector<FrameDef*> frame_def_delete_list_;
std::vector<MeshData*> mesh_data_creates_;
std::vector<MeshData*> 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<ImageMesh> screen_mesh_;
Object::Ref<ImageMesh> progress_bar_bottom_mesh_;
Object::Ref<ImageMesh> progress_bar_top_mesh_;
@ -406,49 +500,10 @@ class Graphics {
Object::Ref<SpriteMesh> shadow_blotch_mesh_;
Object::Ref<SpriteMesh> shadow_blotch_soft_mesh_;
Object::Ref<SpriteMesh> shadow_blotch_soft_obj_mesh_;
std::string fps_string_;
std::string ping_string_;
std::string net_info_string_;
std::vector<uint16_t> blotch_indices_;
std::vector<VertexSprite> blotch_verts_;
std::vector<uint16_t> blotch_soft_indices_;
std::vector<VertexSprite> blotch_soft_verts_;
std::vector<uint16_t> blotch_soft_obj_indices_;
std::vector<VertexSprite> blotch_soft_obj_verts_;
std::map<std::string, Object::Ref<NetGraph>> debug_graphs_;
std::mutex frame_def_delete_list_mutex_;
std::vector<FrameDef*> frame_def_delete_list_;
Object::Ref<Camera> camera_;
millisecs_t next_stat_update_time_{};
int last_total_frames_rendered_{};
int last_fps_{};
std::list<ScreenMessageEntry> screen_messages_;
std::list<ScreenMessageEntry> 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<PythonContextCall> fade_end_call_;
Object::Ref<Snapshot<GraphicsSettings>> settings_snapshot_;
Object::Ref<Snapshot<GraphicsClientContext>> client_context_snapshot_;
};
} // namespace ballistica::base

View File

@ -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<Snapshot<GraphicsClientContext>>(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<int>(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<int>(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<int>(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<float>(kBaseVirtualResX)
/ static_cast<float>(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<float>(kBaseVirtualResX)
// / static_cast<float>(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<Object::Ref<Asset>*>& components) {

View File

@ -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<uint32_t>(t)))
!= 0u);
}
void SetTextureCompressionTypes(
const std::list<TextureCompressionType>& 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<GraphicsClientContext>* 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<Matrix44f> 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<Matrix44f> model_view_stack_;
std::list<MeshData*> mesh_datas_;
Timer* render_timer_{};
Renderer* renderer_{};
FrameDef* frame_def_{};
std::mutex frame_def_mutex_{};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,13 +7,14 @@
#include <vector>
#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<double>(display_time_microsecs_) / 1000000.0;
auto display_time() const -> seconds_t {
return static_cast<seconds_t>(display_time_microsecs_) / 1000000.0;
}
auto display_time_elapsed() const -> seconds_t {
return static_cast<seconds_t>(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<Asset>& 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<Object::Ref<Asset>>& {
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<Snapshot<GraphicsSettings>> 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<MeshData*> mesh_data_creates_;
@ -245,7 +263,8 @@ class FrameDef {
std::unique_ptr<RenderPass> vr_cover_pass_;
std::unique_ptr<RenderPass> overlay_3d_pass_;
std::unique_ptr<RenderPass> 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_{};

View File

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

View File

@ -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<uint32_t>(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_

View File

@ -0,0 +1,27 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/base/graphics/support/graphics_settings.h"
#include <algorithm>
#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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,6 @@
#define BALLISTICA_BASE_LOGIC_LOGIC_H_
#include <memory>
#include <set>
#include <string>
#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<TimerList> 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<TimerList> display_timers_;
};
} // namespace ballistica::base

View File

@ -4,6 +4,7 @@
#include "ballistica/base/platform/apple/base_platform_apple.h"
#if BA_XCODE_BUILD
#include <BallisticaKit-Swift.h>
#include <unistd.h>
#endif
#include <uuid/uuid.h>
@ -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);

View File

@ -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<PyMethodDef> {
PyAppNameUpperDef,
PyIsXCodeBuildDef,
PyCanDisplayFullUnicodeDef,
PyDisplayLogDef,
PyEmitLogDef,
PyV1CloudLogDef,
PySetStressTestingDef,
PyEnvDef,

View File

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

View File

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

View File

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

View File

@ -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<microsecs_t>(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.
}

View File

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

View File

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

View File

@ -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<std::string> override;
auto GetIsStdinATerminal() -> bool override;

View File

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

View File

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

View File

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

View File

@ -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<MaterialAction> action;
Object::Ref<Collision> collision;
Object::WeakRef<Node> node1; // first event node
Object::WeakRef<Node> node2; // second event node
CollisionEvent(Node* node1_in, Node* node2_in,
const Object::Ref<MaterialAction>& action_in,
const Object::Ref<Collision>& collision_in)
CollisionEvent_(Node* node1_in, Node* node2_in,
const Object::Ref<MaterialAction>& action_in,
const Object::Ref<Collision>& collision_in)
: node1(node1_in),
node2(node2_in),
action(action_in),
collision(collision_in) {}
};
class Dynamics::SrcPartCollideMap {
class Dynamics::SrcPartCollideMap_ {
public:
std::unordered_map<int, Object::Ref<Collision> > dst_part_collisions;
};
class Dynamics::DstNodeCollideMap {
class Dynamics::DstNodeCollideMap_ {
public:
std::unordered_map<int, SrcPartCollideMap> src_parts;
std::unordered_map<int, SrcPartCollideMap_> src_parts;
int collideDisabled;
DstNodeCollideMap() : collideDisabled(0) {}
~DstNodeCollideMap() = default;
DstNodeCollideMap_() : collideDisabled(0) {}
~DstNodeCollideMap_() = default;
};
class Dynamics::SrcNodeCollideMap {
class Dynamics::SrcNodeCollideMap_ {
public:
std::unordered_map<int64_t, DstNodeCollideMap> dst_nodes;
std::unordered_map<int64_t, DstNodeCollideMap_> 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<int64_t, Dynamics::SrcNodeCollideMap>::iterator&
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap_>::iterator&
i,
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap>::iterator&
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap_>::iterator&
j,
const std::unordered_map<int, SrcPartCollideMap>::iterator& k,
const std::unordered_map<int, SrcPartCollideMap_>::iterator& k,
const std::unordered_map<int, Object::Ref<Collision> >::iterator& l);
private:
Dynamics* dynamics_{};
// Contains in-progress collisions for current nodes.
std::unordered_map<int64_t, SrcNodeCollideMap> node_collisions_;
std::unordered_map<int64_t, SrcNodeCollideMap_> node_collisions_;
friend class Dynamics;
};
Dynamics::Dynamics(Scene* scene_in)
: scene_(scene_in),
collision_cache_(new base::CollisionCache()),
impl_(std::make_unique<Impl>(this)) {
ResetODE();
impl_(std::make_unique<Impl_>(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<int64_t, Dynamics::SrcNodeCollideMap>::iterator& i,
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap>::iterator& j,
const std::unordered_map<int, SrcPartCollideMap>::iterator& k,
void Dynamics::Impl_::HandleDisconnect(
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap_>::iterator&
i,
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap_>::iterator&
j,
const std::unordered_map<int, SrcPartCollideMap_>::iterator& k,
const std::unordered_map<int, Object::Ref<Collision> >::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<int64_t, SrcNodeCollideMap>::iterator i_next;
std::unordered_map<int64_t, DstNodeCollideMap>::iterator j_next;
std::unordered_map<int, SrcPartCollideMap>::iterator k_next;
std::unordered_map<int64_t, SrcNodeCollideMap_>::iterator i_next;
std::unordered_map<int64_t, DstNodeCollideMap_>::iterator j_next;
std::unordered_map<int, SrcPartCollideMap_>::iterator k_next;
std::unordered_map<int, Object::Ref<Collision> >::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<Dynamics*>(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);

View File

@ -4,7 +4,6 @@
#define BALLISTICA_SCENE_V1_DYNAMICS_DYNAMICS_H_
#include <memory>
#include <unordered_map>
#include <vector>
#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<CollisionReset> 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<CollisionReset_> 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<CollisionEvent> 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<CollisionEvent_> 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> 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<dGeomID> 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<Node> active_collide_src_node_;
Object::WeakRef<Node> active_collide_dst_node_;
std::vector<dGeomID> trimeshes_;
std::unique_ptr<Impl_> impl_;
std::unique_ptr<base::CollisionCache> collision_cache_;
friend class Impl;
};
} // namespace ballistica::scene_v1

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<float> color_ = {1.0f, 1.0f, 1.0f};
Object::Ref<RigidBody> body_;
@ -56,15 +59,13 @@ class FlagNode : public Node {
Object::Ref<FullShadowSet> full_shadow_set_;
Object::Ref<SimpleShadowSet> 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]{};

View File

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

View File

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

View File

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

View File

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

View File

@ -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<RigidBody> hair_front_right_body_;
JointFixedEF* hair_front_right_joint_{};
Object::Ref<RigidBody> hair_front_left_body_;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Object::WeakRef<Node> >& {
return out_of_bounds_nodes_;
}

View File

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

View File

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

View File

@ -33,8 +33,6 @@ void Session::OnScreenSizeChange() {}
void Session::LanguageChanged() {}
void Session::GraphicsQualityChanged(base::GraphicsQuality q) {}
void Session::DebugSpeedMultChanged() {}
void Session::DumpFullState(SessionStream* out) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <typename T = Object>
class Ref;
template <typename T = Object>
@ -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<T*>(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<T*>(obj_);
}
@ -263,8 +307,8 @@ class Object {
auto operator=(U* ptr) -> WeakRef<T>& {
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<SomeType>(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<SomeType>(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<T>& 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<SomeType>(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<SomeType>(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<T>& 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<Type>(): 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 <typename TRETURN, typename TALLOC = TRETURN, typename... ARGS>
[[nodiscard]] static auto New(ARGS&&... args) -> Object::Ref<TRETURN> {
auto* ptr = new TALLOC(std::forward<ARGS>(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

View File

@ -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 <typename T>
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_

View File

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

View File

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