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/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55", "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/arabic.json": "295c559911fa251f401f8cdcad91c226",
"build/assets/ba_data/data/languages/belarussian.json": "e151808b6b4f6dc159cf55ee62adad3c", "build/assets/ba_data/data/languages/belarussian.json": "e151808b6b4f6dc159cf55ee62adad3c",
"build/assets/ba_data/data/languages/chinese.json": "b0d4e874ba8d22c8fd0d7a0eaaf96ac9", "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/english.json": "e70277fc6325126d3d893524c8df03c9",
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
"build/assets/ba_data/data/languages/filipino.json": "347f38524816691170d266708fe25894", "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/german.json": "450fa41ae264f29a5d1af22143d0d0ad",
"build/assets/ba_data/data/languages/gibberish.json": "7863ceeedb1e87eef46f7769bae5f842", "build/assets/ba_data/data/languages/gibberish.json": "b8dfd407fb7fd9b268129c364b70ca54",
"build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a", "build/assets/ba_data/data/languages/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3",
"build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a", "build/assets/ba_data/data/languages/hindi.json": "8ea0c58a44a24edb131d0e53b074d1f6",
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e", "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/italian.json": "58ecf53a963dbeca1bbf3605e5ab6a2f",
"build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2", "build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2",
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38", "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/polish.json": "e1a1a801851924748ad38fa68216439a",
"build/assets/ba_data/data/languages/portuguese.json": "9fcd6b4da9e5d0dc0e337ab00b5debe2", "build/assets/ba_data/data/languages/portuguese.json": "9fcd6b4da9e5d0dc0e337ab00b5debe2",
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826", "build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
"build/assets/ba_data/data/languages/russian.json": "70f79c606ccc5ec7bd6ce0303fdece70", "build/assets/ba_data/data/languages/russian.json": "70f79c606ccc5ec7bd6ce0303fdece70",
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69", "build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef", "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/swedish.json": "77d671f10613291ebf9c71da66f18a18",
"build/assets/ba_data/data/languages/tamil.json": "65ab7798d637fa62a703750179eeb723", "build/assets/ba_data/data/languages/tamil.json": "65ab7798d637fa62a703750179eeb723",
"build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc", "build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc",
"build/assets/ba_data/data/languages/turkish.json": "42318070b817663f671d78a9c8f3019c", "build/assets/ba_data/data/languages/turkish.json": "42318070b817663f671d78a9c8f3019c",
"build/assets/ba_data/data/languages/ukrainian.json": "f72eb51abfbbb56e27866895d7e947d2", "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/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba",
"build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054", "build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054",
"build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422", "build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422",
@ -4056,50 +4056,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "ece14cc6d7a449f581c810a2d6d3449d", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "48b5a58b401a2b22b88491f7bcd0e22a",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "b0b75bde134af8c73aa1f7e239bd84dc", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "6d83849db3e1398503e2bb682eb4323e",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "f0742e77993c006a5f2df3e9bee6732e", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "b054c284b778dc77edc9c9b046303f46",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b803f154b4bf2aeb908a603fa7888301", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b16789743a603fac02763c09bbca446b",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "2be90b3e6fc6908448a7677dd3cfb594", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "ab0a78d42cc511b4041478205e892897",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4d12d1887901f7c77b5df965bb0b4622", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4bbb41936ffe72a7fe9bdc803761b937",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "987f4e024c7ed08e58223369b40aa309", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "a1427251545496f84c4d4e2d90e6e25a",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "7dd6ce5ab63d9d255029fb907cf6fb63", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "729bec30bafe25cac07f920c0cc30ab8",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ad505c3ad979b2cf52c664ee79798575", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f8b086fdf6bca929ebc75b117b80f522",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "3773aa5c6d396b4c38883321067f5523", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "7d8f3ffe791e5a665ecbb2c517483814",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "748f3877c0ac40f48ebc5d8aab442173", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "5f4207138b152a110593c6c5ea8a9b32",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "2d059f03286603ac416718eb262241ab", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "4211f250197995e7df6942d32cffd202",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "3bd988564ed41c15b4d0f493eced88ef", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "40be1e38cbac3baf88dee161eeb912e1",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "f01539e046d72d86d63da0b4b6fc28df", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "d4aacb95a1855e969d1cc8db33732c40",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "d554e6d3ef9709ad7d7c848633901089", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "570f49dc1de66e3fe75d76ee5f9306e0",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "967375f76d43831afd7e10208502dcc1", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "ad319e6fb3cd7043a597f0780de42a98",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "7dba8e8a0b8ffbe7f8d73b33b0c41ed5", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "4c15b26f43b4cb81f433beeb927c8aa6",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "11ccabb65197c9f2e3059ac434888e11", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "a06aaa7a95abb56d49ba7925cc503a28",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "8d79aece6620eb017896a7e816a78f0d", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "82619b88184faf3ef7ae4bf41ea282ce",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "a3331c3d60962e7f0c2b62728bf7f43e", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "61251e6fe58347224750fdf30d4bf8bc",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "1636f9569ee8b8a6c0abed5c9e31e3f7", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "bbd5b31cd9b4d30e48ce46e2cf968fcf",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "3fc153ee973090358916b90938429931", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "1636f9569ee8b8a6c0abed5c9e31e3f7", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "bbd5b31cd9b4d30e48ce46e2cf968fcf",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "3fc153ee973090358916b90938429931", "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1c7ed5b60c2961cf7d1a918157f90bce", "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "b483573e1ef7e6be1090c67187e9d1d8",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "0cbfd345b7e6a02d2a6bdfe7966d03d1", "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1c7ed5b60c2961cf7d1a918157f90bce", "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "b483573e1ef7e6be1090c67187e9d1d8",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "0cbfd345b7e6a02d2a6bdfe7966d03d1", "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "1360496e008c0d0fb631b2fde94e0d54", "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "cf4e9ef8006953c365b0928c68f5a21b",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "f64f8060f46a1f7088c7aadef33220dd", "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "1360496e008c0d0fb631b2fde94e0d54", "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "cf4e9ef8006953c365b0928c68f5a21b",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "f64f8060f46a1f7088c7aadef33220dd", "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "dc45874c7796f4fc740c224243efac28", "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "840e96e79a56393c353184475cf28fbc",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c7e7528347b1ec5bc37b13ed8ae88df1", "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "abeca8c975a6cd5766fc90df99e8dcd1", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "d0940a2237c33b922cf3747cbf3910ef",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c7e7528347b1ec5bc37b13ed8ae88df1", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "7aa3fa305f66461ec5e5bbc550aa742d", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "52ccfcbba95dcf3d06620748690446be",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "89c02f2300860fded6b44855f9b8407f", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "8369a217dc8cd95db308851de9f35d86",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "69f97da125d43fc396eeaea8013cb133", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6bc8e9e67a0cbe50ab2c6891d454570f",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "9e56ac32e0cc2785811a162de68c69da", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "2ce9d1659647bca4725f404d192c3554",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a62570a46fed2002590be0bafe5055e8", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "8fd2fd1ec12170942823e809332e8cb9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "b2a10b1eb917197da8f981d5a5daed44", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "f40bd1a61620168791b88901975ea8ee",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "c3af2f896ddb7a0b5f2ee2f35bac0318", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4e3f244ac43cd400ffdbd2ac2e887399",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "30628de8aa6a7d9cfccf09f102ff9953", "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/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101", "src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "bb96031e3f844704fcc9a0549a6d2c41", "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, - Massively cleaned up code related to rendering and window systems (OpenGL,
SDL, etc). This code had been growing into a nasty tangle for 15 years SDL, etc). This code had been growing into a nasty tangle for 15 years

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/camera.h
${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.cc ${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/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.cc
${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.h ${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.h
${BA_SRC_ROOT}/ballistica/base/graphics/support/render_command_buffer.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/lambda_runnable.h
${BA_SRC_ROOT}/ballistica/shared/generic/runnable.cc ${BA_SRC_ROOT}/ballistica/shared/generic/runnable.cc
${BA_SRC_ROOT}/ballistica/shared/generic/runnable.h ${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.cc
${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.h ${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.h
${BA_SRC_ROOT}/ballistica/shared/generic/utf8.cc ${BA_SRC_ROOT}/ballistica/shared/generic/utf8.cc

View File

@ -345,6 +345,10 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" /> <ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" /> <ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" /> <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" /> <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\net_graph.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.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" /> <ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" /> <ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" /> <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" /> <ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" /> <ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" /> <ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />

View File

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

View File

@ -340,6 +340,10 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" /> <ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" /> <ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" /> <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" /> <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\net_graph.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.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" /> <ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" /> <ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" /> <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" /> <ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" /> <ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" /> <ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />

View File

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

View File

@ -185,10 +185,8 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None:
def _on_log(entry: LogEntry) -> None: def _on_log(entry: LogEntry) -> None:
# Forward this along to the engine to display in the in-app # Forward this along to the engine to display in the in-app
# console, in the Android log, etc. # console, in the Android log, etc.
_babase.display_log( _babase.emit_log(
name=entry.name, name=entry.name, level=entry.level.name, message=entry.message
level=entry.level.name,
message=entry.message,
) )
# We also want to feed some logs to the old v1-cloud-log system. # 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 # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21479 TARGET_BALLISTICA_BUILD = 21486
TARGET_BALLISTICA_VERSION = '1.7.28' TARGET_BALLISTICA_VERSION = '1.7.28'

View File

@ -2,7 +2,7 @@
#include "ballistica/base/app_adapter/app_adapter.h" #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" #include "ballistica/base/app_adapter/app_adapter_android.h"
#endif #endif
#include "ballistica/base/app_adapter/app_adapter_apple.h" #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."); } void AppAdapter::TerminateApp() { FatalError("Fixme unimplemented."); }
auto AppAdapter::HasDirectKeyboardInput() -> bool { return false; } 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 } // namespace ballistica::base

View File

@ -30,6 +30,16 @@ class AppAdapter {
virtual void OnScreenSizeChange(); virtual void OnScreenSizeChange();
virtual void DoApplyAppConfig(); 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. /// Return whether this class manages the main thread event loop itself.
/// Default is true. If this is true, RunMainThreadEventLoopToCompletion() /// Default is true. If this is true, RunMainThreadEventLoopToCompletion()
/// will be called to run the app. This should return false on builds /// 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. /// should be callable from any thread.
virtual auto HasDirectKeyboardInput() -> bool; 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: protected:
AppAdapter(); AppAdapter();
virtual ~AppAdapter(); virtual ~AppAdapter();

View File

@ -44,58 +44,27 @@ void AppAdapterApple::DoPushMainThreadRunnable(Runnable* runnable) {
BallisticaKit::FromCppPushRawRunnableToMain(runnable); BallisticaKit::FromCppPushRawRunnableToMain(runnable);
} }
void AppAdapterApple::DoApplyAppConfig() { void AppAdapterApple::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
assert(g_base->InLogicThread());
g_base->graphics_server->PushSetScreenPixelScaleCall( void AppAdapterApple::ApplyGraphicsSettings(const GraphicsSettings* settings) {
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale)); auto* graphics_server = g_base->graphics_server;
auto graphics_quality_requested = // We need a full renderer reload if quality values have changed
g_base->graphics->GraphicsQualityFromAppConfig(); // or if we don't have a renderer yet.
bool need_full_reload = ((graphics_server->texture_quality_requested()
auto texture_quality_requested = != settings->texture_quality)
g_base->graphics->TextureQualityFromAppConfig(); || (graphics_server->graphics_quality_requested()
!= settings->graphics_quality));
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 // We need a full renderer reload if quality values have changed or if we
// don't have one yet. // don't yet have a renderer.
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());
if (need_full_reload) { 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_( void AppAdapterApple::ReloadRenderer_(const GraphicsSettings* settings) {
GraphicsQualityRequest graphics_quality_requested,
TextureQualityRequest texture_quality_requested) {
auto* gs = g_base->graphics_server; auto* gs = g_base->graphics_server;
if (gs->renderer() && gs->renderer_loaded()) { 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 // along the latest real resolution just before each frame draw, but we
// need *something* here or else we'll get errors due to framebuffers // need *something* here or else we'll get errors due to framebuffers
// getting made at size 0/etc. // 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. // Update graphics quality based on request.
gs->set_graphics_quality_requested(graphics_quality_requested); gs->set_graphics_quality_requested(settings->graphics_quality);
gs->set_texture_quality_requested(texture_quality_requested); gs->set_texture_quality_requested(settings->texture_quality);
// (Re)load stuff with these latest quality settings. // (Re)load stuff with these latest quality settings.
gs->LoadRenderer(); gs->LoadRenderer();
@ -123,12 +92,6 @@ void AppAdapterApple::UpdateScreenSizes_() {
assert(g_base->app_adapter->InGraphicsContext()); 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 AppAdapterApple::TryRender() -> bool {
auto allow = ScopedAllowGraphics_(this); auto allow = ScopedAllowGraphics_(this);
@ -146,10 +109,45 @@ auto AppAdapterApple::TryRender() -> bool {
call->RunAndLogErrors(); call->RunAndLogErrors();
delete call; 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 { auto AppAdapterApple::InGraphicsContext() -> bool {

View File

@ -11,6 +11,7 @@
#include "ballistica/base/app_adapter/app_adapter.h" #include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/shared/generic/runnable.h" #include "ballistica/shared/generic/runnable.h"
#include "ballistica/shared/math/vector2f.h"
namespace ballistica::base { namespace ballistica::base {
@ -32,7 +33,7 @@ class AppAdapterApple : public AppAdapter {
auto TryRender() -> bool; auto TryRender() -> bool;
/// Called by FromSwift. /// 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 FullscreenControlAvailable() const -> bool override;
auto FullscreenControlGet() const -> bool override; auto FullscreenControlGet() const -> bool override;
@ -42,6 +43,8 @@ class AppAdapterApple : public AppAdapter {
auto HasDirectKeyboardInput() -> bool override; auto HasDirectKeyboardInput() -> bool override;
void EnableResizeFriendlyMode(int width, int height);
protected: protected:
void DoPushMainThreadRunnable(Runnable* runnable) override; void DoPushMainThreadRunnable(Runnable* runnable) override;
void DoPushGraphicsContextRunnable(Runnable* runnable) override; void DoPushGraphicsContextRunnable(Runnable* runnable) override;
@ -50,16 +53,18 @@ class AppAdapterApple : public AppAdapter {
auto HasHardwareCursor() -> bool override; auto HasHardwareCursor() -> bool override;
void SetHardwareCursorVisible(bool visible) override; void SetHardwareCursorVisible(bool visible) override;
void TerminateApp() override; void TerminateApp() override;
void ApplyGraphicsSettings(const GraphicsSettings* settings) override;
private: private:
void UpdateScreenSizes_();
class ScopedAllowGraphics_; class ScopedAllowGraphics_;
void SetScreen_(TextureQualityRequest texture_quality_requested,
GraphicsQualityRequest graphics_quality_requested); void UpdateScreenSizes_();
void ReloadRenderer_(GraphicsQualityRequest graphics_quality_requested, void ReloadRenderer_(const GraphicsSettings* settings);
TextureQualityRequest texture_quality_requested);
std::thread::id graphics_thread_{}; 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::mutex graphics_calls_mutex_;
std::vector<Runnable*> graphics_calls_; std::vector<Runnable*> graphics_calls_;
}; };

View File

@ -4,6 +4,7 @@
#include "ballistica/base/app_adapter/app_adapter_headless.h" #include "ballistica/base/app_adapter/app_adapter_headless.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/graphics/support/graphics_client_context.h"
#include "ballistica/shared/ballistica.h" #include "ballistica/shared/ballistica.h"
namespace ballistica::base { namespace ballistica::base {
@ -19,12 +20,7 @@ void AppAdapterHeadless::OnMainThreadStartApp() {
new EventLoop(EventLoopID::kMain, ThreadSource::kWrapCurrent); new EventLoop(EventLoopID::kMain, ThreadSource::kWrapCurrent);
} }
void AppAdapterHeadless::DoApplyAppConfig() { 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::RunMainThreadEventLoopToCompletion() { void AppAdapterHeadless::RunMainThreadEventLoopToCompletion() {
assert(g_core->InMainThread()); assert(g_core->InMainThread());
@ -40,6 +36,11 @@ void AppAdapterHeadless::DoExitMainThreadEventLoop() {
main_event_loop_->Exit(); main_event_loop_->Exit();
} }
auto AppAdapterHeadless::GetGraphicsClientContext() -> GraphicsClientContext* {
// Special dummy form.
return new GraphicsClientContext(0);
}
} // namespace ballistica::base } // namespace ballistica::base
#endif // BA_HEADLESS_BUILD #endif // BA_HEADLESS_BUILD

View File

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

View File

@ -104,32 +104,115 @@ void AppAdapterSDL::OnMainThreadStartApp() {
SDL_ShowCursor(SDL_DISABLE); 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() { void AppAdapterSDL::DoApplyAppConfig() {
assert(g_base->InLogicThread()); 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. // Android res string.
// std::string android_res = // std::string android_res =
// g_base->app_config->Resolve(AppConfig::StringID::kResolutionAndroid); // 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(); void AppAdapterSDL::ApplyGraphicsSettings(
int max_fps = g_base->app_config->Resolve(AppConfig::IntID::kMaxFPS); const GraphicsSettings* settings_base) {
assert(g_core->InMainThread());
assert(!g_core->HeadlessMode());
// Tell the main thread to set up the screen with these settings. // In strict mode, allow graphics stuff while in here.
g_base->app_adapter->PushMainThreadCall([=] { auto allow = ScopedAllowGraphics_(this);
SetScreen_(fullscreen, max_fps, vsync, texture_quality_requested,
graphics_quality_requested); // 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() { void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
@ -158,7 +241,7 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
auto AppAdapterSDL::TryRender() -> bool { auto AppAdapterSDL::TryRender() -> bool {
if (strict_graphics_context_) { 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. // anywhere in the main thread.
auto allow = ScopedAllowGraphics_(this); auto allow = ScopedAllowGraphics_(this);
@ -179,7 +262,7 @@ auto AppAdapterSDL::TryRender() -> bool {
// Lastly render. // Lastly render.
return g_base->graphics_server->TryRender(); return g_base->graphics_server->TryRender();
} else { } else {
// Simple path; just render. // Simpler path; just render.
return g_base->graphics_server->TryRender(); 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 // Special case: if we're hidden, we simply sleep for a long bit; no fancy
// timing. // timing.
if (hidden_) { if (hidden_) {
g_core->platform->SleepMillisecs(100); g_core->platform->SleepSeconds(0.1);
return; return;
} }
@ -359,7 +442,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
break; break;
case SDL_QUIT: 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. // If they hit the window close button, skip the confirm.
g_base->QuitApp(false); g_base->QuitApp(false);
} else { } else {
@ -380,7 +463,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
case SDL_WINDOWEVENT_CLOSE: { case SDL_WINDOWEVENT_CLOSE: {
// Simply note that this happened. We use this to adjust our // Simply note that this happened. We use this to adjust our
// SDL_QUIT behavior (quit is called right after this). // SDL_QUIT behavior (quit is called right after this).
last_windowevent_close_time_ = g_core->GetAppTimeMillisecs(); last_windowevent_close_time_ = g_core->GetAppTimeSeconds();
break; break;
} }
@ -546,114 +629,96 @@ auto AppAdapterSDL::GetSDLJoystickInput_(int sdl_joystick_id) const
return nullptr; // Epic fail. return nullptr; // Epic fail.
} }
void AppAdapterSDL::SetScreen_( // void AppAdapterSDL::ApplyGraphicsSettings_(const GraphicsSettings_* settings)
bool fullscreen, int max_fps, VSyncRequest vsync_requested, // {
TextureQualityRequest texture_quality_requested, // assert(g_core->InMainThread());
GraphicsQualityRequest graphics_quality_requested) { // assert(!g_core->HeadlessMode());
assert(g_core->InMainThread());
assert(!g_core->HeadlessMode());
// In strict mode, allow graphics stuff in here. // // In strict mode, allow graphics stuff while in here.
auto allow = ScopedAllowGraphics_(this); // auto allow = ScopedAllowGraphics_(this);
// If we know what we support, filter our request types to what is // bool do_toggle_fs{};
// supported. This will keep us from rebuilding contexts if request type // bool do_set_existing_fullscreen{};
// 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{}; // auto* graphics_server = g_base->graphics_server;
bool do_set_existing_fullscreen{};
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 // if (need_full_reload) {
// or if we don't have one yet. // ReloadRenderer_(settings->fullscreen, settings->graphics_quality,
bool need_full_reload = // settings->texture_quality);
((sdl_window_ == nullptr // } else if (settings->fullscreen != fullscreen_) {
|| gs->texture_quality_requested() != texture_quality_requested) // SDL_SetWindowFullscreen(
|| (gs->graphics_quality_requested() != graphics_quality_requested) // sdl_window_, settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP :
|| !gs->texture_quality_set() || !gs->graphics_quality_set()); // 0);
// fullscreen_ = settings->fullscreen;
// }
if (need_full_reload) { // // VSync always gets set independent of the screen (though we set it down
ReloadRenderer_(fullscreen, graphics_quality_requested, // // here to make sure we have a screen when its set).
texture_quality_requested); // VSync vsync;
} else if (fullscreen != fullscreen_) { // switch (settings->vsync) {
SDL_SetWindowFullscreen(sdl_window_, // case VSyncRequest::kNever:
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); // vsync = VSync::kNever;
fullscreen_ = fullscreen; // 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 // // This we can set anytime. Probably could have just set it from the logic
// here to make sure we have a screen when its set). // // thread where we read it, but let's be pedantic and keep everything to
VSync vsync; // // the main thread.
switch (vsync_requested) { // max_fps_ = settings->max_fps;
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 // // Take -1 to mean no max. Otherwise clamp to a reasonable range.
// thread where we read it, but let's be pedantic and keep everything to // if (max_fps_ != -1) {
// the main thread. // max_fps_ = std::max(10, max_fps_);
max_fps_ = max_fps; // max_fps_ = std::min(99999, max_fps_);
// }
// }
// Take -1 to mean no max. Otherwise clamp to a reasonable range. void AppAdapterSDL::ReloadRenderer_(const GraphicsSettings_* settings) {
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) {
assert(g_base->app_adapter->InGraphicsContext()); assert(g_base->app_adapter->InGraphicsContext());
auto* gs = g_base->graphics_server; 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 we don't haven't yet, create our window and renderer.
if (!sdl_window_) { if (!sdl_window_) {
fullscreen_ = fullscreen; fullscreen_ = settings->fullscreen;
// A reasonable default window size. // A reasonable default window size.
auto width = static_cast<int>(kBaseVirtualResX * 0.8f); auto width = static_cast<int>(kBaseVirtualResX * 0.8f);
@ -672,7 +737,7 @@ void AppAdapterSDL::ReloadRenderer_(
uint32_t flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN uint32_t flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
| SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
if (fullscreen) { if (settings->fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} }
@ -729,15 +794,16 @@ void AppAdapterSDL::ReloadRenderer_(
} }
} }
// Update graphics quality based on request. // Update graphics-server's qualities based on request.
gs->set_graphics_quality_requested(graphics_quality_requested); gs->set_graphics_quality_requested(settings->graphics_quality);
gs->set_texture_quality_requested(texture_quality_requested); gs->set_texture_quality_requested(settings->texture_quality);
gs->LoadRenderer(); gs->LoadRenderer();
} }
void AppAdapterSDL::UpdateScreenSizes_() { 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 // Grab logical window dimensions (points?). This is the coordinate space
// SDL's events deal in. // SDL's events deal in.
@ -749,8 +815,13 @@ void AppAdapterSDL::UpdateScreenSizes_() {
// dimensions. // dimensions.
int pixels_x, pixels_y; int pixels_x, pixels_y;
SDL_GL_GetDrawableSize(sdl_window_, &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 { auto AppAdapterSDL::InGraphicsContext() -> bool {

View File

@ -41,6 +41,9 @@ class AppAdapterSDL : public AppAdapter {
auto SupportsMaxFPS() -> bool const override; auto SupportsMaxFPS() -> bool const override;
auto HasDirectKeyboardInput() -> bool override; auto HasDirectKeyboardInput() -> bool override;
void ApplyGraphicsSettings(const GraphicsSettings* settings) override;
auto GetGraphicsSettings() -> GraphicsSettings* override;
protected: protected:
void DoPushMainThreadRunnable(Runnable* runnable) override; void DoPushMainThreadRunnable(Runnable* runnable) override;
@ -52,14 +55,11 @@ class AppAdapterSDL : public AppAdapter {
private: private:
class ScopedAllowGraphics_; class ScopedAllowGraphics_;
void SetScreen_(bool fullscreen, int max_fps, VSyncRequest vsync_requested, struct GraphicsSettings_;
TextureQualityRequest texture_quality_requested,
GraphicsQualityRequest graphics_quality_requested);
void HandleSDLEvent_(const SDL_Event& event); void HandleSDLEvent_(const SDL_Event& event);
void UpdateScreenSizes_(); void UpdateScreenSizes_();
void ReloadRenderer_(bool fullscreen, void ReloadRenderer_(const GraphicsSettings_* settings);
GraphicsQualityRequest graphics_quality_requested,
TextureQualityRequest texture_quality_requested);
void OnSDLJoystickAdded_(int index); void OnSDLJoystickAdded_(int index);
void OnSDLJoystickRemoved_(int index); void OnSDLJoystickRemoved_(int index);
// Given an SDL joystick ID, returns our Ballistica input for it. // Given an SDL joystick ID, returns our Ballistica input for it.
@ -70,6 +70,7 @@ class AppAdapterSDL : public AppAdapter {
void RemoveSDLInputDevice_(int index); void RemoveSDLInputDevice_(int index);
void SleepUntilNextEventCycle_(microsecs_t cycle_start_time); void SleepUntilNextEventCycle_(microsecs_t cycle_start_time);
int max_fps_{60};
bool done_ : 1 {}; bool done_ : 1 {};
bool fullscreen_ : 1 {}; bool fullscreen_ : 1 {};
bool vsync_actually_enabled_ : 1 {}; bool vsync_actually_enabled_ : 1 {};
@ -85,17 +86,16 @@ class AppAdapterSDL : public AppAdapter {
/// that require such a setup. /// that require such a setup.
bool strict_graphics_context_ : 1 {}; bool strict_graphics_context_ : 1 {};
bool strict_graphics_allowed_ : 1 {}; bool strict_graphics_allowed_ : 1 {};
std::mutex strict_graphics_calls_mutex_;
std::vector<Runnable*> strict_graphics_calls_;
VSync vsync_{VSync::kUnset}; VSync vsync_{VSync::kUnset};
uint32_t sdl_runnable_event_id_{}; 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_{}; microsecs_t oversleep_{};
std::vector<JoystickInput*> sdl_joysticks_; std::vector<JoystickInput*> sdl_joysticks_;
Vector2f window_size_{1.0f, 1.0f}; Vector2f window_size_{1.0f, 1.0f};
SDL_Window* sdl_window_{}; SDL_Window* sdl_window_{};
void* sdl_gl_context_{}; void* sdl_gl_context_{};
millisecs_t last_windowevent_close_time_{}; seconds_t last_windowevent_close_time_{};
}; };
} // namespace ballistica::base } // namespace ballistica::base

View File

@ -40,7 +40,8 @@ void AppAdapterVR::PushVRSimpleRemoteStateCall(
} }
void AppAdapterVR::VRSetDrawDimensions(int w, int h) { 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() { void AppAdapterVR::VRPreDraw() {

View File

@ -36,8 +36,6 @@ void AppMode::HandleGameQuery(const char* buffer, size_t size,
auto AppMode::DoesWorldFillScreen() -> bool { return false; } auto AppMode::DoesWorldFillScreen() -> bool { return false; }
void AppMode::GraphicsQualityChanged(GraphicsQuality quality) {}
void AppMode::DrawWorld(FrameDef* frame_def) {} void AppMode::DrawWorld(FrameDef* frame_def) {}
void AppMode::ChangeGameSpeed(int offs) {} void AppMode::ChangeGameSpeed(int offs) {}

View File

@ -62,8 +62,6 @@ class AppMode {
virtual void DrawWorld(FrameDef* frame_def); virtual void DrawWorld(FrameDef* frame_def);
virtual void GraphicsQualityChanged(GraphicsQuality quality);
/// Called whenever screen size changes. /// Called whenever screen size changes.
virtual void OnScreenSizeChange(); virtual void OnScreenSizeChange();

View File

@ -82,8 +82,9 @@ void Assets::StartLoading() {
assert(g_base); assert(g_base);
assert(g_base->audio_server && g_base->assets_server assert(g_base->audio_server && g_base->assets_server
&& g_base->graphics_server); && g_base->graphics_server);
assert(g_base->graphics_server->texture_compression_types_are_set()); assert(g_base->graphics->has_client_context());
assert(g_base->graphics_server->texture_quality_set()); // 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. assert(!asset_loads_allowed_); // We should only be called once.
asset_loads_allowed_ = true; asset_loads_allowed_ = true;
@ -1102,10 +1103,13 @@ auto Assets::FindAssetFile(FileType type, const std::string& name)
} }
} }
assert(g_base->graphics_server // Make sure we know what compression/quality to use.
&& g_base->graphics_server->texture_compression_types_are_set()); assert(g_base->graphics->has_client_context());
assert(g_base->graphics_server // assert(g_base->graphics_server
&& g_base->graphics_server->texture_quality_set()); // &&
// g_base->graphics_server->texture_compression_types_are_set());
// assert(g_base->graphics_server
// && g_base->graphics_server->texture_quality_set());
prefix = "textures/"; prefix = "textures/";
#if BA_OSTYPE_ANDROID && !BA_ANDROID_DDS_BUILD #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) std::unordered_map<std::string, Object::Ref<T> >* c_list)
-> Object::Ref<T>; -> Object::Ref<T>;
std::vector<std::string> asset_paths_; int language_state_{};
bool have_pending_loads_[static_cast<int>(AssetType::kLast)]{}; 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_; std::unordered_map<std::string, std::string> packages_;
// For use by AssetListLock; don't manually acquire. // For use by AssetListLock; don't manually acquire.
std::mutex asset_lists_mutex_; 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_textures_;
std::vector<Object::Ref<TextureAsset> > system_cube_map_textures_; std::vector<Object::Ref<TextureAsset> > system_cube_map_textures_;
std::vector<Object::Ref<SoundAsset> > system_sounds_; std::vector<Object::Ref<SoundAsset> > system_sounds_;
@ -177,7 +178,6 @@ class Assets {
// Text & Language (need to mold this into more asset-like concepts). // Text & Language (need to mold this into more asset-like concepts).
std::mutex language_mutex_; std::mutex language_mutex_;
std::unordered_map<std::string, std::string> language_; std::unordered_map<std::string, std::string> language_;
int language_state_{};
std::mutex special_char_mutex_; std::mutex special_char_mutex_;
std::unordered_map<SpecialChar, std::string> special_char_strings_; 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/asset.h"
#include "ballistica/base/assets/assets.h" #include "ballistica/base/assets/assets.h"
#include "ballistica/base/graphics/graphics.h"
#include "ballistica/base/graphics/graphics_server.h" #include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/support/huffman.h" #include "ballistica/base/support/huffman.h"
#include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/event_loop.h"
@ -221,12 +222,18 @@ void AssetsServer::WriteReplayMessages() {
void AssetsServer::Process() { void AssetsServer::Process() {
// Make sure we don't do any loading until we know what kind/quality of // Make sure we don't do any loading until we know what kind/quality of
// textures we'll be loading. // textures we'll be loading.
if (!g_base->assets || !g_base->graphics_server
|| !g_base->graphics_server // FIXME - we'll need to revisit this when adding support for
->texture_compression_types_are_set() // NOLINT // renderer switches, since this is not especially thread-safe.
|| !g_base->graphics_server->texture_quality_set()) {
if (!g_base->graphics->has_client_context()) {
return; 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 // 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 // (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() { void TextureAsset::DoPreload() {
assert(valid_); assert(valid_);
assert(g_base->graphics_server // Make sure we're not loading without knowing what texture types we
&& g_base->graphics_server->texture_compression_types_are_set()); // 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. // Figure out which LOD should be our base level based on texture quality.
TextureQuality texture_quality = g_base->graphics_server->texture_quality(); auto texture_quality = g_base->graphics->placeholder_texture_quality();
// If we're a text-texture. // If we're a text-texture.
if (packer_.Exists()) { if (packer_.Exists()) {
@ -218,12 +221,14 @@ void TextureAsset::DoPreload() {
&preload_datas_[0].base_level); &preload_datas_[0].base_level);
// We should only be loading this if we support etc1 in hardware. // We should only be loading this if we support etc1 in hardware.
assert(g_base->graphics_server->SupportsTextureCompressionType( assert(g_base->graphics->placeholder_client_context()
TextureCompressionType::kETC1)); ->SupportsTextureCompressionType(
TextureCompressionType::kETC1));
// Decompress dxt1/dxt5 ones if we don't natively support S3TC. // Decompress dxt1/dxt5 ones if we don't natively support S3TC.
if (!g_base->graphics_server->SupportsTextureCompressionType( if (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kS3TC)) { ->SupportsTextureCompressionType(
TextureCompressionType::kS3TC)) {
if ((preload_datas_[0].formats[preload_datas_[0].base_level] if ((preload_datas_[0].formats[preload_datas_[0].base_level]
== TextureFormat::kDXT5) == TextureFormat::kDXT5)
|| (preload_datas_[0].formats[preload_datas_[0].base_level] || (preload_datas_[0].formats[preload_datas_[0].base_level]
@ -241,8 +246,9 @@ void TextureAsset::DoPreload() {
&preload_datas_[0].base_level); &preload_datas_[0].base_level);
// Decompress dxt1/dxt5 if we don't natively support it. // Decompress dxt1/dxt5 if we don't natively support it.
if (!g_base->graphics_server->SupportsTextureCompressionType( if (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kS3TC)) { ->SupportsTextureCompressionType(
TextureCompressionType::kS3TC)) {
preload_datas_[0].ConvertToUncompressed(this); preload_datas_[0].ConvertToUncompressed(this);
} }
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4, } else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
@ -264,16 +270,18 @@ void TextureAsset::DoPreload() {
== TextureFormat::kETC2_RGB) == TextureFormat::kETC2_RGB)
|| (preload_datas_[0].formats[preload_datas_[0].base_level] || (preload_datas_[0].formats[preload_datas_[0].base_level]
== TextureFormat::kETC2_RGBA)) == TextureFormat::kETC2_RGBA))
&& (!g_base->graphics_server->SupportsTextureCompressionType( && (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kETC2))) { ->SupportsTextureCompressionType(
TextureCompressionType::kETC2))) {
preload_datas_[0].ConvertToUncompressed(this); preload_datas_[0].ConvertToUncompressed(this);
} }
// Decompress etc1 if we don't natively support it. // Decompress etc1 if we don't natively support it.
if ((preload_datas_[0].formats[preload_datas_[0].base_level] if ((preload_datas_[0].formats[preload_datas_[0].base_level]
== TextureFormat::kETC1) == TextureFormat::kETC1)
&& (!g_base->graphics_server->SupportsTextureCompressionType( && (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kETC1))) { ->SupportsTextureCompressionType(
TextureCompressionType::kETC1))) {
preload_datas_[0].ConvertToUncompressed(this); preload_datas_[0].ConvertToUncompressed(this);
} }
@ -287,8 +295,9 @@ void TextureAsset::DoPreload() {
&preload_datas_[0].base_level); &preload_datas_[0].base_level);
// We should only be loading this if we support pvr in hardware. // We should only be loading this if we support pvr in hardware.
assert(g_base->graphics_server->SupportsTextureCompressionType( assert(
TextureCompressionType::kPVR)); g_base->graphics->placeholder_client_context()
->SupportsTextureCompressionType(TextureCompressionType::kPVR));
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4, } else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
".nop")) { ".nop")) {
// Dummy path for headless; nothing to do here. // 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. // We should only be loading this if we support etc1 in hardware.
assert(g_base->graphics_server->SupportsTextureCompressionType( assert(g_base->graphics->placeholder_client_context()
TextureCompressionType::kETC1)); ->SupportsTextureCompressionType(
TextureCompressionType::kETC1));
// Decompress dxt1/dxt5 ones if we don't natively support S3TC. // Decompress dxt1/dxt5 ones if we don't natively support S3TC.
if (!g_base->graphics_server->SupportsTextureCompressionType( if (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kS3TC)) { ->SupportsTextureCompressionType(
TextureCompressionType::kS3TC)) {
if ((preload_datas_[d].formats[preload_datas_[d].base_level] if ((preload_datas_[d].formats[preload_datas_[d].base_level]
== TextureFormat::kDXT5) == TextureFormat::kDXT5)
|| (preload_datas_[d].formats[preload_datas_[d].base_level] || (preload_datas_[d].formats[preload_datas_[d].base_level]
@ -365,8 +376,9 @@ void TextureAsset::DoPreload() {
&preload_datas_[d].base_level); &preload_datas_[d].base_level);
// Decompress dxt1/dxt5 if we don't natively support it. // Decompress dxt1/dxt5 if we don't natively support it.
if (!g_base->graphics_server->SupportsTextureCompressionType( if (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kS3TC)) { ->SupportsTextureCompressionType(
TextureCompressionType::kS3TC)) {
preload_datas_[d].ConvertToUncompressed(this); preload_datas_[d].ConvertToUncompressed(this);
} }
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4, } else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
@ -383,16 +395,18 @@ void TextureAsset::DoPreload() {
== TextureFormat::kETC2_RGB) == TextureFormat::kETC2_RGB)
|| (preload_datas_[d].formats[preload_datas_[d].base_level] || (preload_datas_[d].formats[preload_datas_[d].base_level]
== TextureFormat::kETC2_RGBA)) == TextureFormat::kETC2_RGBA))
&& (!g_base->graphics_server->SupportsTextureCompressionType( && (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kETC2))) { ->SupportsTextureCompressionType(
TextureCompressionType::kETC2))) {
preload_datas_[d].ConvertToUncompressed(this); preload_datas_[d].ConvertToUncompressed(this);
} }
// Decompress etc1 if we don't natively support it. // Decompress etc1 if we don't natively support it.
if ((preload_datas_[d].formats[preload_datas_[d].base_level] if ((preload_datas_[d].formats[preload_datas_[d].base_level]
== TextureFormat::kETC1) == TextureFormat::kETC1)
&& (!g_base->graphics_server->SupportsTextureCompressionType( && (!g_base->graphics->placeholder_client_context()
TextureCompressionType::kETC1))) { ->SupportsTextureCompressionType(
TextureCompressionType::kETC1))) {
preload_datas_[d].ConvertToUncompressed(this); preload_datas_[d].ConvertToUncompressed(this);
} }

View File

@ -5,6 +5,7 @@
#include "ballistica/base/assets/sound_asset.h" #include "ballistica/base/assets/sound_asset.h"
#include "ballistica/base/audio/audio_server.h" #include "ballistica/base/audio/audio_server.h"
#include "ballistica/base/audio/audio_source.h" #include "ballistica/base/audio/audio_source.h"
#include "ballistica/base/graphics/graphics.h"
#include "ballistica/base/support/app_config.h" #include "ballistica/base/support/app_config.h"
#include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/event_loop.h"
@ -12,6 +13,19 @@ namespace ballistica::base {
Audio::Audio() = default; 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() { void Audio::Reset() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
g_base->audio_server->PushResetCall(); g_base->audio_server->PushResetCall();

View File

@ -29,36 +29,41 @@ class Audio {
virtual void OnScreenSizeChange(); virtual void OnScreenSizeChange();
virtual void StepDisplayTime(); 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 SetVolumes(float music_volume, float sound_volume);
void SetListenerPosition(const Vector3f& p); void SetListenerPosition(const Vector3f& p);
void SetListenerOrientation(const Vector3f& forward, const Vector3f& up); void SetListenerOrientation(const Vector3f& forward, const Vector3f& up);
void SetSoundPitch(float pitch); void SetSoundPitch(float pitch);
// Return a pointer to a locked sound source, or nullptr if they're all busy. /// 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 /// The sound source will be reset to standard settings (no loop, fade 1, pos
// 0,0,0, etc.). /// 0,0,0, etc.).
// Send the source any immediate commands and then unlock it. /// Send the source any immediate commands and then unlock it.
// For later modifications, re-retrieve the sound with GetPlayingSound() /// For later modifications, re-retrieve the sound with GetPlayingSound()
auto SourceBeginNew() -> AudioSource*; auto SourceBeginNew() -> AudioSource*;
// If a sound play id is playing, locks and returns its sound source. /// If a sound play id is playing, locks and returns its sound source.
// on success, you must unlock the source once done with it. /// on success, you must unlock the source once done with it.
auto SourceBeginExisting(uint32_t play_id, int debug_id) -> AudioSource*; auto SourceBeginExisting(uint32_t play_id, int debug_id) -> AudioSource*;
// Return true if the sound id is currently valid. This is not guaranteed /// 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 /// to be super accurate, but can be used to determine if a sound is still
// playing. /// playing.
auto IsSoundPlaying(uint32_t play_id) -> bool; 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 PlaySound(SoundAsset* s, float volume = 1.0f) -> std::optional<uint32_t>;
auto PlaySoundAtPosition(SoundAsset* sound, float volume, float x, float y, auto PlaySoundAtPosition(SoundAsset* sound, float volume, float x, float y,
float z) -> std::optional<uint32_t>; float z) -> std::optional<uint32_t>;
// Call this if you want to prevent repeated plays of the same sound. It'll /// 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 /// tell you if the sound has been played recently. The one-shot sound-play
// functions use this under the hood. (PlaySound, PlaySoundAtPosition). /// functions use this under the hood. (PlaySound, PlaySoundAtPosition).
auto ShouldPlay(SoundAsset* s) -> bool; auto ShouldPlay(SoundAsset* s) -> bool;
// Hmm; shouldn't these be accessed through the Source class? // Hmm; shouldn't these be accessed through the Source class?
@ -73,15 +78,15 @@ class Audio {
} }
private: private:
// Flat list of client sources indexed by id. /// Flat list of client sources indexed by id.
std::vector<AudioSource*> client_sources_; std::vector<AudioSource*> client_sources_;
// List of sources that are ready to use. /// List of sources that are ready to use.
// This is kept filled by the audio thread /// This is kept filled by the audio thread
// and used by the client. /// and used by the client.
std::vector<AudioSource*> available_sources_; 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_; std::mutex available_sources_mutex_;
}; };

View File

@ -420,6 +420,10 @@ auto BaseFeatureSet::IsUnmodifiedBlessedBuild() -> bool {
return false; return false;
} }
auto BaseFeatureSet::InMainThread() const -> bool {
return g_core->InMainThread();
}
auto BaseFeatureSet::InAssetsThread() const -> bool { auto BaseFeatureSet::InAssetsThread() const -> bool {
if (auto* loop = assets_server->event_loop()) { if (auto* loop = assets_server->event_loop()) {
return loop->ThreadIsCurrent(); return loop->ThreadIsCurrent();

View File

@ -59,6 +59,8 @@ class DataAsset;
class FrameDef; class FrameDef;
class Graphics; class Graphics;
class GraphicsServer; class GraphicsServer;
struct GraphicsSettings;
struct GraphicsClientContext;
class Huffman; class Huffman;
class ImageMesh; class ImageMesh;
class Input; class Input;
@ -662,6 +664,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
/// allowing certain functionality before this time. /// allowing certain functionality before this time.
auto IsBaseCompletelyImported() -> bool; auto IsBaseCompletelyImported() -> bool;
auto InMainThread() const -> bool;
auto InAssetsThread() const -> bool override; auto InAssetsThread() const -> bool override;
auto InLogicThread() const -> bool override; auto InLogicThread() const -> bool override;
auto InAudioThread() 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) { void BGDynamics::Step(const Vector3f& cam_pos, int step_millisecs) {
assert(g_base->InLogicThread()); 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; // 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 // 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(); 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 we're really getting behind, start pruning stuff.
if (step_count > 3) { 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 // Pass a newly allocated raw pointer to the bg-dynamics thread; it takes care
// of disposing it when done. // of disposing it when done.
auto d = Object::NewDeferred<BGDynamicsServer::StepData>(); 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->step_millisecs = step_millisecs;
d->cam_pos = cam_pos; 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 // In high-quality, we draw in the overlay pass so that we don't get wiped
// out by depth-of-field. // 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() SpriteComponent c(draw_in_overlay ? frame_def->overlay_3d_pass()
: frame_def->beauty_pass()); : frame_def->beauty_pass());
c.SetCameraAligned(true); c.SetCameraAligned(true);
@ -232,7 +240,7 @@ void BGDynamics::Draw(FrameDef* frame_def) {
tendrils_mesh_->SetIndexData(ds->tendril_indices); tendrils_mesh_->SetIndexData(ds->tendril_indices);
tendrils_mesh_->SetData( tendrils_mesh_->SetData(
Object::Ref<MeshBuffer<VertexSmokeFull>>(ds->tendril_vertices)); 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() SmokeComponent c(draw_in_overlay ? frame_def->overlay_3d_pass()
: frame_def->beauty_pass()); : frame_def->beauty_pass());
c.SetOverlay(draw_in_overlay); c.SetOverlay(draw_in_overlay);

View File

@ -2282,7 +2282,8 @@ void BGDynamicsServer::Step(StepData* step_data) {
auto ref(Object::CompleteDeferred(step_data)); auto ref(Object::CompleteDeferred(step_data));
// Keep our quality in sync with the graphics thread's. // 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; cam_pos_ = step_data->cam_pos;

View File

@ -73,6 +73,7 @@ class BGDynamicsServer {
auto GetDefaultOwnerThread() const -> EventLoopID override { auto GetDefaultOwnerThread() const -> EventLoopID override {
return EventLoopID::kBGDynamics; return EventLoopID::kBGDynamics;
} }
GraphicsQuality graphics_quality{};
int step_millisecs{}; int step_millisecs{};
Vector3f cam_pos{0.0f, 0.0f, 0.0f}; Vector3f cam_pos{0.0f, 0.0f, 0.0f};

View File

@ -238,10 +238,10 @@ class RendererGL::ProgramGL {
// Update matrices as necessary. // Update matrices as necessary.
uint32_t mvpState = int mvp_state =
g_base->graphics_server->GetModelViewProjectionMatrixState(); g_base->graphics_server->GetModelViewProjectionMatrixState();
if (mvpState != mvp_state_) { if (mvp_state != mvp_state_) {
mvp_state_ = mvpState; mvp_state_ = mvp_state;
glUniformMatrix4fv( glUniformMatrix4fv(
mvp_uniform_, 1, 0, mvp_uniform_, 1, 0,
g_base->graphics_server->GetModelViewProjectionMatrix().m); g_base->graphics_server->GetModelViewProjectionMatrix().m);
@ -251,7 +251,7 @@ class RendererGL::ProgramGL {
if (pflags_ & PFLAG_USES_MODEL_WORLD_MATRIX) { if (pflags_ & PFLAG_USES_MODEL_WORLD_MATRIX) {
// With world space points this would be identity; don't waste time. // With world space points this would be identity; don't waste time.
assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS)); 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_) { if (state != model_world_matrix_state_) {
model_world_matrix_state_ = state; model_world_matrix_state_ = state;
glUniformMatrix4fv(model_world_matrix_uniform_, 1, 0, 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. // With world space points this would be identity; don't waste time.
assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS)); assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS));
// There's no state for just modelview but this works. // There's no state for just modelview but this works.
uint32_t state = int state = g_base->graphics_server->GetModelViewProjectionMatrixState();
g_base->graphics_server->GetModelViewProjectionMatrixState();
if (state != model_view_matrix_state_) { if (state != model_view_matrix_state_) {
model_view_matrix_state_ = state; model_view_matrix_state_ = state;
glUniformMatrix4fv(model_view_matrix_uniform_, 1, 0, glUniformMatrix4fv(model_view_matrix_uniform_, 1, 0,
@ -275,7 +274,7 @@ class RendererGL::ProgramGL {
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_CAM_POS) { 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_) { if (state != cam_pos_state_) {
cam_pos_state_ = state; cam_pos_state_ = state;
const Vector3f& p(g_base->graphics_server->cam_pos()); const Vector3f& p(g_base->graphics_server->cam_pos());
@ -285,7 +284,7 @@ class RendererGL::ProgramGL {
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_CAM_ORIENT_MATRIX) { 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_) { if (state != cam_orient_matrix_state_) {
cam_orient_matrix_state_ = state; cam_orient_matrix_state_ = state;
glUniformMatrix4fv(cam_orient_matrix_uniform_, 1, 0, glUniformMatrix4fv(cam_orient_matrix_uniform_, 1, 0,
@ -295,7 +294,7 @@ class RendererGL::ProgramGL {
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_SHADOW_PROJECTION_MATRIX) { if (pflags_ & PFLAG_USES_SHADOW_PROJECTION_MATRIX) {
uint32_t state = int state =
g_base->graphics_server->light_shadow_projection_matrix_state(); g_base->graphics_server->light_shadow_projection_matrix_state();
if (state != light_shadow_projection_matrix_state_) { if (state != light_shadow_projection_matrix_state_) {
light_shadow_projection_matrix_state_ = state; light_shadow_projection_matrix_state_ = state;
@ -336,19 +335,19 @@ class RendererGL::ProgramGL {
Object::Ref<VertexShaderGL> vertex_shader_; Object::Ref<VertexShaderGL> vertex_shader_;
std::string name_; std::string name_;
GLuint program_{}; GLuint program_{};
int pflags_{};
uint32_t mvp_state_{};
GLint mvp_uniform_{}; GLint mvp_uniform_{};
GLint model_world_matrix_uniform_{}; GLint model_world_matrix_uniform_{};
GLint model_view_matrix_uniform_{}; GLint model_view_matrix_uniform_{};
GLint light_shadow_projection_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_{}; GLint cam_pos_uniform_{};
uint32_t cam_pos_state_{};
GLint cam_orient_matrix_uniform_{}; 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); 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 // Both GL 3 and GL ES 3.0 support depth textures (and thus our high
// quality mode) as a core feature. // quality mode) as a core feature.
g_base->graphics->SetSupportsHighQualityGraphics(true); // g_base->graphics->SetSupportsHighQualityGraphics(true);
// Store the tex-compression type we support. // Store the tex-compression type we support.
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
@ -2598,7 +2598,8 @@ void RendererGL::RetainShader_(ProgramGL* p) { shaders_.emplace_back(p); }
void RendererGL::Load() { void RendererGL::Load() {
assert(g_base->app_adapter->InGraphicsContext()); assert(g_base->app_adapter->InGraphicsContext());
assert(!data_loaded_); assert(!data_loaded_);
assert(g_base->graphics_server->graphics_quality_set()); assert(g_base->graphics_server->graphics_quality()
!= GraphicsQuality::kUnset);
BA_DEBUG_CHECK_GL_ERROR; BA_DEBUG_CHECK_GL_ERROR;
if (!got_screen_framebuffer_) { if (!got_screen_framebuffer_) {
got_screen_framebuffer_ = true; got_screen_framebuffer_ = true;

View File

@ -115,9 +115,14 @@ void Graphics::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
void Graphics::DoApplyAppConfig() { void Graphics::DoApplyAppConfig() {
assert(g_base->InLogicThread()); 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_fps_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowFPS);
show_ping_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowPing); 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 = bool disable_camera_shake =
g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraShake); g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraShake);
@ -126,6 +131,52 @@ void Graphics::DoApplyAppConfig() {
bool disable_camera_gyro = bool disable_camera_gyro =
g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraGyro); g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraGyro);
set_camera_gyro_explicitly_disabled(disable_camera_gyro); 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()); } void Graphics::StepDisplayTime() { assert(g_base->InLogicThread()); }
@ -976,6 +1027,20 @@ auto Graphics::GetEmptyFrameDef() -> FrameDef* {
return frame_def; 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() { void Graphics::ClearFrameDefDeleteList() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
std::scoped_lock lock(frame_def_delete_list_mutex_); std::scoped_lock lock(frame_def_delete_list_mutex_);
@ -1120,6 +1185,8 @@ void Graphics::DrawDevUI(FrameDef* frame_def) {
void Graphics::BuildAndPushFrameDef() { void Graphics::BuildAndPushFrameDef() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete());
assert(camera_.Exists()); assert(camera_.Exists());
assert(!g_core->HeadlessMode()); assert(!g_core->HeadlessMode());
@ -1128,10 +1195,6 @@ void Graphics::BuildAndPushFrameDef() {
assert(!building_frame_def_); assert(!building_frame_def_);
building_frame_def_ = true; 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(); microsecs_t app_time_microsecs = g_core->GetAppTimeMicrosecs();
// Store how much time this frame_def represents. // Store how much time this frame_def represents.
@ -1187,13 +1250,6 @@ void Graphics::BuildAndPushFrameDef() {
internal_components_inited_ = true; 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); ApplyCamera(frame_def);
if (progress_bar_) { if (progress_bar_) {
@ -1254,7 +1310,7 @@ void Graphics::BuildAndPushFrameDef() {
RunCleanFrameCommands(); RunCleanFrameCommands();
} }
frame_def->Finalize(); frame_def->Complete();
// Include all mesh-data loads and unloads that have accumulated up to // Include all mesh-data loads and unloads that have accumulated up to
// this point the graphics thread will have to handle these before // 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() { void Graphics::ClearScreenMessageTranslations() {
assert(g_base && g_base->InLogicThread()); assert(g_base && g_base->InLogicThread());
for (auto&& i : screen_messages_) { for (auto&& i : screen_messages_) {
@ -1922,20 +1973,55 @@ auto Graphics::ScreenMessageEntry::GetText() -> TextGroup& {
void Graphics::OnScreenSizeChange() {} void Graphics::OnScreenSizeChange() {}
void Graphics::SetScreenSize(float virtual_width, float virtual_height, void Graphics::CalcVirtualRes_(float* x, float* y) {
float pixel_width, float pixel_height) { 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()); assert(g_base->InLogicThread());
res_x_virtual_ = virtual_width;
res_y_virtual_ = virtual_height; // Ignore redundant sets.
res_x_ = pixel_width; if (res_x_ == x && res_y_ == y) {
res_y_ = pixel_height; 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). // Need to rebuild internal components (some are sized to the screen).
internal_components_inited_ = false; internal_components_inited_ = false;
// This will inform all applicable logic thread subsystems. // Inform all our logic thread buddies of this change.
g_base->logic->OnScreenSizeChange(virtual_width, virtual_height, pixel_width, g_base->logic->OnScreenSizeChange(res_x_virtual_, res_y_virtual_, res_x_,
pixel_height); 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() { void Graphics::ScreenMessageEntry::UpdateTranslation() {
@ -2030,4 +2116,61 @@ void Graphics::LanguageChanged() {
ClearScreenMessageTranslations(); 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 } // namespace ballistica::base

View File

@ -6,13 +6,15 @@
#include <list> #include <list>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include "ballistica/base/base.h" #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/object.h"
#include "ballistica/shared/foundation/types.h" #include "ballistica/shared/foundation/types.h"
#include "ballistica/shared/generic/snapshot.h"
#include "ballistica/shared/math/matrix44f.h" #include "ballistica/shared/math/matrix44f.h"
#include "ballistica/shared/math/rect.h" #include "ballistica/shared/math/rect.h"
#include "ballistica/shared/math/vector2f.h" #include "ballistica/shared/math/vector2f.h"
@ -62,10 +64,10 @@ class Graphics {
void OnScreenSizeChange(); void OnScreenSizeChange();
void DoApplyAppConfig(); void DoApplyAppConfig();
/// Called by the graphics server to keep us up to date in the logic /// Should be called by the app-adapter to keep the engine informed
/// thread. Dispatches the news to all logic subsystems that care. /// on the drawable area it has to work with (in pixels).
void SetScreenSize(float virtual_width, float virtual_height, void SetScreenResolution(float x, float y);
float physical_width, float physical_height);
void StepDisplayTime(); void StepDisplayTime();
auto TextureQualityFromAppConfig() -> TextureQualityRequest; auto TextureQualityFromAppConfig() -> TextureQualityRequest;
@ -97,13 +99,25 @@ class Graphics {
// Called when the GraphicsServer has sent us a frame-def for deletion. // Called when the GraphicsServer has sent us a frame-def for deletion.
void ReturnCompletedFrameDef(FrameDef* frame_def); void ReturnCompletedFrameDef(FrameDef* frame_def);
auto screen_pixel_width() const { return res_x_; } auto screen_pixel_width() const {
auto screen_pixel_height() const { return res_y_; } 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. // be used for interface positioning, etc.
auto screen_virtual_width() const { return res_x_virtual_; } auto screen_virtual_width() const {
auto screen_virtual_height() const { return res_y_virtual_; } assert(g_base->InLogicThread());
return res_x_virtual_;
}
auto screen_virtual_height() const {
assert(g_base->InLogicThread());
return res_y_virtual_;
}
void ClearScreenMessageTranslations(); void ClearScreenMessageTranslations();
@ -226,10 +240,10 @@ class Graphics {
float upper_top); float upper_top);
void ReleaseFadeEndCommand(); void ReleaseFadeEndCommand();
auto tv_border() const { // auto tv_border() const {
assert(g_base->InLogicThread()); // assert(g_base->InLogicThread());
return tv_border_; // return tv_border_;
} // }
// Nodes that draw flat stuff into the overlay pass should query this z value // Nodes that draw flat stuff into the overlay pass should query this z value
// for where to draw in z. // for where to draw in z.
@ -270,17 +284,6 @@ class Graphics {
return y * (res_y_virtual_ / res_y_); 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) { void set_internal_components_inited(bool val) {
internal_components_inited_ = val; internal_components_inited_ = val;
} }
@ -322,19 +325,65 @@ class Graphics {
camera_gyro_explicitly_disabled_ = disabled; 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: protected:
class ScreenMessageEntry;
Graphics(); Graphics();
virtual ~Graphics(); virtual ~Graphics();
virtual void DoDrawFade(FrameDef* frame_def, float amt); virtual void DoDrawFade(FrameDef* frame_def, float amt);
static void CalcVirtualRes_(float* x, float* y);
private:
class ScreenMessageEntry;
void DrawBoxingGlovesTest(FrameDef* frame_def); void DrawBoxingGlovesTest(FrameDef* frame_def);
void DrawBlotches(FrameDef* frame_def); void DrawBlotches(FrameDef* frame_def);
void DrawCursor(FrameDef* frame_def); void DrawCursor(FrameDef* frame_def);
void DrawFades(FrameDef* frame_def); void DrawFades(FrameDef* frame_def);
void DrawDebugBuffers(RenderPass* pass); void DrawDebugBuffers(RenderPass* pass);
void UpdateAndDrawProgressBar(FrameDef* frame_def); void UpdateAndDrawProgressBar(FrameDef* frame_def);
void DoDrawBlotch(std::vector<uint16_t>* indices, void DoDrawBlotch(std::vector<uint16_t>* indices,
std::vector<VertexSprite>* verts, const Vector3f& pos, std::vector<VertexSprite>* verts, const Vector3f& pos,
@ -347,44 +396,47 @@ class Graphics {
void DrawProgressBar(RenderPass* pass, float opacity); void DrawProgressBar(RenderPass* pass, float opacity);
void UpdateProgressBarProgress(float target); void UpdateProgressBarProgress(float target);
void UpdateGyro(microsecs_t time, microsecs_t elapsed); void UpdateGyro(microsecs_t time, microsecs_t elapsed);
void UpdateInitialGraphicsSettingsSend_();
bool drawing_transparent_only_{}; int last_total_frames_rendered_{};
bool drawing_opaque_only_{}; int last_fps_{};
bool has_supports_high_quality_graphics_value_{}; int progress_bar_loads_{};
bool supports_high_quality_graphics_{}; int frame_def_count_{};
bool internal_components_inited_{}; int frame_def_count_filtered_{};
bool fade_out_{true}; int next_settings_index_{};
bool progress_bar_{}; TextureQuality texture_quality_placeholder_{};
bool progress_bar_fade_in_{}; bool drawing_transparent_only_ : 1 {};
bool debug_draw_{}; bool drawing_opaque_only_ : 1 {};
bool network_debug_display_enabled_{}; bool internal_components_inited_ : 1 {};
bool hardware_cursor_visible_{}; bool fade_out_ : 1 {true};
bool camera_shake_disabled_{}; bool progress_bar_ : 1 {};
bool camera_gyro_explicitly_disabled_{}; bool progress_bar_fade_in_ : 1 {};
bool gyro_enabled_{true}; bool debug_draw_ : 1 {};
bool show_fps_{}; bool network_debug_display_enabled_ : 1 {};
bool show_ping_{}; bool hardware_cursor_visible_ : 1 {};
bool show_net_info_{}; bool camera_shake_disabled_ : 1 {};
bool tv_border_{}; bool camera_gyro_explicitly_disabled_ : 1 {};
bool floor_reflection_{}; bool gyro_enabled_ : 1 {true};
bool building_frame_def_{}; bool show_fps_ : 1 {};
bool shadow_ortho_{}; bool show_ping_ : 1 {};
bool fetched_overlay_node_z_depth_{}; bool show_net_info_ : 1 {};
bool gyro_broken_{}; bool tv_border_ : 1 {};
GraphicsQuality last_frame_def_graphics_quality_{GraphicsQuality::kUnset}; bool floor_reflection_ : 1 {};
std::list<Object::Ref<PythonContextCall>> clean_frame_commands_; bool building_frame_def_ : 1 {};
std::vector<MeshData*> mesh_data_creates_; bool shadow_ortho_ : 1 {};
std::vector<MeshData*> mesh_data_destroys_; bool fetched_overlay_node_z_depth_ : 1 {};
microsecs_t last_create_frame_def_time_microsecs_{}; bool gyro_broken_ : 1 {};
millisecs_t last_create_frame_def_time_millisecs_{}; 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}; Vector3f shadow_offset_{0.0f, 0.0f, 0.0f};
Vector2f shadow_scale_{1.0f, 1.0f}; Vector2f shadow_scale_{1.0f, 1.0f};
Vector3f tint_{1.0f, 1.0f, 1.0f}; Vector3f tint_{1.0f, 1.0f, 1.0f};
Vector3f ambient_color_{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_outer_{0.0f, 0.0f, 0.0f};
Vector3f vignette_inner_{1.0f, 1.0f, 1.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 jitter_{0.0f, 0.0f, 0.0f};
Vector3f accel_smoothed_{0.0f, 0.0f, 0.0f}; Vector3f accel_smoothed_{0.0f, 0.0f, 0.0f};
Vector3f accel_smoothed2_{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_smoothed_{0.0f, 0.0f, 0.0f};
Vector3f tilt_vel_{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 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 gyro_mag_test_{};
float overlay_node_z_depth_{}; 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> screen_mesh_;
Object::Ref<ImageMesh> progress_bar_bottom_mesh_; Object::Ref<ImageMesh> progress_bar_bottom_mesh_;
Object::Ref<ImageMesh> progress_bar_top_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_mesh_;
Object::Ref<SpriteMesh> shadow_blotch_soft_mesh_; Object::Ref<SpriteMesh> shadow_blotch_soft_mesh_;
Object::Ref<SpriteMesh> shadow_blotch_soft_obj_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_; 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<PythonContextCall> fade_end_call_;
Object::Ref<Snapshot<GraphicsSettings>> settings_snapshot_;
Object::Ref<Snapshot<GraphicsClientContext>> client_context_snapshot_;
}; };
} // namespace ballistica::base } // 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 { auto GraphicsServer::TryRender() -> bool {
assert(g_base->app_adapter->InGraphicsContext()); assert(g_base->app_adapter->InGraphicsContext());
bool success{}; bool success{};
if (FrameDef* frame_def = WaitForRenderFrameDef_()) { if (FrameDef* frame_def = WaitForRenderFrameDef_()) {
// Apply settings such as tv-mode that were passed along via the // Apply any new graphics settings passed along via the frame-def.
// frame-def. ApplySettings(frame_def->settings());
ApplyFrameDefSettings(frame_def);
// Note: we run mesh-updates on each frame-def that comes through even // Note: we run mesh-updates on each frame-def that comes through even
// if we don't actually render the frame. // 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. // Send this frame_def back to the logic thread for deletion or recycling.
g_base->graphics->ReturnCompletedFrameDef(frame_def); g_base->graphics->ReturnCompletedFrameDef(frame_def);
} }
return success; return success;
} }
@ -113,11 +187,6 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
return nullptr; 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. // Runs any mesh updates contained in the frame-def.
void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) { void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) {
assert(g_base->app_adapter->InGraphicsContext()); assert(g_base->app_adapter->InGraphicsContext());
@ -250,14 +319,15 @@ void GraphicsServer::SetNullGraphics() {
SetTextureCompressionTypes(c_types); SetTextureCompressionTypes(c_types);
graphics_quality_requested_ = GraphicsQualityRequest::kLow; graphics_quality_requested_ = GraphicsQualityRequest::kLow;
graphics_quality_ = GraphicsQuality::kLow; graphics_quality_ = GraphicsQuality::kLow;
graphics_quality_set_ = true; // graphics_quality_set_ = true;
texture_quality_requested_ = TextureQualityRequest::kLow; texture_quality_requested_ = TextureQualityRequest::kLow;
texture_quality_ = TextureQuality::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). // Let the logic thread know screen creation is done (or lack thereof).
g_base->logic->event_loop()->PushCall( // g_base->logic->event_loop()->PushCall(
[] { g_base->logic->OnGraphicsReady(); }); // [] { g_base->logic->OnGraphicsReady(); });
} }
void GraphicsServer::set_renderer(Renderer* renderer) { void GraphicsServer::set_renderer(Renderer* renderer) {
@ -279,59 +349,43 @@ void GraphicsServer::LoadRenderer() {
return; return;
} }
switch (graphics_quality_requested_) { graphics_quality_ = Graphics::GraphicsQualityFromRequest(
case GraphicsQualityRequest::kLow: graphics_quality_requested_, renderer_->GetAutoGraphicsQuality());
graphics_quality_ = GraphicsQuality::kLow;
break; texture_quality_ = Graphics::TextureQualityFromRequest(
case GraphicsQualityRequest::kMedium: texture_quality_requested_, renderer_->GetAutoTextureQuality());
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;
}
// If we don't support high quality graphics, make sure we're no higher than // If we don't support high quality graphics, make sure we're no higher than
// medium. // medium.
BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value()); // BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value());
if (!g_base->graphics->supports_high_quality_graphics() // if (!g_base->graphics->supports_high_quality_graphics()
&& graphics_quality_ > GraphicsQuality::kMedium) { // && graphics_quality_ > GraphicsQuality::kMedium) {
graphics_quality_ = GraphicsQuality::kMedium; // graphics_quality_ = GraphicsQuality::kMedium;
} // }
graphics_quality_set_ = true; // graphics_quality_set_ = true;
// Update texture quality based on request. // Update texture quality based on request.
switch (texture_quality_requested_) { // switch (texture_quality_requested_) {
case TextureQualityRequest::kLow: // case TextureQualityRequest::kLow:
texture_quality_ = TextureQuality::kLow; // texture_quality_ = TextureQuality::kLow;
break; // break;
case TextureQualityRequest::kMedium: // case TextureQualityRequest::kMedium:
texture_quality_ = TextureQuality::kMedium; // texture_quality_ = TextureQuality::kMedium;
break; // break;
case TextureQualityRequest::kHigh: // case TextureQualityRequest::kHigh:
texture_quality_ = TextureQuality::kHigh; // texture_quality_ = TextureQuality::kHigh;
break; // break;
case TextureQualityRequest::kAuto: // case TextureQualityRequest::kAuto:
texture_quality_ = renderer_->GetAutoTextureQuality(); // texture_quality_ = renderer_->GetAutoTextureQuality();
break; // break;
default: // default:
Log(LogLevel::kError, // Log(LogLevel::kError,
"Unhandled TextureQualityRequest value: " // "Unhandled TextureQualityRequest value: "
+ std::to_string(static_cast<int>(texture_quality_requested_))); // +
texture_quality_ = TextureQuality::kLow; // std::to_string(static_cast<int>(texture_quality_requested_)));
} // texture_quality_ = TextureQuality::kLow;
texture_quality_set_ = true; // }
// texture_quality_set_ = true;
// Ok we've got our qualities figured out; now load/update the renderer. // Ok we've got our qualities figured out; now load/update the renderer.
renderer_->Load(); renderer_->Load();
@ -389,56 +443,56 @@ void GraphicsServer::UnloadRenderer() {
} }
// Given physical res, calculate virtual res. // Given physical res, calculate virtual res.
void GraphicsServer::CalcVirtualRes_(float* x, float* y) { // void GraphicsServer::CalcVirtualRes_(float* x, float* y) {
float x_in = *x; // float x_in = *x;
float y_in = *y; // float y_in = *y;
if (*x / *y > static_cast<float>(kBaseVirtualResX) // if (*x / *y > static_cast<float>(kBaseVirtualResX)
/ static_cast<float>(kBaseVirtualResY)) { // / static_cast<float>(kBaseVirtualResY)) {
*y = kBaseVirtualResY; // *y = kBaseVirtualResY;
*x = *y * (x_in / y_in); // *x = *y * (x_in / y_in);
} else { // } else {
*x = kBaseVirtualResX; // *x = kBaseVirtualResX;
*y = *x * (y_in / x_in); // *y = *x * (y_in / x_in);
} // }
} // }
void GraphicsServer::UpdateVirtualScreenRes_() { // void GraphicsServer::UpdateVirtualScreenRes_() {
assert(g_base->app_adapter->InGraphicsContext()); // assert(g_base->app_adapter->InGraphicsContext());
// In vr mode our virtual res is independent of our screen size. // // In vr mode our virtual res is independent of our screen size.
// (since it gets drawn to an overlay) // // (since it gets drawn to an overlay)
if (g_core->IsVRMode()) { // if (g_core->IsVRMode()) {
res_x_virtual_ = kBaseVirtualResX; // res_x_virtual_ = kBaseVirtualResX;
res_y_virtual_ = kBaseVirtualResY; // res_y_virtual_ = kBaseVirtualResY;
} else { // } else {
res_x_virtual_ = res_x_; // res_x_virtual_ = res_x_;
res_y_virtual_ = res_y_; // res_y_virtual_ = res_y_;
CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_); // CalcVirtualRes_(&res_x_virtual_, &res_y_virtual_);
} // }
} // }
void GraphicsServer::SetScreenResolution(float h, float v) { // void GraphicsServer::SetScreenResolution(float h, float v) {
assert(g_base->app_adapter->InGraphicsContext()); // assert(g_base->app_adapter->InGraphicsContext());
// Ignore redundant sets. // // Ignore redundant sets.
if (res_x_ == h && res_y_ == v) { // if (res_x_ == h && res_y_ == v) {
return; // return;
} // }
res_x_ = h; // res_x_ = h;
res_y_ = v; // res_y_ = v;
UpdateVirtualScreenRes_(); // // UpdateVirtualScreenRes_();
// Inform renderer of the change. // // Inform renderer of the change.
if (renderer_) { // if (renderer_) {
renderer_->OnScreenSizeChange(); // renderer_->OnScreenSizeChange();
} // }
// Inform all logic thread bits of this change. // // Inform all logic thread bits of this change.
g_base->logic->event_loop()->PushCall( // g_base->logic->event_loop()->PushCall(
[vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] { // [vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] {
g_base->graphics->SetScreenSize(vx, vy, x, y); // g_base->graphics->SetScreenSize(vx, vy, x, y);
}); // });
} // }
// FIXME: Shouldn't have android-specific code in here. // FIXME: Shouldn't have android-specific code in here.
void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) { void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) {
@ -579,15 +633,15 @@ void GraphicsServer::PushReloadMediaCall() {
g_base->app_adapter->PushGraphicsContextCall([this] { ReloadMedia_(); }); g_base->app_adapter->PushGraphicsContextCall([this] { ReloadMedia_(); });
} }
void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { // void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] { // g_base->app_adapter->PushGraphicsContextCall([this, pixel_scale] {
assert(g_base->app_adapter->InGraphicsContext()); // assert(g_base->app_adapter->InGraphicsContext());
if (!renderer_) { // if (!renderer_) {
return; // return;
} // }
renderer_->set_pixel_scale(pixel_scale); // renderer_->set_pixel_scale(pixel_scale);
}); // });
} // }
void GraphicsServer::PushComponentUnloadCall( void GraphicsServer::PushComponentUnloadCall(
const std::vector<Object::Ref<Asset>*>& components) { const std::vector<Object::Ref<Asset>*>& components) {

View File

@ -11,6 +11,7 @@
#include "ballistica/base/base.h" #include "ballistica/base/base.h"
#include "ballistica/shared/foundation/object.h" #include "ballistica/shared/foundation/object.h"
#include "ballistica/shared/generic/snapshot.h"
#include "ballistica/shared/math/matrix44f.h" #include "ballistica/shared/math/matrix44f.h"
namespace ballistica::base { namespace ballistica::base {
@ -51,16 +52,18 @@ class GraphicsServer {
return renderer_loaded_; return renderer_loaded_;
} }
void ApplySettings(const GraphicsSettings* settings);
/// The AppAdapter should call this to inform the engine of screen size /// 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 /// changes. Changes will be applied to the server and then sent to the
/// logic thread to apply to various app systems (ui, etc.). /// 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 /// Used by headless builds to init the graphics-server into a
/// non-functional state. /// non-functional state.
void SetNullGraphics(); void SetNullGraphics();
void PushSetScreenPixelScaleCall(float pixel_scale); // void PushSetScreenPixelScaleCall(float pixel_scale);
void PushReloadMediaCall(); void PushReloadMediaCall();
void PushRemoveRenderHoldCall(); void PushRemoveRenderHoldCall();
void PushComponentUnloadCall( void PushComponentUnloadCall(
@ -71,8 +74,6 @@ class GraphicsServer {
/// rendering. /// rendering.
void EnqueueFrameDef(FrameDef* framedef); void EnqueueFrameDef(FrameDef* framedef);
void ApplyFrameDefSettings(FrameDef* frame_def);
void RunFrameDefMeshUpdates(FrameDef* frame_def); void RunFrameDefMeshUpdates(FrameDef* frame_def);
// Renders shadow passes and other common parts of a frame_def. // Renders shadow passes and other common parts of a frame_def.
@ -108,9 +109,7 @@ class GraphicsServer {
projection_matrix_state_++; projection_matrix_state_++;
} }
auto projection_matrix_state() -> uint32_t { auto projection_matrix_state() { return projection_matrix_state_; }
return projection_matrix_state_;
}
void SetLightShadowProjectionMatrix(const Matrix44f& p) { void SetLightShadowProjectionMatrix(const Matrix44f& p) {
// This will generally get repeatedly set to the same value // 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_; 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 light_shadow_projection_matrix_;
} }
// Return the modelview * projection matrix. // Return the modelview * projection matrix.
auto GetModelViewProjectionMatrix() -> const Matrix44f& { const auto& GetModelViewProjectionMatrix() {
UpdateModelViewProjectionMatrix_(); UpdateModelViewProjectionMatrix_();
return model_view_projection_matrix_; return model_view_projection_matrix_;
} }
auto GetModelViewProjectionMatrixState() -> uint32_t { auto GetModelViewProjectionMatrixState() {
UpdateModelViewProjectionMatrix_(); UpdateModelViewProjectionMatrix_();
return model_view_projection_matrix_state_; return model_view_projection_matrix_state_;
} }
auto GetModelWorldMatrix() -> const Matrix44f& { const auto& GetModelWorldMatrix() {
UpdateModelWorldMatrix_(); UpdateModelWorldMatrix_();
return model_world_matrix_; return model_world_matrix_;
} }
auto GetModelWorldMatrixState() -> uint32_t { auto GetModelWorldMatrixState() {
UpdateModelWorldMatrix_(); UpdateModelWorldMatrix_();
return model_world_matrix_state_; 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_(); UpdateCamOrientMatrix_();
return cam_orient_matrix_; return cam_orient_matrix_;
} }
auto GetCamOrientMatrixState() -> uint32_t { auto GetCamOrientMatrixState() {
UpdateCamOrientMatrix_(); UpdateCamOrientMatrix_();
return cam_orient_matrix_state_; return cam_orient_matrix_state_;
} }
auto model_view_matrix() const -> const Matrix44f& { const auto& model_view_matrix() const { return model_view_matrix_; }
return model_view_matrix_;
}
void SetModelViewMatrix(const Matrix44f& m) { void SetModelViewMatrix(const Matrix44f& m) {
model_view_matrix_ = m; model_view_matrix_ = m;
model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true; model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true;
} }
auto projection_matrix() const -> const Matrix44f& { const auto& projection_matrix() const { return projection_matrix_; }
return projection_matrix_;
}
void PushTransform() { void PushTransform() {
model_view_stack_.push_back(model_view_matrix_); model_view_stack_.push_back(model_view_matrix_);
@ -209,32 +204,34 @@ class GraphicsServer {
model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true; model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true;
} }
auto quality() const -> GraphicsQuality { auto quality() const {
assert(graphics_quality_set_); assert(InGraphicsContext_());
assert(graphics_quality_ != GraphicsQuality::kUnset);
return graphics_quality_; return graphics_quality_;
} }
auto texture_quality() const -> TextureQuality { auto texture_quality() const {
assert(texture_quality_set_); assert(InGraphicsContext_());
assert(texture_quality_ != TextureQuality::kUnset);
return texture_quality_; return texture_quality_;
} }
auto screen_pixel_width() const -> float { auto screen_pixel_width() const {
assert(InGraphicsContext_()); assert(InGraphicsContext_());
return res_x_; return res_x_;
} }
auto screen_pixel_height() const -> float { auto screen_pixel_height() const {
assert(InGraphicsContext_()); assert(InGraphicsContext_());
return res_y_; return res_y_;
} }
auto screen_virtual_width() const -> float { auto screen_virtual_width() const {
assert(InGraphicsContext_()); assert(InGraphicsContext_());
return res_x_virtual_; return res_x_virtual_;
} }
auto screen_virtual_height() const -> float { auto screen_virtual_height() const {
assert(InGraphicsContext_()); assert(InGraphicsContext_());
return res_y_virtual_; return res_y_virtual_;
} }
@ -244,45 +241,69 @@ class GraphicsServer {
return tv_border_; 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 { auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool {
assert(InGraphicsContext_());
assert(texture_compression_types_set_); assert(texture_compression_types_set_);
return ((texture_compression_types_ & (0x01u << static_cast<uint32_t>(t))) return ((texture_compression_types_ & (0x01u << static_cast<uint32_t>(t)))
!= 0u); != 0u);
} }
void SetTextureCompressionTypes( void SetTextureCompressionTypes(
const std::list<TextureCompressionType>& types); const std::list<TextureCompressionType>& types);
auto texture_compression_types_are_set() const { // auto texture_compression_types_are_set() const {
return texture_compression_types_set_; // return texture_compression_types_set_;
} // }
void set_renderer_context_lost(bool lost) { renderer_context_lost_ = lost; } void set_renderer_context_lost(bool lost) { renderer_context_lost_ = lost; }
auto renderer_context_lost() const { return renderer_context_lost_; } auto renderer_context_lost() const { return renderer_context_lost_; }
auto graphics_quality_requested() const { auto graphics_quality_requested() const {
assert(InGraphicsContext_());
return graphics_quality_requested_; return graphics_quality_requested_;
} }
void set_graphics_quality_requested(GraphicsQualityRequest val) { void set_graphics_quality_requested(GraphicsQualityRequest val) {
assert(InGraphicsContext_());
graphics_quality_requested_ = val; graphics_quality_requested_ = val;
} }
void set_texture_quality_requested(TextureQualityRequest val) { void set_texture_quality_requested(TextureQualityRequest val) {
assert(InGraphicsContext_());
texture_quality_requested_ = val; 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); void HandlePushAndroidRes(const std::string& android_res);
auto texture_compression_types() const {
assert(texture_compression_types_set_);
return texture_compression_types_;
}
private: 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. // So we don't have to include app_adapter.h here for asserts.
auto InGraphicsContext_() const -> bool; auto InGraphicsContext_() const -> bool;
@ -293,8 +314,8 @@ class GraphicsServer {
auto WaitForRenderFrameDef_() -> FrameDef*; auto WaitForRenderFrameDef_() -> FrameDef*;
// Update virtual screen dimensions based on the current physical ones. // Update virtual screen dimensions based on the current physical ones.
static void CalcVirtualRes_(float* x, float* y); // static void CalcVirtualRes_(float* x, float* y);
void UpdateVirtualScreenRes_(); // void UpdateVirtualScreenRes_();
void UpdateCamOrientMatrix_(); void UpdateCamOrientMatrix_();
void ReloadMedia_(); void ReloadMedia_();
void UpdateModelViewProjectionMatrix_() { void UpdateModelViewProjectionMatrix_() {
@ -314,23 +335,17 @@ class GraphicsServer {
} }
bool renderer_loaded_ : 1 {}; bool renderer_loaded_ : 1 {};
bool v_sync_ : 1 {};
bool auto_vsync_ : 1 {};
bool model_view_projection_matrix_dirty_ : 1 {true}; bool model_view_projection_matrix_dirty_ : 1 {true};
bool model_world_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 tv_border_ : 1 {};
bool renderer_context_lost_ : 1 {}; bool renderer_context_lost_ : 1 {};
bool texture_compression_types_set_ : 1 {}; bool texture_compression_types_set_ : 1 {};
bool cam_orient_matrix_dirty_ : 1 {true}; bool cam_orient_matrix_dirty_ : 1 {true};
TextureQualityRequest texture_quality_requested_{ Snapshot<GraphicsClientContext>* client_context_{};
TextureQualityRequest::kUnset}; TextureQualityRequest texture_quality_requested_{};
TextureQuality texture_quality_{TextureQuality::kLow}; TextureQuality texture_quality_{};
GraphicsQualityRequest graphics_quality_requested_{ GraphicsQualityRequest graphics_quality_requested_{};
GraphicsQualityRequest::kUnset}; GraphicsQuality graphics_quality_{};
GraphicsQuality graphics_quality_{GraphicsQuality::kUnset};
int render_hold_{};
float res_x_{}; float res_x_{};
float res_y_{}; float res_y_{};
float res_x_virtual_{}; float res_x_virtual_{};
@ -340,20 +355,21 @@ class GraphicsServer {
Matrix44f projection_matrix_{kMatrix44fIdentity}; Matrix44f projection_matrix_{kMatrix44fIdentity};
Matrix44f model_view_projection_matrix_{kMatrix44fIdentity}; Matrix44f model_view_projection_matrix_{kMatrix44fIdentity};
Matrix44f model_world_matrix_{kMatrix44fIdentity}; Matrix44f model_world_matrix_{kMatrix44fIdentity};
std::vector<Matrix44f> model_view_stack_;
uint32_t texture_compression_types_{}; uint32_t texture_compression_types_{};
uint32_t projection_matrix_state_{1}; int render_hold_{};
uint32_t model_view_projection_matrix_state_{1}; int projection_matrix_state_{};
uint32_t model_world_matrix_state_{1}; int model_view_projection_matrix_state_{};
uint32_t light_shadow_projection_matrix_state_{1}; int model_world_matrix_state_{};
uint32_t cam_pos_state_{1}; int light_shadow_projection_matrix_state_{};
uint32_t cam_orient_matrix_state_{1}; int cam_pos_state_{};
int cam_orient_matrix_state_{};
int settings_index_{-1};
Vector3f cam_pos_{0.0f, 0.0f, 0.0f}; Vector3f cam_pos_{0.0f, 0.0f, 0.0f};
Vector3f cam_target_{0.0f, 0.0f, 0.0f}; Vector3f cam_target_{0.0f, 0.0f, 0.0f};
Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity}; Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity};
Matrix44f cam_orient_matrix_ = kMatrix44fIdentity; Matrix44f cam_orient_matrix_ = kMatrix44fIdentity;
std::vector<Matrix44f> model_view_stack_;
std::list<MeshData*> mesh_datas_; std::list<MeshData*> mesh_datas_;
Timer* render_timer_{};
Renderer* renderer_{}; Renderer* renderer_{};
FrameDef* frame_def_{}; FrameDef* frame_def_{};
std::mutex frame_def_mutex_{}; 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_); g_base->graphics_server->SetProjectionMatrix(projection_matrix_);
} }
void RenderPass::Finalize() { void RenderPass::Complete() {
if (UsesWorldLists()) { if (UsesWorldLists()) {
for (auto& command : commands_) { for (auto& command : commands_) {
command->Finalize(); command->Finalize();

View File

@ -112,7 +112,7 @@ class RenderPass {
return model_view_projection_matrix_; return model_view_projection_matrix_;
} }
auto HasDrawCommands() const -> bool; auto HasDrawCommands() const -> bool;
void Finalize(); void Complete();
void Reset(); void Reset();
// Whether this pass draws stuff from the per-shader command lists // 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 // If this frame_def was made in a different quality mode than we're
// currently in, don't attempt to render it. // currently in, don't attempt to render it.
if (frame_def->quality() != g_base->graphics_server->quality()) { // UPDATE - scratch that; we now set our quality FROM the frame def.
frame_def->set_rendering(false); // if (frame_def->quality() != g_base->graphics_server->quality()) {
return; // frame_def->set_rendering(false);
} // return;
// }
frame_def->set_rendering(true); frame_def->set_rendering(true);
// Some VR environments muck with render states before/after // 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_pitch() const -> float { return light_pitch_; }
auto light_heading() const -> float { return light_heading_; } auto light_heading() const -> float { return light_heading_; }
void set_pixel_scale(float s) { pixel_scale_requested_ = s; } 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; } void set_debug_draw_mode(bool debugModeIn) { debug_draw_mode_ = debugModeIn; }
auto debug_draw_mode() -> bool { return debug_draw_mode_; } 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_outer_{0.0f, 0.0f, 0.0f};
Vector3f vignette_inner_{1.0f, 1.0f, 1.0f}; Vector3f vignette_inner_{1.0f, 1.0f, 1.0f};
int shadow_res_{-1}; int shadow_res_{-1};
// float screen_gamma_requested_{1.0f};
float screen_gamma_{1.0f}; float screen_gamma_{1.0f};
float pixel_scale_requested_{1.0f}; float pixel_scale_requested_{1.0f};
float pixel_scale_{1.0f}; float pixel_scale_{1.0f};

View File

@ -1013,7 +1013,7 @@ void Camera::ApplyToFrameDef(FrameDef* frame_def) {
up_, 4, 1000.0f, up_, 4, 1000.0f,
-1.0f, // Auto x fov. -1.0f, // Auto x fov.
final_fov_y 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. false, 0, 0, 0, 0, // Not using tangent fovs.
area_of_interest_points_); area_of_interest_points_);
} }

View File

@ -49,6 +49,13 @@ auto FrameDef::GetOverlayFlatPass() -> RenderPass* {
void FrameDef::Reset() { void FrameDef::Reset() {
assert(g_base->InLogicThread()); 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; app_time_microsecs_ = 0;
display_time_microsecs_ = 0; display_time_microsecs_ = 0;
display_time_elapsed_microsecs_ = 0; display_time_elapsed_microsecs_ = 0;
@ -68,11 +75,17 @@ void FrameDef::Reset() {
mesh_index_sizes_.clear(); mesh_index_sizes_.clear();
mesh_buffers_.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); 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_offset_ = g_base->graphics->shadow_offset();
shadow_scale_ = g_base->graphics->shadow_scale(); shadow_scale_ = g_base->graphics->shadow_scale();
@ -99,21 +112,21 @@ void FrameDef::Reset() {
beauty_pass_->set_floor_reflection(g_base->graphics->floor_reflection()); beauty_pass_->set_floor_reflection(g_base->graphics->floor_reflection());
} }
void FrameDef::Finalize() { void FrameDef::Complete() {
assert(!defining_component_); assert(!defining_component_);
light_pass_->Finalize(); light_pass_->Complete();
light_shadow_pass_->Finalize(); light_shadow_pass_->Complete();
beauty_pass_->Finalize(); beauty_pass_->Complete();
beauty_pass_bg_->Finalize(); beauty_pass_bg_->Complete();
overlay_pass_->Finalize(); overlay_pass_->Complete();
overlay_front_pass_->Finalize(); overlay_front_pass_->Complete();
if (g_core->IsVRMode()) { if (g_core->IsVRMode()) {
overlay_fixed_pass_->Finalize(); overlay_fixed_pass_->Complete();
overlay_flat_pass_->Finalize(); overlay_flat_pass_->Complete();
vr_cover_pass_->Finalize(); vr_cover_pass_->Complete();
} }
overlay_3d_pass_->Finalize(); overlay_3d_pass_->Complete();
blit_pass_->Finalize(); blit_pass_->Complete();
} }
void FrameDef::AddMesh(Mesh* mesh) { void FrameDef::AddMesh(Mesh* mesh) {

View File

@ -7,13 +7,14 @@
#include <vector> #include <vector>
#include "ballistica/base/assets/asset.h" #include "ballistica/base/assets/asset.h"
#include "ballistica/shared/generic/snapshot.h"
#include "ballistica/shared/math/matrix44f.h" #include "ballistica/shared/math/matrix44f.h"
#include "ballistica/shared/math/vector2f.h" #include "ballistica/shared/math/vector2f.h"
namespace ballistica::base { namespace ballistica::base {
/// A flattened representation of a frame; generated by the logic thread and /// 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 { class FrameDef {
public: public:
auto light_pass() -> RenderPass* { return light_pass_.get(); } 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 // A number incremented for each frame renderered. Note that graphics code
// should not plug this directly into things like flash calculations since // 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 // different than a 240hz flash. Use frame_number_filtered() for such
// purposes. // purposes.
auto frame_number() const { return frame_number_; } auto frame_number() const { return frame_number_; }
// A number incremented for each frame rendered, but a maximum of 60 times // 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 // per second. Code for drawing flashes or other crisp blink-y effects
// this value instead of regular frame_number so that things don't turn // should use this value instead of regular frame_number so that things
// muddy at extremely high frame rates. // don't turn muddy at extremely high frame rates.
auto frame_number_filtered() const { return frame_number_filtered_; } auto frame_number_filtered() const { return frame_number_filtered_; }
// Returns the display-time this frame-def was created at (tries to match // 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 { auto display_time_millisecs() const -> millisecs_t {
return display_time_microsecs_ / 1000; return display_time_microsecs_ / 1000;
} }
auto display_time_microsecs() const -> microsecs_t { auto display_time_microsecs() const -> microsecs_t {
return display_time_microsecs_; 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 { auto display_time_elapsed_millisecs() const -> millisecs_t {
return display_time_elapsed_millisecs_; return display_time_elapsed_millisecs_;
} }
@ -82,7 +88,9 @@ class FrameDef {
return display_time_elapsed_microsecs_; 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 orbiting() const -> bool { return orbiting_; }
auto shadow_offset() const -> const Vector3f& { return shadow_offset_; } auto shadow_offset() const -> const Vector3f& { return shadow_offset_; }
auto shadow_scale() const -> const Vector2f& { return shadow_scale_; } auto shadow_scale() const -> const Vector2f& { return shadow_scale_; }
@ -115,11 +123,12 @@ class FrameDef {
vr_overlay_screen_matrix_fixed_ = mat; vr_overlay_screen_matrix_fixed_ = mat;
} }
// Effects requiring availability of a depth texture should // Effects requiring availability of a depth texture should check this to
// check this to determine whether they should draw. // determine whether they should draw.
auto has_depth_texture() const -> bool { auto HasDepthTexture() const -> bool {
return (quality_ >= GraphicsQuality::kHigh); return (quality_ >= GraphicsQuality::kHigh);
} }
void AddComponent(const Object::Ref<Asset>& component) { void AddComponent(const Object::Ref<Asset>& component) {
// Add a reference to this component only if we havn't yet. // Add a reference to this component only if we havn't yet.
if (component->last_frame_def_num() != frame_number_) { if (component->last_frame_def_num() != frame_number_) {
@ -134,7 +143,7 @@ class FrameDef {
FrameDef(); FrameDef();
~FrameDef(); ~FrameDef();
void Reset(); void Reset();
void Finalize(); void Complete();
void set_display_time_elapsed_microsecs(microsecs_t val) { void set_display_time_elapsed_microsecs(microsecs_t val) {
display_time_elapsed_microsecs_ = val; display_time_elapsed_microsecs_ = val;
@ -187,7 +196,7 @@ class FrameDef {
auto media_components() const -> const std::vector<Object::Ref<Asset>>& { auto media_components() const -> const std::vector<Object::Ref<Asset>>& {
return media_components_; 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_camera_mode(CameraMode val) { camera_mode_ = val; }
void set_rendering(bool val) { rendering_ = val; } void set_rendering(bool val) { rendering_ = val; }
@ -204,18 +213,27 @@ class FrameDef {
} }
#endif #endif
// auto pixel_scale() const { return pixel_scale_; }
auto* settings() const {
assert(settings_snapshot_.Exists());
return settings_snapshot_->Get();
}
private: private:
bool needs_clear_{}; Object::Ref<Snapshot<GraphicsSettings>> settings_snapshot_;
bool rendering_{}; bool needs_clear_ : 1 {};
bool orbiting_{}; bool rendering_ : 1 {};
bool tv_border_{}; bool orbiting_ : 1 {};
bool shadow_ortho_{}; // bool tv_border_ : 1 {};
bool shadow_ortho_ : 1 {};
BenchmarkType benchmark_type_{BenchmarkType::kNone}; BenchmarkType benchmark_type_{BenchmarkType::kNone};
CameraMode camera_mode_{CameraMode::kFollow}; CameraMode camera_mode_{CameraMode::kFollow};
Vector3f cam_original_{0.0f, 0.0f, 0.0f}; Vector3f cam_original_{0.0f, 0.0f, 0.0f};
Vector3f cam_target_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}; Vector3f shake_original_{0.0f, 0.0f, 0.0f};
float vr_near_clip_{}; float vr_near_clip_{};
// float pixel_scale_{};
Matrix44f vr_overlay_screen_matrix_ = kMatrix44fIdentity; Matrix44f vr_overlay_screen_matrix_ = kMatrix44fIdentity;
Matrix44f vr_overlay_screen_matrix_fixed_ = kMatrix44fIdentity; Matrix44f vr_overlay_screen_matrix_fixed_ = kMatrix44fIdentity;
std::vector<MeshData*> mesh_data_creates_; std::vector<MeshData*> mesh_data_creates_;
@ -245,7 +263,8 @@ class FrameDef {
std::unique_ptr<RenderPass> vr_cover_pass_; std::unique_ptr<RenderPass> vr_cover_pass_;
std::unique_ptr<RenderPass> overlay_3d_pass_; std::unique_ptr<RenderPass> overlay_3d_pass_;
std::unique_ptr<RenderPass> blit_pass_; std::unique_ptr<RenderPass> blit_pass_;
GraphicsQuality quality_{GraphicsQuality::kLow}; GraphicsQuality quality_{};
TextureQuality texture_quality_{};
microsecs_t app_time_microsecs_{}; microsecs_t app_time_microsecs_{};
microsecs_t display_time_microsecs_{}; microsecs_t display_time_microsecs_{};
microsecs_t display_time_elapsed_microsecs_{}; microsecs_t display_time_elapsed_microsecs_{};

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 // For the first announcement just say "X controllers detected" and don't
// have a sound. // have a sound.
if (first_print && g_core->GetAppTimeMillisecs() < 10000) { if (first_print && g_core->GetAppTimeSeconds() < 5.0) {
first_print = false; first_print = false;
// Disabling this completely for now; being more lenient with devices // Disabling this completely on Android for now; we often get large
// allowed on Android means this will often come back with large // numbers of devices there that aren't actually devices.
// numbers.
bool do_print{false}; bool do_print_initial_counts{!g_buildconfig.ostype_android()};
// If there's been several connected, just give a number. // 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) { if (newly_connected_controllers_.size() > 1) {
std::string s = std::string s =
g_base->assets->GetResourceString("controllersDetectedText"); g_base->assets->GetResourceString("controllersDetectedText");

View File

@ -76,11 +76,11 @@ void Logic::OnAppStart() {
void Logic::OnGraphicsReady() { void Logic::OnGraphicsReady() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
if (on_initial_screen_creation_complete_called_) { if (graphics_ready_) {
// Only want to fire this logic the first time. // Only want to fire this logic the first time.
return; 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 // 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 // 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_ #define BALLISTICA_BASE_LOGIC_LOGIC_H_
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include "ballistica/shared/foundation/object.h" #include "ballistica/shared/foundation/object.h"
@ -118,6 +117,8 @@ class Logic {
auto shutting_down() const { return shutting_down_; } auto shutting_down() const { return shutting_down_; }
auto shutdown_completed() const { return shutdown_completed_; } auto shutdown_completed() const { return shutdown_completed_; }
auto graphics_ready() const { return graphics_ready_; }
private: private:
void UpdateDisplayTimeForFrameDraw_(); void UpdateDisplayTimeForFrameDraw_();
void UpdateDisplayTimeForHeadlessMode_(); void UpdateDisplayTimeForHeadlessMode_();
@ -127,31 +128,31 @@ class Logic {
void UpdatePendingWorkTimer_(); void UpdatePendingWorkTimer_();
void StepDisplayTime_(); void StepDisplayTime_();
double display_time_{}; seconds_t display_time_{};
double display_time_increment_{1.0 / 60.0}; seconds_t display_time_increment_{1.0 / 60.0};
microsecs_t display_time_microsecs_{}; microsecs_t display_time_microsecs_{};
microsecs_t display_time_increment_microsecs_{1000000 / 60}; 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. // Headless scheduling.
Timer* headless_display_time_step_timer_{}; Timer* headless_display_time_step_timer_{};
Timer* process_pending_work_timer_{}; // GUI scheduling.
Timer* asset_prune_timer_{}; seconds_t last_display_time_update_app_time_{-1.0};
Timer* debug_timer_{}; seconds_t recent_display_time_increments_[kDisplayTimeSampleCount]{};
EventLoop* event_loop_{}; int recent_display_time_increments_index_{-1};
std::unique_ptr<TimerList> display_timers_;
bool app_bootstrapping_complete_ : 1 {}; bool app_bootstrapping_complete_ : 1 {};
bool have_pending_loads_ : 1 {}; bool have_pending_loads_ : 1 {};
bool debug_log_display_time_ : 1 {}; bool debug_log_display_time_ : 1 {};
bool applied_app_config_ : 1 {}; bool applied_app_config_ : 1 {};
bool shutting_down_ : 1 {}; bool shutting_down_ : 1 {};
bool shutdown_completed_ : 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 } // namespace ballistica::base

View File

@ -4,6 +4,7 @@
#include "ballistica/base/platform/apple/base_platform_apple.h" #include "ballistica/base/platform/apple/base_platform_apple.h"
#if BA_XCODE_BUILD #if BA_XCODE_BUILD
#include <BallisticaKit-Swift.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <uuid/uuid.h> #include <uuid/uuid.h>
@ -53,9 +54,14 @@ void BasePlatformApple::PurchaseAck(const std::string& purchase,
void BasePlatformApple::DoOpenURL(const std::string& url) { void BasePlatformApple::DoOpenURL(const std::string& url) {
#if BA_XCODE_BUILD #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 // Go ahead and do this ourself. Though perhaps the default
// Python path would be fine. // Python path would be fine.
AppleUtils::OpenURL(url.c_str()); // AppleUtils::OpenURL(url.c_str());
#else #else
// Otherwise go with the default (Python webbrowser module). // Otherwise go with the default (Python webbrowser module).
BasePlatform::DoOpenURL(url); BasePlatform::DoOpenURL(url);

View File

@ -810,9 +810,9 @@ static PyMethodDef PySetStressTestingDef = {
"(internal)", "(internal)",
}; };
// ------------------------------ display_log ---------------------------------- // -------------------------------- emit_log -----------------------------------
static auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds) static auto PyEmitLog(PyObject* self, PyObject* args, PyObject* keywds)
-> PyObject* { -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
static const char* kwlist[] = {"name", "level", "message", nullptr}; static const char* kwlist[] = {"name", "level", "message", nullptr};
@ -839,25 +839,25 @@ static auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds)
level = LogLevel::kCritical; level = LogLevel::kCritical;
} else { } else {
// Assume we should avoid Log() calls here since it could infinite loop. // 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; level = LogLevel::kInfo;
} }
Logging::DisplayLog(name, level, message); Logging::EmitLog(name, level, message);
Py_RETURN_NONE; Py_RETURN_NONE;
BA_PYTHON_CATCH; BA_PYTHON_CATCH;
} }
static PyMethodDef PyDisplayLogDef = { static PyMethodDef PyEmitLogDef = {
"display_log", // name "emit_log", // name
(PyCFunction)PyDisplayLog, // method (PyCFunction)PyEmitLog, // method
METH_VARARGS | METH_KEYWORDS, // flags 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" "\n"
"(internal)\n" "(internal)\n"
"\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" "log destinations (Android log, etc.). This generally is not called\n"
"directly and should instead be fed Python logging output.", "directly and should instead be fed Python logging output.",
}; };
@ -1654,7 +1654,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyAppNameUpperDef, PyAppNameUpperDef,
PyIsXCodeBuildDef, PyIsXCodeBuildDef,
PyCanDisplayFullUnicodeDef, PyCanDisplayFullUnicodeDef,
PyDisplayLogDef, PyEmitLogDef,
PyV1CloudLogDef, PyV1CloudLogDef,
PySetStressTestingDef, PySetStressTestingDef,
PyEnvDef, PyEnvDef,

View File

@ -348,13 +348,14 @@ static PyMethodDef PySafeColorDef = {
static auto PyGetMaxGraphicsQuality(PyObject* self) -> PyObject* { static auto PyGetMaxGraphicsQuality(PyObject* self) -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
if (g_base->graphics // if (g_base->graphics
&& g_base->graphics->has_supports_high_quality_graphics_value() // && g_base->graphics->has_supports_high_quality_graphics_value()
&& g_base->graphics->supports_high_quality_graphics()) { // && g_base->graphics->supports_high_quality_graphics()) {
return Py_BuildValue("s", "High"); // return Py_BuildValue("s", "High");
} else { // } else {
return Py_BuildValue("s", "Medium"); // return Py_BuildValue("s", "Medium");
} // }
return Py_BuildValue("s", "Higher");
BA_PYTHON_CATCH; BA_PYTHON_CATCH;
} }

View File

@ -147,17 +147,17 @@ auto CorePlatformApple::IsRunningOnDesktop() -> bool {
#endif #endif
} }
void CorePlatformApple::DisplayLog(const std::string& name, LogLevel level, void CorePlatformApple::EmitPlatformLog(const std::string& name, LogLevel level,
const std::string& msg) { const std::string& msg) {
#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD #if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
// HMM: do we want to use proper logging APIs here or simple printing? // HMM: do we want to use proper logging APIs here or simple printing?
// base::AppleUtils::NSLogStr(msg); // base::AppleUtils::NSLogStr(msg);
CorePlatform::DisplayLog(name, level, msg); CorePlatform::EmitPlatformLog(name, level, msg);
#else #else
// Fall back to default handler... // Fall back to default handler...
CorePlatform::DisplayLog(name, level, msg); CorePlatform::EmitPlatformLog(name, level, msg);
#endif #endif
} }

View File

@ -26,8 +26,8 @@ class CorePlatformApple : public CorePlatform {
auto DoHasTouchScreen() -> bool override; auto DoHasTouchScreen() -> bool override;
auto GetDefaultUIScale() -> UIScale override; auto GetDefaultUIScale() -> UIScale override;
auto IsRunningOnDesktop() -> bool override; auto IsRunningOnDesktop() -> bool override;
void DisplayLog(const std::string& name, LogLevel level, void EmitPlatformLog(const std::string& name, LogLevel level,
const std::string& msg) override; const std::string& msg) override;
void GetTextBoundsAndWidth(const std::string& text, Rect* r, void GetTextBoundsAndWidth(const std::string& text, Rect* r,
float* width) override; float* width) override;
void FreeTextTexture(void* tex) override; void FreeTextTexture(void* tex) override;

View File

@ -465,12 +465,17 @@ auto CorePlatform::IsRunningOnDesktop() -> bool {
return true; return true;
} }
void CorePlatform::SleepMillisecs(millisecs_t ms) { void CorePlatform::SleepSeconds(seconds_t duration) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms)); std::this_thread::sleep_for(
std::chrono::microseconds(static_cast<microsecs_t>(duration * 1000000)));
} }
void CorePlatform::SleepMicrosecs(millisecs_t ms) { void CorePlatform::SleepMillisecs(millisecs_t duration) {
std::this_thread::sleep_for(std::chrono::microseconds(ms)); 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 #pragma clang diagnostic push
@ -481,8 +486,8 @@ auto CorePlatform::GetDefaultUIScale() -> UIScale {
return UIScale::kLarge; return UIScale::kLarge;
} }
void CorePlatform::DisplayLog(const std::string& name, LogLevel level, void CorePlatform::EmitPlatformLog(const std::string& name, LogLevel level,
const std::string& msg) { const std::string& msg) {
// Do nothing by default. // Do nothing by default.
} }

View File

@ -99,8 +99,8 @@ class CorePlatform {
/// Display a message to any default log for the platform (android log, /// Display a message to any default log for the platform (android log,
/// etc.) Note that this can be called from any thread. Default /// etc.) Note that this can be called from any thread. Default
/// implementation does nothing. /// implementation does nothing.
virtual void DisplayLog(const std::string& name, LogLevel level, virtual void EmitPlatformLog(const std::string& name, LogLevel level,
const std::string& msg); const std::string& msg);
#pragma mark ENVIRONMENT ------------------------------------------------------- #pragma mark ENVIRONMENT -------------------------------------------------------
@ -381,9 +381,9 @@ class CorePlatform {
/// to not go backwards. /// to not go backwards.
static auto GetCurrentWholeSeconds() -> int64_t; static auto GetCurrentWholeSeconds() -> int64_t;
static void SleepMillisecs(millisecs_t ms); static void SleepSeconds(seconds_t duration);
static void SleepMillisecs(millisecs_t duration);
static void SleepMicrosecs(microsecs_t ms); static void SleepMicrosecs(microsecs_t duration);
/// Given a C++ symbol, attempt to return a pretty one. /// Given a C++ symbol, attempt to return a pretty one.
virtual auto DemangleCXXSymbol(const std::string& s) -> std::string; virtual auto DemangleCXXSymbol(const std::string& s) -> std::string;

View File

@ -827,11 +827,12 @@ std::string CorePlatformWindows::DoGetDeviceName() {
bool CorePlatformWindows::DoHasTouchScreen() { return false; } bool CorePlatformWindows::DoHasTouchScreen() { return false; }
void CorePlatformWindows::DisplayLog(const std::string& name, LogLevel level, void CorePlatformWindows::EmitPlatformLog(const std::string& name,
const std::string& msg) { LogLevel level,
const std::string& msg) {
// if (have_stdin_stdout_) { // if (have_stdin_stdout_) {
// // On headless builds we use default handler (simple 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. // 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 GetLocale() -> std::string override;
auto DoGetDeviceName() -> std::string override; auto DoGetDeviceName() -> std::string override;
auto DoHasTouchScreen() -> bool override; auto DoHasTouchScreen() -> bool override;
void DisplayLog(const std::string& name, LogLevel level, void EmitPlatformLog(const std::string& name, LogLevel level,
const std::string& msg) override; const std::string& msg) override;
void SetEnv(const std::string& name, const std::string& value) override; void SetEnv(const std::string& name, const std::string& value) override;
auto GetEnv(const std::string& name) -> std::optional<std::string> override; auto GetEnv(const std::string& name) -> std::optional<std::string> override;
auto GetIsStdinATerminal() -> bool 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" "CorePython::LoggingCall() called before Python"
" logging available."}; " logging available."};
if (g_core->platform) { if (g_core->platform) {
g_core->platform->DisplayLog("root", LogLevel::kError, errmsg); g_core->platform->EmitPlatformLog("root", LogLevel::kError, errmsg);
g_core->platform->DisplayLog("root", loglevel, msg); g_core->platform->EmitPlatformLog("root", loglevel, msg);
} }
fprintf(stderr, "%s\n%s\n", errmsg, msg.c_str()); 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.) /// Calls Python logging function (logging.error, logging.warning, etc.)
/// Can be called from any thread at any time. If called before Python /// 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). /// (with an added warning).
void LoggingCall(LogLevel loglevel, const std::string& msg); void LoggingCall(LogLevel loglevel, const std::string& msg);
void ImportPythonObjs(); void ImportPythonObjs();

View File

@ -12,11 +12,12 @@
namespace ballistica::scene_v1 { namespace ballistica::scene_v1 {
// Stores info about an occurring collision. /// 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 /// Note than just because a collision exists between two parts doesn't mean
// determine what behavior, if any, exists between two parts which are currently /// they're physically colliding in the simulation. It is just a shortcut to
// overlapping in the simulation. /// determine what behavior, if any, exists between two parts which are
/// currently overlapping in the simulation.
class Collision : public Object { class Collision : public Object {
public: public:
explicit Collision(Scene* scene) : src_context(scene), dst_context(scene) {} 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 // Stores info about a collision needing a reset
// (used when parts change materials). // (used when parts change materials).
class Dynamics::CollisionReset { class Dynamics::CollisionReset_ {
public: public:
int node1; int node1;
int node2; int node2;
int part1; int part1;
int part2; 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) {} : node1(node1_in), node2(node2_in), part1(part1_in), part2(part2_in) {}
}; };
class Dynamics::CollisionEvent { class Dynamics::CollisionEvent_ {
public: public:
Object::Ref<MaterialAction> action; Object::Ref<MaterialAction> action;
Object::Ref<Collision> collision; Object::Ref<Collision> collision;
Object::WeakRef<Node> node1; // first event node Object::WeakRef<Node> node1; // first event node
Object::WeakRef<Node> node2; // second event node Object::WeakRef<Node> node2; // second event node
CollisionEvent(Node* node1_in, Node* node2_in, CollisionEvent_(Node* node1_in, Node* node2_in,
const Object::Ref<MaterialAction>& action_in, const Object::Ref<MaterialAction>& action_in,
const Object::Ref<Collision>& collision_in) const Object::Ref<Collision>& collision_in)
: node1(node1_in), : node1(node1_in),
node2(node2_in), node2(node2_in),
action(action_in), action(action_in),
collision(collision_in) {} collision(collision_in) {}
}; };
class Dynamics::SrcPartCollideMap { class Dynamics::SrcPartCollideMap_ {
public: public:
std::unordered_map<int, Object::Ref<Collision> > dst_part_collisions; std::unordered_map<int, Object::Ref<Collision> > dst_part_collisions;
}; };
class Dynamics::DstNodeCollideMap { class Dynamics::DstNodeCollideMap_ {
public: public:
std::unordered_map<int, SrcPartCollideMap> src_parts; std::unordered_map<int, SrcPartCollideMap_> src_parts;
int collideDisabled; int collideDisabled;
DstNodeCollideMap() : collideDisabled(0) {} DstNodeCollideMap_() : collideDisabled(0) {}
~DstNodeCollideMap() = default; ~DstNodeCollideMap_() = default;
}; };
class Dynamics::SrcNodeCollideMap { class Dynamics::SrcNodeCollideMap_ {
public: public:
std::unordered_map<int64_t, DstNodeCollideMap> dst_nodes; std::unordered_map<int64_t, DstNodeCollideMap_> dst_nodes;
}; };
class Dynamics::Impl { class Dynamics::Impl_ {
public: 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 // NOTE: we need to implement this here in an Impl class because
// gcc currently chokes on unordered_maps with forward-declared types, // 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 // so we can't have this in our header without pushing all our map/collision
// types there too. // types there too.
void HandleDisconnect( void HandleDisconnect(
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap>::iterator& const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap_>::iterator&
i, i,
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap>::iterator& const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap_>::iterator&
j, 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); const std::unordered_map<int, Object::Ref<Collision> >::iterator& l);
private: private:
Dynamics* dynamics_{}; Dynamics* dynamics_{};
// Contains in-progress collisions for current nodes. // 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; friend class Dynamics;
}; };
Dynamics::Dynamics(Scene* scene_in) Dynamics::Dynamics(Scene* scene_in)
: scene_(scene_in), : scene_(scene_in),
collision_cache_(new base::CollisionCache()), collision_cache_(new base::CollisionCache()),
impl_(std::make_unique<Impl>(this)) { impl_(std::make_unique<Impl_>(this)) {
ResetODE(); ResetODE_();
} }
Dynamics::~Dynamics() { Dynamics::~Dynamics() {
@ -145,7 +145,7 @@ Dynamics::~Dynamics() {
"Dynamics going down within Process() call;" "Dynamics going down within Process() call;"
" should not happen."); " should not happen.");
} }
ShutdownODE(); ShutdownODE_();
} }
void Dynamics::Draw(base::FrameDef* frame_def) { void Dynamics::Draw(base::FrameDef* frame_def) {
@ -203,7 +203,7 @@ void Dynamics::RemoveTrimesh(dGeomID g) {
throw Exception("trimesh not found"); 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* p1;
const Part* p2; const Part* p2;
if (IsInStoreOrder(p1_in.node()->id(), p1_in.id(), p2_in.node()->id(), 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); p2->ApplyMaterials(*cc2, p2, p1);
// If either disabled collisions between these two nodes, store that. // 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()]; &impl_->node_collisions_[p1->node()->id()].dst_nodes[p2->node()->id()];
if (!(*cc1)->node_collide || !(*cc2)->node_collide) { if (!(*cc1)->node_collide || !(*cc2)->node_collide) {
dncm->collideDisabled = true; dncm->collideDisabled = true;
@ -319,10 +319,12 @@ auto Dynamics::GetCollision(Part* p1_in, Part* p2_in, MaterialContext** cc1,
return &(*(i.first->second)); return &(*(i.first->second));
} }
void Dynamics::Impl::HandleDisconnect( void Dynamics::Impl_::HandleDisconnect(
const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap>::iterator& i, const std::unordered_map<int64_t, Dynamics::SrcNodeCollideMap_>::iterator&
const std::unordered_map<int64_t, Dynamics::DstNodeCollideMap>::iterator& j, i,
const std::unordered_map<int, SrcPartCollideMap>::iterator& k, 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) { const std::unordered_map<int, Object::Ref<Collision> >::iterator& l) {
// Handle disconnect equivalents if they were colliding. // Handle disconnect equivalents if they were colliding.
if (l->second->collide) { if (l->second->collide) {
@ -367,7 +369,7 @@ void Dynamics::Impl::HandleDisconnect(
k->second.dst_part_collisions.erase(l); k->second.dst_part_collisions.erase(l);
} }
void Dynamics::ProcessCollisions() { void Dynamics::ProcessCollision_() {
processing_collisions_ = true; processing_collisions_ = true;
collision_count_ = 0; collision_count_ = 0;
@ -441,10 +443,10 @@ void Dynamics::ProcessCollisions() {
// Process all standard collisions. This will trigger our callback which // Process all standard collisions. This will trigger our callback which
// do the real work (add collisions to list, store commands to be // do the real work (add collisions to list, store commands to be
// called, etc). // called, etc).
dSpaceCollide(ode_space_, this, &DoCollideCallback); dSpaceCollide(ode_space_, this, &DoCollideCallback_);
// Collide our trimeshes against everything. // 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. // Do a bit of precalc each cycle.
collision_cache_->Precalc(); collision_cache_->Precalc();
@ -453,9 +455,9 @@ void Dynamics::ProcessCollisions() {
// setting parts' currently-colliding-with lists // setting parts' currently-colliding-with lists
// based on current info, // based on current info,
// removing unclaimed collisions and empty groups. // removing unclaimed collisions and empty groups.
std::unordered_map<int64_t, SrcNodeCollideMap>::iterator i_next; std::unordered_map<int64_t, SrcNodeCollideMap_>::iterator i_next;
std::unordered_map<int64_t, DstNodeCollideMap>::iterator j_next; std::unordered_map<int64_t, DstNodeCollideMap_>::iterator j_next;
std::unordered_map<int, SrcPartCollideMap>::iterator k_next; std::unordered_map<int, SrcPartCollideMap_>::iterator k_next;
std::unordered_map<int, Object::Ref<Collision> >::iterator l_next; std::unordered_map<int, Object::Ref<Collision> >::iterator l_next;
for (auto i = impl_->node_collisions_.begin(); for (auto i = impl_->node_collisions_.begin();
i != impl_->node_collisions_.end(); i = i_next) { i != impl_->node_collisions_.end(); i = i_next) {
@ -507,26 +509,26 @@ void Dynamics::ProcessCollisions() {
collision_events_.clear(); collision_events_.clear();
} }
void Dynamics::process() { void Dynamics::Process() {
in_process_ = true; in_process_ = true;
// Update this once so we can recycle results. // Update this once so we can recycle results.
real_time_ = g_core->GetAppTimeMillisecs(); real_time_ = g_core->GetAppTimeMillisecs();
ProcessCollisions(); ProcessCollision_();
dWorldQuickStep(ode_world_, kGameStepSeconds); dWorldQuickStep(ode_world_, kGameStepSeconds);
dJointGroupEmpty(ode_contact_group_); dJointGroupEmpty(ode_contact_group_);
in_process_ = false; 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); 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 // Run collisions for everything. Store any callbacks that will need to be made
// and run them after all collision constraints are made. // and run them after all collision constraints are made.
// This way we know all bodies and their associated nodes, etc are valid // This way we know all bodies and their associated nodes, etc are valid
// throughout collision processing. // throughout collision processing.
void Dynamics::CollideCallback(dGeomID o1, dGeomID o2) { void Dynamics::CollideCallback_(dGeomID o1, dGeomID o2) {
dBodyID b1 = dGeomGetBody(o1); dBodyID b1 = dGeomGetBody(o1);
dBodyID b2 = dGeomGetBody(o2); dBodyID b2 = dGeomGetBody(o2);
@ -1103,7 +1105,7 @@ void Dynamics::CollideCallback(dGeomID o1, dGeomID o2) {
} }
} }
void Dynamics::ShutdownODE() { void Dynamics::ShutdownODE_() {
if (ode_space_) { if (ode_space_) {
dSpaceDestroy(ode_space_); dSpaceDestroy(ode_space_);
ode_space_ = nullptr; ode_space_ = nullptr;
@ -1118,8 +1120,8 @@ void Dynamics::ShutdownODE() {
} }
} }
void Dynamics::ResetODE() { void Dynamics::ResetODE_() {
ShutdownODE(); ShutdownODE_();
ode_world_ = dWorldCreate(); ode_world_ = dWorldCreate();
assert(ode_world_); assert(ode_world_);
dWorldSetGravity(ode_world_, 0, -20, 0); dWorldSetGravity(ode_world_, 0, -20, 0);

View File

@ -4,7 +4,6 @@
#define BALLISTICA_SCENE_V1_DYNAMICS_DYNAMICS_H_ #define BALLISTICA_SCENE_V1_DYNAMICS_DYNAMICS_H_
#include <memory> #include <memory>
#include <unordered_map>
#include <vector> #include <vector>
#include "ballistica/base/base.h" #include "ballistica/base/base.h"
@ -16,12 +15,12 @@ namespace ballistica::scene_v1 {
class Dynamics : public Object { class Dynamics : public Object {
public: public:
explicit Dynamics(Scene* scene_in); explicit Dynamics(Scene* scene);
~Dynamics() override; ~Dynamics() override;
void Draw(base::FrameDef* frame_def); // Draw any debug stuff, etc. void Draw(base::FrameDef* frame_def); // Draw any debug stuff, etc.
auto ode_world() -> dWorldID { return ode_world_; } auto ode_world() -> dWorldID { return ode_world_; }
auto getContactGroup() -> dJointGroupID { return ode_contact_group_; } auto ode_contact_group() -> dJointGroupID { return ode_contact_group_; }
auto space() -> dSpaceID { return ode_space_; } auto ode_space() -> dSpaceID { return ode_space_; }
// Discontinues a collision. Used by parts when changing materials // Discontinues a collision. Used by parts when changing materials
// so that new collisions may enter effect. // so that new collisions may enter effect.
@ -37,6 +36,7 @@ class Dynamics : public Object {
: active_collide_src_node_) : active_collide_src_node_)
.Get(); .Get();
} }
// Used by collision callbacks - internal. // Used by collision callbacks - internal.
auto GetActiveCollideDstNode() -> Node* { auto GetActiveCollideDstNode() -> Node* {
assert(active_collision_); assert(active_collision_);
@ -49,19 +49,19 @@ class Dynamics : public Object {
} }
// Used by collide message handlers. // Used by collide message handlers.
void set_collide_message_state(bool inCollideMessageIn, void set_collide_message_state(bool in_collide_message,
bool target_other_in = false) { bool target_other = false) {
in_collide_message_ = inCollideMessageIn; in_collide_message_ = in_collide_message;
collide_message_reverse_order_ = target_other_in; collide_message_reverse_order_ = target_other;
} }
auto in_collide_message() const -> bool { return in_collide_message_; } auto in_collide_message() const { return in_collide_message_; }
void process(); void Process();
void increment_skid_sound_count() { skid_sound_count_++; } void IncrementSkidSoundCount() { skid_sound_count_++; }
void decrement_skid_sound_count() { skid_sound_count_--; } void DecrementSkidSoundCount() { skid_sound_count_--; }
auto skid_sound_count() const -> int { return skid_sound_count_; } auto skid_sound_count() const { return skid_sound_count_; }
void incrementRollSoundCount() { roll_sound_count_++; } void IncrementRollSoundCount() { roll_sound_count_++; }
void decrement_roll_sound_count() { roll_sound_count_--; } void DecrementRollSoundCount() { roll_sound_count_--; }
auto getRollSoundCount() const -> int { return roll_sound_count_; } auto roll_sound_count() const { return roll_sound_count_; }
// We do some fancy collision testing stuff for trimeshes instead // We do some fancy collision testing stuff for trimeshes instead
// of going through regular ODE space collision testing.. so we have // of going through regular ODE space collision testing.. so we have
@ -69,55 +69,52 @@ class Dynamics : public Object {
void AddTrimesh(dGeomID g); void AddTrimesh(dGeomID g);
void RemoveTrimesh(dGeomID g); void RemoveTrimesh(dGeomID g);
auto collision_count() const -> int { return collision_count_; } auto collision_count() const { return collision_count_; }
auto process_real_time() const -> millisecs_t { return real_time_; } auto process_real_time() const { return real_time_; }
auto last_impact_sound_time() const -> millisecs_t { auto last_impact_sound_time() const { return last_impact_sound_time_; }
return last_impact_sound_time_; auto in_process() const { return in_process_; }
}
auto in_process() const -> bool { return in_process_; }
private: private:
auto AreColliding(const Part& p1, const Part& p2) -> bool; auto AreColliding_(const Part& p1, const Part& p2) -> bool;
class SrcNodeCollideMap; class SrcNodeCollideMap_;
class DstNodeCollideMap; class DstNodeCollideMap_;
class SrcPartCollideMap; class SrcPartCollideMap_;
class CollisionEvent; class CollisionEvent_;
class CollisionReset; class CollisionReset_;
class Impl; class Impl_;
std::vector<CollisionReset> collision_resets_; std::vector<CollisionReset_> collision_resets_;
// Return a collision object between these two parts, // Return a collision object between these two parts,
// creating a new one if need be. // creating a new one if need be.
auto GetCollision(Part* p1, Part* p2, MaterialContext** cc1, auto GetCollision(Part* p1, Part* p2, MaterialContext** cc1,
MaterialContext** cc2) -> Collision*; MaterialContext** cc2) -> Collision*;
std::vector<CollisionEvent> collision_events_; std::vector<CollisionEvent_> collision_events_;
void ResetODE(); void ResetODE_();
void ShutdownODE(); void ShutdownODE_();
static void DoCollideCallback(void* data, dGeomID o1, dGeomID o2); static void DoCollideCallback_(void* data, dGeomID o1, dGeomID o2);
void CollideCallback(dGeomID o1, dGeomID o2); void CollideCallback_(dGeomID o1, dGeomID o2);
void ProcessCollisions(); void ProcessCollision_();
std::unique_ptr<Impl> impl_; int skid_sound_count_{};
bool processing_collisions_{}; 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_{}; dWorldID ode_world_{};
dJointGroupID ode_contact_group_{}; dJointGroupID ode_contact_group_{};
dSpaceID ode_space_{}; dSpaceID ode_space_{};
millisecs_t real_time_{}; millisecs_t real_time_{};
bool in_process_{};
std::vector<dGeomID> trimeshes_;
millisecs_t last_impact_sound_time_{}; millisecs_t last_impact_sound_time_{};
int skid_sound_count_{};
int roll_sound_count_{};
int collision_count_{};
Scene* scene_{}; Scene* scene_{};
bool in_collide_message_{};
bool collide_message_reverse_order_{};
Collision* active_collision_{}; Collision* active_collision_{};
Object::WeakRef<Node> active_collide_src_node_; Object::WeakRef<Node> active_collide_src_node_;
Object::WeakRef<Node> active_collide_dst_node_; Object::WeakRef<Node> active_collide_dst_node_;
std::vector<dGeomID> trimeshes_;
std::unique_ptr<Impl_> impl_;
std::unique_ptr<base::CollisionCache> collision_cache_; std::unique_ptr<base::CollisionCache> collision_cache_;
friend class Impl;
}; };
} // namespace ballistica::scene_v1 } // namespace ballistica::scene_v1

View File

@ -2,7 +2,7 @@
#include "ballistica/scene_v1/dynamics/material/impact_sound_material_action.h" #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/dynamics.h"
#include "ballistica/scene_v1/dynamics/material/material_context.h" #include "ballistica/scene_v1/dynamics/material/material_context.h"
#include "ballistica/scene_v1/support/client_session.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.Exists());
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
// For now lets avoid this in low-quality graphics mode (should we make // Avoid this if we're cutting corners.
// a low-quality sound mode?) if (g_base->audio->UseLowQualityAudio()) {
if (g_base->graphics_server
&& g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) {
return; return;
} }

View File

@ -42,7 +42,7 @@ MaterialContext::SkidSoundEntry::SkidSoundEntry(
assert(context->dynamics.Exists()); assert(context->dynamics.Exists());
#endif #endif
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
context->dynamics->increment_skid_sound_count(); context->dynamics->IncrementSkidSoundCount();
} }
MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in, MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in,
@ -57,13 +57,13 @@ MaterialContext::SkidSoundEntry::SkidSoundEntry(MaterialContext* context_in,
assert(context); assert(context);
assert(context->dynamics.Exists()); assert(context->dynamics.Exists());
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
context->dynamics->increment_skid_sound_count(); context->dynamics->IncrementSkidSoundCount();
} }
MaterialContext::SkidSoundEntry::~SkidSoundEntry() { MaterialContext::SkidSoundEntry::~SkidSoundEntry() {
assert(context); assert(context);
assert(context->dynamics.Exists()); assert(context->dynamics.Exists());
context->dynamics->decrement_skid_sound_count(); context->dynamics->DecrementSkidSoundCount();
if (playing) { if (playing) {
g_base->audio->PushSourceFadeOutCall(play_id, 200); g_base->audio->PushSourceFadeOutCall(play_id, 200);
} }
@ -81,7 +81,7 @@ MaterialContext::RollSoundEntry::RollSoundEntry(MaterialContext* context_in,
assert(context); assert(context);
assert(context->dynamics.Exists()); assert(context->dynamics.Exists());
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
context->dynamics->incrementRollSoundCount(); context->dynamics->IncrementRollSoundCount();
} }
MaterialContext::RollSoundEntry::RollSoundEntry( MaterialContext::RollSoundEntry::RollSoundEntry(
@ -90,13 +90,13 @@ MaterialContext::RollSoundEntry::RollSoundEntry(
assert(context); assert(context);
assert(context->dynamics.Exists()); assert(context->dynamics.Exists());
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
context->dynamics->incrementRollSoundCount(); context->dynamics->IncrementRollSoundCount();
} }
MaterialContext::RollSoundEntry::~RollSoundEntry() { MaterialContext::RollSoundEntry::~RollSoundEntry() {
assert(context); assert(context);
assert(context->dynamics.Exists()); assert(context->dynamics.Exists());
context->dynamics->decrement_roll_sound_count(); context->dynamics->DecrementRollSoundCount();
if (playing) { if (playing) {
g_base->audio->PushSourceFadeOutCall(play_id, 200); 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/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/dynamics.h"
#include "ballistica/scene_v1/dynamics/material/material_context.h" #include "ballistica/scene_v1/dynamics/material/material_context.h"
#include "ballistica/scene_v1/support/client_session.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.Exists());
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
// For now lets avoid this in low-quality graphics mode // Avoid this if we're cutting corners.
// (should we make a low-quality sound mode?) if (g_base->audio->UseLowQualityAudio()) {
if (g_base->graphics
&& g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) {
return; return;
} }
// Let's limit the amount of skid-sounds we spawn, otherwise we'll // 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 // 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, context->roll_sounds.emplace_back(context, sound.Get(), target_impulse,
volume); volume);
context->complex_sound = true; context->complex_sound = true;

View File

@ -2,7 +2,7 @@
#include "ballistica/scene_v1/dynamics/material/skid_sound_material_action.h" #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/dynamics.h"
#include "ballistica/scene_v1/dynamics/material/material_context.h" #include "ballistica/scene_v1/dynamics/material/material_context.h"
#include "ballistica/scene_v1/support/client_session.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.Exists());
assert(context->dynamics->in_process()); assert(context->dynamics->in_process());
// For now lets avoid this in low-quality graphics mode // Avoid this if we're cutting corners.
// (should we make a low-quality sound mode?). if (g_base->audio->UseLowQualityAudio()) {
if (g_base->graphics_server
&& g_base->graphics_server->quality() < base::GraphicsQuality::kMedium) {
return; return;
} }

View File

@ -65,23 +65,23 @@ RigidBody::RigidBody(int id_in, Part* part_in, Type type_in, Shape shape_in,
case Shape::kSphere: { case Shape::kSphere: {
dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.3f; dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.3f;
geoms_.resize(1); geoms_.resize(1);
geoms_[0] = dCreateSphere(dynamics_->space(), dimensions_[0]); geoms_[0] = dCreateSphere(dynamics_->ode_space(), dimensions_[0]);
break; break;
} }
case Shape::kBox: { case Shape::kBox: {
dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.6f; dimensions_[0] = dimensions_[1] = dimensions_[2] = 0.6f;
geoms_.resize(1); geoms_.resize(1);
geoms_[0] = dCreateBox(dynamics_->space(), dimensions_[0], dimensions_[1], geoms_[0] = dCreateBox(dynamics_->ode_space(), dimensions_[0],
dimensions_[2]); dimensions_[1], dimensions_[2]);
break; break;
} }
case Shape::kCapsule: { case Shape::kCapsule: {
dimensions_[0] = dimensions_[1] = 0.3f; dimensions_[0] = dimensions_[1] = 0.3f;
geoms_.resize(1); geoms_.resize(1);
geoms_[0] = geoms_[0] = dCreateCCylinder(dynamics_->ode_space(), dimensions_[0],
dCreateCCylinder(dynamics_->space(), dimensions_[0], dimensions_[1]); dimensions_[1]);
break; break;
} }
@ -98,14 +98,15 @@ RigidBody::RigidBody(int id_in, Part* part_in, Type type_in, Shape shape_in,
Vector3f p = Vector3f p =
Matrix44fRotate(Vector3f(0, 1, 0), static_cast<float>(i) * inc) Matrix44fRotate(Vector3f(0, 1, 0), static_cast<float>(i) * inc)
* Vector3f(offset, 0, 0); * 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); geoms_[i * 2 + 1] = dCreateSphere(nullptr, sub_rad);
dGeomTransformSetGeom(geoms_[i * 2], geoms_[i * 2 + 1]); dGeomTransformSetGeom(geoms_[i * 2], geoms_[i * 2 + 1]);
dGeomSetPosition(geoms_[i * 2 + 1], p.v[0], p.v[1], p.v[2]); 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. // 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; break;
} }

View File

@ -129,9 +129,6 @@ FlagNode::FlagNode(Scene* scene) : Node(scene, node_type), part_(this) {
mesh_.SetIndexData(indices); mesh_.SetIndexData(indices);
mesh_.SetStaticData(v_static); mesh_.SetStaticData(v_static);
// Create our shadow set.
UpdateForGraphicsQuality(g_base->graphics_server->quality());
} }
auto FlagNode::getPosition() const -> std::vector<float> { 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) { void FlagNode::Draw(base::FrameDef* frame_def) {
if (graphics_quality_ != frame_def->quality()) {
graphics_quality_ = frame_def->quality();
UpdateForGraphicsQuality(graphics_quality_);
}
// Flag cloth. // Flag cloth.
{ {
// Update the dynamic portion of our mesh data. // 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(); FullShadowSet* full_shadows = full_shadow_set_.Get();
if (full_shadows) { // Update our shadow objects.
// Pole bottom. if (!g_core->HeadlessMode()) {
{ dBodyID b = body_->body();
full_shadows->shadow_pole_bottom_.GetValues(&s_scale, &s_density); assert(b);
const Vector3f& p(full_shadows->shadow_pole_bottom_.GetPosition()); dVector3 p;
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, if (FullShadowSet* full_shadows = full_shadow_set_.Get()) {
s_density * 0.25f); 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. // Pole middle.
{ {
full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density); full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density);
const Vector3f& p(full_shadows->shadow_pole_middle_.GetPosition()); const Vector3f& p(full_shadows->shadow_pole_middle_.GetPosition());
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
s_density * 0.25f); s_density * 0.25f);
} }
// Pole top. // Pole top.
{ {
full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density); full_shadows->shadow_pole_middle_.GetValues(&s_scale, &s_density);
const Vector3f& p(full_shadows->shadow_pole_top_.GetPosition()); const Vector3f& p(full_shadows->shadow_pole_top_.GetPosition());
g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0, g_base->graphics->DrawBlotch(p, 0.4f * s_scale, 0, 0, 0,
s_density * 0.25f); s_density * 0.25f);
} }
// Flag center. // Flag center.
{ {
full_shadows->shadow_flag_.GetValues(&s_scale, &s_density); full_shadows->shadow_flag_.GetValues(&s_scale, &s_density);
const Vector3f& p(full_shadows->shadow_flag_.GetPosition()); 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, 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(); c.Submit();
} }
@ -398,30 +415,6 @@ void FlagNode::Step() {
// FIXME: This should probably happen for RBDs automatically? // FIXME: This should probably happen for RBDs automatically?
body_->UpdateBlending(); 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())) { if (dBodyIsEnabled(body_->body())) {
// Try to keep upright by pushing the top of the // Try to keep upright by pushing the top of the
// flag to be above the bottom. // flag to be above the bottom.
@ -675,10 +668,6 @@ void FlagNode::GetRigidBodyPickupLocations(int id, float* obj, float* character,
hand1[2] = -0.05f; hand1[2] = -0.05f;
} }
void FlagNode::OnGraphicsQualityChanged(base::GraphicsQuality q) {
UpdateForGraphicsQuality(q);
}
void FlagNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) { void FlagNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) {
if (!g_core->HeadlessMode()) { if (!g_core->HeadlessMode()) {
if (quality >= base::GraphicsQuality::kMedium) { if (quality >= base::GraphicsQuality::kMedium) {

View File

@ -44,10 +44,13 @@ class FlagNode : public Node {
void UpdateDimensions(); void UpdateDimensions();
void ResetFlagMesh(); void ResetFlagMesh();
void UpdateFlagMesh(); void UpdateFlagMesh();
void OnGraphicsQualityChanged(base::GraphicsQuality q) override;
void UpdateForGraphicsQuality(base::GraphicsQuality q); void UpdateForGraphicsQuality(base::GraphicsQuality q);
void UpdateSpringPoint(int p1, int p2, float rest_length); 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_; Part part_;
std::vector<float> color_ = {1.0f, 1.0f, 1.0f}; std::vector<float> color_ = {1.0f, 1.0f, 1.0f};
Object::Ref<RigidBody> body_; Object::Ref<RigidBody> body_;
@ -56,15 +59,13 @@ class FlagNode : public Node {
Object::Ref<FullShadowSet> full_shadow_set_; Object::Ref<FullShadowSet> full_shadow_set_;
Object::Ref<SimpleShadowSet> simple_shadow_set_; Object::Ref<SimpleShadowSet> simple_shadow_set_;
int wind_rand_{}; int wind_rand_{};
int footing_{};
float wind_rand_x_{}; float wind_rand_x_{};
float wind_rand_y_{}; float wind_rand_y_{};
float wind_rand_z_{}; float wind_rand_z_{};
float flag_impulse_add_x_{}; float flag_impulse_add_x_{};
float flag_impulse_add_y_{}; float flag_impulse_add_y_{};
float flag_impulse_add_z_{}; float flag_impulse_add_z_{};
bool have_flag_impulse_{};
int footing_{};
bool light_weight_{};
Vector3f flag_points_[25]{}; Vector3f flag_points_[25]{};
Vector3f flag_normals_[25]{}; Vector3f flag_normals_[25]{};
Vector3f flag_velocities_[25]{}; Vector3f flag_velocities_[25]{};

View File

@ -40,7 +40,6 @@ class Node : public Object {
/// Called when the language changes. /// Called when the language changes.
virtual void OnLanguageChange() {} virtual void OnLanguageChange() {}
virtual void OnGraphicsQualityChanged(base::GraphicsQuality q) {}
/// The node can rule out collisions between particular bodies using this. /// The node can rule out collisions between particular bodies using this.
virtual auto PreFilterCollision(RigidBody* b1, RigidBody* r2) -> bool { virtual auto PreFilterCollision(RigidBody* b1, RigidBody* r2) -> bool {

View File

@ -576,7 +576,7 @@ auto PropNode::CollideCallback(dContact* c, int count,
dBodyGetMass(b2, &m); dBodyGetMass(b2, &m);
dJointID j = dJointID j =
dJointCreateFixed(scene()->dynamics()->ode_world(), dJointCreateFixed(scene()->dynamics()->ode_world(),
scene()->dynamics()->getContactGroup()); scene()->dynamics()->ode_contact_group());
dJointAttach(j, b1, b2); dJointAttach(j, b1, b2);
dJointSetFixed(j); dJointSetFixed(j);
dJointSetFixedSpringMode(j, 1, 1, false); dJointSetFixedSpringMode(j, 1, 1, false);

View File

@ -260,7 +260,7 @@ void ShieldNode::Draw(base::FrameDef* frame_def) {
c.Submit(); c.Submit();
// Nifty intersection effects in fancy graphics mode. // 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()); base::ShieldComponent c2(frame_def->overlay_3d_pass());
{ {
auto xf = c2.ScopedTransform(); auto xf = c2.ScopedTransform();
@ -273,7 +273,7 @@ void ShieldNode::Draw(base::FrameDef* frame_def) {
} }
c2.Submit(); c2.Submit();
} }
if (frame_def->has_depth_texture()) { if (frame_def->HasDepthTexture()) {
base::PostProcessComponent c2(frame_def->blit_pass()); base::PostProcessComponent c2(frame_def->blit_pass());
c2.SetNormalDistort(distort); c2.SetNormalDistort(distort);
{ {

View File

@ -916,9 +916,6 @@ SpazNode::SpazNode(Scene* scene)
// Give joints initial vals. // Give joints initial vals.
UpdateJoints(); UpdateJoints();
// FIXME: should do this on draw.
UpdateForGraphicsQuality(g_base->graphics_server->quality());
// We want to have an area of interest by default. // We want to have an area of interest by default.
SetIsAreaOfInterest(true); 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. // Update wings if we've got 'em.
if (wings_) { if (wings_) {
float maxDist = 0.8f; float maxDist = 0.8f;
@ -4547,6 +4510,11 @@ static void DrawRadialMeter(base::MeshIndexedSimpleFull* m,
void SpazNode::Draw(base::FrameDef* frame_def) { void SpazNode::Draw(base::FrameDef* frame_def) {
#if !BA_HEADLESS_BUILD #if !BA_HEADLESS_BUILD
if (graphics_quality_ != frame_def->quality()) {
graphics_quality_ = frame_def->quality();
UpdateForGraphicsQuality(graphics_quality_);
}
#if BA_OSTYPE_MACOS #if BA_OSTYPE_MACOS
if (g_base->graphics_server->renderer()->debug_draw_mode()) { if (g_base->graphics_server->renderer()->debug_draw_mode()) {
base::SimpleComponent c(frame_def->overlay_3d_pass()); 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]; sc[2] = weight * freeze_color[2] + (1.0f - weight) * sc[2];
} }
FullShadowSet* full_shadows = full_shadow_set_.Get(); // Update and draw shadows.
if (full_shadows) { if (!g_core->HeadlessMode()) {
DrawBrightSpot(full_shadows->lower_left_leg_shadow_, 0.3f * death_scale, if (FullShadowSet* full_shadows = full_shadow_set_.Get()) {
death_fade * (frozen_ ? 0.3f : 0.2f), sc); full_shadows->torso_shadow_.SetPosition(
DrawBrightSpot(full_shadows->lower_right_leg_shadow_, 0.3f * death_scale, Vector3f(dBodyGetPosition(body_torso_->body())));
death_fade * (frozen_ ? 0.3f : 0.2f), sc); full_shadows->head_shadow_.SetPosition(
DrawBrightSpot(full_shadows->head_shadow_, 0.45f * death_scale, Vector3f(dBodyGetPosition(body_head_->body())));
death_fade * (frozen_ ? 0.8f : 0.14f), sc); 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) { DrawBrightSpot(full_shadows->lower_left_leg_shadow_, 0.3f * death_scale,
DrawShadow(full_shadows->torso_shadow_, 0.19f * death_scale, 0.9f, sc); death_fade * (frozen_ ? 0.3f : 0.2f), sc);
DrawShadow(full_shadows->head_shadow_, 0.15f * death_scale, 0.7f, sc); DrawBrightSpot(full_shadows->lower_right_leg_shadow_,
DrawShadow(full_shadows->pelvis_shadow_, 0.15f * death_scale, 0.7f, sc); 0.3f * death_scale, death_fade * (frozen_ ? 0.3f : 0.2f),
DrawShadow(full_shadows->lower_left_leg_shadow_, 0.08f * death_scale, sc);
1.0f, sc); DrawBrightSpot(full_shadows->head_shadow_, 0.45f * death_scale,
DrawShadow(full_shadows->lower_right_leg_shadow_, 0.08f * death_scale, death_fade * (frozen_ ? 0.8f : 0.14f), sc);
1.0f, sc); DrawShadow(full_shadows->torso_shadow_, 0.19f * death_scale, 0.9f, sc);
DrawShadow(full_shadows->upper_left_leg_shadow_, 0.08f * death_scale, DrawShadow(full_shadows->head_shadow_, 0.15f * death_scale, 0.7f, sc);
1.0f, sc); DrawShadow(full_shadows->pelvis_shadow_, 0.15f * death_scale, 0.7f, sc);
DrawShadow(full_shadows->upper_right_leg_shadow_, 0.08f * death_scale, DrawShadow(full_shadows->lower_left_leg_shadow_, 0.08f * death_scale,
1.0f, sc); 1.0f, sc);
DrawShadow(full_shadows->upper_left_arm_shadow_, 0.08f * death_scale, DrawShadow(full_shadows->lower_right_leg_shadow_, 0.08f * death_scale,
0.5f, sc); 1.0f, sc);
DrawShadow(full_shadows->lower_left_arm_shadow_, 0.08f * death_scale, DrawShadow(full_shadows->upper_left_leg_shadow_, 0.08f * death_scale,
0.3f, sc); 1.0f, sc);
DrawShadow(full_shadows->lower_right_arm_shadow_, 0.08f * death_scale, DrawShadow(full_shadows->upper_right_leg_shadow_, 0.08f * death_scale,
0.3f, sc); 1.0f, sc);
DrawShadow(full_shadows->upper_right_arm_shadow_, 0.08f * death_scale, DrawShadow(full_shadows->upper_left_arm_shadow_, 0.08f * death_scale,
0.5f, sc); 0.5f, sc);
} else { DrawShadow(full_shadows->lower_left_arm_shadow_, 0.08f * death_scale,
SimpleShadowSet* simple_shadows = simple_shadow_set_.Get(); 0.3f, sc);
assert(simple_shadows); DrawShadow(full_shadows->lower_right_arm_shadow_, 0.08f * death_scale,
DrawShadow(simple_shadows->shadow_, 0.2f * death_scale, 2.0f, sc); 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 #endif // !BA_HEADLESS_BUILD
} // NOLINT (yes i know this is too big) } // NOLINT (yes i know this is too big)
void SpazNode::OnGraphicsQualityChanged(base::GraphicsQuality q) {
UpdateForGraphicsQuality(q);
}
void SpazNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) { void SpazNode::UpdateForGraphicsQuality(base::GraphicsQuality quality) {
#if !BA_HEADLESS_BUILD #if !BA_HEADLESS_BUILD
if (quality >= base::GraphicsQuality::kMedium) { 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. // Reset to a standing, non-moving state at the given point.
void Stand(float x, float y, float z, float angle); void Stand(float x, float y, float z, float angle);
void OnGraphicsQualityChanged(base::GraphicsQuality q) override;
void UpdateForGraphicsQuality(base::GraphicsQuality q); void UpdateForGraphicsQuality(base::GraphicsQuality q);
void UpdateAreaOfInterest(); void UpdateAreaOfInterest();
auto CollideCallback(dContact* c, int count, RigidBody* colliding_body, auto CollideCallback(dContact* c, int count, RigidBody* colliding_body,
@ -515,6 +514,7 @@ class SpazNode : public Node {
bool has_eyelids_{true}; bool has_eyelids_{true};
bool running_{}; bool running_{};
bool billboard_cross_out_{}; bool billboard_cross_out_{};
base::GraphicsQuality graphics_quality_{};
Object::Ref<RigidBody> hair_front_right_body_; Object::Ref<RigidBody> hair_front_right_body_;
JointFixedEF* hair_front_right_joint_{}; JointFixedEF* hair_front_right_joint_{};
Object::Ref<RigidBody> hair_front_left_body_; Object::Ref<RigidBody> hair_front_left_body_;

View File

@ -483,9 +483,6 @@ void HostActivity::PruneSessionBaseTimers() {
void HostActivity::OnScreenSizeChange() { scene()->OnScreenSizeChange(); } void HostActivity::OnScreenSizeChange() { scene()->OnScreenSizeChange(); }
void HostActivity::LanguageChanged() { scene()->LanguageChanged(); } void HostActivity::LanguageChanged() { scene()->LanguageChanged(); }
void HostActivity::DebugSpeedMultChanged() { UpdateStepTimerLength(); } void HostActivity::DebugSpeedMultChanged() { UpdateStepTimerLength(); }
void HostActivity::GraphicsQualityChanged(base::GraphicsQuality q) {
scene()->GraphicsQualityChanged(q);
}
void HostActivity::Draw(base::FrameDef* frame_def) { void HostActivity::Draw(base::FrameDef* frame_def) {
if (!started_) { if (!started_) {

View File

@ -57,7 +57,6 @@ class HostActivity : public SceneV1Context {
void OnScreenSizeChange(); void OnScreenSizeChange();
void LanguageChanged(); void LanguageChanged();
void DebugSpeedMultChanged(); void DebugSpeedMultChanged();
void GraphicsQualityChanged(base::GraphicsQuality q);
// Used to register python calls created in this context so we can make sure // Used to register python calls created in this context so we can make sure
// they got properly cleaned up. // 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 { auto HostSession::DoesFillScreen() const -> bool {
// FIXME not necessarily the case. // FIXME not necessarily the case.
return true; return true;

View File

@ -70,7 +70,6 @@ class HostSession : public Session {
void Draw(base::FrameDef* f) override; void Draw(base::FrameDef* f) override;
void OnScreenSizeChange() override; void OnScreenSizeChange() override;
void LanguageChanged() override; void LanguageChanged() override;
void GraphicsQualityChanged(base::GraphicsQuality q) override;
void DebugSpeedMultChanged() override; void DebugSpeedMultChanged() override;
auto GetHostSession() -> HostSession* override; auto GetHostSession() -> HostSession* override;
auto GetMutableScene() -> Scene* override; auto GetMutableScene() -> Scene* override;

View File

@ -166,7 +166,7 @@ void Scene::Step() {
} }
// Lastly step our sim. // Lastly step our sim.
dynamics_->process(); dynamics_->Process();
time_ += kGameStepMilliseconds; time_ += kGameStepMilliseconds;
stepnum_++; 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() { void Scene::OnScreenSizeChange() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
for (auto&& i : nodes_) { 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 SetMapBounds(float x, float y, float z, float X, float Y, float Z);
void OnScreenSizeChange(); void OnScreenSizeChange();
void LanguageChanged(); void LanguageChanged();
void GraphicsQualityChanged(base::GraphicsQuality q);
auto out_of_bounds_nodes() -> const std::vector<Object::WeakRef<Node> >& { auto out_of_bounds_nodes() -> const std::vector<Object::WeakRef<Node> >& {
return out_of_bounds_nodes_; 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) { void SceneV1AppMode::SetPublicPartyPlayerCount(int count) {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
if (count == public_party_player_count_) { if (count == public_party_player_count_) {

View File

@ -172,7 +172,6 @@ class SceneV1AppMode : public base::AppMode {
sockaddr_storage* from) override; sockaddr_storage* from) override;
void DrawWorld(base::FrameDef* frame_def) override; void DrawWorld(base::FrameDef* frame_def) override;
auto DoesWorldFillScreen() -> bool override; auto DoesWorldFillScreen() -> bool override;
void GraphicsQualityChanged(base::GraphicsQuality quality) override;
void RunMainMenu(); void RunMainMenu();
auto dynamics_sync_time() const { return dynamics_sync_time_; } auto dynamics_sync_time() const { return dynamics_sync_time_; }

View File

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

View File

@ -34,7 +34,6 @@ class Session : public SceneV1Context {
virtual auto GetForegroundContext() -> base::ContextRef; virtual auto GetForegroundContext() -> base::ContextRef;
virtual void OnScreenSizeChange(); virtual void OnScreenSizeChange();
virtual void LanguageChanged(); virtual void LanguageChanged();
virtual void GraphicsQualityChanged(base::GraphicsQuality q);
virtual void DebugSpeedMultChanged(); virtual void DebugSpeedMultChanged();
auto benchmark_type() const -> base::BenchmarkType { return benchmark_type_; } auto benchmark_type() const -> base::BenchmarkType { return benchmark_type_; }
void set_benchmark_type(base::BenchmarkType val) { benchmark_type_ = val; } 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 { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21479; const int kEngineBuildNumber = 21486;
const char* kEngineVersion = "1.7.28"; const char* kEngineVersion = "1.7.28";
const int kEngineApiVersion = 8; 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' // momentarily, and also go to platform-specific logging and good ol'
// stderr. // stderr.
Logging::V1CloudLog(logmsg); Logging::V1CloudLog(logmsg);
Logging::DisplayLog("root", LogLevel::kCritical, logmsg); Logging::EmitLog("root", LogLevel::kCritical, logmsg);
fprintf(stderr, "%s\n", logmsg.c_str()); fprintf(stderr, "%s\n", logmsg.c_str());
std::string prefix = "FATAL-ERROR-LOG:"; std::string prefix = "FATAL-ERROR-LOG:";
@ -175,10 +175,10 @@ auto FatalError::HandleFatalError(bool exit_cleanly,
// bring the app down ourself. // bring the app down ourself.
if (!in_top_level_exception_handler) { if (!in_top_level_exception_handler) {
if (exit_cleanly) { if (exit_cleanly) {
Logging::DisplayLog("root", LogLevel::kCritical, "Calling exit(1)..."); Logging::EmitLog("root", LogLevel::kCritical, "Calling exit(1)...");
exit(1); exit(1);
} else { } else {
Logging::DisplayLog("root", LogLevel::kCritical, "Calling abort()..."); Logging::EmitLog("root", LogLevel::kCritical, "Calling abort()...");
abort(); abort();
} }
} }

View File

@ -20,16 +20,16 @@ void Logging::Log(LogLevel level, const std::string& msg) {
g_core->python->LoggingCall(level, msg); g_core->python->LoggingCall(level, msg);
} }
void Logging::DisplayLog(const std::string& name, LogLevel level, void Logging::EmitLog(const std::string& name, LogLevel level,
const std::string& msg) { const std::string& msg) {
// Print to the in-app console (with a newline added). // Print to the dev console.
if (g_base_soft) { if (g_base_soft) {
g_base_soft->PushDevConsolePrintCall(msg + "\n"); g_base_soft->PushDevConsolePrintCall(msg + "\n");
} }
// Ship to platform-specific display mechanisms (android log, etc). // Ship to platform-specific display mechanisms (android log, etc).
if (g_core) { 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. /// babase is imported may not be visible in the app for that same reason.
static void Log(LogLevel level, const std::string& msg); static void Log(LogLevel level, const std::string& msg);
/// Immediately display a log message in the in-app console, /// Send a log message to the in-app console, platform-specific logs, etc.
/// platform-specific logs, etc. This generally should not be called /// This generally should not be called directly but instead wired up to
/// directly but instead wired up to display messages coming from the /// log messages coming through the Python logging system.
/// Python logging system. static void EmitLog(const std::string& name, LogLevel level,
static void DisplayLog(const std::string& name, LogLevel level, const std::string& msg);
const std::string& msg);
/// Write a message to the v1 cloud log. This is considered legacy and /// Write a message to the v1 cloud log. This is considered legacy and
/// will be phased out eventually. /// will be phased out eventually.

View File

@ -20,7 +20,6 @@ using core::g_core;
Object::Object() { Object::Object() {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
// Mark when we were born. // Mark when we were born.
// NOTE: Using core's internal globals here; don't do this normally.
assert(g_core); assert(g_core);
object_birth_time_ = g_core->GetAppTimeMillisecs(); object_birth_time_ = g_core->GetAppTimeMillisecs();
@ -41,7 +40,6 @@ Object::Object() {
Object::~Object() { Object::~Object() {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
{ {
// NOTE: using core's internal globals here; don't do this normally!
assert(g_core); assert(g_core);
// Pull ourself from the global obj list. // Pull ourself from the global obj list.
std::scoped_lock lock(g_core->object_list_mutex); std::scoped_lock lock(g_core->object_list_mutex);
@ -70,6 +68,7 @@ Object::~Object() {
#endif // BA_DEBUG_BUILD #endif // BA_DEBUG_BUILD
// Invalidate all our weak refs. // Invalidate all our weak refs.
//
// We could call Release() on each but we'd have to deactivate the // 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 // thread-check since virtual functions won't work as expected in a
// destructor. Also we can take a few shortcuts here since we know // destructor. Also we can take a few shortcuts here since we know
@ -85,7 +84,6 @@ Object::~Object() {
auto Object::GetObjectTypeName() const -> std::string { auto Object::GetObjectTypeName() const -> std::string {
// Default implementation just returns type name. // Default implementation just returns type name.
// Note: using core's globals directly; don't normally do this!
if (g_core) { if (g_core) {
return g_core->platform->DemangleCXXSymbol(typeid(*this).name()); return g_core->platform->DemangleCXXSymbol(typeid(*this).name());
} }
@ -112,7 +110,6 @@ auto Object::GetThreadOwnership() const -> Object::ThreadOwnership {
void Object::LsObjects() { void Object::LsObjects() {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
// Note: using core's internal globals here; don't normally do this.
assert(g_core); assert(g_core);
std::string s; std::string s;
{ {
@ -192,12 +189,12 @@ void Object::ObjectUpdateForAcquire() {
} }
} }
void Object::ObjectThreadCheck() { void Object::ObjectThreadCheck() const {
if (!thread_checks_enabled_) { if (!thread_checks_enabled_) {
return; return;
} }
ThreadOwnership thread_ownership = GetThreadOwnership(); auto thread_ownership = GetThreadOwnership();
// Special case; graphics context (not simply a thread so have to handle // Special case; graphics context (not simply a thread so have to handle
// specially). // specially).

View File

@ -12,23 +12,26 @@
namespace ballistica { namespace ballistica {
/// Objects supporting strong and weak referencing and thread enforcement. /// 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 { 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(); Object();
public:
virtual ~Object(); virtual ~Object();
// Object classes can provide descriptive names for themselves; // Object classes can provide descriptive names for themselves; these are
// these are used for debugging and other purposes. // used for debugging and other purposes. The default is to use the C++
// The default is to use the C++ symbol name, demangling it when possible. // symbol name, demangling it when possible. IMPORTANT: Do not rely on
// IMPORTANT: Do not rely on this being consistent across builds/platforms. // this being consistent across builds/platforms.
virtual auto GetObjectTypeName() const -> std::string; virtual auto GetObjectTypeName() const -> std::string;
// Provide a brief description of this particular object; by default returns // Provide a brief description of this particular object; by default
// type-name plus address. // returns type-name plus address.
virtual auto GetObjectDescription() const -> std::string; virtual auto GetObjectDescription() const -> std::string;
enum class ThreadOwnership { enum class ThreadOwnership {
@ -42,25 +45,26 @@ class Object {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
/// This is called when adding or removing a reference to an Object; /// This is called when adding or removing a reference to an Object; it
/// it can perform sanity-tests to make sure references are not being /// can perform sanity-tests to make sure references are not being added
/// added at incorrect times or from incorrect threads. /// at incorrect times or from incorrect threads. The default
/// The default implementation uses the per-object /// implementation uses the per-object ThreadOwnership/EventLoopID values
/// ThreadOwnership/EventLoopID values accessible below. NOTE: this /// accessible below.
/// check runs only in the debug build so don't add any logical side-effects! ///
virtual void ObjectThreadCheck(); /// NOTE: This check runs *only* in the debug build so don't include any
/// logical side-effects in these checks!
void ObjectThreadCheck() const;
#endif #endif
/// Called by the default ObjectThreadCheck() to determine ThreadOwnership /// Called by the default ObjectThreadCheck() to determine ownership for
/// for an Object. The default uses the object's individual value /// an Object. By default, an object is owned by a specific thread,
/// (which defaults to ThreadOwnership::kClassDefault and can be set via /// defaulting to the logic thread.
/// SetThreadOwnership())
virtual auto GetThreadOwnership() const -> ThreadOwnership; virtual auto GetThreadOwnership() const -> ThreadOwnership;
/// Return the exact thread to check for with ThreadOwnership::kClassDefault /// Return the exact thread to check for with
/// (in the default ObjectThreadCheck implementation at least). /// ThreadOwnership::kClassDefault (in the default ObjectThreadCheck
/// Default returns EventLoopID::kLogic /// implementation at least). Default returns EventLoopID::kLogic
virtual auto GetDefaultOwnerThread() const -> EventLoopID; virtual auto GetDefaultOwnerThread() const -> EventLoopID;
/// Set thread ownership for an individual object. /// Set thread ownership for an individual object.
@ -73,12 +77,12 @@ class Object {
#endif #endif
} }
// Return true if the provided obj ptr is not null, is ref-counted, and has at // Return true if the provided obj ptr is not null, is ref-counted, and
// least 1 strong ref. This is generally a good thing for calls accepting // has at least 1 strong ref. This is generally a good thing for calls
// object ptrs to check. It is considered bad practice to perform operations // accepting object ptrs to check. It is considered bad practice to
// with not-yet-reffed objects. Note that in some cases this may return // perform operations with not-yet-reffed objects. Note that in some cases
// false positives, so only use this as a sanity check and only take action // this may return false positives, so only use this as a sanity check and
// for a negative result. // only take action for a negative result.
static auto IsValidManagedObject(Object* obj) -> bool { static auto IsValidManagedObject(Object* obj) -> bool {
if (!obj) { if (!obj) {
return false; return false;
@ -91,11 +95,11 @@ class Object {
return (obj->object_strong_ref_count_ > 0); return (obj->object_strong_ref_count_ > 0);
} }
// Return true if the object seems to be valid and was allocated as unmanaged. // Return true if the object seems to be valid and was allocated as
// Code that plans to explicitly delete raw passed pointers can check this // unmanaged. Code that plans to explicitly delete raw passed pointers can
// for peace of mind. Note that for some build types this will return false // check this for peace of mind. Note that for some build types this will
// positives, so only use this as a sanity check and only take action for // return false positives, so only use this as a sanity check and only
// negative results. // take action for negative results.
static auto IsValidUnmanagedObject(Object* obj) -> bool { static auto IsValidUnmanagedObject(Object* obj) -> bool {
if (!obj) { if (!obj) {
return false; return false;
@ -108,12 +112,53 @@ class Object {
return false; return false;
} }
#endif #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; return true;
} }
auto object_strong_ref_count() const -> int { auto object_strong_ref_count() const -> int {
return object_strong_ref_count_; 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> template <typename T = Object>
class Ref; class Ref;
template <typename T = Object> template <typename T = Object>
@ -193,9 +238,8 @@ class Object {
PyExcType::kReference); PyExcType::kReference);
} }
// Yes, reinterpret_cast is evil, but we make sure // Yes, reinterpret_cast is evil, but we make sure we only operate on
// we only operate on cases where this is valid // cases where this is valid (see Acquire()).
// (see Acquire()).
return *reinterpret_cast<T*>(obj_); return *reinterpret_cast<T*>(obj_);
} }
@ -207,8 +251,8 @@ class Object {
PyExcType::kReference); PyExcType::kReference);
} }
// Yes, reinterpret_cast is evil, but we make sure we only operate // Yes, reinterpret_cast is evil, but we make sure we only operate on
// on cases where this is valid (see Acquire()). // cases where this is valid (see Acquire()).
return reinterpret_cast<T*>(obj_); return reinterpret_cast<T*>(obj_);
} }
@ -263,8 +307,8 @@ class Object {
auto operator=(U* ptr) -> WeakRef<T>& { auto operator=(U* ptr) -> WeakRef<T>& {
Release(); Release();
// Go through our template type instead of assigning directly // Go through our template type instead of assigning directly to our
// to our Object* so we catch invalid assigns at compile-time. // Object* so we catch invalid assigns at compile-time.
T* tmp = ptr; T* tmp = ptr;
if (tmp) Acquire(tmp); if (tmp) Acquire(tmp);
@ -295,13 +339,13 @@ class Object {
// Default constructor. // Default constructor.
WeakRef() = default; WeakRef() = default;
/// Copy constructor. Note that, by making this explicit, we require code /// Copy constructor. Note that, by making this explicit, we require
/// to be a bit more verbose. For example, we can't just do 'return /// 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 /// some_ref;' from a function that returns a WeakRef and instead have
/// Object::WeakRef<SomeType>(some_ref)'. However I feel this extra /// to do 'return Object::WeakRef<SomeType>(some_ref)'. However I feel
/// verbosity is good; we're tossing around a mix of pointers and /// this extra verbosity is good; we're tossing around a mix of pointers
/// strong-refs and weak-refs so it's good to be aware exactly where refs /// and strong-refs and weak-refs so it's good to be aware exactly where
/// are being added/etc. /// refs are being added/etc.
explicit WeakRef(const WeakRef<T>& ref) { *this = ref.Get(); } explicit WeakRef(const WeakRef<T>& ref) { *this = ref.Get(); }
/// Create from a pointer of any compatible type. /// Create from a pointer of any compatible type.
@ -469,13 +513,13 @@ class Object {
/// Default constructor. /// Default constructor.
Ref() = default; Ref() = default;
/// Copy constructor. Note that, by making this explicit, we require code /// Copy constructor. Note that, by making this explicit, we require
/// to be a bit more verbose. For example, we can't just do 'return /// 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 /// some_ref;' from a function that returns a Ref and instead have to do
/// Object::Ref<SomeType>(some_ref)'. However I feel this extra verbosity /// 'return Object::Ref<SomeType>(some_ref)'. However I feel this extra
/// is good; we're tossing around a mix of pointers and strong-refs and /// verbosity is good; we're tossing around a mix of pointers and
/// weak-refs so it's good to be aware exactly where refs are being /// strong-refs and weak-refs so it's good to be aware exactly where
/// added/etc. /// refs are being added/etc.
explicit Ref(const Ref<T>& ref) { *this = ref.Get(); } explicit Ref(const Ref<T>& ref) { *this = ref.Get(); }
/// Create from a compatible pointer. /// Create from a compatible pointer.
@ -505,42 +549,16 @@ class Object {
PyExcType::kReference); PyExcType::kReference);
} }
#if BA_DEBUG_BUILD obj->ObjectIncrementStrongRefCount();
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_ = obj; obj_ = obj;
} }
void Release() { void Release() {
if (obj_ != nullptr) { if (obj_ != nullptr) {
#if BA_DEBUG_BUILD auto* obj = obj_;
obj_->ObjectThreadCheck(); // Invalidate ref *before* to avoid potential recursive-release.
#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.
obj_ = nullptr; obj_ = nullptr;
if (tmp->object_strong_ref_count_ == 0) { obj->ObjectDecrementStrongRefCount();
#if BA_DEBUG_BUILD
tmp->object_is_dead_ = true;
#endif
delete tmp;
}
} }
} }
T* obj_{}; T* obj_{};
@ -549,10 +567,10 @@ class Object {
/// Object::New<Type>(): The preferred way to create ref-counted Objects. /// Object::New<Type>(): The preferred way to create ref-counted Objects.
/// Allocates a new Object with the provided args and returns a strong /// Allocates a new Object with the provided args and returns a strong
/// reference to it. /// reference to it.
/// Generally you pass a single type to be instantiated and returned, ///
/// but you can optionally specify the two separately. /// Generally you pass a single type to be instantiated and returned, but
/// (for instance you may want to create a Button but return /// you can optionally specify the two separately. For example, you may
/// a Ref to a Widget) /// want to create a Button but return a Ref to a Widget.
template <typename TRETURN, typename TALLOC = TRETURN, typename... ARGS> template <typename TRETURN, typename TALLOC = TRETURN, typename... ARGS>
[[nodiscard]] static auto New(ARGS&&... args) -> Object::Ref<TRETURN> { [[nodiscard]] static auto New(ARGS&&... args) -> Object::Ref<TRETURN> {
auto* ptr = new TALLOC(std::forward<ARGS>(args)...); auto* ptr = new TALLOC(std::forward<ARGS>(args)...);
@ -642,17 +660,19 @@ class Object {
private: private:
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
// Making operator new private here to help ensure all of our dynamic // Making operator new private here purely to help enforce all of our
// allocation/deallocation goes through our special functions (New(), // dynamic allocation/deallocation going through our special functions
// NewDeferred(), etc.). However, sticking with original new for release // (New(), NewDeferred(), etc.). However, sticking with original new for
// builds since it may handle corner cases that this does not. // 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]; } auto operator new(size_t size) -> void* { return new char[size]; }
void ObjectUpdateForAcquire(); void ObjectUpdateForAcquire();
bool object_has_been_strong_reffed_{};
bool object_is_ref_counted_{}; bool object_has_been_strong_reffed_ : 1 {};
bool object_is_pending_deferred_{}; bool object_is_ref_counted_ : 1 {};
bool object_is_unmanaged_{}; bool object_is_pending_deferred_ : 1 {};
bool object_is_dead_{}; bool object_is_unmanaged_ : 1 {};
bool object_is_dead_ : 1 {};
Object* object_next_{}; Object* object_next_{};
Object* object_prev_{}; Object* object_prev_{};
ThreadOwnership thread_ownership_{ThreadOwnership::kClassDefault}; ThreadOwnership thread_ownership_{ThreadOwnership::kClassDefault};
@ -661,8 +681,8 @@ class Object {
millisecs_t object_birth_time_{}; millisecs_t object_birth_time_{};
bool object_printed_warning_{}; bool object_printed_warning_{};
#endif #endif
int object_strong_ref_count_{};
WeakRefBase* object_weak_refs_{}; WeakRefBase* object_weak_refs_{};
int object_strong_ref_count_{};
BA_DISALLOW_CLASS_COPIES(Object); BA_DISALLOW_CLASS_COPIES(Object);
}; // 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); retry_attempt + 1);
} }
} else if (errno == ECONNREFUSED) { } 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 // guessing there's some race condition at the OS level where the
// port-file write goes through before the socket is actually truly // port-file write goes through before the socket is actually truly
// accepting connections. A retry should succeed. // 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, ctx->instance_prefix, ctx->instance_num, ctx->pid,
retry_attempt + 1); 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 { } else {
// Currently not retrying on other errors. // Currently not retrying on other errors.
fprintf(stderr, fprintf(stderr,

View File

@ -983,8 +983,8 @@ class XCodeBuild:
# on to clarify in that case (unless requested not to). # on to clarify in that case (unless requested not to).
assert self._section is not None assert self._section is not None
sys.stdout.write( sys.stdout.write(
f'{Clr.YLW}Unexpected {self._section.value}' f'{Clr.YLW}Unfiltered Output (Section {self._section.value}):'
f' Output:{Clr.RST} {line}' f'{Clr.RST} {line}'
) )
else: else:
sys.stdout.write(line) sys.stdout.write(line)