graphics/window-system revamp work-in-progress

This commit is contained in:
Eric 2023-09-27 09:20:31 -07:00
parent 034094d0a3
commit 5b8310352f
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
288 changed files with 34011 additions and 16186 deletions

134
.efrocachemap generated
View File

@ -421,42 +421,42 @@
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
"build/assets/ba_data/data/langdata.json": "44b7cb7d2ce62346834ab48d0d1e81bc",
"build/assets/ba_data/data/languages/arabic.json": "db961f7fe0541a31880929e1c17ea957",
"build/assets/ba_data/data/langdata.json": "ffaf99c11311bb311421f8537be1fab9",
"build/assets/ba_data/data/languages/arabic.json": "c246699b9c1949ce7542d32d30d47632",
"build/assets/ba_data/data/languages/belarussian.json": "995ee0abd5bc05704e9f5a7712774663",
"build/assets/ba_data/data/languages/chinese.json": "8fc810e920164f3d7374aaab8000e9ae",
"build/assets/ba_data/data/languages/chinesetraditional.json": "3fe960a8f0ca529aa57b4f9cb7385abc",
"build/assets/ba_data/data/languages/chinese.json": "8d889accdd49334591209bdaf6eaf02f",
"build/assets/ba_data/data/languages/chinesetraditional.json": "19be7dcc11f5a9ed4fc408a0216ab36b",
"build/assets/ba_data/data/languages/croatian.json": "766532c67af5bd0144c2d63cab0516fa",
"build/assets/ba_data/data/languages/czech.json": "f3ce219840946cb8f9aa6d3e25927ab3",
"build/assets/ba_data/data/languages/czech.json": "70992c2e2ac08a1f95a3e94318ab3332",
"build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e",
"build/assets/ba_data/data/languages/dutch.json": "22b44a33bf81142ba2befad14eb5746e",
"build/assets/ba_data/data/languages/english.json": "6d261a19b40a27eca92f6199a26f5779",
"build/assets/ba_data/data/languages/english.json": "b38d54aecf3ac47b8d8ca97d8bab3006",
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
"build/assets/ba_data/data/languages/filipino.json": "58f363cfd8a3ccf0c904ab753d95789b",
"build/assets/ba_data/data/languages/french.json": "6057b18878ad8379e51b507fa94958d8",
"build/assets/ba_data/data/languages/french.json": "d8527da977a563185de25ef02bacf826",
"build/assets/ba_data/data/languages/german.json": "549754d2a530d825200c6126be56df5c",
"build/assets/ba_data/data/languages/gibberish.json": "d23fe0936bb6177443f4b74bf5981b13",
"build/assets/ba_data/data/languages/gibberish.json": "837423db378b3e7679683805826aa26e",
"build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a",
"build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a",
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e",
"build/assets/ba_data/data/languages/indonesian.json": "00b351a98d6fc301df604e1e9d56a055",
"build/assets/ba_data/data/languages/italian.json": "11f0a95abce8ef7b667b0d557746ecbe",
"build/assets/ba_data/data/languages/italian.json": "338e7a03dff47f4eefc0ca3a995cd4f4",
"build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2",
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
"build/assets/ba_data/data/languages/persian.json": "0cf93f27181dd3ef4b0d03b88bf899cf",
"build/assets/ba_data/data/languages/persian.json": "71cc5b33abda0f285b970b8cc4a014a8",
"build/assets/ba_data/data/languages/polish.json": "826c5b0402c2f0bcc29bc6f48b833545",
"build/assets/ba_data/data/languages/portuguese.json": "99b27c598c90fd522132af3536aef0ee",
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
"build/assets/ba_data/data/languages/russian.json": "75ee4f36356f4f8a8413d4e0b6f5e268",
"build/assets/ba_data/data/languages/russian.json": "910cf653497654a16d5c4f067d6def22",
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef",
"build/assets/ba_data/data/languages/spanish.json": "b59d7ad1a98831afbc4ab162af08ec51",
"build/assets/ba_data/data/languages/spanish.json": "0122b0b24aa111ab259af02bbae9b7b6",
"build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18",
"build/assets/ba_data/data/languages/tamil.json": "b9d4b4e107456ea6420ee0f9d9d7a03e",
"build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc",
"build/assets/ba_data/data/languages/turkish.json": "a5347d5f7fc9dc994053001a936964a4",
"build/assets/ba_data/data/languages/turkish.json": "9d7e58c9062dc517c3779c255a9b3142",
"build/assets/ba_data/data/languages/ukrainian.json": "f72eb51abfbbb56e27866895d7e947d2",
"build/assets/ba_data/data/languages/venetian.json": "b0e3d73ccf96c5fa490a54f090ee77a5",
"build/assets/ba_data/data/languages/venetian.json": "71ff7ec07a1f9715fea93229bff249e1",
"build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba",
"build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054",
"build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422",
@ -1416,10 +1416,10 @@
"build/assets/ba_data/textures/crossOutMask.pvr": "94110cc4e3e47f81b68f548951a33c2b",
"build/assets/ba_data/textures/crossOutMask_preview.png": "d5df4d494cfbf700e3c8726b3693716c",
"build/assets/ba_data/textures/crossOut_preview.png": "a0628f1e6b7e9f7d3b73d1c835ec9286",
"build/assets/ba_data/textures/cursor.dds": "68e40aed124670a2b53c78101a8b3889",
"build/assets/ba_data/textures/cursor.ktx": "627cbabe2eceff9992d31d4a48e98966",
"build/assets/ba_data/textures/cursor.pvr": "704963da7a62d555e1dd907076033a02",
"build/assets/ba_data/textures/cursor_preview.png": "8d80f75bcdc7f50b479d7756e68b8629",
"build/assets/ba_data/textures/cursor.dds": "575b05e3adc74adf5a5d4b482a54adc9",
"build/assets/ba_data/textures/cursor.ktx": "56ef6481222c23cbc1ab0fe825f19b03",
"build/assets/ba_data/textures/cursor.pvr": "344b8856a315af23f495ebd283ee54fa",
"build/assets/ba_data/textures/cursor_preview.png": "0f6820abfe6b79b4133971ace8f3bc42",
"build/assets/ba_data/textures/cuteSpaz.dds": "5876162f89e558a2220935a1d63493c3",
"build/assets/ba_data/textures/cuteSpaz.ktx": "4a3bc3c1739991298d21a66256289d57",
"build/assets/ba_data/textures/cuteSpaz.pvr": "a236803464dc49b61b63a5e83d305c4c",
@ -1520,10 +1520,10 @@
"build/assets/ba_data/textures/folder.ktx": "293ec1bc118dd368e41623d5341e4428",
"build/assets/ba_data/textures/folder.pvr": "22c439c211b592f41987b865d770bc77",
"build/assets/ba_data/textures/folder_preview.png": "6e4892d7d43289bc22c8ff94c7c15955",
"build/assets/ba_data/textures/fontBig.dds": "962a8dce3f3fdeebbb8abcc0682bcb74",
"build/assets/ba_data/textures/fontBig.ktx": "3b999da15c56a6f38bd7cdff5632af9d",
"build/assets/ba_data/textures/fontBig.pvr": "3d931bdabdc5097b37be763594b28678",
"build/assets/ba_data/textures/fontBig_preview.png": "540b6bca8f5050a8ed246dce542aef93",
"build/assets/ba_data/textures/fontBig.dds": "4242bbb85bc1ebd1ed63d7660587bd3c",
"build/assets/ba_data/textures/fontBig.ktx": "94b56c2488d6c9ebabfbbb740eca07dd",
"build/assets/ba_data/textures/fontBig.pvr": "dff3f6c04a8c7b0bb937001640b42c8d",
"build/assets/ba_data/textures/fontBig_preview.png": "f8b15cb04f0deca7774def335a72f053",
"build/assets/ba_data/textures/fontExtras.dds": "7ab11df1b3a3daa651dfad34219b89f5",
"build/assets/ba_data/textures/fontExtras.ktx": "30c3c8ca2cdf1209ff177017bb10f0a8",
"build/assets/ba_data/textures/fontExtras.pvr": "fd3b0bd902c30e4b7aa5fe00e1eec4be",
@ -4042,7 +4042,7 @@
"build/assets/windows/Win32/Lib/zoneinfo/_tzpath.py": "08a2e9502e68a3e35c45067f7439b108",
"build/assets/windows/Win32/Lib/zoneinfo/_zoneinfo.py": "88e71c4db229ce21321b991cd7162d23",
"build/assets/windows/Win32/OpenAL32.dll": "8bcdadebd8bc95a591e727a04faebda8",
"build/assets/windows/Win32/SDL2.dll": "e358fddbc36ebf4dc8aba79a99466747",
"build/assets/windows/Win32/SDL2.dll": "3937d3151ffb6544bf9657d0e45a8d0a",
"build/assets/windows/Win32/libvorbis.dll": "dcfa5c5534900b7c109adc820ae57d74",
"build/assets/windows/Win32/libvorbisfile.dll": "8abd2d7131857b9cc689b2857d04d018",
"build/assets/windows/Win32/msvcp140d.dll": "d172cacd2087cf34db2fb8bb3c29c337",
@ -4056,50 +4056,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "e7171e1db64d2c0fdebbaeb45d99422e",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "4f6ed2673aa7716c211c94baeb611da3",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "629d63c3627b863e817a98b891b38325",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b6315ff0d68b436500e48de435b69c77",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "aa98cf408c857767bcbb41ce2b63ce76",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "fa56d6e2beb16a44eff88dd9ca3e0b3e",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "3f026a96404b41c4369bd40694eae4de",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "9713455275e8d7b6bc98fa85fbc1e3ee",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "051d1a2636fe1b5dadb1f9806b9040a3",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "ff117aec63d1413c9f48bac4fd20c9a5",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0502bb185cf85e224628b3be5e18750a",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "8999ff0a5601c423e2fa10b59425bfa2",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "f2d0d08db00ee44a5c98023ea8521bcc",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "35e1d1017b406d9ed31b2b901a1d0dbe",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "6346c88ec18632ac3ad63beae6122569",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "a08613797c5064a0b2302b9468dc88e9",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "f3d4f9dc7162d7f736c32f8a8ead5477",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "dbddd278a058fb61b8ca5301a3ae4a09",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "aca30445a8fc28b69a2bce57bd651dc3",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "1ba63aa89b4b45d893499a491e3047cd",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "1e786451b0abe1451f17b908c2d8abb3",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "80db3e75458db18efe1657f8ce686996",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "1e786451b0abe1451f17b908c2d8abb3",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "80db3e75458db18efe1657f8ce686996",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "a29bc1b96b2422dce9154ef8a404a8e6",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "c36dc72d78f9df240ae9f640dee470c2",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "a29bc1b96b2422dce9154ef8a404a8e6",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "c36dc72d78f9df240ae9f640dee470c2",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "d1e2aef8e1ba4fac62908efd3b8079c4",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "4dd471e8559a31c1425cd344646261b5",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "d1e2aef8e1ba4fac62908efd3b8079c4",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "4dd471e8559a31c1425cd344646261b5",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "0bff76811b9640d20c7104b8dabf27e8",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "b6e72c87d43dbf2a93e9a0b74952677b",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "3c8593e81564012a7638a20c0dc0267b",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "b6e72c87d43dbf2a93e9a0b74952677b",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "a6b742a577b1a2a5b99ef4acc4949735",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "3019670a3df31b6ed2bbd5d3a847afbb",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "85335e293ae1433afdf40a83e346d379",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "172855f5f89e9bd36efe7bacad3d5fc9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "6fa16da3c74bc305a550697b7b453b21",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "dc2d4d3655e8cd65d83e355e731441a8",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "24a6884294c6c6da095e24f0dfa0faf4",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "68f39e024d3ca6bef9a0dc593ba139ab",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "24a88ffc78cd3f59a7fa7df974d79e25",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "ba97e6d831cfc129ebc46443b49c3055",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "f054f613e6a053a281b3d75c0bc19a8b",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "c95435393ac3946b0cdd288cea6fd578",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "a0ef35af73a0d6566f57cd57c603c3cd",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "ef54d4c6043e4fd789936d5fc60757af",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "a2dc425aa51d6fd086346e9418d21b97",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "73ba9c896b534902308178627e67746b",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "bc6e2a93f1cb4cf0b19ceb577eb15ab9",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "4e813fd82f679f2d87d420535cd0d549",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "24497a56d078c29c8ebaca01b655dc7c",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "955dd7032ecd7fb0e329a40964cdebb3",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "cb0519ef32e0e5a2cdcb0405def7bd9c",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "df6c673f248be2612388edc56b1e5288",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "a4913759d8f9b6013fae9c213441298d",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "1f4534097a3b1bfa1f26aec4e7eefff3",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "c97a6cc985522514c5ba5b57cd4a6b70",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "4cd0b0cd2f114ef58dc25cf148e3be64",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "4beca89d3ef221cf195bef82933a5e96",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "6d97073ef999fe7e44fa9aa8ef1b5a42",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "f3d305e647a7f77dd70a48f615cfd750",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "931ce8eab9859d20ad86c47d196ba62c",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "f3d305e647a7f77dd70a48f615cfd750",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "931ce8eab9859d20ad86c47d196ba62c",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "e80b5f536b30a23fe107852a0c2f7536",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "076df48013d64bc07aa59001819f8583",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "e80b5f536b30a23fe107852a0c2f7536",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "076df48013d64bc07aa59001819f8583",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "fcfdeb63ced9156995cf1b08ae5c861f",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d82ad28301dc8e5b0ca98cf1da5d907c",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "fcfdeb63ced9156995cf1b08ae5c861f",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d82ad28301dc8e5b0ca98cf1da5d907c",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "6db0247bf985f9d8c7ed85a5e5508a8b",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "097e17c460bf798edf61303789860596",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "14df40bc07bdde8184843d16d5ba7798",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "097e17c460bf798edf61303789860596",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8542bbc154faedf633dd82bc78dc50b7",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "eb625ccd56cd37e26b6abb99ecc385c3",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "835d9280dddb9fa6554e631184a62f6d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "5aca1f8dfd9ff294cf2ec2229e393d8e",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "f270764cfbb3791752fbd265b5787889",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "64ca7172f66176fef0ef71d5b1e51663",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "81b8aeeecb650f57d4f89636cbdf5916",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "a3c06c787f387c586de3ff6b092c32dc",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "c81b2b1f3a14b4cd20a7b93416fe893a",

1
.gitignore vendored
View File

@ -62,7 +62,6 @@ libs/
# Visual Studio
.vs
*.vcxproj.user
*.aps
*.ncb
*.opendb

View File

@ -1975,6 +1975,7 @@
<w>noninteractively</w>
<w>nonmultipart</w>
<w>noone</w>
<w>nopull</w>
<w>norun</w>
<w>nospeak</w>
<w>nosub</w>
@ -2447,6 +2448,7 @@
<w>rawkey</w>
<w>rawpath</w>
<w>rawpaths</w>
<w>rawval</w>
<w>rayd</w>
<w>rcade</w>
<w>rcfile</w>
@ -2821,6 +2823,7 @@
<w>stdobj</w>
<w>stdsettings</w>
<w>stdspaz</w>
<w>steamdeck</w>
<w>stedit</w>
<w>steelseries</w>
<w>stgdict</w>
@ -3318,6 +3321,7 @@
<w>writeclasses</w>
<w>writefuncs</w>
<w>wslpath</w>
<w>wslview</w>
<w>wspath</w>
<w>wsroot</w>
<w>wtcolor</w>

View File

@ -1,5 +1,29 @@
### 1.7.28 (build 21342, api 8, 2023-09-13)
### 1.7.28 (build 21385, api 8, 2023-09-27)
- 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
attempting to support various old/hacked versions of SDL, etc. I ripped out
huge chunks of it and put back still-relevant pieces in a much more cleanly
designed way. This should put us in a much better place for supporting various
platforms and making graphical improvements going forward. See
`ballistica/base/app_adapter/app_adapter_sdl.cc` for an example of the now
nicely implemented system.
- The engine now requires OpenGL 3.0 or newer on desktop and OpenGL ES 3.0 or
newer on mobile. This means we're cutting off a few percent of old devices on
Android that only support ES 2, but ES 3 has been out for 10 years now so I
feel it is time. As mentioned above, this allows massively cleaning up the
graphics code which means we can start to improve it.
- Removed gamma controls. These were only active on the old Mac version anyway
and are being removed from the upcoming SDL3, so if we want this sort of thing
we should do it through shading in the renderer now.
- Implemented both vsync and max-fps for the SDL build of the game. This means
you can finally take advantage of that nice high frame rate monitor on your
PC. Vsync supports 'Disable', 'Enabled' and 'Auto', which attempts to use
'adaptive' vsync if available, and no vsync otherwise.
- Spent some time tuning a few frame-timing mechanisms, so motion in the game
should appear significantly smoother in some cases. Please let me know if it
ever appears *less* smooth than before or if you see what looks like weird
speed changes which could be timing problems.
- Renamed Console to DevConsole, and added an option under advanced settings to
always show a 'dev' button onscreen which can be used to toggle it. The
backtick key still works also for anyone with a keyboard. I plan to add more
@ -42,6 +66,17 @@
to fail in some builds/runs (thanks Rikko for the heads-up).
- (build 21327) Fixed an issue that could cause the app to pause for 3 seconds
at shutdown.
- Worked to improve sanity checking on C++ RenderComponents in debug builds to
make it easier to use and avoid sending broken commands to the renderer. Some
specifics follow.
- RenderComponents no longer need an explicit Submit() at the end; if one goes
out of scope not in the submitted state it will implicitly run a submit.
Hopefully this will encourage concise code where RenderComponents are defined
in tight scopes.
- RenderComponents now have a ScopedTransform() call which can be used to push
and pop the transform stack based on C++ scoping instead of the old
PushTransform/PopTransform. This should make it harder to accidentally break
the transform stack with unbalanced components.
### 1.7.27 (build 21282, api 8, 2023-08-30)

View File

@ -955,8 +955,7 @@ WINDOWS_CONFIGURATION ?= Debug
# Stage assets and other files so a built binary will run.
windows-staging: assets-windows resources meta
$(STAGE_BUILD) -win-$(WINPLT) -$(WINCFGLC) \
build/windows/$(WINCFG)_$(WINPLT)
$(STAGE_BUILD) -win-$(WINPLT) -$(WINCFGLC) build/windows/$(WINCFG)_$(WINPLT)
# Build and run a debug windows build (from WSL).
windows-debug: windows-debug-build

View File

@ -1176,6 +1176,7 @@
<w>noninteractively</w>
<w>nonlint</w>
<w>noone</w>
<w>nopull</w>
<w>notarytool</w>
<w>nothin</w>
<w>notorize</w>
@ -1446,6 +1447,7 @@
<w>raspbian</w>
<w>rasterizer</w>
<w>rawkey</w>
<w>rawval</w>
<w>rayd</w>
<w>rcade</w>
<w>rcva</w>
@ -1675,6 +1677,7 @@
<w>staticdata</w>
<w>statictest</w>
<w>stdint</w>
<w>steamdeck</w>
<w>stepfast</w>
<w>stephane</w>
<w>stepnum</w>
@ -1950,6 +1953,7 @@
<w>wreadlink</w>
<w>writeauxiliaryfile</w>
<w>writecall</w>
<w>wslview</w>
<w>wspath</w>
<w>wsroot</w>
<w>wtfslice</w>

View File

@ -205,6 +205,8 @@ set(BALLISTICA_SOURCES
# AUTOGENERATED_PUBLIC_BEGIN (this section is managed by the "update_project" tool)
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter.cc
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter.h
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter_apple.cc
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter_apple.h
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter_headless.cc
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter_headless.h
${BA_SRC_ROOT}/ballistica/base/app_adapter/app_adapter_sdl.cc
@ -284,10 +286,31 @@ set(BALLISTICA_SOURCES
${BA_SRC_ROOT}/ballistica/base/graphics/component/special_component.h
${BA_SRC_ROOT}/ballistica/base/graphics/component/sprite_component.cc
${BA_SRC_ROOT}/ballistica/base/graphics/component/sprite_component.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/framebuffer_object_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/gl_sys.cc
${BA_SRC_ROOT}/ballistica/base/graphics/gl/gl_sys.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/gl_sys_windows.cc
${BA_SRC_ROOT}/ballistica/base/graphics/gl/gl_sys_windows.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_dual_texture_full_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_object_split_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_simple_full_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_simple_split_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_smoke_full_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/mesh/mesh_data_sprite_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_blur_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_object_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_post_process_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_shield_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_simple_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_smoke_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/program/program_sprite_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/render_target_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/renderer_gl.cc
${BA_SRC_ROOT}/ballistica/base/graphics/gl/renderer_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/gl/texture_data_gl.h
${BA_SRC_ROOT}/ballistica/base/graphics/graphics.cc
${BA_SRC_ROOT}/ballistica/base/graphics/graphics.h
${BA_SRC_ROOT}/ballistica/base/graphics/graphics_server.cc

View File

@ -191,6 +191,8 @@
<ItemGroup>
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter.cc" />
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter.h" />
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.cc" />
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.h" />
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_headless.cc" />
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter_headless.h" />
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_sdl.cc" />
@ -270,10 +272,31 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\component\special_component.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\component\sprite_component.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\component\sprite_component.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\framebuffer_object_gl.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_asset_data_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_dual_texture_full_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_object_split_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_full_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_split_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_smoke_full_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_sprite_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_blur_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_object_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_post_process_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_shield_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_simple_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_smoke_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_sprite_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\render_target_gl.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\texture_data_gl.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\graphics.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\graphics.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\graphics_server.cc" />

View File

@ -7,6 +7,12 @@
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter.h">
<Filter>ballistica\base\app_adapter</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.cc">
<Filter>ballistica\base\app_adapter</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.h">
<Filter>ballistica\base\app_adapter</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_headless.cc">
<Filter>ballistica\base\app_adapter</Filter>
</ClCompile>
@ -244,18 +250,81 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\component\sprite_component.h">
<Filter>ballistica\base\graphics\component</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\framebuffer_object_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys.cc">
<Filter>ballistica\base\graphics\gl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.cc">
<Filter>ballistica\base\graphics\gl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_asset_data_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_dual_texture_full_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_object_split_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_full_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_split_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_smoke_full_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_sprite_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_blur_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_object_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_post_process_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_shield_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_simple_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_smoke_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_sprite_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\render_target_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.cc">
<Filter>ballistica\base\graphics\gl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\texture_data_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\graphics.cc">
<Filter>ballistica\base\graphics</Filter>
</ClCompile>
@ -1880,6 +1949,8 @@
<Filter Include="ballistica\base\graphics" />
<Filter Include="ballistica\base\graphics\component" />
<Filter Include="ballistica\base\graphics\gl" />
<Filter Include="ballistica\base\graphics\gl\mesh" />
<Filter Include="ballistica\base\graphics\gl\program" />
<Filter Include="ballistica\base\graphics\mesh" />
<Filter Include="ballistica\base\graphics\renderer" />
<Filter Include="ballistica\base\graphics\support" />

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View File

@ -186,6 +186,8 @@
<ItemGroup>
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter.cc" />
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter.h" />
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.cc" />
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.h" />
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_headless.cc" />
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter_headless.h" />
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_sdl.cc" />
@ -265,10 +267,31 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\component\special_component.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\component\sprite_component.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\component\sprite_component.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\framebuffer_object_gl.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_asset_data_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_dual_texture_full_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_object_split_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_full_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_split_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_smoke_full_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_sprite_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_blur_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_object_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_post_process_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_shield_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_simple_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_smoke_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_sprite_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\render_target_gl.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.h" />
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\texture_data_gl.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\graphics.cc" />
<ClInclude Include="..\..\src\ballistica\base\graphics\graphics.h" />
<ClCompile Include="..\..\src\ballistica\base\graphics\graphics_server.cc" />

View File

@ -7,6 +7,12 @@
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter.h">
<Filter>ballistica\base\app_adapter</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.cc">
<Filter>ballistica\base\app_adapter</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\app_adapter\app_adapter_apple.h">
<Filter>ballistica\base\app_adapter</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\app_adapter\app_adapter_headless.cc">
<Filter>ballistica\base\app_adapter</Filter>
</ClCompile>
@ -244,18 +250,81 @@
<ClInclude Include="..\..\src\ballistica\base\graphics\component\sprite_component.h">
<Filter>ballistica\base\graphics\component</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\framebuffer_object_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys.cc">
<Filter>ballistica\base\graphics\gl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.cc">
<Filter>ballistica\base\graphics\gl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\gl_sys_windows.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_asset_data_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_dual_texture_full_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_object_split_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_full_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_simple_split_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_smoke_full_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\mesh\mesh_data_sprite_gl.h">
<Filter>ballistica\base\graphics\gl\mesh</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_blur_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_object_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_post_process_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_shield_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_simple_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_smoke_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\program\program_sprite_gl.h">
<Filter>ballistica\base\graphics\gl\program</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\render_target_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.cc">
<Filter>ballistica\base\graphics\gl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\renderer_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ballistica\base\graphics\gl\texture_data_gl.h">
<Filter>ballistica\base\graphics\gl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\graphics\graphics.cc">
<Filter>ballistica\base\graphics</Filter>
</ClCompile>
@ -1880,6 +1949,8 @@
<Filter Include="ballistica\base\graphics" />
<Filter Include="ballistica\base\graphics\component" />
<Filter Include="ballistica\base\graphics\gl" />
<Filter Include="ballistica\base\graphics\gl\mesh" />
<Filter Include="ballistica\base\graphics\gl\program" />
<Filter Include="ballistica\base\graphics\mesh" />
<Filter Include="ballistica\base\graphics\renderer" />
<Filter Include="ballistica\base\graphics\support" />

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerWorkingDirectory>$(TargetDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View File

@ -9,12 +9,12 @@
"src/ballistica/base/graphics/texture/dds.h",
"src/ballistica/base/graphics/texture/ktx.cc",
"src/ballistica/core/platform/android/android_gl3.h",
"src/ballistica/core/platform/apple/app_delegate.h",
"src/ballistica/core/platform/apple/scripting_bridge_music.h",
"src/ballistica/base/platform/apple/app_delegate.h",
"src/ballistica/base/platform/apple/scripting_bridge_music.h",
"src/ballistica/core/platform/android/utf8/checked.h",
"src/ballistica/core/platform/android/utf8/unchecked.h",
"src/ballistica/core/platform/android/utf8/core.h",
"src/ballistica/core/platform/apple/sdl_main_mac.h",
"src/ballistica/base/platform/apple/sdl_main_mac.h",
"src/ballistica/base/platform/oculus/main_rift.cc",
"src/ballistica/core/platform/android/android_gl3.c"
],

View File

@ -246,6 +246,7 @@ ctx.filter_file_extensions = {
'.sh',
'.sln',
'.vcxproj',
'.user',
'.cmd',
'.hlsl',
'.gradle',

View File

@ -27,6 +27,7 @@ from _babase import (
apptime,
apptimer,
AppTimer,
can_toggle_fullscreen,
charstr,
clipboard_get_text,
clipboard_has_text,
@ -51,7 +52,6 @@ from _babase import (
get_string_width,
get_v1_cloud_log_file_path,
getsimplesound,
has_gamma_control,
has_user_run_commands,
have_chars,
have_permission,
@ -90,6 +90,8 @@ from _babase import (
shutdown_suppress_end,
shutdown_suppress_count,
SimpleSound,
supports_max_fps,
supports_vsync,
unlock_all_input,
user_agent_string,
Vec3,
@ -192,6 +194,7 @@ __all__ = [
'apptimer',
'AppTimer',
'Call',
'can_toggle_fullscreen',
'charstr',
'clipboard_get_text',
'clipboard_has_text',
@ -230,7 +233,6 @@ __all__ = [
'getclass',
'getsimplesound',
'handle_leftover_v1_cloud_log_file',
'has_gamma_control',
'has_user_run_commands',
'have_chars',
'have_permission',
@ -297,6 +299,8 @@ __all__ = [
'storagename',
'StringEditAdapter',
'StringEditSubsystem',
'supports_max_fps',
'supports_vsync',
'TeamNotFoundError',
'timestring',
'UIScale',

View File

@ -736,7 +736,7 @@ class App:
if self.state is self.State.PAUSED:
self._on_resume()
# Handle initially entering or returning to other states.
# Entering or returning to running state
if self._initial_sign_in_completed and self._meta_scan_completed:
if self.state != self.State.RUNNING:
self.state = self.State.RUNNING
@ -744,6 +744,7 @@ class App:
if not self._called_on_running:
self._called_on_running = True
self._on_running()
# Entering or returning to loading state:
elif self._init_completed:
if self.state is not self.State.LOADING:
self.state = self.State.LOADING
@ -751,6 +752,8 @@ class App:
if not self._called_on_loading:
self._called_on_loading = True
self._on_loading()
# Entering or returning to initing state:
elif self._native_bootstrapping_completed:
if self.state is not self.State.INITING:
self.state = self.State.INITING
@ -758,13 +761,21 @@ class App:
if not self._called_on_initing:
self._called_on_initing = True
self._on_initing()
else:
# Only possibility left is app-start. We shouldn't be
# getting called before at least that happens.
assert self._native_start_called
assert self.state is self.State.NOT_RUNNING
if bool(True):
# Entering or returning to native bootstrapping:
elif self._native_start_called:
if self.state is not self.State.NATIVE_BOOTSTRAPPING:
self.state = self.State.NATIVE_BOOTSTRAPPING
_babase.lifecyclelog('app state native bootstrapping')
else:
# Only logical possibility left is NOT_RUNNING, in which
# case we should not be getting called.
logging.warning(
'App._update_state called while in %s state;'
' should not happen.',
self.state.value,
stack_info=True,
)
async def _shutdown(self) -> None:
import asyncio

View File

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

View File

@ -31,6 +31,7 @@ from babase import (
apptimer,
AppTimer,
Call,
can_toggle_fullscreen,
charstr,
clipboard_is_supported,
clipboard_set_text,
@ -52,7 +53,6 @@ from babase import (
get_string_width,
get_type_name,
getclass,
has_gamma_control,
have_permission,
in_logic_thread,
increment_analytics_count,
@ -76,6 +76,8 @@ from babase import (
set_low_level_config_value,
set_ui_input_device,
SpecialChar,
supports_max_fps,
supports_vsync,
timestring,
UIScale,
unlock_all_input,
@ -136,6 +138,7 @@ __all__ = [
'buttonwidget',
'Call',
'can_show_ad',
'can_toggle_fullscreen',
'charstr',
'checkboxwidget',
'clipboard_is_supported',
@ -165,7 +168,6 @@ __all__ = [
'getmesh',
'getsound',
'gettexture',
'has_gamma_control',
'has_video_ads',
'have_incentivized_ad',
'have_permission',
@ -205,6 +207,8 @@ __all__ = [
'show_online_score_ui',
'Sound',
'SpecialChar',
'supports_max_fps',
'supports_vsync',
'Texture',
'textwidget',
'timestring',

View File

@ -243,6 +243,7 @@ class AdvancedSettingsWindow(bui.Window):
# Don't rebuild if the menu is open or if our language and
# language-list hasn't changed.
# NOTE - although we now support widgets updating their own
# translations, we still change the label formatting on the language
# menu based on the language so still need this. ...however we could

View File

@ -4,12 +4,15 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, cast
from bauiv1lib.popup import PopupMenu
from bauiv1lib.config import ConfigCheckBox, ConfigNumberEdit
from bauiv1lib.config import ConfigCheckBox
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Any
class GraphicsSettingsWindow(bui.Window):
"""Window for graphics settings."""
@ -42,23 +45,23 @@ class GraphicsSettingsWindow(bui.Window):
uiscale = app.ui_v1.uiscale
width = 450.0
height = 302.0
self._max_fps_dirty = False
self._last_max_fps_set_time = bui.apptime()
self._last_max_fps_str = ''
self._show_fullscreen = False
fullscreen_spacing_top = spacing * 0.2
fullscreen_spacing = spacing * 1.2
if uiscale == bui.UIScale.LARGE and app.classic.platform != 'android':
if bui.can_toggle_fullscreen():
self._show_fullscreen = True
height += fullscreen_spacing + fullscreen_spacing_top
show_gamma = False
gamma_spacing = spacing * 1.3
if bui.has_gamma_control():
show_gamma = True
height += gamma_spacing
show_vsync = bui.supports_vsync()
show_tv_mode = not bui.app.env.vr
show_vsync = False
if app.classic.platform == 'mac':
show_vsync = True
show_max_fps = bui.supports_max_fps()
if show_max_fps:
height += 50
show_resolution = True
if app.env.vr:
@ -70,7 +73,7 @@ class GraphicsSettingsWindow(bui.Window):
assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale
base_scale = (
2.4
2.0
if uiscale is bui.UIScale.SMALL
else 1.5
if uiscale is bui.UIScale.MEDIUM
@ -91,19 +94,20 @@ class GraphicsSettingsWindow(bui.Window):
)
)
btn = bui.buttonwidget(
back_button = bui.buttonwidget(
parent=self._root_widget,
position=(35, height - 50),
size=(120, 60),
# size=(120, 60),
size=(60, 60),
scale=0.8,
text_scale=1.2,
autoselect=True,
label=bui.Lstr(resource='backText'),
button_type='back',
label=bui.charstr(bui.SpecialChar.BACK),
button_type='backSmall',
on_activate_call=self._back,
)
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
bui.containerwidget(edit=self._root_widget, cancel_button=back_button)
bui.textwidget(
parent=self._root_widget,
@ -115,15 +119,7 @@ class GraphicsSettingsWindow(bui.Window):
v_align='top',
)
bui.buttonwidget(
edit=btn,
button_type='backSmall',
size=(60, 60),
label=bui.charstr(bui.SpecialChar.BACK),
)
self._fullscreen_checkbox: bui.Widget | None = None
self._gamma_controls: ConfigNumberEdit | None = None
if self._show_fullscreen:
v -= fullscreen_spacing_top
self._fullscreen_checkbox = ConfigCheckBox(
@ -149,34 +145,10 @@ class GraphicsSettingsWindow(bui.Window):
self._have_selected_child = True
v -= fullscreen_spacing
if show_gamma:
self._gamma_controls = gmc = ConfigNumberEdit(
parent=self._root_widget,
position=(90, v),
configkey='Screen Gamma',
displayname=bui.Lstr(resource=self._r + '.gammaText'),
minval=0.1,
maxval=2.0,
increment=0.1,
xoffset=-70,
textscale=0.85,
)
if bui.app.ui_v1.use_toolbars:
bui.widget(
edit=gmc.plusbutton,
right_widget=bui.get_special_widget('party_button'),
)
if not self._have_selected_child:
bui.containerwidget(
edit=self._root_widget, selected_child=gmc.minusbutton
)
self._have_selected_child = True
v -= gamma_spacing
self._selected_color = (0.5, 1, 0.5, 1)
self._unselected_color = (0.7, 0.7, 0.7, 1)
# quality
# Quality
bui.textwidget(
parent=self._root_widget,
position=(60, v),
@ -208,7 +180,7 @@ class GraphicsSettingsWindow(bui.Window):
on_value_change_call=self._set_quality,
)
# texture controls
# Texture controls
bui.textwidget(
parent=self._root_widget,
position=(230, v),
@ -244,8 +216,9 @@ class GraphicsSettingsWindow(bui.Window):
h_offs = 0
resolution_popup: PopupMenu | None = None
if show_resolution:
# resolution
bui.textwidget(
parent=self._root_widget,
position=(h_offs + 60, v),
@ -258,32 +231,17 @@ class GraphicsSettingsWindow(bui.Window):
v_align='center',
)
# on standard android we have 'Auto', 'Native', and a few
# HD standards
# On standard android we have 'Auto', 'Native', and a few
# HD standards.
if app.classic.platform == 'android':
# on cardboard/daydream android we have a few
# render-target-scale options
if app.classic.subplatform == 'cardboard':
rawval = bui.app.config.resolve('GVR Render Target Scale')
current_res_cardboard = (
str(
min(
100,
max(
10,
int(
round(
bui.app.config.resolve(
'GVR Render Target Scale'
)
* 100.0
)
),
),
)
)
+ '%'
str(min(100, max(10, int(round(rawval * 100.0))))) + '%'
)
PopupMenu(
resolution_popup = PopupMenu(
parent=self._root_widget,
position=(h_offs + 60, v - 50),
width=120,
@ -301,16 +259,16 @@ class GraphicsSettingsWindow(bui.Window):
bui.Lstr(resource='nativeText'),
]
for res in [1440, 1080, 960, 720, 480]:
# nav bar is 72px so lets allow for that in what
# choices we show
# Nav bar is 72px so lets allow for that in what
# choices we show.
if native_res[1] >= res - 72:
res_str = str(res) + 'p'
res_str = f'{res}p'
choices.append(res_str)
choices_display.append(bui.Lstr(value=res_str))
current_res_android = bui.app.config.resolve(
'Resolution (Android)'
)
PopupMenu(
resolution_popup = PopupMenu(
parent=self._root_widget,
position=(h_offs + 60, v - 50),
width=120,
@ -325,26 +283,11 @@ class GraphicsSettingsWindow(bui.Window):
# set pixel-scale instead.
current_res = bui.get_display_resolution()
if current_res is None:
rawval = bui.app.config.resolve('Screen Pixel Scale')
current_res2 = (
str(
min(
100,
max(
10,
int(
round(
bui.app.config.resolve(
'Screen Pixel Scale'
)
* 100.0
)
),
),
)
)
+ '%'
str(min(100, max(10, int(round(rawval * 100.0))))) + '%'
)
PopupMenu(
resolution_popup = PopupMenu(
parent=self._root_widget,
position=(h_offs + 60, v - 50),
width=120,
@ -355,11 +298,16 @@ class GraphicsSettingsWindow(bui.Window):
)
else:
raise RuntimeError(
'obsolete path; discrete resolutions'
'obsolete code path; discrete resolutions'
' no longer supported'
)
if resolution_popup is not None:
bui.widget(
edit=resolution_popup.get_button(),
left_widget=back_button,
)
# vsync
vsync_popup: PopupMenu | None = None
if show_vsync:
bui.textwidget(
parent=self._root_widget,
@ -372,8 +320,7 @@ class GraphicsSettingsWindow(bui.Window):
h_align='center',
v_align='center',
)
PopupMenu(
vsync_popup = PopupMenu(
parent=self._root_widget,
position=(230, v - 50),
width=150,
@ -387,8 +334,59 @@ class GraphicsSettingsWindow(bui.Window):
current_choice=bui.app.config.resolve('Vertical Sync'),
on_value_change_call=self._set_vsync,
)
if resolution_popup is not None:
bui.widget(
edit=vsync_popup.get_button(),
left_widget=resolution_popup.get_button(),
)
if resolution_popup is not None and vsync_popup is not None:
bui.widget(
edit=resolution_popup.get_button(),
right_widget=vsync_popup.get_button(),
)
v -= 90
self._max_fps_text: bui.Widget | None = None
if show_max_fps:
v -= 5
bui.textwidget(
parent=self._root_widget,
position=(155, v + 10),
size=(0, 0),
text=bui.Lstr(resource=self._r + '.maxFPSText'),
color=bui.app.ui_v1.heading_color,
scale=0.9,
maxwidth=90,
h_align='right',
v_align='center',
)
max_fps_str = str(bui.app.config.resolve('Max FPS'))
self._last_max_fps_str = max_fps_str
self._max_fps_text = bui.textwidget(
parent=self._root_widget,
position=(170, v - 5),
size=(105, 30),
text=max_fps_str,
max_chars=5,
editable=True,
h_align='left',
v_align='center',
on_return_press_call=self._on_max_fps_return_press,
)
v -= 45
if self._max_fps_text is not None and resolution_popup is not None:
bui.widget(
edit=resolution_popup.get_button(),
down_widget=self._max_fps_text,
)
bui.widget(
edit=self._max_fps_text,
up_widget=resolution_popup.get_button(),
)
fpsc = ConfigCheckBox(
parent=self._root_widget,
position=(69, v - 6),
@ -398,9 +396,17 @@ class GraphicsSettingsWindow(bui.Window):
displayname=bui.Lstr(resource=self._r + '.showFPSText'),
maxwidth=130,
)
if self._max_fps_text is not None:
bui.widget(
edit=self._max_fps_text,
down_widget=fpsc.widget,
)
bui.widget(
edit=fpsc.widget,
up_widget=self._max_fps_text,
)
# (tv mode doesnt apply to vr)
if not bui.app.env.vr:
if show_tv_mode:
tvc = ConfigCheckBox(
parent=self._root_widget,
position=(240, v - 6),
@ -410,13 +416,8 @@ class GraphicsSettingsWindow(bui.Window):
displayname=bui.Lstr(resource=self._r + '.tvBorderText'),
maxwidth=130,
)
# grumble..
bui.widget(edit=fpsc.widget, right_widget=tvc.widget)
try:
pass
except Exception:
logging.exception('Exception wiring up graphics settings UI.')
bui.widget(edit=tvc.widget, left_widget=fpsc.widget)
v -= spacing
@ -429,6 +430,10 @@ class GraphicsSettingsWindow(bui.Window):
def _back(self) -> None:
from bauiv1lib.settings import allsettings
# Applying max-fps takes a few moments. Apply if it hasn't been
# yet.
self._apply_max_fps()
bui.containerwidget(
edit=self._root_widget, transition=self._transition_out
)
@ -469,7 +474,60 @@ class GraphicsSettingsWindow(bui.Window):
cfg['Vertical Sync'] = val
cfg.apply_and_commit()
def _on_max_fps_return_press(self) -> None:
self._apply_max_fps()
bui.containerwidget(
edit=self._root_widget, selected_child=cast(bui.Widget, 0)
)
def _apply_max_fps(self) -> None:
if not self._max_fps_dirty or not self._max_fps_text:
return
val: Any = bui.textwidget(query=self._max_fps_text)
assert isinstance(val, str)
# If there's a broken value, replace it with the default.
try:
ival = int(val)
except ValueError:
ival = bui.app.config.default_value('Max FPS')
assert isinstance(ival, int)
# Clamp to reasonable limits (allow -1 to mean no max).
if ival != -1:
ival = max(10, ival)
ival = min(99999, ival)
# Store it to the config.
cfg = bui.app.config
cfg['Max FPS'] = ival
cfg.apply_and_commit()
# Update the display if we changed the value.
if str(ival) != val:
bui.textwidget(edit=self._max_fps_text, text=str(ival))
self._max_fps_dirty = False
def _update_controls(self) -> None:
if self._max_fps_text is not None:
# Keep track of when the max-fps value changes. Once it
# remains stable for a few moments, apply it.
val: Any = bui.textwidget(query=self._max_fps_text)
assert isinstance(val, str)
if val != self._last_max_fps_str:
# Oop; it changed. Note the time and the fact that we'll
# need to apply it at some point.
self._max_fps_dirty = True
self._last_max_fps_str = val
self._last_max_fps_set_time = bui.apptime()
else:
# If its been stable long enough, apply it.
if (
self._max_fps_dirty
and bui.apptime() - self._last_max_fps_set_time > 1.0
):
self._apply_max_fps()
if self._show_fullscreen:
bui.checkboxwidget(
edit=self._fullscreen_checkbox,

View File

@ -2,11 +2,19 @@
#include "ballistica/base/app_adapter/app_adapter.h"
#if BA_OSTYPE_ANDROID
#include "ballistica/base/app_adapter/app_adapter_android.h"
#endif
#include "ballistica/base/app_adapter/app_adapter_apple.h"
#include "ballistica/base/app_adapter/app_adapter_headless.h"
#include "ballistica/base/app_adapter/app_adapter_sdl.h"
#include "ballistica/base/app_adapter/app_adapter_vr.h"
#include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/graphics/renderer/renderer.h"
#include "ballistica/base/input/input.h"
#include "ballistica/base/networking/network_reader.h"
#include "ballistica/base/networking/networking.h"
#include "ballistica/base/platform/base_platform.h"
#include "ballistica/base/support/stress_test.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/shared/foundation/event_loop.h"
@ -14,20 +22,47 @@
namespace ballistica::base {
auto AppAdapter::Create() -> AppAdapter* {
assert(g_core);
// TEMP - need to init sdl on our legacy mac build even though its not
// technically an SDL app. Kill this once the old mac build is gone.
#if BA_LEGACY_MACOS_BUILD
AppAdapterSDL::InitSDL();
#endif
AppAdapter* app_adapter{};
#if BA_HEADLESS_BUILD
app_adapter = new AppAdapterHeadless();
#elif BA_OSTYPE_ANDROID
app_adapter = new AppAdapterAndroid();
#elif BA_XCODE_BUILD
app_adapter = new AppAdapterApple();
#elif BA_RIFT_BUILD
// Rift build can spin up in either VR or regular mode.
if (g_core->vr_mode) {
app_adapter = new AppAdapterVR();
} else {
app_adapter = new AppAdapterSDL();
}
#elif BA_CARDBOARD_BUILD
app_adapter = new AppAdapterVR();
#elif BA_SDL_BUILD
app_adapter = new AppAdapterSDL();
#else
#error No app adapter defined for this build.
#endif
assert(app_adapter);
return app_adapter;
}
AppAdapter::AppAdapter() = default;
AppAdapter::~AppAdapter() = default;
void AppAdapter::LogicThreadDoApplyAppConfig() {
assert(g_base->InLogicThread());
}
auto AppAdapter::ManagesEventLoop() const -> bool {
// We have 2 redundant values for essentially the same thing;
// should get rid of IsEventPushMode() once we've created
// App subclasses for our various platforms.
return !g_core->platform->IsEventPushMode();
}
auto AppAdapter::ManagesMainThreadEventLoop() const -> bool { return true; }
void AppAdapter::OnMainThreadStartApp() {
assert(g_base);
@ -37,11 +72,12 @@ void AppAdapter::OnMainThreadStartApp() {
// Add some common input devices where applicable. More specific ones (SDL
// Joysticks, etc.) get added in subclasses.
// If we've got a nice themed hardware cursor, show it. Otherwise we'll
// render it manually, which is laggier but gets the job done.
g_core->platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor());
// FIXME: This stuff should probably go elsewhere.
if (!g_core->HeadlessMode()) {
// If we've got a nice themed hardware cursor, show it. Otherwise we'll
// render it manually, which is laggier but gets the job done.
g_base->platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor());
// On desktop systems we just assume keyboard input exists and add it
// immediately.
if (g_core->platform->IsRunningOnDesktop()) {
@ -56,74 +92,82 @@ void AppAdapter::OnMainThreadStartApp() {
}
}
void AppAdapter::DrawFrame(bool during_resize) {
assert(g_base->InGraphicsThread());
void AppAdapter::OnAppStart() { assert(g_base->InLogicThread()); }
void AppAdapter::OnAppPause() { assert(g_base->InLogicThread()); }
void AppAdapter::OnAppResume() { assert(g_base->InLogicThread()); }
void AppAdapter::OnAppShutdown() { assert(g_base->InLogicThread()); }
void AppAdapter::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
void AppAdapter::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
void AppAdapter::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
// It's possible to be asked to draw before we're ready.
if (!g_base->graphics_server || !g_base->graphics_server->renderer()) {
return;
}
// void AppAdapter::DrawFrame(bool during_resize) {
// assert(g_base->InGraphicsThread());
millisecs_t starttime = g_core->GetAppTimeMillisecs();
// // It's possible to be asked to draw before we're ready.
// if (!g_base->graphics_server || !g_base->graphics_server->renderer()) {
// return;
// }
// A resize-draw event means that we're drawing due to a window resize.
// In this case we ignore regular draw events for a short while
// afterwards which makes resizing smoother.
//
// FIXME: should figure out the *correct* way to handle this; I believe
// the underlying cause here is some sort of context contention across
// threads.
if (during_resize) {
last_resize_draw_event_time_ = starttime;
} else {
if (starttime - last_resize_draw_event_time_ < (1000 / 30)) {
return;
}
}
g_base->graphics_server->TryRender();
RunRenderUpkeepCycle();
}
// millisecs_t starttime = g_core->GetAppTimeMillisecs();
void AppAdapter::RunRenderUpkeepCycle() {
// This should only be firing if the OS is handling the event loop.
assert(!ManagesEventLoop());
// // A resize-draw event means that we're drawing due to a window resize.
// // In this case we ignore regular draw events for a short while
// // afterwards which makes resizing smoother.
// //
// // FIXME: should figure out the *correct* way to handle this; I believe
// // the underlying cause here is some sort of context contention across
// // threads.
// if (during_resize) {
// last_resize_draw_event_time_ = starttime;
// } else {
// if (starttime - last_resize_draw_event_time_ < (1000 / 30)) {
// return;
// }
// }
// g_base->graphics_server->TryRender();
// // RunRenderUpkeepCycle();
// }
// Pump the main event loop (when we're being driven by frame-draw
// callbacks, this is the only place that gets done).
g_core->main_event_loop()->RunSingleCycle();
// void AppAdapter::RunRenderUpkeepCycle() {
// // This should only be firing if the OS is handling the event loop.
// assert(!ManagesMainThreadEventLoop());
// Now do the general app event cycle for whoever needs to process things.
// FIXME KILL THIS.
RunEvents();
}
// // Pump the main event loop (when we're being driven by frame-draw
// // callbacks, this is the only place that gets done).
// g_core->main_event_loop()->RunSingleCycle();
// // Now do the general app event cycle for whoever needs to process things.
// // FIXME KILL THIS.
// RunEvents();
// }
// FIXME KILL THIS.
void AppAdapter::RunEvents() {
// There's probably a better place for this.
g_base->stress_test()->Update();
// void AppAdapter::RunEvents() {
// There's probably a better place for this.
// g_base->stress_test()->Update();
// Give platforms a chance to pump/handle their own events.
//
// FIXME: now that we have app class overrides, platform should really not
// be doing event handling. (need to fix Rift build in this regard).
g_core->platform->RunEvents();
}
// Give platforms a chance to pump/handle their own events.
//
// FIXME: now that we have app class overrides, platform should really not
// be doing event handling. (need to fix Rift build in this regard).
// g_core->platform->RunEvents();
// }
void AppAdapter::UpdatePauseResume_() {
if (app_paused_) {
// Unpause if no one wants pause.
if (!app_pause_requested_) {
OnAppResume_();
app_paused_ = false;
}
} else {
// OnAppPause if anyone wants.
if (app_pause_requested_) {
OnAppPause_();
app_paused_ = true;
}
}
}
// void AppAdapter::UpdatePauseResume_() {
// if (app_paused_) {
// // Unpause if no one wants pause.
// if (!app_pause_requested_) {
// OnAppResume_();
// app_paused_ = false;
// }
// } else {
// // OnAppPause if anyone wants.
// if (app_pause_requested_) {
// OnAppPause_();
// app_paused_ = true;
// }
// }
// }
void AppAdapter::OnAppPause_() {
assert(g_core->InMainThread());
@ -144,7 +188,7 @@ void AppAdapter::OnAppPause_() {
void AppAdapter::OnAppResume_() {
assert(g_core->InMainThread());
last_app_resume_time_ = g_core->GetAppTimeMillisecs();
// last_app_resume_time_ = g_core->GetAppTimeMillisecs();
// Spin all event-loops back up.
EventLoop::SetEventLoopsPaused(false);
@ -173,6 +217,13 @@ void AppAdapter::OnAppResume_() {
void AppAdapter::PauseApp() {
assert(g_core);
assert(g_core->InMainThread());
if (app_paused_) {
Log(LogLevel::kWarning,
"AppAdapter::PauseApp() called with app already paused.");
return;
}
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
// Apple mentioned 5 seconds to run stuff once backgrounded or they bring
@ -181,9 +232,10 @@ void AppAdapter::PauseApp() {
g_core->platform->DebugLog(
"PauseApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs()));
assert(!app_pause_requested_);
app_pause_requested_ = true;
UpdatePauseResume_();
// assert(!app_pause_requested_);
// app_pause_requested_ = true;
OnAppPause_();
// UpdatePauseResume_();
// We assume that the OS will completely suspend our process the moment we
// return from this call (though this is not technically true on all
@ -219,13 +271,21 @@ void AppAdapter::PauseApp() {
}
void AppAdapter::ResumeApp() {
assert(g_core && g_core->InMainThread());
assert(g_core);
assert(g_core->InMainThread());
if (!app_paused_) {
Log(LogLevel::kWarning,
"AppAdapter::ResumeApp() called with app not in paused state.");
return;
}
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
g_core->platform->DebugLog(
"ResumeApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs()));
assert(app_pause_requested_);
app_pause_requested_ = false;
UpdatePauseResume_();
// assert(app_pause_requested_);
// app_pause_requested_ = false;
// UpdatePauseResume_();
OnAppResume_();
if (g_buildconfig.debug_build()) {
Log(LogLevel::kDebug,
"ResumeApp() completed in "
@ -235,21 +295,18 @@ void AppAdapter::ResumeApp() {
}
}
void AppAdapter::DidFinishRenderingFrame(FrameDef* frame) {}
void AppAdapter::PrimeMainThreadEventPump() {
assert(!ManagesEventLoop());
// Need to release the GIL while we're doing this so other thread
// can do their Python-y stuff.
Python::ScopedInterpreterLockRelease release;
// Pump events manually until a screen gets created.
// At that point we use frame-draws to drive our event loop.
while (!g_base->graphics_server->initial_screen_created()) {
g_core->main_event_loop()->RunSingleCycle();
core::CorePlatform::SleepMillisecs(1);
}
void AppAdapter::RunMainThreadEventLoopToCompletion() {
FatalError("RunMainThreadEventLoopToCompletion is not implemented here.");
}
void AppAdapter::DoExitMainThreadEventLoop() {
FatalError("DoExitMainThreadEventLoop is not implemented here.");
}
auto AppAdapter::CanToggleFullscreen() -> bool const { return false; }
auto AppAdapter::SupportsVSync() -> bool const { return false; }
auto AppAdapter::SupportsMaxFPS() -> bool const { return false; }
} // namespace ballistica::base

View File

@ -3,40 +3,54 @@
#ifndef BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_H_
#define BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_H_
#include <string>
#include "ballistica/base/base.h"
#include "ballistica/shared/generic/lambda_runnable.h"
namespace ballistica::base {
/// Adapts app behavior specific to a particular paradigm and/or api
/// environment. For example, 'Headless', 'VROculus', 'SDL', etc. Multiple
/// of these may be supported on a single platform, unlike the Platform
/// classes where generally there is a single one for the whole platform.
/// For example, on Windows, we might have GUI, VR, and Headless
/// AppAdapters, but they all might share the same CorePlatform and
/// BasePlatform classes.
/// environment. For example, 'Headless', 'VROculus', 'SDL', etc. These may
/// be mixed & matched with platform classes to define a build. For example,
/// on Windows, we might have SDL, VR, and Headless AppAdapters, but they
/// all might share the same CorePlatform and BasePlatform classes.
class AppAdapter {
public:
AppAdapter();
virtual ~AppAdapter();
/// Instantiate the AppAdapter subclass for the current build.
static auto Create() -> AppAdapter*;
/// Called in the main thread when the app is being started.
virtual void OnMainThreadStartApp();
/// Return whether this class runs its own event loop.
auto ManagesEventLoop() const -> bool;
// Logic thread callbacks.
virtual void OnAppStart();
virtual void OnAppPause();
virtual void OnAppResume();
virtual void OnAppShutdown();
virtual void OnAppShutdownComplete();
virtual void OnScreenSizeChange();
virtual void DoApplyAppConfig();
/// Called for non-event-loop-managing apps to give them an opportunity to
/// ensure they are self-sustaining. For instance, an app relying on
/// frame-draws for its main thread event processing may need to manually
/// pump events until a screen-creation event goes through which should
/// keep things running thereafter.
virtual void PrimeMainThreadEventPump();
/// Return whether this class manages the main thread event loop itself.
/// Default is true. If this is true, RunMainThreadEventLoopToCompletion()
/// will be called to run the app. This should return false on builds
/// where the OS manages the main thread event loop and we just sit in it
/// and receive events via callbacks/etc.
virtual auto ManagesMainThreadEventLoop() const -> bool;
/// Handle any pending OS events. On normal graphical builds this is
/// triggered by RunRenderUpkeepCycle(); timer intervals for headless
/// builds, etc. Should process any pending OS events, etc.
virtual void RunEvents();
/// When called, the main thread event loop should be run until
/// ExitMainThreadEventLoop() is called. This will only be called if
/// ManagesMainThreadEventLoop() returns true.
virtual void RunMainThreadEventLoopToCompletion();
/// Called when the main thread event loop should exit. Will only be
/// called if ManagesMainThreadEventLoop() returns true.
virtual void DoExitMainThreadEventLoop();
/// Push a call to be run in the app's main thread.
template <typename F>
void PushMainThreadCall(const F& lambda) {
DoPushMainThreadRunnable(NewLambdaRunnableUnmanaged(lambda));
}
/// Put the app into a paused state. Should be called from the main
/// thread. Pauses work, closes network sockets, etc. May correspond to
@ -53,34 +67,29 @@ class AppAdapter {
auto app_paused() const { return app_paused_; }
/// The last time the app was resumed (uses GetAppTimeMillisecs() value).
auto last_app_resume_time() const { return last_app_resume_time_; }
/// Return whether this AppAdapter supports a 'fullscreen' toggle for its
/// display. This currently will simply affect whether that option is
/// available in display settings or via a hotkey.
virtual auto CanToggleFullscreen() -> bool const;
/// Attempt to draw a frame.
void DrawFrame(bool during_resize = false);
/// Return whether this AppAdapter supports vsync controls for its display.
virtual auto SupportsVSync() -> bool const;
/// Gets called when the app config is being applied. Note that this call
/// happens in the logic thread, so we should do any reading that needs to
/// happen in the logic thread and then forward the values to ourself back
/// in our main thread.
virtual void LogicThreadDoApplyAppConfig();
/// Return whether this AppAdapter supports max-fps controls for its display.
virtual auto SupportsMaxFPS() -> bool const;
/// Used on platforms where our main thread event processing is driven by
/// frame-draw commands given to us. This should be called after drawing a
/// frame in order to bring game state up to date and process OS events.
void RunRenderUpkeepCycle();
protected:
AppAdapter();
virtual ~AppAdapter();
/// Called by the graphics-server when drawing completes for a frame.
virtual void DidFinishRenderingFrame(FrameDef* frame);
/// Push a raw pointer Runnable to the platform's 'main' thread. The main
/// thread should call its RunAndLogErrors() method and then delete it.
virtual void DoPushMainThreadRunnable(Runnable* runnable) = 0;
private:
void UpdatePauseResume_();
void OnAppPause_();
void OnAppResume_();
bool app_pause_requested_{};
bool app_paused_{};
millisecs_t last_resize_draw_event_time_{};
millisecs_t last_app_resume_time_{};
};
} // namespace ballistica::base

View File

@ -0,0 +1,25 @@
// Released under the MIT License. See LICENSE for details.
#if BA_XCODE_BUILD
#include "ballistica/base/app_adapter/app_adapter_apple.h"
#include <BallisticaKit-Swift.h>
#include "ballistica/shared/ballistica.h"
namespace ballistica::base {
auto AppAdapterApple::ManagesMainThreadEventLoop() const -> bool {
// Nope; we run under a standard Cocoa/UIKit environment and they call us; we
// don't call them.
return false;
}
void AppAdapterApple::DoPushMainThreadRunnable(Runnable* runnable) {
// Kick this along to swift.
BallisticaKit::PushRawRunnableToMain(runnable);
}
} // namespace ballistica::base
#endif // BA_XCODE_BUILD

View File

@ -0,0 +1,26 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_APPLE_H_
#define BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_APPLE_H_
#if BA_XCODE_BUILD
#include "ballistica/base/app_adapter/app_adapter.h"
namespace ballistica::base {
class AppAdapterApple : public AppAdapter {
public:
auto ManagesMainThreadEventLoop() const -> bool override;
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
private:
};
} // namespace ballistica::base
#endif // BA_XCODE_BUILD
#endif // BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_APPLE_H_

View File

@ -3,20 +3,41 @@
#include "ballistica/base/app_adapter/app_adapter_headless.h"
#include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/shared/ballistica.h"
namespace ballistica::base {
// We could technically use the vanilla App class here since we're not
// changing anything.
AppAdapterHeadless::AppAdapterHeadless() {
// Handle a few misc things like stress-test updates.
// (SDL builds set up a similar timer so we need to also).
// This can probably go away at some point.
g_core->main_event_loop()->NewTimer(10, true, NewLambdaRunnable([this] {
assert(g_base->app_adapter);
g_base->app_adapter->RunEvents();
}));
AppAdapterHeadless::AppAdapterHeadless() {}
void AppAdapterHeadless::OnMainThreadStartApp() {
assert(g_core->InMainThread());
// We're not embedded into any sort of event system, so we just
// spin up our very own event loop for the main thread.
main_event_loop_ =
new EventLoop(EventLoopID::kMain, ThreadSource::kWrapCurrent);
}
void AppAdapterHeadless::DoApplyAppConfig() {
// Normal graphical app-adapters kick off screen creation here
// which then leads to remaining app bootstrapping. We create
// a 'null' screen purely for the same effect.
PushMainThreadCall([] { g_base->graphics_server->SetNullGraphics(); });
}
void AppAdapterHeadless::RunMainThreadEventLoopToCompletion() {
assert(g_core->InMainThread());
main_event_loop_->RunToCompletion();
}
void AppAdapterHeadless::DoPushMainThreadRunnable(Runnable* runnable) {
main_event_loop_->PushRunnable(runnable);
}
void AppAdapterHeadless::DoExitMainThreadEventLoop() {
assert(g_core->InMainThread());
main_event_loop_->Exit();
}
} // namespace ballistica::base

View File

@ -12,6 +12,18 @@ namespace ballistica::base {
class AppAdapterHeadless : public AppAdapter {
public:
AppAdapterHeadless();
void OnMainThreadStartApp() override;
void DoApplyAppConfig() override;
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
void RunMainThreadEventLoopToCompletion() override;
void DoExitMainThreadEventLoop() override;
private:
EventLoop* main_event_loop_{};
};
} // namespace ballistica::base

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
#ifndef BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_SDL_H_
#define BALLISTICA_BASE_APP_ADAPTER_APP_ADAPTER_SDL_H_
#include "ballistica/base/base.h"
#if BA_SDL_BUILD
#include <vector>
@ -10,51 +11,83 @@
#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/shared/math/vector2f.h"
// Predeclare for pointers.
struct SDL_Window;
namespace ballistica::base {
class AppAdapterSDL : public AppAdapter {
public:
static void InitSDL();
AppAdapterSDL();
void HandleSDLEvent(const SDL_Event& event);
void RunEvents() override;
void DidFinishRenderingFrame(FrameDef* frame) override;
void SetAutoVSync(bool enable);
void OnMainThreadStartApp() override;
/// Return g_base->app_adapter as an AppAdapterSDL. (assumes it actually is
/// one).
/// Return g_base->app_adapter as an AppAdapterSDL. (Assumes it actually
/// is one).
static AppAdapterSDL* Get() {
assert(g_base && g_base->app_adapter != nullptr);
assert(dynamic_cast<AppAdapterSDL*>(g_base->app_adapter)
== static_cast<AppAdapterSDL*>(g_base->app_adapter));
return static_cast<AppAdapterSDL*>(g_base->app_adapter);
}
void SetInitialScreenDimensions(const Vector2f& dimensions);
AppAdapterSDL();
void OnMainThreadStartApp() override;
void DoApplyAppConfig() override;
auto CanToggleFullscreen() -> bool const override;
auto SupportsVSync() -> bool const override;
auto SupportsMaxFPS() -> bool const override;
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
void RunMainThreadEventLoopToCompletion() override;
void DoExitMainThreadEventLoop() override;
private:
static void SDLJoystickConnected_(int index);
static void SDLJoystickDisconnected_(int index);
void SetScreen_(bool fullscreen, int max_fps, VSyncRequest vsync_requested,
TextureQualityRequest texture_quality_requested,
GraphicsQualityRequest graphics_quality_requested);
void HandleSDLEvent_(const SDL_Event& event);
void UpdateScreenSizes_();
void ReloadRenderer_(bool fullscreen,
GraphicsQualityRequest graphics_quality_requested,
TextureQualityRequest texture_quality_requested);
void OnSDLJoystickAdded_(int index);
void OnSDLJoystickRemoved_(int index);
// Given an SDL joystick ID, returns our Ballistica input for it.
auto GetSDLJoystickInput_(int sdl_joystick_id) const -> JoystickInput*;
// The same but using sdl events.
auto GetSDLJoystickInput_(const SDL_Event* e) const -> JoystickInput*;
void DoSwap_();
void SwapBuffers_();
void UpdateAutoVSync_(int diff);
// void DoSwap_();
// void SwapBuffers_();
// void UpdateAutoVSync_(int diff);
void AddSDLInputDevice_(JoystickInput* input, int index);
void RemoveSDLInputDevice_(int index);
millisecs_t last_swap_time_{};
millisecs_t swap_start_time_{};
int too_slow_frame_count_{};
bool auto_vsync_{};
bool vsync_enabled_{true};
float average_vsync_fps_{60.0f};
int vsync_good_frame_count_{};
int vsync_bad_frame_count_{};
// millisecs_t last_swap_time_{};
// millisecs_t swap_start_time_{};
// int too_slow_frame_count_{};
// bool auto_vsync_{};
// bool vsync_enabled_{true};
// float average_vsync_fps_{60.0f};
// int vsync_good_frame_count_{};
// int vsync_bad_frame_count_{};
uint32_t sdl_runnable_event_id_{};
std::vector<JoystickInput*> sdl_joysticks_;
/// This is in points, not pixels.
Vector2f screen_dimensions_{1.0f, 1.0f};
Vector2f window_size_{1.0f, 1.0f};
SDL_Window* sdl_window_{};
void* sdl_gl_context_{};
// SDL_Surface* sdl_screen_surface_{};
bool done_{};
bool fullscreen_{};
VSyncRequest vsync_{VSyncRequest::kNever};
bool vsync_enabled_{};
microsecs_t oversleep{};
int max_fps_{60};
bool debug_log_sdl_frame_timing_{};
// std::unique_ptr<GLContext> gl_context_;
// TEMP
// friend class GLContext;
friend class GraphicsServer;
};
} // namespace ballistica::base

View File

@ -13,9 +13,11 @@ namespace ballistica::base {
AppAdapterVR::AppAdapterVR() {}
auto AppAdapterVR::ManagesMainThreadEventLoop() const -> bool { return false; }
void AppAdapterVR::PushVRSimpleRemoteStateCall(
const VRSimpleRemoteState& state) {
g_core->main_event_loop()->PushCall([this, state] {
g_base->app_adapter->PushMainThreadCall([this, state] {
// Convert this to a full hands state, adding in some simple elbow
// positioning of our own and left/right.
VRHandsState s;
@ -47,16 +49,19 @@ void AppAdapterVR::VRPreDraw() {
return;
}
assert(g_base->InGraphicsThread());
if (FrameDef* frame_def = g_base->graphics_server->GetRenderFrameDef()) {
// Note: this could be part of PreprocessRenderFrameDef but the non-vr
// path needs it to be separate since preprocess doesn't happen
// sometimes. Should probably clean that up.
g_base->graphics_server->RunFrameDefMeshUpdates(frame_def);
// FIXME - this is internal graphics-server details that the render-server
// should handle.
Log(LogLevel::kWarning, "FIXME: Have GraphicsServer handle VR drawing.");
// if (FrameDef* frame_def = g_base->graphics_server->GetRenderFrameDef()) {
// // Note: this could be part of PreprocessRenderFrameDef but the non-vr
// // path needs it to be separate since preprocess doesn't happen
// // sometimes. Should probably clean that up.
// g_base->graphics_server->RunFrameDefMeshUpdates(frame_def);
// store this for the duration of this frame
vr_render_frame_def_ = frame_def;
g_base->graphics_server->PreprocessRenderFrameDef(frame_def);
}
// // store this for the duration of this frame
// vr_render_frame_def_ = frame_def;
// g_base->graphics_server->PreprocessRenderFrameDef(frame_def);
// }
}
void AppAdapterVR::VRPostDraw() {
@ -69,7 +74,8 @@ void AppAdapterVR::VRPostDraw() {
g_base->graphics_server->FinishRenderFrameDef(vr_render_frame_def_);
vr_render_frame_def_ = nullptr;
}
RunRenderUpkeepCycle();
Log(LogLevel::kWarning, "WOULD RUN RENDER UPKEEP CYCLE");
// RunRenderUpkeepCycle();
}
void AppAdapterVR::VRSetHead(float tx, float ty, float tz, float yaw,
@ -118,6 +124,22 @@ void AppAdapterVR::VRDrawEye(int eye, float yaw, float pitch, float roll,
}
}
void AppAdapterVR::RunMainThreadEventLoopToCompletion() {
// FIXME - can basically copy sdl path here methinks.
FatalError(
"FIXME: IMPLEMENT AppAdapterVR::RunMainThreadEventLoopToCompletion");
// g_core->main_event_loop()->RunToCompletion();
}
void AppAdapterVR::DoPushMainThreadRunnable(Runnable* runnable) {
FatalError("FIXME: DoPushMainThreadRunnable unimplemented here.");
}
void AppAdapterVR::DoExitMainThreadEventLoop() {
FatalError("FIXME: IMPLEMENT AppAdapterVR::DoExitMainThreadEventLoop");
// g_core->main_event_loop()->Exit();
}
} // namespace ballistica::base
#endif // BA_VR_BUILD

View File

@ -19,8 +19,10 @@ class AppAdapterVR : public AppAdapter {
float r2 = 0.0f;
};
auto ManagesMainThreadEventLoop() const -> bool override;
/// Return g_app as a AppAdapterVR. (assumes it actually is one).
static auto get() -> AppAdapterVR* {
static auto Get() -> AppAdapterVR* {
assert(g_base != nullptr && g_base->app_adapter != nullptr);
assert(dynamic_cast<AppAdapterVR*>(g_base->app_adapter)
== static_cast<AppAdapterVR*>(g_base->app_adapter));
@ -39,6 +41,11 @@ class AppAdapterVR : public AppAdapter {
float tan_r, float tan_b, float tan_t, float eye_x,
float eye_y, float eye_z, int viewport_x, int viewport_y);
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
void RunMainThreadEventLoopToCompletion() override;
void DoExitMainThreadEventLoop() override;
private:
FrameDef* vr_render_frame_def_{};
};

View File

@ -19,9 +19,9 @@ const microsecs_t kAppModeMaxHeadlessDisplayStep{500000};
const microsecs_t kAppModeMinHeadlessDisplayStep{1000};
/// Represents 'what the app is doing'. The global app-mode can be switched
/// as the app is running. Be aware that, unlike the AppAdapter classes
/// which primarily deal with the main thread, most functionality here is
/// based in the logic thread.
/// as the app is running. The Python layer has its own Python AppMode
/// classes, and generally when one of them becomes active it calls down
/// to the C++ layer to make a corresponding C++ AppMode class active.
class AppMode {
public:
AppMode();
@ -33,13 +33,12 @@ class AppMode {
/// Called just before the app-mode ceases being the active one.
virtual void OnDeactivate();
/// Logic thread callbacks that run while the app-mode is active.
virtual void OnAppStart();
virtual void OnAppPause();
virtual void OnAppResume();
virtual void OnAppShutdown();
virtual void OnAppShutdownComplete();
/// Apply the app config.
virtual void DoApplyAppConfig();
/// Update the logic thread for a new display-time. Can be called at any

View File

@ -41,29 +41,29 @@ void AppModeEmpty::DrawWorld(base::FrameDef* frame_def) {
}
auto& grp(*hello_text_group_);
auto* pass = frame_def->overlay_pass();
SimpleComponent c(pass);
c.SetTransparent(true);
c.SetColor(0.7f, 0.0f, 1.0f, 1.0f);
c.PushTransform();
{
auto xf = c.ScopedTransform();
auto xoffs =
sinf(static_cast<float>(frame_def->display_time_millisecs()) / 600.0f);
auto yoffs =
cosf(static_cast<float>(frame_def->display_time_millisecs()) / 600.0f);
auto xoffs =
sinf(static_cast<float>(frame_def->display_time_millisecs()) / 600.0f);
auto yoffs =
cosf(static_cast<float>(frame_def->display_time_millisecs()) / 600.0f);
// Z value -1 will draw us under most everything.
c.Translate(pass->virtual_width() * 0.5f - 70.0f + xoffs * 200.0f,
pass->virtual_height() * 0.5f - 20.0f + yoffs * 200.0f, -1.0f);
c.Scale(2.0, 2.0);
// Z value -1 will draw us under most everything.
c.Translate(pass->virtual_width() * 0.5f - 70.0f + xoffs * 200.0f,
pass->virtual_height() * 0.5f - 20.0f + yoffs * 200.0f, -1.0f);
c.Scale(2.0, 2.0);
int text_elem_count = grp.GetElementCount();
for (int e = 0; e < text_elem_count; e++) {
c.SetTexture(grp.GetElementTexture(e));
c.SetFlatness(1.0f);
c.DrawMesh(grp.GetElementMesh(e));
int text_elem_count = grp.GetElementCount();
for (int e = 0; e < text_elem_count; e++) {
c.SetTexture(grp.GetElementTexture(e));
c.SetFlatness(1.0f);
c.DrawMesh(grp.GetElementMesh(e));
}
}
c.PopTransform();
c.Submit();
}

View File

@ -41,6 +41,7 @@ static void rgba8888_unpremultiply_in_place(uint8_t* src, size_t cb) {
}
TextureAsset::TextureAsset() = default;
TextureAsset::TextureAsset(const std::string& file_in, TextureType type_in,
TextureMinQuality min_quality_in)
: file_name_(file_in), type_(type_in), min_quality_(min_quality_in) {
@ -59,19 +60,19 @@ TextureAsset::TextureAsset(TextPacker* packer) : packer_(packer) {
}
TextureAsset::TextureAsset(const std::string& qr_url) : is_qr_code_(true) {
int hard_limit{96};
int soft_limit{64};
size_t hard_limit{96};
size_t soft_limit{64};
if (qr_url.size() > hard_limit) {
char buffer[512];
snprintf(buffer, sizeof(buffer),
"QR code url byte length %zu exceeds hard-limit of %d;"
"QR code url byte length %zu exceeds hard-limit of %zu;"
" please use shorter urls. (url=%s)",
qr_url.size(), hard_limit, qr_url.c_str());
throw Exception(buffer, PyExcType::kValue);
} else if (qr_url.size() > soft_limit) {
char buffer[512];
snprintf(buffer, sizeof(buffer),
"QR code url byte length %zu exceeds soft-limit of %d;"
"QR code url byte length %zu exceeds soft-limit of %zu;"
" please use shorter urls. (url=%s)",
qr_url.size(), soft_limit, qr_url.c_str());
Log(LogLevel::kWarning, buffer);
@ -211,15 +212,11 @@ void TextureAsset::DoPreload() {
if (file_name_size > 12
&& !strcmp(file_name_full_.c_str() + file_name_size - 12,
".android_dds")) {
#if BA_ENABLE_OPENGL
LoadDDS(file_name_full_, preload_datas_[0].buffers,
preload_datas_[0].widths, preload_datas_[0].heights,
preload_datas_[0].formats, preload_datas_[0].sizes,
texture_quality, static_cast<uint8_t>(min_quality_),
&preload_datas_[0].base_level);
#else
throw Exception();
#endif
// We should only be loading this if we support etc1 in hardware.
assert(g_base->graphics_server->SupportsTextureCompressionType(
@ -238,15 +235,11 @@ void TextureAsset::DoPreload() {
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
".dds")) {
// Dxt1 for non-alpha and dxt5 for alpha (.dds files).
#if BA_ENABLE_OPENGL
LoadDDS(file_name_full_, preload_datas_[0].buffers,
preload_datas_[0].widths, preload_datas_[0].heights,
preload_datas_[0].formats, preload_datas_[0].sizes,
texture_quality, static_cast<int>(min_quality_),
&preload_datas_[0].base_level);
#else
throw Exception();
#endif
// Decompress dxt1/dxt5 if we don't natively support it.
if (!g_base->graphics_server->SupportsTextureCompressionType(
@ -257,15 +250,11 @@ void TextureAsset::DoPreload() {
".ktx")) {
// Etc2 or etc1 for non-alpha and etc2 for alpha (.ktx files).
try {
#if BA_ENABLE_OPENGL
LoadKTX(file_name_full_, preload_datas_[0].buffers,
preload_datas_[0].widths, preload_datas_[0].heights,
preload_datas_[0].formats, preload_datas_[0].sizes,
texture_quality, static_cast<uint8_t>(min_quality_),
&preload_datas_[0].base_level);
#else
throw Exception();
#endif
} catch (const std::exception& e) {
throw Exception("Error loading file '" + file_name_full_
+ "': " + e.what());
@ -343,15 +332,11 @@ void TextureAsset::DoPreload() {
&& !strcmp(file_name_full_.c_str() + file_name_size - 12,
".android_dds")) {
try {
#if BA_ENABLE_OPENGL
LoadDDS(name, preload_datas_[d].buffers, preload_datas_[d].widths,
preload_datas_[d].heights, preload_datas_[d].formats,
preload_datas_[d].sizes, texture_quality,
static_cast<uint8_t>(min_quality_),
&preload_datas_[d].base_level);
#else
throw Exception();
#endif
} catch (const std::exception& e) {
throw Exception("Error loading file '" + file_name_full_
+ "': " + e.what());
@ -373,16 +358,12 @@ void TextureAsset::DoPreload() {
}
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
".dds")) {
#if BA_ENABLE_OPENGL
// Dxt1 for non-alpha and dxt5 for alpha (.dds files).
LoadDDS(name, preload_datas_[d].buffers, preload_datas_[d].widths,
preload_datas_[d].heights, preload_datas_[d].formats,
preload_datas_[d].sizes, texture_quality,
static_cast<uint8_t>(min_quality_),
&preload_datas_[d].base_level);
#else
throw Exception();
#endif
// Decompress dxt1/dxt5 if we don't natively support it.
if (!g_base->graphics_server->SupportsTextureCompressionType(
@ -392,15 +373,11 @@ void TextureAsset::DoPreload() {
} else if (!strcmp(file_name_full_.c_str() + file_name_size - 4,
".ktx")) {
// Etc2 or etc1 for non-alpha and etc2 for alpha (.ktx files)
#if BA_ENABLE_OPENGL
LoadKTX(name, preload_datas_[d].buffers, preload_datas_[d].widths,
preload_datas_[d].heights, preload_datas_[d].formats,
preload_datas_[d].sizes, texture_quality,
static_cast<uint8_t>(min_quality_),
&preload_datas_[d].base_level);
#else
throw Exception();
#endif
// Decompress etc2 ones if we don't natively support them.
if (((preload_datas_[d].formats[preload_datas_[d].base_level]

View File

@ -12,7 +12,8 @@ namespace ballistica::base {
const int kMaxTextureLevels = 14;
// Temporary data that is passed along to the renderer when creating
// rendererdata. This may include sdl surfaces and/or compressed buffers.
// renderer-data. This may include things like sdl surfaces and/or
// compressed buffers.
class TextureAssetPreloadData {
public:
static void rgba8888_to_rgba4444_in_place(void* src, size_t cb);

View File

@ -7,8 +7,7 @@
namespace ballistica::base {
// Renderer-specific data (gl tex, etc)
// this is extended by the renderer
// Renderer-specific data (gl tex, etc). To be extended by the renderer.
class TextureAssetRendererData : public Object {
public:
auto GetDefaultOwnerThread() const -> EventLoopID override {
@ -18,9 +17,7 @@ class TextureAssetRendererData : public Object {
// Create the renderer data but don't load it in yet.
TextureAssetRendererData() = default;
// load the data.
// if incremental is true, return whether the load was completed
// (non-incremental loads should always complete)
// Load the data.
virtual void Load() = 0;
};

View File

@ -9,7 +9,7 @@
#if BA_ENABLE_AUDIO
#if HAVE_FRAMEWORK_OPENAL
#if BA_HAVE_FRAMEWORK_OPENAL
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else

View File

@ -33,7 +33,9 @@ LPALCDEVICERESUMESOFT alcDeviceResumeSOFT;
const int kAudioProcessIntervalNormal{500};
const int kAudioProcessIntervalFade{50};
const int kAudioProcessIntervalPendingLoad{1};
#if (BA_DEBUG_BUILD || BA_TEST_BUILD)
const bool kShowInUseSounds{};
#endif
int AudioServer::al_source_count_ = 0;

View File

@ -38,7 +38,7 @@ core::CoreFeatureSet* g_core{};
BaseFeatureSet* g_base{};
BaseFeatureSet::BaseFeatureSet()
: app_adapter{BasePlatform::CreateAppAdapter()},
: app_adapter{AppAdapter::Create()},
app_config{new AppConfig()},
app_mode_{AppModeEmpty::GetSingleton()},
assets{new Assets()},
@ -50,7 +50,7 @@ BaseFeatureSet::BaseFeatureSet()
bg_dynamics_server{g_core->HeadlessMode() ? nullptr
: new BGDynamicsServer},
context_ref{new ContextRef(nullptr)},
graphics{BasePlatform::CreateGraphics()},
graphics{Graphics::Create()},
graphics_server{new GraphicsServer()},
huffman{new Huffman()},
input{new Input()},
@ -58,7 +58,7 @@ BaseFeatureSet::BaseFeatureSet()
network_reader{new NetworkReader()},
network_writer{new NetworkWriter()},
networking{new Networking()},
platform{BasePlatform::CreatePlatform()},
platform{BasePlatform::Create()},
python{new BasePython()},
stdio_console{g_buildconfig.enable_stdio_console() ? new StdioConsole()
: nullptr},
@ -223,8 +223,8 @@ void BaseFeatureSet::OnAppShutdownComplete() {
g_core->LifecycleLog("app exiting (main thread)");
// Flag our own event loop to exit (or ask the OS to if they're managing).
if (app_adapter->ManagesEventLoop()) {
g_core->main_event_loop()->Quit();
if (app_adapter->ManagesMainThreadEventLoop()) {
app_adapter->DoExitMainThreadEventLoop();
} else {
platform->TerminateApp();
}
@ -276,14 +276,14 @@ void BaseFeatureSet::set_app_mode(AppMode* mode) {
}
}
auto BaseFeatureSet::AppManagesEventLoop() -> bool {
return app_adapter->ManagesEventLoop();
auto BaseFeatureSet::AppManagesMainThreadEventLoop() -> bool {
return app_adapter->ManagesMainThreadEventLoop();
}
void BaseFeatureSet::RunAppToCompletion() {
BA_PRECONDITION(g_core->InMainThread());
BA_PRECONDITION(g_base);
BA_PRECONDITION(g_base->app_adapter->ManagesEventLoop());
BA_PRECONDITION(g_base->app_adapter->ManagesMainThreadEventLoop());
BA_PRECONDITION(!called_run_app_to_completion_);
called_run_app_to_completion_ = true;
@ -294,12 +294,12 @@ void BaseFeatureSet::RunAppToCompletion() {
// Let go of the GIL while we're running.
Python::ScopedInterpreterLockRelease gil_release;
g_core->main_event_loop()->RunToCompletion();
g_base->app_adapter->RunMainThreadEventLoopToCompletion();
}
void BaseFeatureSet::PrimeAppMainThreadEventPump() {
app_adapter->PrimeMainThreadEventPump();
}
// void BaseFeatureSet::PrimeAppMainThreadEventPump() {
// app_adapter->PrimeMainThreadEventPump();
// }
auto BaseFeatureSet::HavePlus() -> bool {
if (!plus_soft_ && !tried_importing_plus_) {
@ -469,10 +469,12 @@ auto BaseFeatureSet::InLogicThread() const -> bool {
}
auto BaseFeatureSet::InGraphicsThread() const -> bool {
if (auto* loop = graphics_server->event_loop()) {
return loop->ThreadIsCurrent();
}
return false;
// FIXME.
return g_core->InMainThread();
// if (auto* loop = graphics_server->event_loop()) {
// return loop->ThreadIsCurrent();
// }
// return false;
}
auto BaseFeatureSet::InAudioThread() const -> bool {

View File

@ -14,7 +14,6 @@
// It predeclares our feature-set's various types and globals and other
// bits.
// Predeclared types from other feature sets that we use.
namespace ballistica::core {
class CoreConfig;
class CoreFeatureSet;
@ -58,7 +57,6 @@ class Context;
class ContextRef;
class DataAsset;
class FrameDef;
class GLContext;
class Graphics;
class GraphicsServer;
class Huffman;
@ -177,6 +175,8 @@ enum class GraphicsQuality {
kHigher,
};
enum class VSyncRequest { kNever, kAlways, kAuto };
/// Requests for exact or auto graphics quality values.
enum class GraphicsQualityRequest {
kUnset,
@ -624,13 +624,13 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
/// Called when app shutdown process completes. Sets app to exit.
void OnAppShutdownComplete();
auto AppManagesEventLoop() -> bool override;
auto AppManagesMainThreadEventLoop() -> bool override;
/// Run app event loop to completion (only applies to flavors which manage
/// their own event loop).
void RunAppToCompletion() override;
void PrimeAppMainThreadEventPump() override;
// void PrimeAppMainThreadEventPump() override;
auto CurrentContext() -> const ContextRef& {
assert(InLogicThread()); // Up to caller to ensure this.

View File

@ -203,7 +203,9 @@ void BGDynamics::Draw(FrameDef* frame_def) {
// Draw shadows.
if (ds->shadow_vertices.Exists()) {
assert(ds->shadow_indices.Exists());
if (!shadows_mesh_.Exists()) shadows_mesh_ = Object::New<SpriteMesh>();
if (!shadows_mesh_.Exists()) {
shadows_mesh_ = Object::New<SpriteMesh>();
}
shadows_mesh_->SetIndexData(ds->shadow_indices);
shadows_mesh_->SetData(
Object::Ref<MeshBuffer<VertexSprite>>(ds->shadow_vertices));

View File

@ -31,46 +31,50 @@ void CollisionCache::Draw(FrameDef* frame_def) {
c.SetColor(0, 1, 0, 0.1f);
float cell_width = (1.0f / static_cast<float>(grid_width_));
float cell_height = (1.0f / static_cast<float>(grid_height_));
c.PushTransform();
c.Translate((x_min_ + x_max_) * 0.5f, 0, (z_min_ + z_max_) * 0.5f);
c.Scale(x_max_ - x_min_, 1, z_max_ - z_min_);
c.PushTransform();
c.Scale(1, 0.01f, 1);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
c.PopTransform();
c.Translate(-0.5f + 0.5f * cell_width, 0, -0.5f + 0.5f * cell_height);
for (int x = 0; x < grid_width_; x++) {
for (int z = 0; z < grid_height_; z++) {
int cell_index = z * grid_width_ + x;
assert(cell_index >= 0 && cell_index < static_cast<int>(glow_.size()));
if (glow_[cell_index]) {
c.SetColor(1, 1, 1, 0.2f);
} else {
c.SetColor(0, 0, 1, 0.2f);
}
c.PushTransform();
c.Translate(static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_collide_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c.Scale(0.95f * cell_width, 0.01f, 0.95f * cell_height);
{
auto xf = c.ScopedTransform();
c.Translate((x_min_ + x_max_) * 0.5f, 0, (z_min_ + z_max_) * 0.5f);
c.Scale(x_max_ - x_min_, 1, z_max_ - z_min_);
{
auto xf = c.ScopedTransform();
c.Scale(1, 0.01f, 1);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
c.PopTransform();
if (glow_[cell_index]) {
c.SetColor(1, 1, 1, 0.2f);
} else {
c.SetColor(1, 0, 0, 0.2f);
}
c.Translate(-0.5f + 0.5f * cell_width, 0, -0.5f + 0.5f * cell_height);
for (int x = 0; x < grid_width_; x++) {
for (int z = 0; z < grid_height_; z++) {
int cell_index = z * grid_width_ + x;
assert(cell_index >= 0 && cell_index < static_cast<int>(glow_.size()));
if (glow_[cell_index]) {
c.SetColor(1, 1, 1, 0.2f);
} else {
c.SetColor(0, 0, 1, 0.2f);
}
{
auto xf = c.ScopedTransform();
c.Translate(static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_collide_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c.Scale(0.95f * cell_width, 0.01f, 0.95f * cell_height);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
}
if (glow_[cell_index]) {
c.SetColor(1, 1, 1, 0.2f);
} else {
c.SetColor(1, 0, 0, 0.2f);
}
{
auto xf = c.ScopedTransform();
c.Translate(static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_empty_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c.Scale(0.95f * cell_width, 0.01f, 0.95f * cell_height);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
}
glow_[cell_index] = 0;
}
c.PushTransform();
c.Translate(static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_empty_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c.Scale(0.95f * cell_width, 0.01f, 0.95f * cell_height);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
c.PopTransform();
glow_[cell_index] = 0;
}
}
c.PopTransform();
c.Submit();
if (explicit_bool(false)) {
@ -79,47 +83,54 @@ void CollisionCache::Draw(FrameDef* frame_def) {
c2.SetColor(1, 0, 0, 1.0f);
float cell_width2 = (1.0f / static_cast<float>(grid_width_));
float cell_height2 = (1.0f / static_cast<float>(grid_height_));
c2.PushTransform();
c2.Translate((x_min_ + x_max_) * 0.5f, 0, (z_min_ + z_max_) * 0.5f);
c2.Scale(x_max_ - x_min_, 1, z_max_ - z_min_);
c2.PushTransform();
c2.Scale(1, 0.01f, 1);
c2.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
c2.PopTransform();
c2.Translate(-0.5f + 0.5f * cell_width2, 0, -0.5f + 0.5f * cell_height2);
for (int x = 0; x < grid_width_; x++) {
for (int z = 0; z < grid_height_; z++) {
int cell_index = z * grid_width_ + x;
assert(cell_index >= 0 && cell_index < static_cast<int>(glow_.size()));
if (glow_[cell_index]) {
c2.SetColor(1, 1, 1, 0.2f);
} else {
c2.SetColor(1, 0, 0, 0.2f);
}
c2.PushTransform();
c2.Translate(static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_empty_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c2.Scale(0.95f * cell_width2, 0.01f, 0.95f * cell_height2);
c2.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
c2.PopTransform();
if (glow_[cell_index]) {
c2.SetColor(1, 1, 1, 0.2f);
} else {
c2.SetColor(0, 0, 1, 0.2f);
}
c2.PushTransform();
c2.Translate(static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_collide_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c2.Scale(0.95f * cell_width2, 0.01f, 0.95f * cell_height2);
c2.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
c2.PopTransform();
{
auto xf = c2.ScopedTransform();
glow_[cell_index] = 0;
c2.Translate((x_min_ + x_max_) * 0.5f, 0, (z_min_ + z_max_) * 0.5f);
c2.Scale(x_max_ - x_min_, 1, z_max_ - z_min_);
{
auto xf = c2.ScopedTransform();
c2.Scale(1, 0.01f, 1);
c2.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
}
c2.Translate(-0.5f + 0.5f * cell_width2, 0, -0.5f + 0.5f * cell_height2);
for (int x = 0; x < grid_width_; x++) {
for (int z = 0; z < grid_height_; z++) {
int cell_index = z * grid_width_ + x;
assert(cell_index >= 0
&& cell_index < static_cast<int>(glow_.size()));
if (glow_[cell_index]) {
c2.SetColor(1, 1, 1, 0.2f);
} else {
c2.SetColor(1, 0, 0, 0.2f);
}
{
auto xf = c2.ScopedTransform();
c2.Translate(
static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_empty_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c2.Scale(0.95f * cell_width2, 0.01f, 0.95f * cell_height2);
c2.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
}
if (glow_[cell_index]) {
c2.SetColor(1, 1, 1, 0.2f);
} else {
c2.SetColor(0, 0, 1, 0.2f);
}
{
auto xf = c2.ScopedTransform();
c2.Translate(
static_cast<float>(x) / static_cast<float>(grid_width_),
cells_[cell_index].height_confirmed_collide_,
static_cast<float>(z) / static_cast<float>(grid_height_));
c2.Scale(0.95f * cell_width2, 0.01f, 0.95f * cell_height2);
c2.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBox));
}
glow_[cell_index] = 0;
}
}
}
c2.PopTransform();
c2.Submit();
}
}

View File

@ -4,10 +4,10 @@
namespace ballistica::base {
void RenderComponent::ScissorPush(const Rect& rIn) {
void RenderComponent::ScissorPush(const Rect& rect) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kScissorPush);
cmd_buffer_->PutFloats(rIn.l, rIn.b, rIn.r, rIn.t);
cmd_buffer_->PutFloats(rect.l, rect.b, rect.r, rect.t);
}
#if BA_DEBUG_BUILD

View File

@ -9,33 +9,86 @@
namespace ballistica::base {
/// RenderComponents are used to assemble command streams to send to the
/// renderer. These do a lot of extra work in debug builds to make sure
/// valid commands are being constructed, so it is best to iterate on them
/// in debug mode when possible.
///
/// The general workflow with a RenderComponents is to set all 'config'
/// options at the beginning and then to issue one or more draw commands
/// after. Check the source of each call for EnsureConfiguring() or
/// EnsureDrawing() to see which is which. Flipping from configuring to
/// drawing can cause shader binding or other work to be done in the
/// graphics api, so switches back and forth should be minimized.
///
/// RenderComponent output goes to a specific draw list in the renderer.
/// Depending on the type of RenderPass, there may be a single draw-list,
/// transparent and opaque draw-lists, draw-lists for different shaders,
/// etc. RenderComponents currently must be sure to only draw to a single
/// draw list; otherwise things like PushTransform/PopTransforms may affect
/// different draw lists. Stay tuned for this system to evolve into
/// something more foolproof.
class RenderComponent {
public:
class ScopedTransformObj {
private:
class ScopedTransformObj_ {
public:
explicit ScopedTransformObj(RenderComponent* c) : c_{c} {
explicit ScopedTransformObj_(RenderComponent* c) : c_{c} {
c_->PushTransform();
}
~ScopedTransformObj() { c_->PopTransform(); }
~ScopedTransformObj_() { c_->PopTransform(); }
private:
RenderComponent* c_;
};
explicit RenderComponent(RenderPass* pass)
: state_(State::kConfiguring), pass_(pass), cmd_buffer_(nullptr) {}
class ScopedScissorObj_ {
public:
explicit ScopedScissorObj_(RenderComponent* c, const Rect& r) : c_{c} {
c_->ScissorPush(r);
}
~ScopedScissorObj_() { c_->ScissorPop(); }
private:
RenderComponent* c_;
};
public:
explicit RenderComponent(RenderPass* pass) : pass_(pass) {
assert(g_base->InLogicThread());
}
~RenderComponent() {
assert(g_base->InLogicThread());
if (state_ != State::kSubmitted) {
Log(LogLevel::kError,
"RenderComponent dying without submit() having been called.");
Submit();
}
}
/// End current drawing by this component. This is implicitly done when a
/// component goes out of scope, but one may choose to do this explicitly
/// to allow other components to draw while this one still exists (only
/// one RenderComponent can be actively drawing in a frame-def at a time).
void Submit() {
if (state_ != State::kSubmitted) {
#if BA_DEBUG_BUILD
if (state_ == State::kDrawing) {
// If we were drawing, let the frame-def know we're done.
assert(pass_->frame_def()->active_render_component() == this);
pass_->frame_def()->set_active_render_component(nullptr);
}
#endif
state_ = State::kSubmitted;
}
}
void DrawMeshAsset(MeshAsset* mesh, uint32_t flags = 0) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kDrawMeshAsset);
cmd_buffer_->PutInt(flags);
cmd_buffer_->PutMeshAsset(mesh);
}
void DrawMeshAssetInstanced(MeshAsset* mesh,
const std::vector<Matrix44f>& matrices,
int flags = 0) {
@ -47,6 +100,7 @@ class RenderComponent {
cmd_buffer_->PutMeshAsset(mesh);
cmd_buffer_->PutMatrices(matrices);
}
void DrawMesh(Mesh* m, int flags = 0) {
EnsureDrawing();
if (m->IsValid()) {
@ -56,134 +110,147 @@ class RenderComponent {
cmd_buffer_->PutMeshData(m->mesh_data_client_handle()->mesh_data);
}
}
void DrawScreenQuad() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kDrawScreenQuad);
}
// draw triangles using old-school gl format.. only for debugging
// and not supported in all configurations
// Draw triangles using old-school gl format.. only for debugging and not
// supported in all configurations.
void BeginDebugDrawTriangles() {
EnsureDrawing();
cmd_buffer_->PutCommand(
RenderCommandBuffer::Command::kBeginDebugDrawTriangles);
}
void BeginDebugDrawLines() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kBeginDebugDrawLines);
}
void Vertex(float x, float y, float z) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kDebugDrawVertex3);
cmd_buffer_->PutFloats(x, y, z);
}
void End() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kEndDebugDraw);
}
void ScissorPush(const Rect& rIn);
void ScissorPop() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kScissorPop);
}
void PushTransform() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kPushTransform);
}
void PopTransform() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kPopTransform);
}
auto ScopedTransform() -> ScopedTransformObj {
return ScopedTransformObj(this);
/// Add a transform push/pop to the component. Remember to assign the
/// result to a variable or the pop will be immediate.
auto ScopedTransform() -> ScopedTransformObj_ {
return ScopedTransformObj_(this);
}
/// Add a scissor push/pop to the component. Remember to assign the result
/// to a variable or the pop will be immediate.
auto ScopedScissor(const Rect& rect) -> ScopedScissorObj_ {
return ScopedScissorObj_(this, rect);
}
void Translate(float x, float y) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kTranslate2);
cmd_buffer_->PutFloats(x, y);
}
void Translate(float x, float y, float z) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kTranslate3);
cmd_buffer_->PutFloats(x, y, z);
}
void CursorTranslate() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kCursorTranslate);
}
void Rotate(float angle, float x, float y, float z) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kRotate);
cmd_buffer_->PutFloats(angle, x, y, z);
}
void Scale(float x, float y) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kScale2);
cmd_buffer_->PutFloats(x, y);
}
void Scale(float x, float y, float z) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kScale3);
cmd_buffer_->PutFloats(x, y, z);
}
void ScaleUniform(float s) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kScaleUniform);
cmd_buffer_->PutFloat(s);
}
void MultMatrix(const float* t) {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kMultMatrix);
cmd_buffer_->PutFloatArray16(t);
}
#if BA_VR_BUILD
void VRTransformToRightHand() {
EnsureDrawing();
cmd_buffer_->PutCommand(
RenderCommandBuffer::Command::kTransformToRightHand);
}
void VRTransformToLeftHand() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kTransformToLeftHand);
}
void VRTransformToHead() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kTransformToHead);
}
#endif // BA_VR_BUILD
void TranslateToProjectedPoint(float x, float y, float z) {
EnsureDrawing();
cmd_buffer_->PutCommand(
RenderCommandBuffer::Command::kTranslateToProjectedPoint);
cmd_buffer_->PutFloats(x, y, z);
}
void FlipCullFace() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kFlipCullFace);
}
void Submit() {
if (state_ != State::kSubmitted) {
// If we were drawing, make note that we're done.
if (state_ == State::kDrawing) {
#if BA_DEBUG_BUILD
assert(pass_->frame_def()->defining_component());
pass_->frame_def()->set_defining_component(false);
#endif
}
state_ = State::kSubmitted;
}
}
protected:
enum class State { kConfiguring, kDrawing, kSubmitted };
void EnsureConfiguring() {
if (state_ != State::kConfiguring) {
// if we were drawing, make note that we're done
#if BA_DEBUG_BUILD
// FIXME: currently releasing status as active-render-component here
// but should perhaps hold on to it for consistency.
if (state_ == State::kDrawing) {
assert(pass_->frame_def()->defining_component());
pass_->frame_def()->set_defining_component(false);
assert(pass_->frame_def()->active_render_component() == this);
pass_->frame_def()->set_active_render_component(nullptr);
}
#endif // BA_DEBUG_BUILD
#endif
state_ = State::kConfiguring;
}
}
@ -209,33 +276,31 @@ class RenderComponent {
// Given a shader type, sets up the config target buffer.
void ConfigForShading(ShadingType shading_type) {
// Determine which buffer to write to, etc.
// Debugging: if we've got transparent-only or opaque-only mode flipped on,
// make sure only those type of components are being submitted.
#if BA_DEBUG_BUILD
// Debugging: if we've got transparent-only or opaque-only mode flipped
// on, make sure only those type of components are being submitted.
ConfigForShadingDebugChecks(shading_type);
// Also make sure only transparent stuff is going into the
// light/shadow/overlay3D passes (we skip rendering the opaque lists there
// since there shouldn't be anything in them, and we're not using depth
// for those so it wouldn't be much of an optimization..)
// light/shadow/overlay3D passes (we skip rendering the opaque lists
// there since there shouldn't be anything in them, and we're not using
// depth for those so it wouldn't be much of an optimization).
if ((pass_->type() == RenderPass::Type::kLightPass
|| pass_->type() == RenderPass::Type::kLightShadowPass
|| pass_->type() == RenderPass::Type::kOverlay3DPass)
&& !Graphics::IsShaderTransparent(shading_type)) {
throw Exception(
"Opaque component submitted to light/shadow/overlay3d pass;"
" not cool man.");
FatalError("Opaque component submitted to light/shadow/overlay3d pass.");
}
// Likewise the blit pass should consist solely of opaque stuff.
if (pass_->type() == RenderPass::Type::kBlitPass
&& Graphics::IsShaderTransparent(shading_type)) {
throw Exception(
"Transparent component submitted to blit pass;"
" not cool man.");
FatalError("Transparent component submitted to blit pass.");
}
#endif // BA_DEBUG_BUILD
// Certain passes (overlay, etc) draw objects in the order
// provided. Other passes group by shader for efficiency.
// Certain passes (overlay, etc) draw objects in the order provided.
// Other passes group by shader for efficiency.
if (pass_->UsesWorldLists()) {
cmd_buffer_ = pass_->GetCommands(shading_type);
} else {
@ -255,21 +320,28 @@ class RenderComponent {
if (state_ != State::kDrawing) {
WriteConfig();
state_ = State::kDrawing;
// make sure we're the only one drawing until we're submitted
#if BA_DEBUG_BUILD
assert(!pass_->frame_def()->defining_component());
pass_->frame_def()->set_defining_component(true);
#endif // BA_DEBUG_BUILD
// Let the frame-def know we're the active component drawing to it now.
assert(pass_->frame_def()->active_render_component() == nullptr);
pass_->frame_def()->set_active_render_component(this);
#endif
}
}
// subclasses should override this to dump
// their needed data to the stream
// Subclasses should override this to dump their needed data to the
// stream.
virtual void WriteConfig() = 0;
protected:
RenderCommandBuffer* cmd_buffer_;
State state_;
RenderCommandBuffer* cmd_buffer_{};
State state_{State::kConfiguring};
RenderPass* pass_;
public:
void ScissorPush(const Rect& rect);
void ScissorPop() {
EnsureDrawing();
cmd_buffer_->PutCommand(RenderCommandBuffer::Command::kScissorPop);
}
};
} // namespace ballistica::base

View File

@ -5,9 +5,9 @@
namespace ballistica::base {
void SimpleComponent::WriteConfig() {
// if we're transparent we don't want to do optimization-based
// shader swapping (ie: when color is 1). This is because it can
// affect draw order, which is important unlike with opaque stuff.
// If we're transparent, we don't want to do optimization-based shader
// swapping (ie: when color is 1). This is because it can affect draw
// order, which is important unlike with opaque stuff.
if (transparent_) {
if (texture_.Exists()) {
if (colorize_texture_.Exists()) {
@ -52,7 +52,7 @@ void SimpleComponent::WriteConfig() {
cmd_buffer_->PutTexture(colorize_texture_);
}
} else {
// non-colorized with texture
// Non-colorized with texture.
if (double_sided_) {
assert(!mask_texture_.Exists()); // unimplemented combo
assert(flatness_ == 0.0f); // unimplemented combo
@ -111,7 +111,7 @@ void SimpleComponent::WriteConfig() {
}
} else {
if (flatness_ != 0.0f) {
assert(!mask_texture_.Exists()); // unimplemented
assert(!mask_texture_.Exists()); // unimplemented combo
ConfigForShading(
ShadingType::kSimpleTextureModulatedTransFlatness);
cmd_buffer_->PutInt(premultiplied_);
@ -120,8 +120,8 @@ void SimpleComponent::WriteConfig() {
cmd_buffer_->PutTexture(texture_);
} else {
if (mask_texture_.Exists()) {
// currently mask functionality requires colorize too, so
// just send a black texture for that..
// Currently mask functionality requires colorize too, so
// just send a black texture for that.
ConfigForShading(
ShadingType::
kSimpleTextureModulatedTransparentColorized2Masked);
@ -165,12 +165,12 @@ void SimpleComponent::WriteConfig() {
}
}
} else {
// when we're opaque we can do some shader-swapping optimizations
// When we're opaque, we can do some shader-swapping optimizations
// since draw order doesn't matter.
assert(flatness_ == 0.0f); // unimplemented combo
assert(glow_amount_ == 0.0f); // unimplemented combo
assert(shadow_opacity_ == 0.0f); // unimplemented combo
assert(!double_sided_); // not implemented
assert(!double_sided_); // unimplemented combo
assert(!mask_uv2_texture_.Exists()); // unimplemented combo
if (texture_.Exists()) {
if (colorize_texture_.Exists()) {
@ -194,8 +194,8 @@ void SimpleComponent::WriteConfig() {
} else {
assert(!do_colorize_2_); // unsupported combo
if (mask_texture_.Exists()) {
// currently mask functionality requires colorize too, so
// we have to send a black texture along for that..
// Currently mask functionality requires colorize too, so
// we have to send a black texture along for that.
ConfigForShading(
ShadingType::kSimpleTextureModulatedColorized2Masked);
cmd_buffer_->PutFloats(color_r_, color_g_, color_b_, color_a_,
@ -207,7 +207,7 @@ void SimpleComponent::WriteConfig() {
g_base->assets->SysTexture(SysTextureID::kBlack));
cmd_buffer_->PutTexture(mask_texture_);
} else {
// if no color was provided we can do a super-cheap version
// If no color was provided, we can do a super-cheap version.
if (!have_color_) {
ConfigForShading(ShadingType::kSimpleTexture);
cmd_buffer_->PutTexture(texture_);

View File

@ -36,18 +36,22 @@ class SimpleComponent : public RenderComponent {
have_color_(false),
double_sided_(false),
do_colorize_2_(false) {}
void SetPremultiplied(bool val) {
EnsureConfiguring();
premultiplied_ = val;
}
void SetTransparent(bool val) {
EnsureConfiguring();
transparent_ = val;
}
void SetTexture(TextureAsset* t) {
EnsureConfiguring();
texture_ = t;
}
void SetTexture(const Object::Ref<TextureAsset>& t) {
EnsureConfiguring();
texture_ = t;
@ -59,31 +63,36 @@ class SimpleComponent : public RenderComponent {
EnsureConfiguring();
colorize_texture_ = t;
}
// red multiplies source color, green adds colorize1-color,
// and blue adds white
// (currently requires colorize1 and colorize 2 to be set)
// Red multiplies source color, green adds colorize1-color, and blue adds
// white (currently requires colorize1 and colorize 2 to be set).
void SetMaskTexture(TextureAsset* t) {
EnsureConfiguring();
mask_texture_ = t;
}
void SetMaskUV2Texture(TextureAsset* t) {
EnsureConfiguring();
mask_uv2_texture_ = t;
}
void ClearMaskUV2Texture() {
EnsureConfiguring();
mask_uv2_texture_.Clear();
}
void SetDoubleSided(bool enable) {
EnsureConfiguring();
double_sided_ = enable;
}
void SetColor(float r, float g, float b, float a = 1.0f) {
// we support fast inline color changes with drawing streams
// (avoids having to re-send a whole configure for every color change)
// ..make sure to only allow this if we have a color already; otherwise we
// We support fast inline color changes with drawing streams (avoids
// having to re-send a whole configure for every color change).
// Make sure to only allow this if we have a color already; otherwise we
// need to config since we might be implicitly switch shaders by setting
// color
// color.
if (state_ == State::kDrawing && have_color_) {
cmd_buffer_->PutCommand(
RenderCommandBuffer::Command::kSimpleComponentInlineColor);
@ -97,6 +106,7 @@ class SimpleComponent : public RenderComponent {
color_b_ = b;
color_a_ = a;
}
void SetColorizeColor(float r, float g, float b, float a = 1.0f) {
EnsureConfiguring();
colorize_color_r_ = r;
@ -104,6 +114,7 @@ class SimpleComponent : public RenderComponent {
colorize_color_b_ = b;
colorize_color_a_ = a;
}
void SetColorizeColor2(float r, float g, float b, float a = 1.0f) {
EnsureConfiguring();
colorize_color2_r_ = r;
@ -112,6 +123,7 @@ class SimpleComponent : public RenderComponent {
colorize_color2_a_ = a;
do_colorize_2_ = true;
}
void SetShadow(float offsetX, float offsetY, float blur, float opacity) {
EnsureConfiguring();
shadow_offset_x_ = offsetX;
@ -119,11 +131,13 @@ class SimpleComponent : public RenderComponent {
shadow_blur_ = blur;
shadow_opacity_ = opacity;
}
void setGlow(float amount, float blur) {
void SetGlow(float amount, float blur) {
EnsureConfiguring();
glow_amount_ = amount;
glow_blur_ = blur;
}
void SetFlatness(float flatness) {
EnsureConfiguring();
flatness_ = flatness;

View File

@ -0,0 +1,334 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_FRAMEBUFFER_OBJECT_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_FRAMEBUFFER_OBJECT_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::FramebufferObjectGL : public Framebuffer {
public:
FramebufferObjectGL(RendererGL* renderer_in, int width_in, int height_in,
bool linear_interp_in, bool depth_in, bool is_texture_in,
bool depth_is_texture_in, bool high_quality_in,
bool msaa_in, bool alpha_in)
: width_(width_in),
height_(height_in),
linear_interp_(linear_interp_in),
depth_(depth_in),
is_texture_(is_texture_in),
depth_is_texture_(depth_is_texture_in),
renderer_(renderer_in),
high_quality_(high_quality_in),
msaa_(msaa_in),
alpha_(alpha_in) {
// Desktop stuff is always high-quality.
#if BA_OSTYPE_MACOS || BA_OSTYPE_LINUX || BA_OSTYPE_WINDOWS
high_quality_ = true;
#endif
// Things are finally getting to the point where we can default to
// desktop quality on some mobile stuff.
#if BA_OSTYPE_ANDROID
if (renderer_->is_tegra_k1_) {
high_quality_ = true;
}
#endif
Load();
}
~FramebufferObjectGL() override { Unload(); }
void Load(bool force_low_quality = false) {
if (loaded_) return;
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
GLenum status;
BA_DEBUG_CHECK_GL_ERROR;
glGenFramebuffers(1, &framebuffer_);
renderer_->BindFramebuffer(framebuffer_);
BA_DEBUG_CHECK_GL_ERROR;
bool do_high_quality = high_quality_;
if (force_low_quality) do_high_quality = false;
int samples = 0;
if (msaa_) {
// Can't multisample with texture buffers currently.
assert(!is_texture_ && !depth_is_texture_);
int target_samples =
renderer_->GetMSAASamplesForFramebuffer_(width_, height_);
if (do_high_quality) {
samples = std::min(target_samples, renderer_->msaa_max_samples_rgb8());
} else {
samples =
std::min(target_samples, renderer_->msaa_max_samples_rgb565());
}
}
if (is_texture_) {
// Attach a texture for the color target.
glGenTextures(1, &texture_);
renderer_->BindTexture_(GL_TEXTURE_2D, texture_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
linear_interp_ ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
linear_interp_ ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// On android/ios lets go with 16 bit unless they explicitly request
// high quality.
#if BA_OSTYPE_ANDROID || BA_OSTYPE_IOS_TVOS
GLenum format;
if (alpha_) {
format = do_high_quality ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_4_4_4_4;
} else {
format = do_high_quality ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_5_6_5;
}
#else
GLenum format = GL_UNSIGNED_BYTE;
#endif
// if (srgbTest) {
// glTexImage2D(GL_TEXTURE_2D, 0, alpha_?GL_SRGB8_ALPHA8:GL_SRGB8,
// _width, _height, 0, alpha_?GL_RGBA:GL_RGB, format, nullptr);
// } else {
glTexImage2D(GL_TEXTURE_2D, 0, alpha_ ? GL_RGBA : GL_RGB, width_, height_,
0, alpha_ ? GL_RGBA : GL_RGB, format, nullptr);
// }
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture_, 0);
} else {
// Regular renderbuffer.
assert(!alpha_); // fixme
#if BA_OSTYPE_IOS_TVOS
GLenum format =
GL_RGB565; // FIXME; need to pull ES3 headers in for GL_RGB8
#elif BA_OSTYPE_ANDROID
GLenum format = do_high_quality ? GL_RGB8 : GL_RGB565;
#else
GLenum format = GL_RGB8;
#endif
glGenRenderbuffers(1, &render_buffer_);
BA_DEBUG_CHECK_GL_ERROR;
glBindRenderbuffer(GL_RENDERBUFFER, render_buffer_);
BA_DEBUG_CHECK_GL_ERROR;
if (samples > 0) {
#if BA_OSTYPE_IOS_TVOS
throw Exception();
#else
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, format,
width_, height_);
#endif
} else {
glRenderbufferStorage(GL_RENDERBUFFER, format, width_, height_);
}
BA_DEBUG_CHECK_GL_ERROR;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, render_buffer_);
BA_DEBUG_CHECK_GL_ERROR;
}
BA_DEBUG_CHECK_GL_ERROR;
if (depth_) {
if (depth_is_texture_) {
glGenTextures(1, &depth_texture_);
BA_DEBUG_CHECK_GL_ERROR;
renderer_->BindTexture_(GL_TEXTURE_2D, depth_texture_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
BA_DEBUG_CHECK_GL_ERROR;
// FIXME: need to pull in ES3 stuff for iOS to get GL_DEPTH_COMPONENT24.
// #if BA_OSTYPE_IOS_TVOS
// glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width_,
// height_, 0,
// GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, nullptr);
// #else
if (do_high_quality) {
// #if BA_OSTYPE_ANDROID
// assert(g_running_es3);
// #endif // BA_OSTYPE_ANDROID
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width_, height_,
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
} else {
glTexImage2D(
GL_TEXTURE_2D, 0,
renderer_->gl_is_es() ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
width_, height_, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
nullptr);
}
// #endif // BA_OSTYPE_IOS_TVOS
BA_DEBUG_CHECK_GL_ERROR;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D, depth_texture_, 0);
BA_DEBUG_CHECK_GL_ERROR;
} else {
// Just use a plain old renderbuffer if we don't need it as a texture
// (this is more widely supported).
glGenRenderbuffers(1, &depth_render_buffer_);
BA_DEBUG_CHECK_GL_ERROR;
glBindRenderbuffer(GL_RENDERBUFFER, depth_render_buffer_);
BA_DEBUG_CHECK_GL_ERROR;
if (samples > 0) {
// #if BA_OSTYPE_IOS_TVOS
// throw Exception();
// #else
// (GL_DEPTH_COMPONENT24 not available in ES2 it looks like)
bool do24;
// #if BA_OSTYPE_ANDROID
// do24 = (do_high_quality && g_running_es3);
// #else
do24 = do_high_quality;
// #endif
glRenderbufferStorageMultisample(
GL_RENDERBUFFER, samples,
do24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, width_,
height_);
// (do_high_quality &&
// g_running_es3)?GL_DEPTH_COMPONENT24:GL_DEPTH_COMPONENT16, _width,
// _height);
// #endif
} else {
// FIXME - need to pull in es3 headers to get GL_DEPTH_COMPONENT24 on
// iOS
// #if BA_OSTYPE_IOS_TVOS
// GLenum format = GL_DEPTH_COMPONENT16;
// #else
// GL_DEPTH_COMPONENT24 not available in ES2 it looks like.
GLenum format = (do_high_quality && renderer_->gl_is_es())
? GL_DEPTH_COMPONENT24
: GL_DEPTH_COMPONENT16;
// #endif
glRenderbufferStorage(GL_RENDERBUFFER, format, width_, height_);
}
BA_DEBUG_CHECK_GL_ERROR;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depth_render_buffer_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
const char* version = (const char*)glGetString(GL_VERSION);
const char* vendor = (const char*)glGetString(GL_VENDOR);
const char* renderer = (const char*)glGetString(GL_RENDERER);
throw Exception(
"Framebuffer setup failed for " + std::to_string(width_) + " by "
+ std::to_string(height_) + " fb with depth " + std::to_string(depth_)
+ " asTex " + std::to_string(depth_is_texture_) + " gl-version "
+ version + " vendor " + vendor + " renderer " + renderer);
}
// GLint enc;
// glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
// GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &enc); if
// (enc == GL_SRGB) {
// Log(LogLevel::kInfo, "GOT SRGB!!!!!!!!!!!");
// } else if (enc == GL_LINEAR) {
// Log(LogLevel::kInfo, "GOT LINEAR...");
// } else {
// Log(LogLevel::kInfo, "GOT OTHER..");
// }
loaded_ = true;
}
void Unload() {
assert(g_base->InGraphicsThread());
if (!loaded_) return;
// If our textures are currently bound as anything, clear that out.
// (otherwise a new texture with that same ID won't be bindable)
for (int& i : renderer_->bound_textures_2d_) {
if (i == texture_) { // NOLINT(bugprone-branch-clone)
i = -1;
} else if (depth_ && (i == depth_texture_)) {
i = -1;
}
}
if (!g_base->graphics_server->renderer_context_lost()) {
// Tear down the FBO and texture attachment
if (is_texture_) {
glDeleteTextures(1, &texture_);
} else {
glDeleteRenderbuffers(1, &render_buffer_);
}
if (depth_) {
if (depth_is_texture_) {
glDeleteTextures(1, &depth_texture_);
} else {
glDeleteRenderbuffers(1, &depth_render_buffer_);
}
BA_DEBUG_CHECK_GL_ERROR;
}
// If this one is current, make sure we re-bind next time.
// (otherwise we might prevent a new framebuffer with a recycled id from
// binding)
if (renderer_->active_framebuffer_ == framebuffer_) {
renderer_->active_framebuffer_ = -1;
}
glDeleteFramebuffers(1, &framebuffer_);
BA_DEBUG_CHECK_GL_ERROR;
}
loaded_ = false;
}
void Bind() {
assert(g_base->InGraphicsThread());
renderer_->BindFramebuffer(framebuffer_);
// if (time(nullptr)%2 == 0) {
// glDisable(GL_FRAMEBUFFER_SRGB);
// }
}
auto texture() const -> GLuint {
assert(is_texture_);
return texture_;
}
auto depth_texture() const -> GLuint {
assert(depth_ && depth_is_texture_);
return depth_texture_;
}
auto width() const -> int { return width_; }
auto height() const -> int { return height_; }
auto id() const -> GLuint { return framebuffer_; }
private:
RendererGL* renderer_{};
bool depth_{};
bool is_texture_{};
bool depth_is_texture_{};
bool high_quality_{};
bool msaa_{};
bool alpha_{};
bool linear_interp_{};
bool loaded_{};
int width_{}, height_{};
GLuint framebuffer_{};
GLuint texture_{};
GLuint depth_texture_{};
GLuint render_buffer_{};
GLuint depth_render_buffer_{};
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_FRAMEBUFFER_OBJECT_GL_H_

View File

@ -3,364 +3,58 @@
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/gl_sys.h"
#include "ballistica/base/app_adapter/app_adapter_sdl.h"
#include "ballistica/base/base.h"
#include "ballistica/core/core.h"
#include "ballistica/shared/ballistica.h"
#if BA_OSTYPE_ANDROID
#include <EGL/egl.h>
#if !BA_USE_ES3_INCLUDES
#include "ballistica/core/platform/android/android_gl3.h"
#endif
#endif
// #include "ballistica/base/app_adapter/app_adapter_sdl.h"
// #include "ballistica/base/base.h"
// #include "ballistica/core/core.h"
#if BA_OSTYPE_WINDOWS
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#endif
// #if BA_OSTYPE_ANDROID
// #include <EGL/egl.h>
// #if !BA_USE_ES3_INCLUDES
// #include "ballistica/core/platform/android/android_gl3.h"
// #endif
// #endif
#if BA_OSTYPE_MACOS
#include <OpenGL/CGLContext.h>
#include <OpenGL/CGLTypes.h>
#include <OpenGL/OpenGL.h>
#endif
// #if BA_OSTYPE_MACOS
// #include <OpenGL/CGLContext.h>
// #include <OpenGL/CGLTypes.h>
// #include <OpenGL/OpenGL.h>
// #endif
#if BA_DEBUG_BUILD
#define DEBUG_CHECK_GL_ERROR \
{ \
GLenum err = glGetError(); \
if (err != GL_NO_ERROR) \
Log(LogLevel::kError, "OPENGL ERROR AT LINE " + std::to_string(__LINE__) \
+ ": " + GLErrorToString(err)); \
}
#else
#define DEBUG_CHECK_GL_ERROR
#endif
// #if BA_OSTYPE_IOS
// void (*glInvalidateFramebuffer)(GLenum target, GLsizei num_attachments,
// const GLenum* attachments) = nullptr;
// #endif
#if BA_OSTYPE_ANDROID
PFNGLDISCARDFRAMEBUFFEREXTPROC _glDiscardFramebufferEXT = nullptr;
#endif
#if BA_OSTYPE_WINDOWS
PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ = nullptr;
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC
glGetFramebufferAttachmentParameteriv = nullptr;
PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate = nullptr;
PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = nullptr;
PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB = nullptr;
PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB = nullptr;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
PFNGLCREATESHADERPROC glCreateShader = nullptr;
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
PFNGLGETINFOLOGARBPROC glGetInfoLogARB = nullptr;
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
PFNGLUSEPROGRAMOBJECTARBPROC glUseProgram = nullptr;
PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr;
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = nullptr;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
PFNGLUNIFORM1IPROC glUniform1i = nullptr;
PFNGLUNIFORM1FPROC glUniform1f = nullptr;
PFNGLUNIFORM1FVPROC glUniform1fv = nullptr;
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
PFNGLUNIFORM3FPROC glUniform3f = nullptr;
PFNGLUNIFORM4FPROC glUniform4f = nullptr;
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr;
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr;
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
PFNGLBUFFERDATAPROC glBufferData = nullptr;
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr;
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample =
nullptr;
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = nullptr;
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr;
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr;
PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fv = nullptr;
PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation = nullptr;
PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D = nullptr;
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
PFNGLDETACHSHADERPROC glDetachShader = nullptr;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
#endif // BA_OSTYPE_WINDOWS
// #if BA_OSTYPE_ANDROID
// PFNGLDISCARDFRAMEBUFFEREXTPROC _glDiscardFramebufferEXT = nullptr;
// #endif
namespace ballistica::base {
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
#pragma ide diagnostic ignored "EmptyDeclOrStmt"
bool g_sys_gl_inited{};
GLContext::GLContext(int target_res_x, int target_res_y, bool fullscreen)
: fullscreen_(fullscreen) {
assert(g_base->InGraphicsThread());
bool need_window = true;
#if BA_RIFT_BUILD
// on the rift build we don't need a window when running in vr mode; we just
// use the context we're created into...
if (g_core->IsVRMode()) {
need_window = false;
}
#endif // BA_RIFT_BUILD
if (explicit_bool(need_window)) {
#if BA_SDL2_BUILD
#if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
int flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS;
#else
// Things are a bit more varied on desktop..
uint32_t flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
| SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
if (fullscreen_) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
#endif
sdl_window_ = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, target_res_x,
target_res_y, flags);
if (!sdl_window_) {
throw Exception("Unable to create SDL Window of size "
+ std::to_string(target_res_x) + " by "
+ std::to_string(target_res_y));
}
sdl_gl_context_ = SDL_GL_CreateContext(sdl_window_);
if (!sdl_gl_context_) {
throw Exception("Unable to create SDL GL Context");
}
SDL_SetWindowTitle(sdl_window_, "BallisticaKit");
// #if 0
// // Fetch needed android gl stuff.
// #if BA_OSTYPE_ANDROID
// #define GET(PTRTYPE, FUNC, REQUIRED) \
// FUNC = (PTRTYPE)eglGetProcAddress(#FUNC); \
// if (!FUNC) FUNC = (PTRTYPE)eglGetProcAddress(#FUNC "EXT"); \
// if (REQUIRED) { \
// BA_PRECONDITION(FUNC != nullptr); \
// }
// GET(PFNGLDISCARDFRAMEBUFFEREXTPROC, _glDiscardFramebufferEXT, false);
// #endif // BA_OSTYPE_ANDROID
// Our actual drawable size could differ from the window size on retina
// devices.
int win_size_x, win_size_y;
SDL_GetWindowSize(sdl_window_, &win_size_x, &win_size_y);
AppAdapterSDL::Get()->SetInitialScreenDimensions(Vector2f(
static_cast<float>(win_size_x), static_cast<float>(win_size_y)));
#if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
res_x_ = win_size_x;
res_y_ = win_size_y;
#else
SDL_GL_GetDrawableSize(sdl_window_, &res_x_, &res_y_);
#endif // BA_OSTYPE_ANDROID
// #endif // 0
// This can come through as zero in some cases (on our cardboard build at
// least).
if (win_size_x != 0) {
pixel_density_ =
static_cast<float>(res_x_) / static_cast<float>(win_size_x);
}
#elif BA_SDL_BUILD // BA_SDL2_BUILD
// Provide an empty implementation of this if noone provided a real one.
#ifndef BA_HAS_SYS_GL_INIT
int v_flags;
v_flags = SDL_OPENGL;
if (fullscreen_) {
v_flags |= SDL_FULLSCREEN;
// convert to the closest valid fullscreen resolution
// (our last 1.2 build is mac and it's got hacked-in fullscreen-window
// support; so we don't need this) getValidResolution(target_res_x,
// target_res_y);
} else {
v_flags |= SDL_RESIZABLE;
}
surface_ = SDL_SetVideoMode(target_res_x, target_res_y, 32, v_flags);
void SysGLInit() { assert(!g_sys_gl_inited); }
// if we failed, fall back to windowed mode.
if (surface_ == nullptr) {
throw Exception("SDL_SetVideoMode() failed for "
+ std::to_string(target_res_x) + " by "
+ std::to_string(target_res_y) + " fullscreen="
+ std::to_string(static_cast<int>(fullscreen_)));
}
res_x_ = surface_->w;
res_y_ = surface_->h;
AppAdapterSDL::Get()->SetInitialScreenDimensions(Vector2f(res_x_, res_y_));
SDL_WM_SetCaption("BallisticaKit", "BallisticaKit");
#elif BA_OSTYPE_ANDROID
// On Android the Java layer creates a GL setup before even calling us.
// So we have nothing to do here. Hooray!
#else
throw Exception("FIXME: Unimplemented");
#endif // BA_SDL2_BUILD
}
// Fetch needed android gl stuff.
#if BA_OSTYPE_ANDROID
#define GET(PTRTYPE, FUNC, REQUIRED) \
FUNC = (PTRTYPE)eglGetProcAddress(#FUNC); \
if (!FUNC) FUNC = (PTRTYPE)eglGetProcAddress(#FUNC "EXT"); \
if (REQUIRED) { \
BA_PRECONDITION(FUNC != nullptr); \
}
GET(PFNGLDISCARDFRAMEBUFFEREXTPROC, _glDiscardFramebufferEXT, false);
#endif // BA_OSTYPE_ANDROID
// Fetch needed windows gl stuff.
#if BA_OSTYPE_WINDOWS
#define GET(PTRTYPE, FUNC, REQUIRED) \
FUNC = (PTRTYPE)wglGetProcAddress(#FUNC); \
if (!FUNC) FUNC = (PTRTYPE)wglGetProcAddress(#FUNC "EXT"); \
if (REQUIRED) { \
BA_PRECONDITION(FUNC != nullptr); \
}
GET(PFNGLGETINTERNALFORMATIVPROC, glGetInternalformativ,
false); // for checking msaa level support
GET(PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC,
glGetFramebufferAttachmentParameteriv, false); // for checking srgb stuff
GET(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate,
false); // needed for VR overlay
GET(PFNGLACTIVETEXTUREPROC, glActiveTexture, true);
GET(PFNGLCLIENTACTIVETEXTUREARBPROC, glClientActiveTextureARB, true);
GET(PFNWGLSWAPINTERVALEXTPROC, wglSwapIntervalEXT, true);
GET(PFNGLPOINTPARAMETERFVARBPROC, glPointParameterfvARB, true);
GET(PFNGLPOINTPARAMETERFARBPROC, glPointParameterfARB, true);
GET(PFNGLCREATEPROGRAMPROC, glCreateProgram, true);
GET(PFNGLCREATESHADERPROC, glCreateShader, true);
GET(PFNGLSHADERSOURCEPROC, glShaderSource, true);
GET(PFNGLCOMPILESHADERPROC, glCompileShader, true);
GET(PFNGLLINKPROGRAMPROC, glLinkProgram, true);
GET(PFNGLGETINFOLOGARBPROC, glGetInfoLogARB, true);
GET(PFNGLATTACHSHADERPROC, glAttachShader, true);
GET(PFNGLUSEPROGRAMOBJECTARBPROC, glUseProgram, true);
GET(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap, true);
GET(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer, true);
GET(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation, true);
GET(PFNGLUNIFORM1IPROC, glUniform1i, true);
GET(PFNGLUNIFORM1FPROC, glUniform1f, true);
GET(PFNGLUNIFORM1FVPROC, glUniform1fv, true);
GET(PFNGLUNIFORM2FPROC, glUniform2f, true);
GET(PFNGLUNIFORM3FPROC, glUniform3f, true);
GET(PFNGLUNIFORM4FPROC, glUniform4f, true);
GET(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers, true);
GET(PFNGLGENBUFFERSPROC, glGenBuffers, true);
GET(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D, true);
GET(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers, true);
GET(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer, true);
GET(PFNGLBINDBUFFERPROC, glBindBuffer, true);
GET(PFNGLBUFFERDATAPROC, glBufferData, true);
GET(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage, true);
GET(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer, true);
GET(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus, true);
GET(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers, true);
GET(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers, true);
GET(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer, true);
GET(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray, true);
GET(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray, true);
GET(PFNGLUNIFORMMATRIX4FVARBPROC, glUniformMatrix4fv, true);
GET(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation, true);
GET(PFNGLCOMPRESSEDTEXIMAGE2DPROC, glCompressedTexImage2D, true);
GET(PFNGLGETSHADERIVPROC, glGetShaderiv, true);
GET(PFNGLGETPROGRAMIVPROC, glGetProgramiv, true);
GET(PFNGLDELETESHADERPROC, glDeleteShader, true);
GET(PFNGLDELETEBUFFERSPROC, glDeleteBuffers, true);
GET(PFNGLDELETEPROGRAMPROC, glDeleteProgram, true);
GET(PFNGLDETACHSHADERPROC, glDetachShader, true);
GET(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog, true);
GET(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog, true);
// Stuff we can live without:
GET(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray, false);
GET(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays, false);
GET(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays, false);
GET(PFNGLBLITFRAMEBUFFERPROC, glBlitFramebuffer, false);
GET(PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC, glRenderbufferStorageMultisample,
false);
#undef GET
#endif // BA_OSTYPE_WINDOWS
// So that our window comes up nice and black.
// FIXME should just make the window's blanking color black.
#if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
// Not needed here.
#else
#if BA_SDL2_BUILD
// Gonna wait and see if if still need this.
#elif BA_SDL_BUILD
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapBuffers();
#endif // BA_SDL2_BUILD
#endif // IOS/ANDROID
}
#pragma clang diagnostic pop
void GLContext::SetVSync(bool enable) {
assert(g_base->InGraphicsThread());
#if BA_OSTYPE_MACOS
CGLContextObj context = CGLGetCurrentContext();
BA_PRECONDITION(context);
GLint sync = enable;
CGLSetParameter(context, kCGLCPSwapInterval, &sync);
#else
#endif // BA_OSTYPE_MACOS
}
GLContext::~GLContext() {
if (!g_base->InGraphicsThread()) {
Log(LogLevel::kError, "GLContext dying in non-graphics thread");
}
#if BA_SDL2_BUILD
#if BA_RIFT_BUILD
// (in rift we only have a window in 2d mode)
if (!g_core->IsVRMode()) {
BA_PRECONDITION_LOG(sdl_window_);
}
#else // BA_RIFT_MODE
BA_PRECONDITION_LOG(sdl_window_);
#endif // BA_RIFT_BUILD
if (sdl_window_) {
SDL_DestroyWindow(sdl_window_);
sdl_window_ = nullptr;
}
#elif BA_SDL_BUILD
BA_PRECONDITION_LOG(surface_);
if (surface_) {
SDL_FreeSurface(surface_);
surface_ = nullptr;
}
#endif
}
auto GLErrorToString(GLenum err) -> std::string {
switch (err) {
case GL_NO_ERROR:
return "GL_NO_ERROR";
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
case GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
default:
return std::to_string(err);
}
}
#endif // BA_HAS_SYS_GL_INIT
} // namespace ballistica::base

View File

@ -3,204 +3,232 @@
#ifndef BALLISTICA_BASE_GRAPHICS_GL_GL_SYS_H_
#define BALLISTICA_BASE_GRAPHICS_GL_GL_SYS_H_
// A single header to include system GL headers along with custom
// per-platform defines/function-pointers/etc.
#if BA_ENABLE_OPENGL
// On most platforms we directly link against GL and want all the functions
// defined in the header for us. On Windows we have to define/load newer
// stuff manually though, so we don't want that.
#if !BA_OSTYPE_WINDOWS
#define GL_GLEXT_PROTOTYPES
#endif
#include <string>
// ----------------------------- BASE GL INCLUDES ------------------------------
#if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
#if BA_USE_ES3_INCLUDES
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#elif BA_OSTYPE_IOS_TVOS
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#else
// On SDL builds, let SDL handle this for us.
#if BA_SDL_BUILD
#include <SDL/SDL.h> // needed for ios?...
#include <SDL/SDL_opengles2.h>
#else
// FIXME: According to https://developer.android.com/ndk/guides/stable_apis
// we can always link against ES3.1 now that we're API 21+, so we shouldn't
// need our funky stubs and function lookups anymore.
// (though we'll still need to check for availability of 3.x features)
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif // BA_SDL_BUILD
#endif // BA_USE_ES3_INCLUDES
// looks like these few defines are currently missing on android
// (s3tc works on some nvidia hardware)
#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
#else // BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
// SDK Desktop builds.
#if BA_SDL2_BUILD
#include <SDL_opengl.h>
#elif BA_SDL_BUILD // BA_SDL2_BUILD
#define NO_SDL_GLEXT
#include <SDL_opengl.h>
#endif // BA_SDL2_BUILD
#endif
#if BA_OSTYPE_MACOS
// For XCode builds, grab Apple's framework-y headers.
#if BA_XCODE_BUILD
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <OpenGL/glu.h>
#endif // BA_XCODE_BUILD
#endif // BA_OSTYPE_MACOS
#endif // BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
#include "ballistica/core/platform/support/min_sdl.h"
#include "ballistica/shared/foundation/object.h"
#if BA_OSTYPE_ANDROID
extern PFNGLDISCARDFRAMEBUFFEREXTPROC _glDiscardFramebufferEXT;
#if BA_OPENGL_IS_ES
#include <OpenGLES/ES3/gl.h>
#include <OpenGLES/ES3/glext.h>
#else
#include <OpenGL/gl3.h>
#include <OpenGL/gl3ext.h>
#endif
#endif
// On Android, we're currently supporting Android API 21 and newer, which
// means we can count on GL ES 3.1 libs/headers always being available. Note
// that hardware may still be limited to older versions so we need to check
// for that and set a limit in our manifest.
#if BA_OSTYPE_ANDROID
#include <GLES3/gl31.h>
#include <GLES3/gl3ext.h>
#endif
// -----------------------------------------------------------------------------
// Now mix in a bit of magic of our own...
// We may use S3TC types even on ES (Android Nvidia hardware supports them)
// but they're not currently in ES's glext.h. Define here if needed.
#ifndef GL_EXT_texture_compression_s3tc
#define GL_EXT_texture_compression_s3tc 1
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif /* GL_EXT_texture_compression_s3tc */
// Anisotropic texturing is still an extension in GL 3 and ES 3.2, so
// define its values if need be (they seem to exist in desktop glext.h
// but not es)
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_EXT_texture_filter_anisotropic 1
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
#endif /* GL_EXT_texture_filter_anisotropic */
// Desktop GL has glDepthRange() which takes a double. GL ES has
// glDepthRangef() which takes a float. Let's always accept doubles and
// down-convert where needed.
#if BA_OPENGL_IS_ES
inline void glDepthRange(double min, double max) {
return glDepthRangef(min, max);
}
#endif
// #if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
// #if BA_USE_ES3_INCLUDES
// #include <GLES3/gl3.h>
// #include <GLES3/gl3ext.h>
// #elif BA_OSTYPE_IOS_TVOS
// #include <OpenGLES/ES2/gl.h>
// #include <OpenGLES/ES2/glext.h>
// #else
// #if BA_SDL_BUILD
// #include <SDL/SDL.h> // needed for ios?...
// #include <SDL/SDL_opengles2.h>
// #else
// // FIXME: According to https://developer.android.com/ndk/guides/stable_apis
// // we can always link against ES3.1 now that we're API 21+, so we shouldn't
// // need our funky stubs and function lookups anymore.
// // (though we'll still need to check for availability of 3.x features)
// #include <GLES2/gl2.h>
// #include <GLES2/gl2ext.h>
// #endif // BA_SDL_BUILD
// #endif // BA_USE_ES3_INCLUDES
// Looks like these few defines are currently missing on android (s3tc works
// on some nvidia hardware).
// #ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
// #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
// #endif
// #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
// #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
// #endif
// #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
// #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
// #endif
// #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
// #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
// #endif
// #if BA_OSTYPE_IOS_TVOS
// extern void (*glInvalidateFramebuffer)(GLenum target, GLsizei
// num_attachments,
// const GLenum* attachments);
// #define glDepthRange glDepthRangef
// #define glGenVertexArrays glGenVertexArraysOES
// #define glDeleteVertexArrays glDeleteVertexArraysOES
// #define glBindVertexArray glBindVertexArrayOES
// #define glClearDepth glClearDepthf
// #endif // BA_OSTYPE_IOS_TVOS
// #else // BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
// SDL Desktop builds.
// #if BA_SDL2_BUILD
// #include <SDL_opengl.h>
// #elif BA_SDL_BUILD // BA_SDL2_BUILD
// #define NO_SDL_GLEXT
// #include <SDL_opengl.h>
// #endif // BA_SDL2_BUILD
// #if BA_OSTYPE_MACOS
// #include <OpenGL/CGLContext.h>
// (NO LONGER APPLIES IN CORE PROFILE)
// #define glGenVertexArrays glGenVertexArraysAPPLE
// #define glDeleteVertexArrays glDeleteVertexArraysAPPLE
// #define glBindVertexArray glBindVertexArrayAPPLE
// #endif // BA_OSTYPE_MACOS
// #endif // BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
// #if BA_OSTYPE_ANDROID
// #include <EGL/egl.h>
// #include <android/log.h>
// #if !BA_USE_ES3_INCLUDES
// #include "ballistica/core/platform/android/android_gl3.h"
// #endif
// #define glDepthRange glDepthRangef
// #define glDiscardFramebufferEXT _glDiscardFramebufferEXT
// #ifndef GL_RGB565_OES
// #define GL_RGB565_OES 0x8D62
// #endif // GL_RGB565_OES
// #define GL_READ_FRAMEBUFFER 0x8CA8
// #define GL_DRAW_FRAMEBUFFER 0x8CA9
// #define GL_READ_FRAMEBUFFER_BINDING 0x8CAA
// #define glClearDepth glClearDepthf
// #endif // BA_OSTYPE_ANDROID
// #if BA_OSTYPE_ANDROID
// extern PFNGLDISCARDFRAMEBUFFEREXTPROC _glDiscardFramebufferEXT;
// #endif
#if BA_OSTYPE_WINDOWS
#ifndef WGL_EXT_swap_control
#define WGL_EXT_swap_control 1
typedef BOOL(WINAPI* PFNWGLSWAPINTERVALEXTPROC)(int interval);
typedef int(WINAPI* PFNWGLGETSWAPINTERVALEXTPROC)(VOID); // NOLINT
#endif // WGL_EXT_swap_control
extern PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ;
extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC
glGetFramebufferAttachmentParameteriv;
extern PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate;
extern PFNGLACTIVETEXTUREPROC glActiveTexture;
extern PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;
extern PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB;
extern PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB;
extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
extern PFNGLCREATESHADERPROC glCreateShader;
extern PFNGLSHADERSOURCEPROC glShaderSource;
extern PFNGLCOMPILESHADERPROC glCompileShader;
extern PFNGLLINKPROGRAMPROC glLinkProgram;
extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
extern PFNGLATTACHSHADERPROC glAttachShader;
extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgram;
extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
extern PFNGLUNIFORM1IPROC glUniform1i;
extern PFNGLUNIFORM1FPROC glUniform1f;
extern PFNGLUNIFORM1FVPROC glUniform1fv;
extern PFNGLUNIFORM2FPROC glUniform2f;
extern PFNGLUNIFORM3FPROC glUniform3f;
extern PFNGLUNIFORM4FPROC glUniform4f;
extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
extern PFNGLGENBUFFERSPROC glGenBuffers;
extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
extern PFNGLBINDBUFFERPROC glBindBuffer;
extern PFNGLBUFFERDATAPROC glBufferData;
extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fv;
extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
extern PFNGLGETSHADERIVPROC glGetShaderiv;
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
extern PFNGLDELETESHADERPROC glDeleteShader;
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
extern PFNGLDETACHSHADERPROC glDetachShader;
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
#endif // BA_OSTYPE_WINDOWS
#ifndef GL_NV_texture_rectangle
#define GL_TEXTURE_RECTANGLE_NV 0x84F5
#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6
#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7
#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
#endif
#ifndef GL_NV_texture_rectangle
#define GL_NV_texture_rectangle 1
#include "ballistica/base/graphics/gl/gl_sys_windows.h"
#endif
// Support for gl object debug labeling.
// #ifndef GL_NV_texture_rectangle
// #define GL_TEXTURE_RECTANGLE_NV 0x84F5
// #define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6
// #define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7
// #define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
// #endif
// #ifndef GL_NV_texture_rectangle
// #define GL_NV_texture_rectangle 1
// #endif
// Support for GL object debug labeling.
#if BA_OSTYPE_IOS_TVOS
#define GL_LABEL_OBJECT(type, obj, label) glLabelObjectEXT(type, obj, 0, label)
#define GL_PUSH_GROUP_MARKER(label) glPushGroupMarkerEXT(0, label)
#define GL_POP_GROUP_MARKER() glPopGroupMarkerEXT()
#define BA_GL_LABEL_OBJECT(type, obj, label) \
glLabelObjectEXT(type, obj, 0, label)
#define BA_GL_PUSH_GROUP_MARKER(label) glPushGroupMarkerEXT(0, label)
#define BA_GL_POP_GROUP_MARKER() glPopGroupMarkerEXT()
#else
#define GL_LABEL_OBJECT(type, obj, label) ((void)0)
#define GL_PUSH_GROUP_MARKER(label) ((void)0)
#define GL_POP_GROUP_MARKER() ((void)0)
#define BA_GL_LABEL_OBJECT(type, obj, label) ((void)0)
#define BA_GL_PUSH_GROUP_MARKER(label) ((void)0)
#define BA_GL_POP_GROUP_MARKER() ((void)0)
#endif
// OpenGL ES uses precision; regular GL doesn't.
#if BA_OPENGL_IS_ES
#define BA_GLSL_LOWP "lowp "
#define BA_GLSL_MEDIUMP "mediump "
#define BA_GLSL_HIGHP "highp "
#else
#define BA_GLSL_LOWP
#define BA_GLSL_MEDIUMP
#define BA_GLSL_HIGHP
#endif // BA_OPENGL_IS_ES
// Our old GLSL source uses 'attribute' and our newer uses 'in'
#if BA_OPENGL_IS_ES
#define BA_GLSL_VERTEX_IN "attribute"
#define BA_GLSL_VERTEX_OUT "varying"
#define BA_GLSL_FRAG_IN "varying"
#define BA_GLSL_FRAGCOLOR "gl_FragColor"
#define BA_GLSL_TEXTURE2D "texture2D"
#define BA_GLSL_TEXTURE2DPROJ "texture2DProj"
#define BA_GLSL_TEXTURECUBE "textureCube"
#else
#define BA_GLSL_VERTEX_IN "in"
#define BA_GLSL_VERTEX_OUT "out"
#define BA_GLSL_FRAG_IN "in"
#define BA_GLSL_FRAGCOLOR "fragColor"
#define BA_GLSL_TEXTURE2D "texture"
#define BA_GLSL_TEXTURE2DPROJ "textureProj"
#define BA_GLSL_TEXTURECUBE "texture"
#endif
namespace ballistica::base {
auto GLErrorToString(GLenum err) -> std::string;
extern bool g_sys_gl_inited;
// Container for OpenGL rendering context data.
class GLContext {
public:
GLContext(int target_res_x, int target_res_y, bool fullScreen);
~GLContext();
auto res_x() const -> int { return res_x_; }
auto res_y() const -> int { return res_y_; }
auto pixel_density() const -> float { return pixel_density_; }
void SetVSync(bool enable);
// Currently no surface/window in this case.
#if BA_SDL2_BUILD
auto sdl_window() const -> SDL_Window* {
assert(sdl_window_);
return sdl_window_;
}
#elif BA_SDL_BUILD // BA_SDL2_BUILD
SDL_Surface* sdl_screen_surface() const {
assert(surface_);
return surface_;
}
#endif // BA_SDL2_BUILD
private:
#if BA_SDL2_BUILD
SDL_Window* sdl_window_{};
SDL_GLContext sdl_gl_context_{};
#endif // BA_SDL2_BUILD
bool fullscreen_{};
int res_x_{};
int res_y_{};
float pixel_density_{1.0f};
#if BA_SDL_BUILD && !BA_SDL2_BUILD
SDL_Surface* surface_{};
#endif
}; // GLContext
// Called when a GL renderer is spinning up. Allows fetching/assigning any
// global function pointers or data needed for GL to function. Will be
// called only once and then g_sys_gl_inited set. A platform that defines
// this should define BA_HAS_SYS_GL_INIT; otherwise a default empty
// implementation will be defined.
void SysGLInit();
} // namespace ballistica::base

View File

@ -0,0 +1,175 @@
// Released under the MIT License. See LICENSE for details.
#if BA_ENABLE_OPENGL && BA_OSTYPE_WINDOWS
#include "ballistica/base/graphics/gl/gl_sys_windows.h"
#include "SDL.h"
#include "ballistica/base/graphics/gl/gl_sys.h"
#include "ballistica/shared/ballistica.h"
#pragma comment(lib, "opengl32.lib")
// #pragma comment(lib, "glu32.lib")
PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ{};
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC
glGetFramebufferAttachmentParameteriv{};
PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate{};
PFNGLACTIVETEXTUREPROC glActiveTextureBA{};
// PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB{};
PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB{};
// PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB{};
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT{};
PFNGLCREATEPROGRAMPROC glCreateProgram{};
PFNGLCREATESHADERPROC glCreateShader{};
PFNGLSHADERSOURCEPROC glShaderSource{};
PFNGLCOMPILESHADERPROC glCompileShader{};
PFNGLLINKPROGRAMPROC glLinkProgram{};
PFNGLGETINFOLOGARBPROC glGetInfoLogARB{};
PFNGLATTACHSHADERPROC glAttachShader{};
PFNGLUSEPROGRAMOBJECTARBPROC glUseProgram{};
PFNGLGENERATEMIPMAPPROC glGenerateMipmap{};
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer{};
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer{};
PFNGLBINDVERTEXARRAYPROC glBindVertexArray{};
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation{};
PFNGLUNIFORM1IPROC glUniform1i{};
PFNGLUNIFORM1FPROC glUniform1f{};
PFNGLUNIFORM1FVPROC glUniform1fv{};
PFNGLUNIFORM2FPROC glUniform2f{};
PFNGLUNIFORM3FPROC glUniform3f{};
PFNGLUNIFORM4FPROC glUniform4f{};
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers{};
PFNGLGENBUFFERSPROC glGenBuffers{};
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays{};
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D{};
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers{};
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer{};
PFNGLBINDBUFFERPROC glBindBuffer{};
PFNGLBUFFERDATAPROC glBufferData{};
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage{};
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample{};
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer{};
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus{};
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers{};
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers{};
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer{};
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray{};
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray{};
PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fv{};
PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation{};
PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2DBA{};
PFNGLGETSHADERIVPROC glGetShaderiv{};
PFNGLGETPROGRAMIVPROC glGetProgramiv{};
PFNGLDELETESHADERPROC glDeleteShader{};
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays{};
PFNGLDELETEBUFFERSPROC glDeleteBuffers{};
PFNGLDELETEPROGRAMPROC glDeleteProgram{};
PFNGLDETACHSHADERPROC glDetachShader{};
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog{};
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog{};
PFNGLGETSTRINGIPROC glGetStringi{};
namespace ballistica::base {
static auto GetGLFunc_(const char* name, bool required) -> void* {
void* func = SDL_GL_GetProcAddress(name);
if (!func) {
func = SDL_GL_GetProcAddress((std::string(name) + "EXT").c_str());
}
if (required && func == nullptr) {
FatalError("OpenGL function '" + std::string(name)
+ "' not found.\nAre your graphics drivers up to date?");
}
return func;
}
// Our variable name matches the name we're fetching from the library.
#define GET(PTRTYPE, FUNC, REQUIRED) FUNC = (PTRTYPE)GetGLFunc_(#FUNC, REQUIRED)
// Our variable name equals the library name + BA. (For symbol clashes).
#define GET2(PTRTYPE, FUNC, REQUIRED) \
FUNC##BA = (PTRTYPE)GetGLFunc_(#FUNC, REQUIRED)
void SysGLInit() {
assert(!g_sys_gl_inited);
SDL_GL_LoadLibrary(nullptr);
void* testval{};
PFNGLGETINTERNALFORMATIVPROC fptr;
fptr = (PFNGLGETINTERNALFORMATIVPROC)testval;
// For checking msaa level support. This is only available in GL 4.2+
// so we can survive without it.
GET(PFNGLGETINTERNALFORMATIVPROC, glGetInternalformativ, false);
// For checking srgb stuff.
GET(PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC,
glGetFramebufferAttachmentParameteriv, false);
// Needed for VR overlay.
GET(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate, false);
GET2(PFNGLACTIVETEXTUREPROC, glActiveTexture, true);
// GET(PFNGLCLIENTACTIVETEXTUREARBPROC, glClientActiveTextureARB, true);
GET(PFNWGLSWAPINTERVALEXTPROC, wglSwapIntervalEXT, true);
GET(PFNGLPOINTPARAMETERFVARBPROC, glPointParameterfvARB, true);
// GET(PFNGLPOINTPARAMETERFARBPROC, glPointParameterfARB, true);
GET(PFNGLCREATEPROGRAMPROC, glCreateProgram, true);
GET(PFNGLCREATESHADERPROC, glCreateShader, true);
GET(PFNGLSHADERSOURCEPROC, glShaderSource, true);
GET(PFNGLCOMPILESHADERPROC, glCompileShader, true);
GET(PFNGLLINKPROGRAMPROC, glLinkProgram, true);
GET(PFNGLGETINFOLOGARBPROC, glGetInfoLogARB, true);
GET(PFNGLATTACHSHADERPROC, glAttachShader, true);
GET(PFNGLUSEPROGRAMOBJECTARBPROC, glUseProgram, true);
GET(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap, true);
GET(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer, true);
GET(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation, true);
GET(PFNGLUNIFORM1IPROC, glUniform1i, true);
GET(PFNGLUNIFORM1FPROC, glUniform1f, true);
GET(PFNGLUNIFORM1FVPROC, glUniform1fv, true);
GET(PFNGLUNIFORM2FPROC, glUniform2f, true);
GET(PFNGLUNIFORM3FPROC, glUniform3f, true);
GET(PFNGLUNIFORM4FPROC, glUniform4f, true);
GET(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers, true);
GET(PFNGLGENBUFFERSPROC, glGenBuffers, true);
GET(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D, true);
GET(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers, true);
GET(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer, true);
GET(PFNGLBINDBUFFERPROC, glBindBuffer, true);
GET(PFNGLBUFFERDATAPROC, glBufferData, true);
GET(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage, true);
GET(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer, true);
GET(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus, true);
GET(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers, true);
GET(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers, true);
GET(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer, true);
GET(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray, true);
GET(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray, true);
GET(PFNGLUNIFORMMATRIX4FVARBPROC, glUniformMatrix4fv, true);
GET(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation, true);
GET2(PFNGLCOMPRESSEDTEXIMAGE2DPROC, glCompressedTexImage2D, true);
GET(PFNGLGETSHADERIVPROC, glGetShaderiv, true);
GET(PFNGLGETPROGRAMIVPROC, glGetProgramiv, true);
GET(PFNGLDELETESHADERPROC, glDeleteShader, true);
GET(PFNGLDELETEBUFFERSPROC, glDeleteBuffers, true);
GET(PFNGLDELETEPROGRAMPROC, glDeleteProgram, true);
GET(PFNGLDETACHSHADERPROC, glDetachShader, true);
GET(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog, true);
GET(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog, true);
GET(PFNGLGETSTRINGIPROC, glGetStringi, true);
GET(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray, true);
GET(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays, true);
GET(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays, true);
GET(PFNGLBLITFRAMEBUFFERPROC, glBlitFramebuffer, true);
GET(PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC, glRenderbufferStorageMultisample,
true);
}
#undef GET
#undef GET2
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL && BA_OSTYPE_WINDOWS

View File

@ -0,0 +1,91 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_GL_SYS_WINDOWS_H_
#define BALLISTICA_BASE_GRAPHICS_GL_GL_SYS_WINDOWS_H_
// System GL bits for windows.
#if BA_ENABLE_OPENGL && BA_OSTYPE_WINDOWS
// We don't *actually* need this because gl_sys.h includes it before
// it includes us, but this keeps things from erroring if we look at
// the header by itself.
#include <SDL_opengl.h>
// We run some init code to grab function ptrs/etc.
#define BA_HAS_SYS_GL_INIT
#ifndef WGL_EXT_swap_control
#define WGL_EXT_swap_control 1
typedef BOOL(WINAPI* PFNWGLSWAPINTERVALEXTPROC)(int interval);
typedef int(WINAPI* PFNWGLGETSWAPINTERVALEXTPROC)(VOID); // NOLINT
#endif
// These seem to be defined by the SDL GL headers even though we asked them
// nicely not to (by not defining GL_GLEXT_PROTOTYPES). So we need to import
// and use it via a custom name.
#define glActiveTexture glActiveTextureBA
#define glCompressedTexImage2D glCompressedTexImage2DBA
extern PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ;
extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC
glGetFramebufferAttachmentParameteriv;
extern PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate;
extern PFNGLACTIVETEXTUREPROC glActiveTextureBA;
// extern PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;
// extern PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB;
extern PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB;
extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
extern PFNGLCREATESHADERPROC glCreateShader;
extern PFNGLSHADERSOURCEPROC glShaderSource;
extern PFNGLCOMPILESHADERPROC glCompileShader;
extern PFNGLLINKPROGRAMPROC glLinkProgram;
extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
extern PFNGLATTACHSHADERPROC glAttachShader;
extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgram;
extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
extern PFNGLUNIFORM1IPROC glUniform1i;
extern PFNGLUNIFORM1FPROC glUniform1f;
extern PFNGLUNIFORM1FVPROC glUniform1fv;
extern PFNGLUNIFORM2FPROC glUniform2f;
extern PFNGLUNIFORM3FPROC glUniform3f;
extern PFNGLUNIFORM4FPROC glUniform4f;
extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
extern PFNGLGENBUFFERSPROC glGenBuffers;
extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
extern PFNGLBINDBUFFERPROC glBindBuffer;
extern PFNGLBUFFERDATAPROC glBufferData;
extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fv;
extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2DBA;
extern PFNGLGETSHADERIVPROC glGetShaderiv;
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
extern PFNGLDELETESHADERPROC glDeleteShader;
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
extern PFNGLDETACHSHADERPROC glDetachShader;
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
extern PFNGLGETSTRINGIPROC glGetStringi;
#endif // BA_ENABLE_OPENGL && BA_OSTYPE_WINDOWS
#endif // BALLISTICA_BASE_GRAPHICS_GL_GL_SYS_WINDOWS_H_

View File

@ -0,0 +1,159 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_ASSET_DATA_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_ASSET_DATA_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/gl_sys.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/graphics/mesh/mesh_renderer_data.h"
#include "ballistica/shared/ballistica.h"
namespace ballistica::base {
class RendererGL::MeshAssetDataGL : public MeshAssetRendererData {
public:
enum BufferType { kVertices, kIndices, kBufferCount };
MeshAssetDataGL(const MeshAsset& model, RendererGL* renderer)
: renderer_(renderer), fake_vao_(nullptr) {
#if BA_DEBUG_BUILD
name_ = model.GetName();
#endif
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
// Create our vertex array to hold all this state.
glGenVertexArrays(1, &vao_);
BA_DEBUG_CHECK_GL_ERROR;
renderer->BindVertexArray_(vao_);
BA_DEBUG_CHECK_GL_ERROR;
glGenBuffers(kBufferCount, vbos_);
BA_DEBUG_CHECK_GL_ERROR;
// Fill our vertex data buffer.
renderer_->BindArrayBuffer(vbos_[kVertices]);
BA_DEBUG_CHECK_GL_ERROR;
glBufferData(GL_ARRAY_BUFFER,
static_cast_check_fit<GLsizeiptr>(model.vertices().size()
* sizeof(VertexObjectFull)),
&(model.vertices()[0]), GL_STATIC_DRAW);
BA_DEBUG_CHECK_GL_ERROR;
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE, sizeof(VertexObjectFull),
reinterpret_cast<void*>(offsetof(VertexObjectFull, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
glVertexAttribPointer(
kVertexAttrUV, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(VertexObjectFull),
reinterpret_cast<void*>(offsetof(VertexObjectFull, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
glVertexAttribPointer(
kVertexAttrNormal, 3, GL_SHORT, GL_TRUE, sizeof(VertexObjectFull),
reinterpret_cast<void*>(offsetof(VertexObjectFull, normal)));
glEnableVertexAttribArray(kVertexAttrNormal);
BA_DEBUG_CHECK_GL_ERROR;
// Fill our index data buffer.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos_[kIndices]);
const GLvoid* index_data;
switch (model.GetIndexSize()) {
case 1: {
elem_count_ = static_cast<uint32_t>(model.indices8().size());
index_type_ = GL_UNSIGNED_BYTE;
index_data = static_cast<const GLvoid*>(model.indices8().data());
break;
}
case 2: {
elem_count_ = static_cast<uint32_t>(model.indices16().size());
index_type_ = GL_UNSIGNED_SHORT;
index_data = static_cast<const GLvoid*>(model.indices16().data());
break;
}
case 4: {
BA_LOG_ONCE(
LogLevel::kWarning,
"GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!");
elem_count_ = static_cast<uint32_t>(model.indices32().size());
index_type_ = GL_UNSIGNED_INT;
index_data = static_cast<const GLvoid*>(model.indices32().data());
break;
}
default:
throw Exception();
}
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
static_cast_check_fit<GLsizeiptr>(elem_count_ * model.GetIndexSize()),
index_data, GL_STATIC_DRAW);
BA_DEBUG_CHECK_GL_ERROR;
}
~MeshAssetDataGL() override {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
// Unbind if we're bound; otherwise if a new vao pops up with our same
// ID it'd be prevented from binding.
if (vao_ == renderer_->current_vertex_array_) {
renderer_->BindVertexArray_(0);
}
if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteVertexArrays(1, &vao_);
}
// Make sure our dying buffer isn't current (don't wanna prevent binding
// to a new buffer with a recycled id).
for (unsigned int vbo : vbos_) {
if (vbo == renderer_->active_array_buffer_) {
renderer_->active_array_buffer_ = -1;
}
}
if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteBuffers(kBufferCount, vbos_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
void Bind() {
renderer_->BindVertexArray_(vao_);
BA_DEBUG_CHECK_GL_ERROR;
}
void Draw() {
BA_DEBUG_CHECK_GL_ERROR;
if (elem_count_ > 0) {
glDrawElements(GL_TRIANGLES, elem_count_, index_type_, nullptr);
}
BA_DEBUG_CHECK_GL_ERROR;
}
#if BA_DEBUG_BUILD
auto name() const -> const std::string& { return name_; }
#endif
private:
#if BA_DEBUG_BUILD
std::string name_;
#endif
RendererGL* renderer_{};
uint32_t elem_count_{};
GLuint index_type_{};
GLuint vao_{};
GLuint vbos_[kBufferCount]{};
FakeVertexArrayObject* fake_vao_{};
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_ASSET_DATA_GL_H_

View File

@ -0,0 +1,44 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_DUAL_TEXTURE_FULL_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_DUAL_TEXTURE_FULL_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h"
namespace ballistica::base {
class RendererGL::MeshDataDualTextureFullGL : public RendererGL::MeshDataGL {
public:
explicit MeshDataDualTextureFullGL(RendererGL* renderer)
: MeshDataGL(renderer, kUsesIndexBuffer) {
// Set up our vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferPrimary]);
glVertexAttribPointer(
kVertexAttrUV, 2, GL_UNSIGNED_SHORT, GL_TRUE,
sizeof(VertexDualTextureFull),
reinterpret_cast<void*>(offsetof(VertexDualTextureFull, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
glVertexAttribPointer(
kVertexAttrUV2, 2, GL_UNSIGNED_SHORT, GL_TRUE,
sizeof(VertexDualTextureFull),
reinterpret_cast<void*>(offsetof(VertexDualTextureFull, uv2)));
glEnableVertexAttribArray(kVertexAttrUV2);
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE,
sizeof(VertexDualTextureFull),
reinterpret_cast<void*>(offsetof(VertexDualTextureFull, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
}
void SetData(MeshBuffer<VertexDualTextureFull>* data) {
UpdateBufferData(kVertexBufferPrimary, data, &primary_state_,
&have_primary_data_,
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
}
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_DUAL_TEXTURE_FULL_GL_H_

View File

@ -0,0 +1,212 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::MeshDataGL : public MeshRendererData {
public:
enum BufferType {
kVertexBufferPrimary,
kIndexBuffer,
kVertexBufferSecondary
};
enum Flags {
kUsesIndexBuffer = 1u,
kUsesSecondaryBuffer = 1u << 1u,
kUsesDynamicDraw = 1u << 2u
};
MeshDataGL(RendererGL* renderer, uint32_t flags)
: renderer_(renderer),
uses_secondary_data_(static_cast<bool>(flags & kUsesSecondaryBuffer)),
uses_index_data_(static_cast<bool>(flags & kUsesIndexBuffer)) {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
// Create our vertex array to hold all this state.
glGenVertexArrays(1, &vao_);
BA_DEBUG_CHECK_GL_ERROR;
renderer->BindVertexArray_(vao_);
BA_DEBUG_CHECK_GL_ERROR;
glGenBuffers(GetBufferCount(), vbos_);
BA_DEBUG_CHECK_GL_ERROR;
if (uses_index_data_) {
renderer_->BindVertexArray_(vao_);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos_[kIndexBuffer]);
}
BA_DEBUG_CHECK_GL_ERROR;
}
auto uses_index_data() const -> bool { return uses_index_data_; }
// Set us up to be recycled.
void Reset() {
index_state_ = primary_state_ = secondary_state_ = 0;
have_index_data_ = have_secondary_data_ = have_primary_data_ = false;
}
void Bind() {
renderer_->BindVertexArray_(vao_);
BA_DEBUG_CHECK_GL_ERROR;
}
void Draw(DrawType draw_type) {
BA_DEBUG_CHECK_GL_ERROR;
assert(have_primary_data_);
assert(have_index_data_ || !uses_index_data_);
assert(have_secondary_data_ || !uses_secondary_data_);
GLuint gl_draw_type;
switch (draw_type) {
case DrawType::kTriangles:
gl_draw_type = GL_TRIANGLES;
break;
case DrawType::kPoints:
gl_draw_type = GL_POINTS;
break;
default:
throw Exception();
}
if (uses_index_data_) {
glDrawElements(gl_draw_type, elem_count_, index_type_, nullptr);
} else {
glDrawArrays(gl_draw_type, 0, elem_count_);
}
BA_DEBUG_CHECK_GL_ERROR;
}
~MeshDataGL() override {
assert(g_base->InGraphicsThread());
// Unbind if we're bound; otherwise we might prevent a new vao that
// reuses our ID from binding.
if (vao_ == renderer_->current_vertex_array_) {
renderer_->BindVertexArray_(0);
}
if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteVertexArrays(1, &vao_);
}
// Make sure our dying buffer isn't current (don't wanna prevent binding
// to a new buffer with a recycled id).
for (int i = 0; i < GetBufferCount(); i++) {
if (vbos_[i] == renderer_->active_array_buffer_) {
renderer_->active_array_buffer_ = -1;
}
}
if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteBuffers(GetBufferCount(), vbos_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
void SetIndexData(MeshIndexBuffer32* data) {
assert(uses_index_data_);
if (data->state != index_state_) {
renderer_->BindVertexArray_(vao_);
elem_count_ = static_cast<uint32_t>(data->elements.size());
assert(elem_count_ > 0);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
static_cast_check_fit<GLsizeiptr>(
data->elements.size() * sizeof(data->elements[0])),
&data->elements[0],
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
index_state_ = data->state;
have_index_data_ = true;
BA_LOG_ONCE(LogLevel::kWarning,
"GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!");
index_type_ = GL_UNSIGNED_INT;
}
BA_DEBUG_CHECK_GL_ERROR;
}
void SetIndexData(MeshIndexBuffer16* data) {
assert(uses_index_data_);
if (data->state != index_state_) {
renderer_->BindVertexArray_(vao_);
elem_count_ = static_cast<uint32_t>(data->elements.size());
assert(elem_count_ > 0);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
static_cast_check_fit<GLsizeiptr>(
data->elements.size() * sizeof(data->elements[0])),
&data->elements[0],
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
index_state_ = data->state;
have_index_data_ = true;
index_type_ = GL_UNSIGNED_SHORT;
}
BA_DEBUG_CHECK_GL_ERROR;
}
// When dynamic-draw is on, it means *all* buffers should be flagged as
// dynamic.
void set_dynamic_draw(bool enable) { dynamic_draw_ = enable; }
auto vao() const -> GLuint { return vao_; }
protected:
template <typename T>
void UpdateBufferData(BufferType buffer_type, MeshBuffer<T>* data,
uint32_t* state, bool* have, GLuint draw_type) {
assert(state && have);
if (data->state != *state) {
BA_DEBUG_CHECK_GL_ERROR;
// Hmmm didnt think we had to have vao bound here but causes problems
// on qualcomm if not.
// #if BA_OSTYPE_ANDROID
// if (g_vao_support && renderer_->is_adreno_) {
// renderer_->BindVertexArray(vao_);
// }
// #endif
renderer_->BindArrayBuffer(vbos_[buffer_type]);
assert(!data->elements.empty());
if (!uses_index_data_ && buffer_type == kVertexBufferPrimary) {
elem_count_ = static_cast<uint32_t>(data->elements.size());
}
glBufferData(GL_ARRAY_BUFFER,
static_cast<GLsizeiptr>(data->elements.size()
* sizeof(data->elements[0])),
&(data->elements[0]), draw_type);
BA_DEBUG_CHECK_GL_ERROR;
*state = data->state;
*have = true;
} else {
assert(*have);
}
}
// FIXME: Should do some sort of ring-buffer system.
GLuint vbos_[3]{};
GLuint vao_{};
auto GetBufferCount() const -> int {
return uses_secondary_data_ ? 3 : (uses_index_data_ ? 2 : 1);
}
uint32_t index_state_{};
uint32_t primary_state_{};
uint32_t secondary_state_{};
bool uses_index_data_{};
bool uses_secondary_data_{};
bool dynamic_draw_{};
bool have_index_data_{};
bool have_primary_data_{};
bool have_secondary_data_{};
RendererGL* renderer_{};
uint32_t elem_count_{};
GLuint index_type_{GL_UNSIGNED_SHORT};
FakeVertexArrayObject* fake_vao_{};
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_GL_H_

View File

@ -0,0 +1,53 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_OBJECT_SPLIT_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_OBJECT_SPLIT_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h"
namespace ballistica::base {
class RendererGL::MeshDataObjectSplitGL : public RendererGL::MeshDataGL {
public:
explicit MeshDataObjectSplitGL(RendererGL* renderer)
: MeshDataGL(renderer, kUsesSecondaryBuffer | kUsesIndexBuffer) {
// Set up our static vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferPrimary]);
glVertexAttribPointer(
kVertexAttrUV, 2, GL_UNSIGNED_SHORT, GL_TRUE,
sizeof(VertexObjectSplitStatic),
reinterpret_cast<void*>(offsetof(VertexObjectSplitStatic, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
// ..and our dynamic vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferSecondary]);
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE,
sizeof(VertexObjectSplitDynamic),
reinterpret_cast<void*>(offsetof(VertexObjectSplitDynamic, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
glVertexAttribPointer(
kVertexAttrNormal, 3, GL_SHORT, GL_TRUE,
sizeof(VertexObjectSplitDynamic),
reinterpret_cast<void*>(offsetof(VertexObjectSplitDynamic, normal)));
glEnableVertexAttribArray(kVertexAttrNormal);
}
void SetStaticData(MeshBuffer<VertexObjectSplitStatic>* data) {
UpdateBufferData(kVertexBufferPrimary, data, &primary_state_,
&have_primary_data_, GL_STATIC_DRAW);
}
void SetDynamicData(MeshBuffer<VertexObjectSplitDynamic>* data) {
assert(uses_secondary_data_);
UpdateBufferData(kVertexBufferSecondary, data, &secondary_state_,
&have_secondary_data_,
GL_DYNAMIC_DRAW); // this is *always* dynamic
}
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_OBJECT_SPLIT_GL_H_

View File

@ -0,0 +1,39 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SIMPLE_FULL_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SIMPLE_FULL_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h"
namespace ballistica::base {
class RendererGL::MeshDataSimpleFullGL : public RendererGL::MeshDataGL {
public:
explicit MeshDataSimpleFullGL(RendererGL* renderer)
: MeshDataGL(renderer, kUsesIndexBuffer) {
// Set up our vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferPrimary]);
glVertexAttribPointer(
kVertexAttrUV, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(VertexSimpleFull),
reinterpret_cast<void*>(offsetof(VertexSimpleFull, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSimpleFull),
reinterpret_cast<void*>(offsetof(VertexSimpleFull, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
}
void SetData(MeshBuffer<VertexSimpleFull>* data) {
UpdateBufferData(kVertexBufferPrimary, data, &primary_state_,
&have_primary_data_,
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
}
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SIMPLE_FULL_GL_H_

View File

@ -0,0 +1,48 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SIMPLE_SPLIT_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SIMPLE_SPLIT_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h"
namespace ballistica::base {
class RendererGL::MeshDataSimpleSplitGL : public RendererGL::MeshDataGL {
public:
explicit MeshDataSimpleSplitGL(RendererGL* renderer)
: MeshDataGL(renderer, kUsesSecondaryBuffer | kUsesIndexBuffer) {
// Set up our static vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferPrimary]);
glVertexAttribPointer(
kVertexAttrUV, 2, GL_UNSIGNED_SHORT, GL_TRUE,
sizeof(VertexSimpleSplitStatic),
reinterpret_cast<void*>(offsetof(VertexSimpleSplitStatic, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
// ..and our dynamic vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferSecondary]);
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE,
sizeof(VertexSimpleSplitDynamic),
reinterpret_cast<void*>(offsetof(VertexSimpleSplitDynamic, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
}
void SetStaticData(MeshBuffer<VertexSimpleSplitStatic>* data) {
UpdateBufferData(kVertexBufferPrimary, data, &primary_state_,
&have_primary_data_, GL_STATIC_DRAW);
}
void SetDynamicData(MeshBuffer<VertexSimpleSplitDynamic>* data) {
assert(uses_secondary_data_);
UpdateBufferData(kVertexBufferSecondary, data, &secondary_state_,
&have_secondary_data_,
GL_DYNAMIC_DRAW); // this is *always* dynamic
}
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SIMPLE_SPLIT_GL_H_

View File

@ -0,0 +1,51 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SMOKE_FULL_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SMOKE_FULL_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h"
namespace ballistica::base {
class RendererGL::MeshDataSmokeFullGL : public RendererGL::MeshDataGL {
public:
explicit MeshDataSmokeFullGL(RendererGL* renderer)
: MeshDataGL(renderer, kUsesIndexBuffer) {
// Set up our vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferPrimary]);
glVertexAttribPointer(
kVertexAttrUV, 2, GL_FLOAT, GL_FALSE, sizeof(VertexSmokeFull),
reinterpret_cast<void*>(offsetof(VertexSmokeFull, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSmokeFull),
reinterpret_cast<void*>(offsetof(VertexSmokeFull, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
glVertexAttribPointer(
kVertexAttrErode, 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VertexSmokeFull),
reinterpret_cast<void*>(offsetof(VertexSmokeFull, erode)));
glEnableVertexAttribArray(kVertexAttrErode);
glVertexAttribPointer(
kVertexAttrDiffuse, 1, GL_UNSIGNED_BYTE, GL_TRUE,
sizeof(VertexSmokeFull),
reinterpret_cast<void*>(offsetof(VertexSmokeFull, diffuse)));
glEnableVertexAttribArray(kVertexAttrDiffuse);
glVertexAttribPointer(
kVertexAttrColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VertexSmokeFull),
reinterpret_cast<void*>(offsetof(VertexSmokeFull, color)));
glEnableVertexAttribArray(kVertexAttrColor);
}
void SetData(MeshBuffer<VertexSmokeFull>* data) {
UpdateBufferData(kVertexBufferPrimary, data, &primary_state_,
&have_primary_data_,
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
}
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SMOKE_FULL_GL_H_

View File

@ -0,0 +1,46 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SPRITE_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SPRITE_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h"
namespace ballistica::base {
class RendererGL::MeshDataSpriteGL : public RendererGL::MeshDataGL {
public:
explicit MeshDataSpriteGL(RendererGL* renderer)
: MeshDataGL(renderer, kUsesIndexBuffer) {
// Set up our vertex data.
renderer_->BindArrayBuffer(vbos_[kVertexBufferPrimary]);
glVertexAttribPointer(
kVertexAttrPosition, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSprite),
reinterpret_cast<void*>(offsetof(VertexSprite, position)));
glEnableVertexAttribArray(kVertexAttrPosition);
glVertexAttribPointer(kVertexAttrUV, 2, GL_UNSIGNED_SHORT, GL_TRUE,
sizeof(VertexSprite),
reinterpret_cast<void*>(offsetof(VertexSprite, uv)));
glEnableVertexAttribArray(kVertexAttrUV);
glVertexAttribPointer(
kVertexAttrSize, 1, GL_FLOAT, GL_FALSE, sizeof(VertexSprite),
reinterpret_cast<void*>(offsetof(VertexSprite, size)));
glEnableVertexAttribArray(kVertexAttrSize);
glVertexAttribPointer(
kVertexAttrColor, 4, GL_FLOAT, GL_FALSE, sizeof(VertexSprite),
reinterpret_cast<void*>(offsetof(VertexSprite, color)));
glEnableVertexAttribArray(kVertexAttrColor);
}
void SetData(MeshBuffer<VertexSprite>* data) {
UpdateBufferData(kVertexBufferPrimary, data, &primary_state_,
&have_primary_data_,
dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
}
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_MESH_MESH_DATA_SPRITE_GL_H_

View File

@ -0,0 +1,138 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_BLUR_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_BLUR_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramBlurGL : public RendererGL::ProgramGL {
public:
enum TextureUnit {
kColorTexUnit,
};
ProgramBlurGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags),
pixel_size_x_(0.0f),
pixel_size_y_(0.0f) {
SetTextureUnit("colorTex", kColorTexUnit);
pixel_size_location_ = glGetUniformLocation(program(), "pixelSize");
assert(pixel_size_location_ != -1);
}
void SetPixelSize(float x, float y) {
assert(IsBound());
if (x != pixel_size_x_ || y != pixel_size_y_) {
pixel_size_x_ = x;
pixel_size_y_ = y;
glUniform2f(pixel_size_location_, pixel_size_x_, pixel_size_y_);
}
}
void SetColorTexture(const TextureAsset* t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetColorTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
private:
auto GetName(int flags) -> std::string {
return std::string("BlurProgramGL");
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR | PFLAG_USES_UV_ATTR;
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
std::string s;
s = "uniform mat4 modelViewProjectionMatrix;\n" BA_GLSL_VERTEX_IN
" vec4 position;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"vec2 uv;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV1;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV2;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV3;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV4;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV5;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV6;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV7;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV8;\n"
"uniform " BA_GLSL_MEDIUMP
"vec2 pixelSize;\n"
"void main() {\n"
" gl_Position = modelViewProjectionMatrix*position;\n"
" vUV1 = uv+vec2(-0.5,0)*pixelSize;\n"
" vUV2 = uv+vec2(-1.5,0)*pixelSize;\n"
" vUV3 = uv+vec2(0.5,0)*pixelSize;\n"
" vUV4 = uv+vec2(1.5,0)*pixelSize;\n"
" vUV5 = uv+vec2(-0.5,1.0)*pixelSize;\n"
" vUV6 = uv+vec2(0.5,1.0)*pixelSize;\n"
" vUV7 = uv+vec2(-0.5,-1.0)*pixelSize;\n"
" vUV8 = uv+vec2(0.5,-1.0)*pixelSize;\n";
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
std::string s;
s = "uniform " BA_GLSL_MEDIUMP "sampler2D colorTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_MEDIUMP "vec2 vUV1;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV2;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV3;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV4;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV5;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV6;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV7;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec2 vUV8;\n"
"void main() {\n"
" " BA_GLSL_FRAGCOLOR " = 0.125*(" BA_GLSL_TEXTURE2D
"(colorTex,vUV1)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV2)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV3)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV4)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV5)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV6)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV7)\n"
" + " BA_GLSL_TEXTURE2D
"(colorTex,vUV8));\n"
"}";
if (flags & SHD_DEBUG_PRINT) {
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
}
return s;
}
int flags_;
GLint pixel_size_location_;
float pixel_size_x_, pixel_size_y_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_BLUR_GL_H_

View File

@ -0,0 +1,358 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
// Base class for fragment/vertex shaders.
class RendererGL::ShaderGL : public Object {
public:
auto GetDefaultOwnerThread() const -> EventLoopID override {
return EventLoopID::kMain;
}
ShaderGL(GLenum type_in, const std::string& src_in) : type_(type_in) {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
assert(type_ == GL_FRAGMENT_SHADER || type_ == GL_VERTEX_SHADER);
shader_ = glCreateShader(type_);
BA_DEBUG_CHECK_GL_ERROR;
BA_PRECONDITION(shader_);
#if !BA_OPENGL_IS_ES
std::string src_fin = src_in;
if (type_ == GL_FRAGMENT_SHADER) {
// gl_FragColor is no more. Define our equivalent.
src_fin = "out vec4 " BA_GLSL_FRAGCOLOR ";\n" + src_fin;
}
src_fin = "#version 150 core\n" + src_fin;
#else
std::string src_fin = src_in;
#endif
const char* s = src_fin.c_str();
glShaderSource(shader_, 1, &s, nullptr);
glCompileShader(shader_);
GLint compile_status;
glGetShaderiv(shader_, GL_COMPILE_STATUS, &compile_status);
if (compile_status == GL_FALSE) {
const char* version = (const char*)glGetString(GL_VERSION);
const char* vendor = (const char*)glGetString(GL_VENDOR);
const char* renderer = (const char*)glGetString(GL_RENDERER);
// Let's not crash here. We have a better chance of calling home this
// way and theres a chance the game will still be playable.
Log(LogLevel::kError,
std::string("Compile failed for ") + GetTypeName()
+ " shader:\n------------SOURCE BEGIN-------------\n" + src_fin
+ "\n-----------SOURCE END-------------\n" + GetInfo()
+ "\nrenderer: " + renderer + "\nvendor: " + vendor
+ "\nversion:" + version);
} else {
assert(compile_status == GL_TRUE);
std::string info = GetInfo();
if (!info.empty()
&& (strstr(info.c_str(), "error:") || strstr(info.c_str(), "warning:")
|| strstr(info.c_str(), "Error:")
|| strstr(info.c_str(), "Warning:"))) {
const char* version = (const char*)glGetString(GL_VERSION);
const char* vendor = (const char*)glGetString(GL_VENDOR);
const char* renderer = (const char*)glGetString(GL_RENDERER);
Log(LogLevel::kError,
std::string("WARNING: info returned for ") + GetTypeName()
+ " shader:\n------------SOURCE BEGIN-------------\n" + src_fin
+ "\n-----------SOURCE END-------------\n" + info
+ "\nrenderer: " + renderer + "\nvendor: " + vendor
+ "\nversion:" + version);
}
}
BA_DEBUG_CHECK_GL_ERROR;
}
~ShaderGL() override {
assert(g_base->InGraphicsThread());
if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteShader(shader_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
auto shader() const -> GLuint { return shader_; }
private:
auto GetTypeName() const -> const char* {
if (type_ == GL_VERTEX_SHADER) {
return "vertex";
} else {
return "fragment";
}
}
auto GetInfo() -> std::string {
static char log[1024];
GLsizei log_size;
glGetShaderInfoLog(shader_, sizeof(log), &log_size, log);
return log;
}
std::string name_;
GLuint shader_{};
GLenum type_{};
BA_DISALLOW_CLASS_COPIES(ShaderGL);
};
class RendererGL::FragmentShaderGL : public RendererGL::ShaderGL {
public:
explicit FragmentShaderGL(const std::string& src_in)
: ShaderGL(GL_FRAGMENT_SHADER, src_in) {}
};
class RendererGL::VertexShaderGL : public RendererGL::ShaderGL {
public:
explicit VertexShaderGL(const std::string& src_in)
: ShaderGL(GL_VERTEX_SHADER, src_in) {}
};
class RendererGL::ProgramGL {
public:
ProgramGL(RendererGL* renderer,
const Object::Ref<VertexShaderGL>& vertex_shader_in,
const Object::Ref<FragmentShaderGL>& fragment_shader_in,
std::string name, int pflags)
: fragment_shader_(fragment_shader_in),
vertex_shader_(vertex_shader_in),
renderer_(renderer),
pflags_(pflags),
name_(std::move(name)) {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
program_ = glCreateProgram();
BA_PRECONDITION(program_);
glAttachShader(program_, fragment_shader_->shader());
glAttachShader(program_, vertex_shader_->shader());
assert(pflags_ & PFLAG_USES_POSITION_ATTR);
if (pflags_ & PFLAG_USES_POSITION_ATTR) {
glBindAttribLocation(program_, kVertexAttrPosition, "position");
}
if (pflags_ & PFLAG_USES_UV_ATTR) {
glBindAttribLocation(program_, kVertexAttrUV, "uv");
}
if (pflags_ & PFLAG_USES_NORMAL_ATTR) {
glBindAttribLocation(program_, kVertexAttrNormal, "normal");
}
if (pflags_ & PFLAG_USES_ERODE_ATTR) {
glBindAttribLocation(program_, kVertexAttrErode, "erode");
}
if (pflags_ & PFLAG_USES_COLOR_ATTR) {
glBindAttribLocation(program_, kVertexAttrColor, "color");
}
if (pflags_ & PFLAG_USES_SIZE_ATTR) {
glBindAttribLocation(program_, kVertexAttrSize, "size");
}
if (pflags_ & PFLAG_USES_DIFFUSE_ATTR) {
glBindAttribLocation(program_, kVertexAttrDiffuse, "diffuse");
}
if (pflags_ & PFLAG_USES_UV2_ATTR) {
glBindAttribLocation(program_, kVertexAttrUV2, "uv2");
}
glLinkProgram(program_);
GLint linkStatus;
glGetProgramiv(program_, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
Log(LogLevel::kError,
"Link failed for program '" + name_ + "':\n" + GetInfo());
} else {
assert(linkStatus == GL_TRUE);
std::string info = GetInfo();
if (!info.empty()
&& (strstr(info.c_str(), "error:") || strstr(info.c_str(), "warning:")
|| strstr(info.c_str(), "Error:")
|| strstr(info.c_str(), "Warning:"))) {
Log(LogLevel::kError, "WARNING: program using frag shader '" + name_
+ "' returned info:\n" + info);
}
}
// Go ahead and bind ourself so child classes can config uniforms and
// whatnot.
Bind();
mvp_uniform_ = glGetUniformLocation(program_, "modelViewProjectionMatrix");
assert(mvp_uniform_ != -1);
if (pflags_ & PFLAG_USES_MODEL_WORLD_MATRIX) {
model_world_matrix_uniform_ =
glGetUniformLocation(program_, "modelWorldMatrix");
assert(model_world_matrix_uniform_ != -1);
}
if (pflags_ & PFLAG_USES_MODEL_VIEW_MATRIX) {
model_view_matrix_uniform_ =
glGetUniformLocation(program_, "modelViewMatrix");
assert(model_view_matrix_uniform_ != -1);
}
if (pflags_ & PFLAG_USES_CAM_POS) {
cam_pos_uniform_ = glGetUniformLocation(program_, "camPos");
assert(cam_pos_uniform_ != -1);
}
if (pflags_ & PFLAG_USES_CAM_ORIENT_MATRIX) {
cam_orient_matrix_uniform_ =
glGetUniformLocation(program_, "camOrientMatrix");
assert(cam_orient_matrix_uniform_ != -1);
}
if (pflags_ & PFLAG_USES_SHADOW_PROJECTION_MATRIX) {
light_shadow_projection_matrix_uniform_ =
glGetUniformLocation(program_, "lightShadowProjectionMatrix");
assert(light_shadow_projection_matrix_uniform_ != -1);
}
}
virtual ~ProgramGL() {
assert(g_base->InGraphicsThread());
if (!g_base->graphics_server->renderer_context_lost()) {
glDetachShader(program_, fragment_shader_->shader());
glDetachShader(program_, vertex_shader_->shader());
glDeleteProgram(program_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
auto IsBound() const -> bool {
return (renderer()->GetActiveProgram_() == this);
}
auto program() const -> GLuint { return program_; }
void Bind() { renderer_->UseProgram_(this); }
auto name() const -> const std::string& { return name_; }
// Should grab matrices from the renderer or whatever else it needs in
// prep for drawing.
void PrepareToDraw() {
BA_DEBUG_CHECK_GL_ERROR;
assert(IsBound());
// Update matrices as necessary.
uint32_t mvpState =
g_base->graphics_server->GetModelViewProjectionMatrixState();
if (mvpState != mvp_state_) {
mvp_state_ = mvpState;
glUniformMatrix4fv(
mvp_uniform_, 1, 0,
g_base->graphics_server->GetModelViewProjectionMatrix().m);
}
BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_MODEL_WORLD_MATRIX) {
// With world space points this would be identity; don't waste time.
assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS));
uint32_t state = g_base->graphics_server->GetModelWorldMatrixState();
if (state != model_world_matrix_state_) {
model_world_matrix_state_ = state;
glUniformMatrix4fv(model_world_matrix_uniform_, 1, 0,
g_base->graphics_server->GetModelWorldMatrix().m);
}
}
BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_MODEL_VIEW_MATRIX) {
// With world space points this would be identity; don't waste time.
assert(!(pflags_ & PFLAG_WORLD_SPACE_PTS));
// There's no state for just modelview but this works.
uint32_t state =
g_base->graphics_server->GetModelViewProjectionMatrixState();
if (state != model_view_matrix_state_) {
model_view_matrix_state_ = state;
glUniformMatrix4fv(model_view_matrix_uniform_, 1, 0,
g_base->graphics_server->model_view_matrix().m);
}
}
BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_CAM_POS) {
uint32_t state = g_base->graphics_server->cam_pos_state();
if (state != cam_pos_state_) {
cam_pos_state_ = state;
const Vector3f& p(g_base->graphics_server->cam_pos());
glUniform4f(cam_pos_uniform_, p.x, p.y, p.z, 1.0f);
}
}
BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_CAM_ORIENT_MATRIX) {
uint32_t state = g_base->graphics_server->GetCamOrientMatrixState();
if (state != cam_orient_matrix_state_) {
cam_orient_matrix_state_ = state;
glUniformMatrix4fv(cam_orient_matrix_uniform_, 1, 0,
g_base->graphics_server->GetCamOrientMatrix().m);
}
}
BA_DEBUG_CHECK_GL_ERROR;
if (pflags_ & PFLAG_USES_SHADOW_PROJECTION_MATRIX) {
uint32_t state =
g_base->graphics_server->light_shadow_projection_matrix_state();
if (state != light_shadow_projection_matrix_state_) {
light_shadow_projection_matrix_state_ = state;
glUniformMatrix4fv(
light_shadow_projection_matrix_uniform_, 1, 0,
g_base->graphics_server->light_shadow_projection_matrix().m);
}
}
BA_DEBUG_CHECK_GL_ERROR;
}
protected:
void SetTextureUnit(const char* tex_name, int unit) {
assert(IsBound());
int c = glGetUniformLocation(program_, tex_name);
if (c == -1) {
Log(LogLevel::kError, "ShaderGL: " + name_
+ ": Can't set texture unit for texture '"
+ tex_name + "'");
BA_DEBUG_CHECK_GL_ERROR;
} else {
glUniform1i(c, unit);
}
}
auto GetInfo() -> std::string {
static char log[1024];
GLsizei log_size;
glGetProgramInfoLog(program_, sizeof(log), &log_size, log);
return log;
}
auto renderer() const -> RendererGL* { return renderer_; }
private:
RendererGL* renderer_{};
Object::Ref<FragmentShaderGL> fragment_shader_;
Object::Ref<VertexShaderGL> vertex_shader_;
std::string name_;
GLuint program_{};
int pflags_{};
uint32_t mvp_state_{};
GLint mvp_uniform_{};
GLint model_world_matrix_uniform_{};
GLint model_view_matrix_uniform_{};
GLint light_shadow_projection_matrix_uniform_{};
uint32_t light_shadow_projection_matrix_state_{};
uint32_t model_world_matrix_state_{};
uint32_t model_view_matrix_state_{};
GLint cam_pos_uniform_{};
uint32_t cam_pos_state_{};
GLint cam_orient_matrix_uniform_{};
GLuint cam_orient_matrix_state_{};
BA_DISALLOW_CLASS_COPIES(ProgramGL);
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_GL_H_

View File

@ -0,0 +1,345 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_OBJECT_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_OBJECT_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramObjectGL : public RendererGL::ProgramGL {
public:
enum TextureUnit {
kColorTexUnit,
kReflectionTexUnit,
kVignetteTexUnit,
kLightShadowTexUnit,
kColorizeTexUnit
};
ProgramObjectGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags),
r_(0),
g_(0),
b_(0),
a_(0),
colorize_r_(0),
colorize_g_(0),
colorize_b_(0),
colorize_a_(0),
colorize2_r_(0),
colorize2_g_(0),
colorize2_b_(0),
colorize2_a_(0),
add_r_(0),
add_g_(0),
add_b_(0),
r_mult_r_(0),
r_mult_g_(0),
r_mult_b_(0),
r_mult_a_(0) {
SetTextureUnit("colorTex", kColorTexUnit);
SetTextureUnit("vignetteTex", kVignetteTexUnit);
color_location_ = glGetUniformLocation(program(), "color");
assert(color_location_ != -1);
if (flags & SHD_REFLECTION) {
SetTextureUnit("reflectionTex", kReflectionTexUnit);
reflect_mult_location_ = glGetUniformLocation(program(), "reflectMult");
assert(reflect_mult_location_ != -1);
}
if (flags & SHD_LIGHT_SHADOW) {
SetTextureUnit("lightShadowTex", kLightShadowTexUnit);
}
if (flags & SHD_ADD) {
color_add_location_ = glGetUniformLocation(program(), "colorAdd");
assert(color_add_location_ != -1);
}
if (flags & SHD_COLORIZE) {
SetTextureUnit("colorizeTex", kColorizeTexUnit);
colorize_color_location_ =
glGetUniformLocation(program(), "colorizeColor");
assert(colorize_color_location_ != -1);
}
if (flags & SHD_COLORIZE2) {
colorize2_color_location_ =
glGetUniformLocation(program(), "colorize2Color");
assert(colorize2_color_location_ != -1);
}
}
void SetColorTexture(const TextureAsset* t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetReflectionTexture(const TextureAsset* t) {
assert(flags_ & SHD_REFLECTION);
renderer()->BindTexture_(GL_TEXTURE_CUBE_MAP, t, kReflectionTexUnit);
}
void SetColor(float r, float g, float b, float a = 1.0f) {
assert(IsBound());
// include tint..
if (r * renderer()->tint().x != r_ || g * renderer()->tint().y != g_
|| b * renderer()->tint().z != b_ || a != a_) {
r_ = r * renderer()->tint().x;
g_ = g * renderer()->tint().y;
b_ = b * renderer()->tint().z;
a_ = a;
glUniform4f(color_location_, r_, g_, b_, a_);
}
}
void SetAddColor(float r, float g, float b) {
assert(IsBound());
if (r != add_r_ || g != add_g_ || b != add_b_) {
add_r_ = r;
add_g_ = g;
add_b_ = b;
glUniform4f(color_add_location_, add_r_, add_g_, add_b_, 0.0f);
}
}
void SetReflectionMult(float r, float g, float b, float a = 0.0f) {
assert(IsBound());
// include tint and ambient color...
auto renderer = this->renderer();
float rFin = r * renderer->tint().x * renderer->ambient_color().x;
float gFin = g * renderer->tint().y * renderer->ambient_color().y;
float bFin = b * renderer->tint().z * renderer->ambient_color().z;
if (rFin != r_mult_r_ || gFin != r_mult_g_ || bFin != r_mult_b_
|| a != r_mult_a_) {
r_mult_r_ = rFin;
r_mult_g_ = gFin;
r_mult_b_ = bFin;
r_mult_a_ = a;
assert(flags_ & SHD_REFLECTION);
glUniform4f(reflect_mult_location_, r_mult_r_, r_mult_g_, r_mult_b_,
r_mult_a_);
}
}
void SetVignetteTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kVignetteTexUnit);
}
void SetLightShadowTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kLightShadowTexUnit);
}
void SetColorizeColor(float r, float g, float b, float a = 1.0f) {
assert(flags_ & SHD_COLORIZE);
assert(IsBound());
if (r != colorize_r_ || g != colorize_g_ || b != colorize_b_
|| a != colorize_a_) {
colorize_r_ = r;
colorize_g_ = g;
colorize_b_ = b;
colorize_a_ = a;
glUniform4f(colorize_color_location_, colorize_r_, colorize_g_,
colorize_b_, colorize_a_);
}
}
void SetColorize2Color(float r, float g, float b, float a = 1.0f) {
assert(flags_ & SHD_COLORIZE2);
assert(IsBound());
if (r != colorize2_r_ || g != colorize2_g_ || b != colorize2_b_
|| a != colorize2_a_) {
colorize2_r_ = r;
colorize2_g_ = g;
colorize2_b_ = b;
colorize2_a_ = a;
glUniform4f(colorize2_color_location_, colorize2_r_, colorize2_g_,
colorize2_b_, colorize2_a_);
}
}
void SetColorizeTexture(const TextureAsset* t) {
assert(flags_ & SHD_COLORIZE);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorizeTexUnit);
}
private:
auto GetName(int flags) -> std::string {
return std::string("ProgramObjectGL")
+ " reflect:" + std::to_string((flags & SHD_REFLECTION) != 0)
+ " lightShadow:" + std::to_string((flags & SHD_LIGHT_SHADOW) != 0)
+ " add:" + std::to_string((flags & SHD_ADD) != 0) + " colorize:"
+ std::to_string((flags & SHD_COLORIZE) != 0) + " colorize2:"
+ std::to_string((flags & SHD_COLORIZE2) != 0) + " transparent:"
+ std::to_string((flags & SHD_OBJ_TRANSPARENT) != 0) + " worldSpace:"
+ std::to_string((flags & SHD_WORLD_SPACE_PTS) != 0);
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR | PFLAG_USES_UV_ATTR;
if (flags & SHD_REFLECTION)
pflags |= (PFLAG_USES_NORMAL_ATTR | PFLAG_USES_CAM_POS);
if (((flags & SHD_REFLECTION) || (flags & SHD_LIGHT_SHADOW))
&& !(flags & SHD_WORLD_SPACE_PTS))
pflags |= PFLAG_USES_MODEL_WORLD_MATRIX;
if (flags & SHD_LIGHT_SHADOW) pflags |= PFLAG_USES_SHADOW_PROJECTION_MATRIX;
if (flags & SHD_WORLD_SPACE_PTS) pflags |= PFLAG_WORLD_SPACE_PTS;
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
std::string s;
s = "uniform mat4 modelViewProjectionMatrix;\n"
"uniform vec4 camPos;\n" BA_GLSL_VERTEX_IN
" vec4 position;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_LOWP
"vec2 uv;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_LOWP
"vec2 vUV;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec4 vScreenCoord;\n";
if ((flags & SHD_REFLECTION) || (flags & SHD_LIGHT_SHADOW))
s += "uniform mat4 modelWorldMatrix;\n";
if (flags & SHD_REFLECTION)
s += BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"vec3 normal;\n" BA_GLSL_VERTEX_OUT
" " BA_GLSL_MEDIUMP "vec3 vReflect;\n";
if (flags & SHD_LIGHT_SHADOW)
s += "uniform mat4 lightShadowProjectionMatrix;\n" BA_GLSL_VERTEX_OUT
" " BA_GLSL_MEDIUMP "vec4 vLightShadowUV;\n";
s +=
"void main() {\n"
" vUV = uv;\n"
" gl_Position = modelViewProjectionMatrix*position;\n"
" vScreenCoord = vec4(gl_Position.xy/gl_Position.w,gl_Position.zw);\n"
" vScreenCoord.xy += vec2(1.0);\n"
" vScreenCoord.xy *= vec2(0.5*vScreenCoord.w);\n";
if (((flags & SHD_LIGHT_SHADOW) || (flags & SHD_REFLECTION))
&& !(flags & SHD_WORLD_SPACE_PTS)) {
s += " vec4 worldPos = modelWorldMatrix*position;\n";
}
if (flags & SHD_LIGHT_SHADOW) {
if (flags & SHD_WORLD_SPACE_PTS)
s += " vLightShadowUV = (lightShadowProjectionMatrix*position);\n";
else
s += " vLightShadowUV = (lightShadowProjectionMatrix*worldPos);\n";
}
if (flags & SHD_REFLECTION) {
if (flags & SHD_WORLD_SPACE_PTS)
s += " vReflect = reflect(vec3(position - camPos),normal);\n";
else
s += " vReflect = reflect(vec3(worldPos - "
"camPos),normalize(vec3(modelWorldMatrix * vec4(normal,0.0))));\n";
}
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
std::string s;
s = "uniform " BA_GLSL_LOWP
"sampler2D colorTex;\n"
"uniform " BA_GLSL_LOWP
"sampler2D vignetteTex;\n"
"uniform " BA_GLSL_LOWP "vec4 color;\n" BA_GLSL_FRAG_IN " " BA_GLSL_LOWP
"vec2 vUV;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec4 vScreenCoord;\n";
if (flags & SHD_ADD) {
s += "uniform " BA_GLSL_LOWP "vec4 colorAdd;\n";
}
if (flags & SHD_REFLECTION) {
s += "uniform " BA_GLSL_LOWP
"samplerCube reflectionTex;\n" BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec3 vReflect;\n"
"uniform " BA_GLSL_LOWP "vec4 reflectMult;\n";
}
if (flags & SHD_COLORIZE) {
s += "uniform " BA_GLSL_LOWP
"sampler2D colorizeTex;\n"
"uniform " BA_GLSL_LOWP "vec4 colorizeColor;\n";
}
if (flags & SHD_COLORIZE2) {
s += "uniform " BA_GLSL_LOWP "vec4 colorize2Color;\n";
}
if (flags & SHD_LIGHT_SHADOW) {
s += "uniform " BA_GLSL_LOWP "sampler2D lightShadowTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_MEDIUMP "vec4 vLightShadowUV;\n";
}
s += "void main() {\n";
if (flags & SHD_LIGHT_SHADOW) {
s += " " BA_GLSL_LOWP "vec4 lightShadVal = " BA_GLSL_TEXTURE2DPROJ
"(lightShadowTex, vLightShadowUV);\n";
}
if ((flags & SHD_COLORIZE) || (flags & SHD_COLORIZE2)) {
s += " " BA_GLSL_LOWP "vec4 colorizeVal = " BA_GLSL_TEXTURE2D
"(colorizeTex, vUV);\n";
}
if (flags & SHD_COLORIZE) {
s += " " BA_GLSL_LOWP "float colorizeA = colorizeVal.r;\n";
}
if (flags & SHD_COLORIZE2) {
s += " " BA_GLSL_LOWP "float colorizeB = colorizeVal.g;\n";
}
s += " " BA_GLSL_FRAGCOLOR " = (color * " BA_GLSL_TEXTURE2D
"(colorTex, vUV)";
if (flags & SHD_COLORIZE) {
s += " * (vec4(1.0-colorizeA)+colorizeColor*colorizeA)";
}
if (flags & SHD_COLORIZE2) {
s += " * (vec4(1.0-colorizeB)+colorize2Color*colorizeB)";
}
s += ")";
// add in lights/shadows
if (flags & SHD_LIGHT_SHADOW) {
if (flags & SHD_OBJ_TRANSPARENT) {
s += " * vec4((2.0 * lightShadVal).rgb, 1) + "
"vec4((lightShadVal - 0.5).rgb,0)";
} else {
s += " * (2.0 * lightShadVal) + (lightShadVal - 0.5)";
}
}
// add glow and reflection
if (flags & SHD_REFLECTION)
s += " + (reflectMult*" BA_GLSL_TEXTURECUBE "(reflectionTex, vReflect))";
if (flags & SHD_ADD) s += " + colorAdd";
// subtract vignette
s += " - vec4(" BA_GLSL_TEXTURE2DPROJ "(vignetteTex, vScreenCoord).rgb,0)";
s += ";\n";
// s += BA_GLSL_FRAGCOLOR " = 0.999 * " BA_GLSL_TEXTURE2DPROJ
// "(vignetteTex,vScreenCoord)
// + 0.01 * BA_GLSL_FRAGCOLOR ";";
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
float r_, g_, b_, a_;
float colorize_r_, colorize_g_, colorize_b_, colorize_a_;
float colorize2_r_, colorize2_g_, colorize2_b_, colorize2_a_;
float add_r_, add_g_, add_b_;
float r_mult_r_, r_mult_g_, r_mult_b_, r_mult_a_;
GLint color_location_;
GLint colorize_color_location_;
GLint colorize2_color_location_;
GLint color_add_location_;
GLint reflect_mult_location_;
int flags_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_OBJECT_GL_H_

View File

@ -0,0 +1,327 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_POST_PROCESS_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_POST_PROCESS_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramPostProcessGL : public RendererGL::ProgramGL {
public:
enum TextureUnit {
kColorTexUnit,
kDepthTexUnit,
kColorSlightBlurredTexUnit,
kColorBlurredTexUnit,
kColorBlurredMoreTexUnit
};
ProgramPostProcessGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags),
dof_near_min_(0),
dof_near_max_(0),
dof_far_min_(0),
dof_far_max_(0),
distort_(0.0f) {
SetTextureUnit("colorTex", kColorTexUnit);
if (UsesSlightBlurredTex()) {
SetTextureUnit("colorSlightBlurredTex", kColorSlightBlurredTexUnit);
}
if (UsesBlurredTexture()) {
SetTextureUnit("colorBlurredTex", kColorBlurredTexUnit);
}
SetTextureUnit("colorBlurredMoreTex", kColorBlurredMoreTexUnit);
SetTextureUnit("depthTex", kDepthTexUnit);
dof_location_ = glGetUniformLocation(program(), "dofRange");
assert(dof_location_ != -1);
if (flags & SHD_DISTORT) {
distort_location_ = glGetUniformLocation(program(), "distort");
assert(distort_location_ != -1);
}
}
auto UsesSlightBlurredTex() -> bool {
return static_cast<bool>(flags_ & SHD_EYES);
}
auto UsesBlurredTexture() -> bool {
return static_cast<bool>(flags_ & (SHD_HIGHER_QUALITY | SHD_EYES));
}
void SetColorTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetColorSlightBlurredTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorSlightBlurredTexUnit);
}
void SetColorBlurredMoreTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorBlurredMoreTexUnit);
}
void SetColorBlurredTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorBlurredTexUnit);
}
void SetDepthTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kDepthTexUnit);
}
void SetDepthOfFieldRanges(float near_min, float near_max, float far_min,
float far_max) {
assert(IsBound());
if (near_min != dof_near_min_ || near_max != dof_near_max_
|| far_min != dof_far_min_ || far_max != dof_far_max_) {
BA_DEBUG_CHECK_GL_ERROR;
dof_near_min_ = near_min;
dof_near_max_ = near_max;
dof_far_min_ = far_min;
dof_far_max_ = far_max;
float vals[4] = {dof_near_min_, dof_near_max_, dof_far_min_,
dof_far_max_};
glUniform1fv(dof_location_, 4, vals);
BA_DEBUG_CHECK_GL_ERROR;
}
}
void SetDistort(float distort) {
assert(IsBound());
assert(flags_ & SHD_DISTORT);
if (distort != distort_) {
BA_DEBUG_CHECK_GL_ERROR;
distort_ = distort;
glUniform1f(distort_location_, distort_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
private:
auto GetName(int flags) -> std::string {
return std::string("PostProcessProgramGL");
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR;
if (flags & SHD_DISTORT) {
pflags |= (PFLAG_USES_NORMAL_ATTR | PFLAG_USES_MODEL_VIEW_MATRIX);
}
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
std::string s;
s = "uniform mat4 modelViewProjectionMatrix;\n" BA_GLSL_VERTEX_IN
" vec4 position;\n";
if (flags & SHD_DISTORT)
s += BA_GLSL_VERTEX_IN " " BA_GLSL_LOWP
"vec3 normal;\n"
"uniform mat4 modelViewMatrix;\n"
"uniform float distort;\n";
if (flags & SHD_EYES) {
s += BA_GLSL_VERTEX_OUT " " BA_GLSL_HIGHP "float calcedDepth;\n";
}
s += BA_GLSL_VERTEX_OUT
" " BA_GLSL_MEDIUMP
"vec4 vScreenCoord;\n"
"void main() {\n"
" gl_Position = modelViewProjectionMatrix*position;\n";
if (flags & SHD_DISTORT) {
s += " float eyeDot = "
"abs(normalize(modelViewMatrix*vec4(normal,0.0))).z;\n"
" vec4 posDistorted = "
"modelViewProjectionMatrix*(position-eyeDot*distort*vec4(normal,0));"
"\n"
" vScreenCoord = "
"vec4(posDistorted.xy/posDistorted.w,posDistorted.zw);\n"
" vScreenCoord.xy += vec2(1.0);\n"
" vScreenCoord.xy *= vec2(0.5*vScreenCoord.w);\n";
} else {
s += " vScreenCoord = "
"vec4(gl_Position.xy/gl_Position.w,gl_Position.zw);\n"
" vScreenCoord.xy += vec2(1.0);\n"
" vScreenCoord.xy *= vec2(0.5*vScreenCoord.w);\n";
}
if (flags & SHD_EYES) {
s += " calcedDepth = " + std::to_string(kBackingDepth3) + "+"
+ std::to_string(kBackingDepth4 - kBackingDepth3)
+ "*(0.5*(gl_Position.z/gl_Position.w)+0.5);\n";
}
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
std::string s;
s = "uniform " BA_GLSL_LOWP
"sampler2D colorTex;\n"
"uniform " BA_GLSL_LOWP
"sampler2D colorBlurredMoreTex;\n"
"uniform " BA_GLSL_HIGHP "sampler2D depthTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_MEDIUMP
"vec4 vScreenCoord;\n"
"uniform " BA_GLSL_LOWP "float dofRange[4];\n";
if (flags & (SHD_HIGHER_QUALITY | SHD_EYES)) {
s += "uniform " BA_GLSL_LOWP "sampler2D colorBlurredTex;\n";
}
if (flags & SHD_EYES) {
s += "uniform " BA_GLSL_LOWP
"sampler2D colorSlightBlurredTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_HIGHP "float calcedDepth;\n";
}
s += "void main() {\n"
" " BA_GLSL_MEDIUMP "float depth = " BA_GLSL_TEXTURE2DPROJ
"(depthTex,vScreenCoord).r;\n";
bool doConditional = ((flags & SHD_CONDITIONAL) && !(flags & (SHD_EYES)));
if (doConditional) {
// special-case completely out of focus areas and completely in-focus
// areas.
s += " if (depth > dofRange[1] && depth < dofRange[2]) {\n";
if (flags & SHD_HIGHER_QUALITY) {
s += " " BA_GLSL_LOWP "vec4 color = " BA_GLSL_TEXTURE2DPROJ
"(colorTex,vScreenCoord);\n"
" " BA_GLSL_LOWP "vec4 colorBlurred = " BA_GLSL_TEXTURE2DPROJ
"(colorBlurredTex,vScreenCoord);\n"
" " BA_GLSL_LOWP
"vec4 colorBlurredMore = "
"0.4*" BA_GLSL_TEXTURE2DPROJ
"(colorBlurredMoreTex,vScreenCoord);\n"
" " BA_GLSL_MEDIUMP
"vec4 diff = colorBlurred-color;\n"
" diff = sign(diff) * max(vec4(0.0),abs(diff)-0.12);\n"
" " BA_GLSL_FRAGCOLOR
" = (0.55*colorBlurredMore) + "
"(0.62+colorBlurredMore)*(color-diff);\n\n";
} else {
s += " " BA_GLSL_FRAGCOLOR " = " BA_GLSL_TEXTURE2DPROJ
"(colorTex,vScreenCoord);\n";
}
s += " }\n"
" else if (depth < dofRange[0] || depth > dofRange[3]) {\n";
if (flags & SHD_HIGHER_QUALITY) {
s += " " BA_GLSL_LOWP "vec4 colorBlurred = " BA_GLSL_TEXTURE2DPROJ
"(colorBlurredTex,vScreenCoord);\n"
" " BA_GLSL_LOWP
"vec4 colorBlurredMore = "
"0.4*" BA_GLSL_TEXTURE2DPROJ
"(colorBlurredMoreTex,vScreenCoord);\n"
" " BA_GLSL_FRAGCOLOR
" = (0.55*colorBlurredMore) + "
"(0.62+colorBlurredMore)*colorBlurred;\n\n";
} else {
s += " " BA_GLSL_FRAGCOLOR
" = "
"" BA_GLSL_TEXTURE2DPROJ "(colorBlurredMoreTex,vScreenCoord);\n";
}
s += " }\n"
" else{\n";
}
// Transition areas.
s += " " BA_GLSL_LOWP "vec4 color = " BA_GLSL_TEXTURE2DPROJ
"(colorTex,vScreenCoord);\n";
if (flags & SHD_EYES)
s += " " BA_GLSL_LOWP
"vec4 colorSlightBlurred = "
"" BA_GLSL_TEXTURE2DPROJ "(colorSlightBlurredTex,vScreenCoord);\n";
// FIXME: Should make proper blur work in VR (perhaps just pass a uniform?
// FIXME2: This will break 2D mode on the VR build.
// #if BA_VR_BUILD
// #define BLURSCALE "0.3 * "
// #else
#define BLURSCALE
// #endif
if (flags & (SHD_HIGHER_QUALITY | SHD_EYES)) {
s += " " BA_GLSL_LOWP "vec4 colorBlurred = " BA_GLSL_TEXTURE2DPROJ
"(colorBlurredTex,vScreenCoord);\n"
" " BA_GLSL_LOWP
"vec4 colorBlurredMore = "
"0.4*" BA_GLSL_TEXTURE2DPROJ
"(colorBlurredMoreTex,vScreenCoord);\n"
" " BA_GLSL_LOWP "float blur = " BLURSCALE
" (smoothstep(dofRange[2],dofRange[3],depth)\n"
" + 1.0 - "
"smoothstep(dofRange[0],dofRange[1],depth));\n"
" " BA_GLSL_MEDIUMP
"vec4 diff = colorBlurred-color;\n"
" diff = sign(diff) * max(vec4(0.0),abs(diff)-0.12);\n"
" " BA_GLSL_FRAGCOLOR
" = (0.55*colorBlurredMore) + "
"(0.62+colorBlurredMore)*mix(color-diff,colorBlurred,blur);\n\n";
} else {
s += " " BA_GLSL_LOWP
"vec4 colorBlurredMore = "
"" BA_GLSL_TEXTURE2DPROJ
"(colorBlurredMoreTex,vScreenCoord);\n"
" " BA_GLSL_LOWP "float blur = " BLURSCALE
" (smoothstep(dofRange[2],dofRange[3],depth)\n"
" + 1.0 - "
"smoothstep(dofRange[0],dofRange[1],depth));\n"
" " BA_GLSL_FRAGCOLOR " = mix(color,colorBlurredMore,blur);\n\n";
}
#undef BLURSCALE
if (flags & SHD_EYES) {
s += " " BA_GLSL_MEDIUMP "vec4 diffEye = colorBlurred-color;\n";
s += " diffEye = sign(diffEye) * max(vec4(0.0),abs(diffEye)-0.06);\n";
s += " " BA_GLSL_LOWP
"vec4 baseColorEye = "
"mix(color-10.0*(diffEye),colorSlightBlurred,0.83);\n";
s += " " BA_GLSL_LOWP
"vec4 eyeColor = (0.55*colorBlurredMore) + "
"(0.62+colorBlurredMore)*mix(baseColorEye,colorBlurred,blur);\n\n";
s += " " BA_GLSL_LOWP
"float dBlend = smoothstep(-0.0004,-0.0001,depth-calcedDepth);\n"
" " BA_GLSL_FRAGCOLOR " = mix(" BA_GLSL_FRAGCOLOR
",eyeColor,dBlend);\n";
}
if (doConditional) {
s += " }\n";
}
// Demonstrates MSAA striation issue:
// s += " gl_FragColor =
// mix(gl_FragColor,vec4(vec3(14.0*(depth-0.76)),1),0.999);\n";
// s += " gl_FragColor =
// vec4(vec3(14.0*(" BA_GLSL_TEXTURE2DPROJ
// "(depthTex,vScreenCoord).r-0.76)),1);\n";
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
int flags_;
float dof_near_min_;
float dof_near_max_;
float dof_far_min_;
float dof_far_max_;
GLint dof_location_;
float distort_;
GLint distort_location_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_POST_PROCESS_GL_H_

View File

@ -0,0 +1,128 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SHIELD_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SHIELD_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramShieldGL : public RendererGL::ProgramGL {
public:
enum TextureUnit {
kDepthTexUnit,
};
ProgramShieldGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags) {
SetTextureUnit("depthTex", kDepthTexUnit);
}
void SetDepthTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kDepthTexUnit);
}
private:
auto GetName(int flags) -> std::string {
return std::string("ShieldProgramGL");
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR;
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
std::string s;
s = "uniform mat4 modelViewProjectionMatrix;\n" BA_GLSL_VERTEX_IN
" vec4 position;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_HIGHP
"vec4 vScreenCoord;\n"
"void main() {\n"
" gl_Position = modelViewProjectionMatrix * position;\n"
" vScreenCoord = vec4(gl_Position.xy / gl_Position.w,"
" gl_Position.zw);\n"
" vScreenCoord.xy += vec2(1.0);\n"
" vScreenCoord.xy *= vec2(0.5 * vScreenCoord.w);\n";
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
std::string s;
s = "uniform " BA_GLSL_HIGHP "sampler2D depthTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_HIGHP
"vec4 vScreenCoord;\n"
"void main() {\n"
" " BA_GLSL_HIGHP "float depth = " BA_GLSL_TEXTURE2DPROJ
"(depthTex, vScreenCoord).r;\n";
// Work around Adreno bug where depth is returned as 0..1 instead of
// glDepthRange().
if (GetFunkyDepthIssue_()) {
s += " depth = " + std::to_string(kBackingDepth3) + " + depth * ("
+ std::to_string(kBackingDepth4) + "-"
+ std::to_string(kBackingDepth3) + ");\n";
}
// s+= " depth =
// "+std::to_string(kBackingDepth3)+"0.15+depth*(0.9-0.15);\n"; " depth
// *=
// 0.936;\n" " depth = 1.0/(65535.0*((1.0/depth)/16777216.0));\n" " depth
//= 1.0/((1.0/depth)+0.08);\n" " depth += 0.1f;\n"
s += " " BA_GLSL_HIGHP
"float d = abs(depth - gl_FragCoord.z);\n"
" d = 1.0 - smoothstep(0.0, 0.0006, d);\n"
" d = 0.2 * smoothstep(0.96, 1.0, d)"
" + 0.2 * d + 0.4 * d * d * d;\n";
// Some mali chips seem to have no high precision and thus this looks
// terrible; in those cases lets done down the intersection effect
// significantly
// if (GetDrawsShieldsFunny_()) {
// s += " " BA_GLSL_FRAGCOLOR " = vec4(d*0.13,d*0.1,d,0);\n";
// } else {
s += " " BA_GLSL_FRAGCOLOR " = vec4(d*0.5, d*0.4, d, 0);\n";
// }
s += "}";
// This shows msaa depth error on bridgit.
//" " BA_GLSL_FRAGCOLOR " =
// vec4(smoothstep(0.73,0.77,depth),0.0,0.0,0.5);\n"
// " d = 1.0 - smoothstep(0.0,0.0006,d);\n"
// " d = 0.2*smoothstep(0.96,1.0,d)+0.2*d+0.4*d*d*d;\n"
//" if (d < 0.01) " BA_GLSL_FRAGCOLOR " = vec4(0.0,1.0,0.0,0.5);\n"
//" " BA_GLSL_FRAGCOLOR " =
// vec4(vec3(10.0*abs(depth-gl_FragCoord.z)),1);\n"
// " " BA_GLSL_FRAGCOLOR " =
// vec4(0,10.0*abs(depth-gl_FragCoord.z),0,0.1);\n" " if (depth <
// gl_FragCoord.z) " BA_GLSL_FRAGCOLOR " =
// vec4(1.0-10.0*(gl_FragCoord.z-depth),0,0,1);\n" " else "
// BA_GLSL_FRAGCOLOR " = vec4(0,1.0-10.0*(depth-gl_FragCoord.z),0,1);\n"
//" " BA_GLSL_FRAGCOLOR " = vec4(vec3(depth),1);\n"
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
int flags_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SHIELD_GL_H_

View File

@ -0,0 +1,413 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SIMPLE_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SIMPLE_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramSimpleGL : public RendererGL::ProgramGL {
public:
enum TextureUnit {
kColorTexUnit,
kColorizeTexUnit,
kMaskTexUnit,
kMaskUV2TexUnit,
kBlurTexUnit
};
ProgramSimpleGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags) {
if (flags & SHD_TEXTURE) {
SetTextureUnit("colorTex", kColorTexUnit);
}
if (flags & SHD_COLORIZE) {
SetTextureUnit("colorizeTex", kColorizeTexUnit);
colorize_color_location_ =
glGetUniformLocation(program(), "colorizeColor");
assert(colorize_color_location_ != -1);
}
if (flags & SHD_COLORIZE2) {
colorize2_color_location_ =
glGetUniformLocation(program(), "colorize2Color");
assert(colorize2_color_location_ != -1);
}
if ((!(flags & SHD_TEXTURE)) || (flags & SHD_MODULATE)) {
color_location_ = glGetUniformLocation(program(), "color");
assert(color_location_ != -1);
}
if (flags & SHD_SHADOW) {
shadow_params_location_ = glGetUniformLocation(program(), "shadowParams");
assert(shadow_params_location_ != -1);
}
if (flags & SHD_GLOW) {
glow_params_location_ = glGetUniformLocation(program(), "glowParams");
assert(glow_params_location_ != -1);
}
if (flags & SHD_FLATNESS) {
flatness_location = glGetUniformLocation(program(), "flatness");
assert(flatness_location != -1);
}
if (flags & SHD_MASKED) {
SetTextureUnit("maskTex", kMaskTexUnit);
}
if (flags & SHD_MASK_UV2) {
SetTextureUnit("maskUV2Tex", kMaskUV2TexUnit);
}
}
void SetColorTexture(const TextureAsset* t) {
assert(flags_ & SHD_TEXTURE);
assert(IsBound());
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetColorTexture(GLuint t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetColor(float r, float g, float b, float a = 1.0f) {
assert((flags_ & SHD_MODULATE) || !(flags_ & SHD_TEXTURE));
assert(IsBound());
if (r != r_ || g != g_ || b != b_ || a != a_) {
r_ = r;
g_ = g;
b_ = b;
a_ = a;
glUniform4f(color_location_, r_, g_, b_, a_);
}
}
void SetColorizeColor(float r, float g, float b, float a = 1.0f) {
assert(flags_ & SHD_COLORIZE);
assert(IsBound());
if (r != colorize_r_ || g != colorize_g_ || b != colorize_b_
|| a != colorize_a_) {
colorize_r_ = r;
colorize_g_ = g;
colorize_b_ = b;
colorize_a_ = a;
glUniform4f(colorize_color_location_, colorize_r_, colorize_g_,
colorize_b_, colorize_a_);
}
}
void SetShadow(float shadow_offset_x, float shadow_offset_y,
float shadow_blur, float shadow_density) {
assert(flags_ & SHD_SHADOW);
assert(IsBound());
if (shadow_offset_x != shadow_offset_x_
|| shadow_offset_y != shadow_offset_y_ || shadow_blur != shadow_blur_
|| shadow_density != shadow_density_) {
shadow_offset_x_ = shadow_offset_x;
shadow_offset_y_ = shadow_offset_y;
shadow_blur_ = shadow_blur;
shadow_density_ = shadow_density;
glUniform4f(shadow_params_location_, shadow_offset_x_, shadow_offset_y_,
shadow_blur_, shadow_density_ * 0.4f);
}
}
void SetGlow(float glow_amount, float glow_blur) {
assert(flags_ & SHD_GLOW);
assert(IsBound());
if (glow_amount != glow_amount_ || glow_blur != glow_blur_) {
glow_amount_ = glow_amount;
glow_blur_ = glow_blur;
glUniform2f(glow_params_location_, glow_amount_, glow_blur_);
}
}
void SetFlatness(float flatness) {
assert(flags_ & SHD_FLATNESS);
assert(IsBound());
if (flatness != flatness_) {
flatness_ = flatness;
glUniform1f(flatness_location, flatness_);
}
}
void SetColorize2Color(float r, float g, float b, float a = 1.0f) {
assert(flags_ & SHD_COLORIZE2);
assert(IsBound());
if (r != colorize2_r_ || g != colorize2_g_ || b != colorize2_b_
|| a != colorize2_a_) {
colorize2_r_ = r;
colorize2_g_ = g;
colorize2_b_ = b;
colorize2_a_ = a;
glUniform4f(colorize2_color_location_, colorize2_r_, colorize2_g_,
colorize2_b_, colorize2_a_);
}
}
void SetColorizeTexture(const TextureAsset* t) {
assert(flags_ & SHD_COLORIZE);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorizeTexUnit);
}
void SetMaskTexture(const TextureAsset* t) {
assert(flags_ & SHD_MASKED);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kMaskTexUnit);
}
void SetMaskUV2Texture(const TextureAsset* t) {
assert(flags_ & SHD_MASK_UV2);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kMaskUV2TexUnit);
}
private:
auto GetName(int flags) -> std::string {
return "SimpleProgramGL texture:"
+ std::to_string((flags & SHD_TEXTURE) != 0)
+ " modulate:" + std::to_string((flags & SHD_MODULATE) != 0)
+ " colorize:" + std::to_string((flags & SHD_COLORIZE) != 0)
+ " colorize2:" + std::to_string((flags & SHD_COLORIZE2) != 0)
+ " premultiply:" + std::to_string((flags & SHD_PREMULTIPLY) != 0)
+ " shadow:" + std::to_string((flags & SHD_SHADOW) != 0)
+ " glow:" + std::to_string((flags & SHD_GLOW) != 0) + " masked:"
+ std::to_string((flags & SHD_MASKED) != 0) + " maskedUV2:"
+ std::to_string((flags & SHD_MASK_UV2) != 0) + " depthBugTest:"
+ std::to_string((flags & SHD_DEPTH_BUG_TEST) != 0)
+ " flatness:" + std::to_string((flags & SHD_FLATNESS) != 0);
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR;
if (flags & SHD_TEXTURE) {
pflags |= PFLAG_USES_UV_ATTR;
}
if (flags & SHD_MASK_UV2) {
pflags |= PFLAG_USES_UV2_ATTR;
}
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
// clang-format off
std::string s;
s = "uniform mat4 modelViewProjectionMatrix;\n"
BA_GLSL_VERTEX_IN " vec4 position;\n";
if ((flags & SHD_TEXTURE) || (flags & SHD_COLORIZE)
|| (flags & SHD_COLORIZE2)) {
s += BA_GLSL_VERTEX_IN " vec2 uv;\n"
BA_GLSL_VERTEX_OUT " vec2 vUV;\n";
}
if (flags & SHD_MASK_UV2) {
s += BA_GLSL_VERTEX_IN " vec2 uv2;\n"
BA_GLSL_VERTEX_OUT " vec2 vUV2;\n";
}
if (flags & SHD_SHADOW) {
s += BA_GLSL_VERTEX_OUT " vec2 vUVShadow;\n"
BA_GLSL_VERTEX_OUT " vec2 vUVShadow2;\n"
BA_GLSL_VERTEX_OUT " vec2 vUVShadow3;\n"
"uniform " BA_GLSL_LOWP "vec4 shadowParams;\n";
}
s += "void main() {\n";
if (flags & SHD_TEXTURE) {
s += " vUV = uv;\n";
}
if (flags & SHD_MASK_UV2) {
s += " vUV2 = uv2;\n";
}
if (flags & SHD_SHADOW) {
s += " vUVShadow = uv + 0.4 *"
" vec2(shadowParams.x, shadowParams.y);\n";
}
if (flags & SHD_SHADOW) {
s += " vUVShadow2 = uv + 0.8 *"
" vec2(shadowParams.x, shadowParams.y);\n";
}
if (flags & SHD_SHADOW) {
s += " vUVShadow3 = uv + 1.3 * vec2(shadowParams.x, shadowParams.y);\n";
}
s += " gl_Position = modelViewProjectionMatrix * position;\n"
"}";
if (flags & SHD_DEBUG_PRINT) {
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
}
// clang-format off
return s;
}
auto GetFragmentCode(int flags) -> std::string {
// clang-format off
std::string s;
if (flags & SHD_TEXTURE) {
s += "uniform " BA_GLSL_LOWP "sampler2D colorTex;\n";
}
if ((flags & SHD_COLORIZE)) {
s += "uniform " BA_GLSL_LOWP "sampler2D colorizeTex;\n"
"uniform " BA_GLSL_LOWP "vec4 colorizeColor;\n";
}
if ((flags & SHD_COLORIZE2)) {
s += "uniform " BA_GLSL_LOWP "vec4 colorize2Color;\n";
}
if ((flags & SHD_TEXTURE) || (flags & SHD_COLORIZE)
|| (flags & SHD_COLORIZE2)) {
s += BA_GLSL_FRAG_IN " " BA_GLSL_LOWP "vec2 vUV;\n";
}
if (flags & SHD_MASK_UV2) {
s += BA_GLSL_FRAG_IN " " BA_GLSL_LOWP "vec2 vUV2;\n";
}
if (flags & SHD_FLATNESS) {
s += "uniform " BA_GLSL_LOWP "float flatness;\n";
}
if (flags & SHD_SHADOW) {
s += BA_GLSL_FRAG_IN " " BA_GLSL_LOWP "vec2 vUVShadow;\n"
BA_GLSL_FRAG_IN " " BA_GLSL_LOWP "vec2 vUVShadow2;\n"
BA_GLSL_FRAG_IN " " BA_GLSL_LOWP "vec2 vUVShadow3;\n"
"uniform " BA_GLSL_LOWP "vec4 shadowParams;\n";
}
if (flags & SHD_GLOW) {
s += "uniform " BA_GLSL_LOWP "vec2 glowParams;\n";
}
if ((flags & SHD_MODULATE) || (!(flags & SHD_TEXTURE))) {
s += "uniform " BA_GLSL_LOWP "vec4 color;\n";
}
if (flags & SHD_MASKED) {
s += "uniform " BA_GLSL_LOWP "sampler2D maskTex;\n";
}
if (flags & SHD_MASK_UV2) {
s += "uniform " BA_GLSL_LOWP "sampler2D maskUV2Tex;\n";
}
s += "void main() {\n";
if (!(flags & SHD_TEXTURE)) {
s += " " BA_GLSL_FRAGCOLOR " = color;\n";
} else {
std::string blur_arg;
if (flags & SHD_GLOW) {
s += " " BA_GLSL_LOWP
"vec4 cVal = " BA_GLSL_TEXTURE2D "(colorTex, vUV, glowParams.g);\n"
" " BA_GLSL_FRAGCOLOR
" = vec4(color.rgb * cVal.rgb * cVal.a * "
"glowParams.r, 0.0)"; // we premultiply this.
if (flags & SHD_MASK_UV2) {
s += " * vec4(" BA_GLSL_TEXTURE2D "(maskUV2Tex, vUV2).a)";
}
s += ";\n";
} else {
if ((flags & SHD_COLORIZE) || (flags & SHD_COLORIZE2)) {
// TEMP TEST
s += " " BA_GLSL_LOWP
"vec4 colorizeVal = " BA_GLSL_TEXTURE2D "(colorizeTex, vUV);\n";
}
if (flags & SHD_COLORIZE) {
s += " " BA_GLSL_LOWP "float colorizeA = colorizeVal.r;\n";
}
if (flags & SHD_COLORIZE2) {
s += " " BA_GLSL_LOWP "float colorizeB = colorizeVal.g;\n";
}
if (flags & SHD_MASKED) {
s += " " BA_GLSL_MEDIUMP "vec4 mask = "
BA_GLSL_TEXTURE2D "(maskTex, vUV);";
}
if (flags & SHD_MODULATE) {
if (flags & SHD_FLATNESS) {
s += " " BA_GLSL_LOWP
"vec4 rawTexColor = " BA_GLSL_TEXTURE2D "(colorTex, vUV);\n"
" " BA_GLSL_FRAGCOLOR " = color * "
"vec4(mix(rawTexColor.rgb, vec3(1.0), flatness),"
" rawTexColor.a)";
} else {
s += " " BA_GLSL_FRAGCOLOR " = color * "
BA_GLSL_TEXTURE2D "(colorTex, vUV)";
}
} else {
s += " " BA_GLSL_FRAGCOLOR " = "
BA_GLSL_TEXTURE2D "(colorTex, vUV)";
}
if (flags & SHD_COLORIZE) {
s += " * (vec4(1.0 - colorizeA) + colorizeColor * colorizeA)";
}
if (flags & SHD_COLORIZE2) {
s += " * (vec4(1.0 - colorizeB) + colorize2Color * colorizeB)";
}
if (flags & SHD_MASKED) {
s += " * vec4(vec3(mask.r), mask.a) + "
"vec4(vec3(mask.g) * colorizeColor.rgb + vec3(mask.b), 0.0)";
}
s += ";\n";
if (flags & SHD_SHADOW) {
s += " " BA_GLSL_LOWP
"float shadowA = ("
BA_GLSL_TEXTURE2D "(colorTex, vUVShadow).a + "
"" BA_GLSL_TEXTURE2D "(colorTex, vUVShadow2, 1.0).a + "
"" BA_GLSL_TEXTURE2D
"(colorTex, vUVShadow3, 2.0).a) * shadowParams.a";
if (flags & SHD_MASK_UV2) {
s += " * " BA_GLSL_TEXTURE2D "(maskUV2Tex, vUV2).a";
}
s += ";\n";
s += " " BA_GLSL_FRAGCOLOR
" = "
"vec4(" BA_GLSL_FRAGCOLOR ".rgb * " BA_GLSL_FRAGCOLOR
".a," BA_GLSL_FRAGCOLOR
".a) + "
"(1.0 - " BA_GLSL_FRAGCOLOR ".a) * vec4(0, 0, 0, shadowA);\n";
s += " " BA_GLSL_FRAGCOLOR
" = "
"vec4(" BA_GLSL_FRAGCOLOR
".rgb / max(0.001, " BA_GLSL_FRAGCOLOR ".a), "
BA_GLSL_FRAGCOLOR ".a);\n";
}
}
if (flags & SHD_DEPTH_BUG_TEST) {
s += " " BA_GLSL_FRAGCOLOR " = vec4(abs(gl_FragCoord.z - "
BA_GLSL_FRAGCOLOR ".r));\n";
}
if (flags & SHD_PREMULTIPLY) {
s += " " BA_GLSL_FRAGCOLOR " = vec4(" BA_GLSL_FRAGCOLOR
".rgb * "
"" BA_GLSL_FRAGCOLOR ".a, " BA_GLSL_FRAGCOLOR ".a);";
}
}
s += "}";
if (flags & SHD_DEBUG_PRINT) {
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
}
// clang-format on
return s;
}
float r_{}, g_{}, b_{}, a_{};
float colorize_r_{}, colorize_g_{}, colorize_b_{}, colorize_a_{};
float colorize2_r_{}, colorize2_g_{}, colorize2_b_{}, colorize2_a_{};
float shadow_offset_x_{}, shadow_offset_y_{}, shadow_blur_{},
shadow_density_{};
float glow_amount_{}, glow_blur_{};
float flatness_{};
GLint color_location_{};
GLint colorize_color_location_{};
GLint colorize2_color_location_{};
GLint shadow_params_location_{};
GLint glow_params_location_{};
GLint flatness_location{};
int flags_{};
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SIMPLE_GL_H_

View File

@ -0,0 +1,176 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SMOKE_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SMOKE_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramSmokeGL : public RendererGL::ProgramGL {
public:
enum TextureUnit { kColorTexUnit, kDepthTexUnit, kBlurTexUnit };
ProgramSmokeGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags),
r_(0),
g_(0),
b_(0),
a_(0) {
SetTextureUnit("colorTex", kColorTexUnit);
if (flags & SHD_OVERLAY) {
SetTextureUnit("depthTex", kDepthTexUnit);
SetTextureUnit("blurTex", kBlurTexUnit);
}
color_location_ = glGetUniformLocation(program(), "colorMult");
assert(color_location_ != -1);
}
void SetColorTexture(const TextureAsset* t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetDepthTexture(GLuint t) {
assert(flags_ & SHD_OVERLAY);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kDepthTexUnit);
}
void SetBlurTexture(GLuint t) {
assert(flags_ & SHD_OVERLAY);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kBlurTexUnit);
}
void SetColor(float r, float g, float b, float a = 1.0f) {
assert(IsBound());
// include tint..
if (r * renderer()->tint().x != r_ || g * renderer()->tint().y != g_
|| b * renderer()->tint().z != b_ || a != a_) {
r_ = r * renderer()->tint().x;
g_ = g * renderer()->tint().y;
b_ = b * renderer()->tint().z;
a_ = a;
glUniform4f(color_location_, r_, g_, b_, a_);
}
}
private:
auto GetName(int flags) -> std::string {
return std::string("SmokeProgramGL");
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR | PFLAG_USES_DIFFUSE_ATTR
| PFLAG_USES_UV_ATTR | PFLAG_WORLD_SPACE_PTS
| PFLAG_USES_ERODE_ATTR | PFLAG_USES_COLOR_ATTR;
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
std::string s;
s = "uniform mat4 modelViewProjectionMatrix;\n" BA_GLSL_VERTEX_IN
" vec4 position;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"vec2 uv;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP
"vec2 vUV;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_LOWP
"float erode;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"float diffuse;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_LOWP
"float vErode;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"vec4 color;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_LOWP
"vec4 vColor;\n"
"uniform " BA_GLSL_MEDIUMP "vec4 colorMult;\n";
if (flags & SHD_OVERLAY) {
s += BA_GLSL_VERTEX_OUT " " BA_GLSL_LOWP
"vec4 cDiffuse;\n" BA_GLSL_VERTEX_OUT
" " BA_GLSL_MEDIUMP "vec4 vScreenCoord;\n";
}
s += "void main() {\n"
" vUV = uv;\n"
" gl_Position = modelViewProjectionMatrix*position;\n"
" vErode = erode;\n";
// in overlay mode we pass color/diffuse to the pixel-shader since we
// combine them there with a blurred bg image to get a soft look. In the
// simple version we just use a flat ambient color here.
if (flags & SHD_OVERLAY) {
s += " vScreenCoord = "
"vec4(gl_Position.xy/gl_Position.w,gl_Position.zw);\n"
" vColor = vec4(vec3(7.0*diffuse),0.7) * color * colorMult;\n"
" cDiffuse = colorMult*(0.3+0.8*diffuse);\n"
" vScreenCoord.xy += vec2(1.0);\n"
" vScreenCoord.xy *= vec2(0.5*vScreenCoord.w);\n";
} else {
s += " vColor = "
"(vec4(vec3(7.0),1.0)*color+vec4(vec3(0.4),0))*vec4(vec3(diffuse),0."
"4) * colorMult;\n";
}
s += " vColor *= vec4(vec3(vColor.a),1.0);\n"; // premultiply
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
auto GetFragmentCode(int flags) -> std::string {
std::string s;
s = "uniform " BA_GLSL_LOWP "sampler2D colorTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_MEDIUMP "vec2 vUV;\n" BA_GLSL_FRAG_IN " " BA_GLSL_LOWP
"float vErode;\n" BA_GLSL_FRAG_IN " " BA_GLSL_LOWP "vec4 vColor;\n";
if (flags & SHD_OVERLAY) {
s += BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec4 vScreenCoord;\n"
"uniform " BA_GLSL_LOWP
"sampler2D depthTex;\n"
"uniform " BA_GLSL_LOWP
"sampler2D blurTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_LOWP "vec4 cDiffuse;\n";
}
s += "void main() {\n";
s += " " BA_GLSL_LOWP
"float erodeMult = smoothstep(vErode,1.0," BA_GLSL_TEXTURE2D
"(colorTex,vUV).r);\n"
" " BA_GLSL_FRAGCOLOR " = (vColor*vec4(erodeMult));";
if (flags & SHD_OVERLAY) {
s += " " BA_GLSL_FRAGCOLOR " += vec4(vec3(" BA_GLSL_FRAGCOLOR
".a),0) * cDiffuse * "
"(0.11+0.8*" BA_GLSL_TEXTURE2DPROJ "(blurTex,vScreenCoord));\n";
s += " " BA_GLSL_MEDIUMP " float depth =" BA_GLSL_TEXTURE2DPROJ
"(depthTex,vScreenCoord).r;\n";
// Work around Adreno bug where depth is returned as 0..1 instead of
// glDepthRange().
if (GetFunkyDepthIssue_()) {
s += " depth = " + std::to_string(kBackingDepth3) + "+depth*("
+ std::to_string(kBackingDepth4) + "-"
+ std::to_string(kBackingDepth3) + ");\n";
}
s += " " BA_GLSL_FRAGCOLOR
" *= "
"(1.0-smoothstep(0.0,0.002,gl_FragCoord.z-depth));\n";
}
s += "}";
if (flags & SHD_DEBUG_PRINT)
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
return s;
}
float r_, g_, b_, a_;
GLint color_location_;
int flags_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SMOKE_GL_H_

View File

@ -0,0 +1,180 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SPRITE_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SPRITE_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/program/program_gl.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::ProgramSpriteGL : public RendererGL::ProgramGL {
public:
enum TextureUnit { kColorTexUnit, kDepthTexUnit };
ProgramSpriteGL(RendererGL* renderer, int flags)
: RendererGL::ProgramGL(
renderer, Object::New<VertexShaderGL>(GetVertexCode(flags)),
Object::New<FragmentShaderGL>(GetFragmentCode(flags)), GetName(flags),
GetPFlags(flags)),
flags_(flags),
r_(0),
g_(0),
b_(0),
a_(0) {
SetTextureUnit("colorTex", kColorTexUnit);
if (flags & SHD_OVERLAY) {
SetTextureUnit("depthTex", kDepthTexUnit);
}
if (flags & SHD_COLOR) {
color_location_ = glGetUniformLocation(program(), "colorU");
assert(color_location_ != -1);
}
BA_DEBUG_CHECK_GL_ERROR;
}
void SetColorTexture(const TextureAsset* t) {
renderer()->BindTexture_(GL_TEXTURE_2D, t, kColorTexUnit);
}
void SetDepthTexture(GLuint t) {
assert(flags_ & SHD_OVERLAY);
renderer()->BindTexture_(GL_TEXTURE_2D, t, kDepthTexUnit);
}
void SetColor(float r, float g, float b, float a = 1.0f) {
assert(flags_ & SHD_COLOR);
assert(IsBound());
if (r != r_ || g != g_ || b != b_ || a != a_) {
r_ = r;
g_ = g;
b_ = b;
a_ = a;
glUniform4f(color_location_, r_, g_, b_, a_);
}
}
private:
auto GetName(int flags) -> std::string {
return std::string("SpriteProgramGL");
}
auto GetPFlags(int flags) -> int {
int pflags = PFLAG_USES_POSITION_ATTR | PFLAG_USES_SIZE_ATTR
| PFLAG_USES_COLOR_ATTR | PFLAG_USES_UV_ATTR;
if (flags & SHD_CAMERA_ALIGNED) pflags |= PFLAG_USES_CAM_ORIENT_MATRIX;
return pflags;
}
auto GetVertexCode(int flags) -> std::string {
std::string s;
s += "uniform mat4 modelViewProjectionMatrix;\n" BA_GLSL_VERTEX_IN
" vec4 position;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"vec2 uv;\n" BA_GLSL_VERTEX_IN " " BA_GLSL_MEDIUMP
"float size;\n" BA_GLSL_VERTEX_OUT " " BA_GLSL_MEDIUMP "vec2 vUV;\n";
if (flags & SHD_COLOR) {
s += "uniform " BA_GLSL_LOWP "vec4 colorU;\n";
}
if (flags & SHD_CAMERA_ALIGNED) {
s += "uniform mat4 camOrientMatrix;\n";
}
if (flags & SHD_OVERLAY) {
s += BA_GLSL_VERTEX_OUT " " BA_GLSL_LOWP "vec4 vScreenCoord;\n";
}
s += BA_GLSL_VERTEX_IN " " BA_GLSL_LOWP "vec4 color;\n" BA_GLSL_VERTEX_OUT
" " BA_GLSL_LOWP
"vec4 vColor;\n"
"void main() {\n";
if (flags & SHD_CAMERA_ALIGNED) {
s += " " BA_GLSL_HIGHP
"vec4 pLocal = "
"(position+camOrientMatrix*vec4((uv.s-0.5)*size,0,(uv.t-0.5)*size,0)"
");\n";
} else {
s += " " BA_GLSL_HIGHP
"vec4 pLocal = "
"(position+vec4((uv.s-0.5)*size,0,(uv.t-0.5)*size,0));\n";
}
s += " gl_Position = modelViewProjectionMatrix*pLocal;\n"
" vUV = uv;\n";
if (flags & SHD_COLOR) {
s += " vColor = color*colorU;\n";
} else {
s += " vColor = color;\n";
}
if (flags & SHD_OVERLAY)
s += " vScreenCoord = "
"vec4(gl_Position.xy/gl_Position.w,gl_Position.zw);\n"
" vScreenCoord.xy += vec2(1.0);\n"
" vScreenCoord.xy *= vec2(0.5*vScreenCoord.w);\n";
s += "}";
if (flags & SHD_DEBUG_PRINT) {
Log(LogLevel::kInfo,
"\nVertex code for shader '" + GetName(flags) + "':\n\n" + s);
}
return s;
}
auto GetFragmentCode(int flags) -> std::string {
std::string s;
s += "uniform " BA_GLSL_LOWP "sampler2D colorTex;\n" BA_GLSL_FRAG_IN
" " BA_GLSL_MEDIUMP "vec2 vUV;\n" BA_GLSL_FRAG_IN " " BA_GLSL_LOWP
"vec4 vColor;\n";
if (flags & SHD_OVERLAY) {
s += BA_GLSL_FRAG_IN " " BA_GLSL_MEDIUMP
"vec4 vScreenCoord;\n"
"uniform " BA_GLSL_MEDIUMP "sampler2D depthTex;\n";
}
s += "void main() {\n"
" " BA_GLSL_FRAGCOLOR " = vColor*vec4(" BA_GLSL_TEXTURE2D
"(colorTex,vUV).r);\n";
if (flags & SHD_EXP2)
s += " " BA_GLSL_FRAGCOLOR
" = vec4(vUV,0,0) + "
"vec4(" BA_GLSL_FRAGCOLOR ".rgb*" BA_GLSL_FRAGCOLOR
".rgb," BA_GLSL_FRAGCOLOR ".a);\n";
if (flags & SHD_OVERLAY) {
s += " " BA_GLSL_MEDIUMP "float depth = " BA_GLSL_TEXTURE2DPROJ
"(depthTex,vScreenCoord).r;\n";
// Adreno 320 bug where depth is returned as 0..1 instead of
// glDepthRange().
if (GetFunkyDepthIssue_()) {
s += " depth = " + std::to_string(kBackingDepth3) + "+depth*("
+ std::to_string(kBackingDepth4) + "-"
+ std::to_string(kBackingDepth3) + ");\n";
}
s += " " BA_GLSL_FRAGCOLOR
" *= "
"(1.0-smoothstep(0.0,0.001,gl_FragCoord.z-depth));\n";
}
s += "}";
if (flags & SHD_DEBUG_PRINT) {
Log(LogLevel::kInfo,
"\nFragment code for shader '" + GetName(flags) + "':\n\n" + s);
}
return s;
}
float r_, g_, b_, a_;
GLint color_location_;
int flags_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_PROGRAM_PROGRAM_SPRITE_GL_H_

View File

@ -0,0 +1,129 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_RENDER_TARGET_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_RENDER_TARGET_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/graphics/gl/framebuffer_object_gl.h"
namespace ballistica::base {
class RendererGL::RenderTargetGL : public RenderTarget {
public:
void Bind() {
if (type_ == Type::kFramebuffer) {
assert(framebuffer_.Exists());
framebuffer_->Bind();
} else {
assert(type_ == Type::kScreen);
renderer_->BindFramebuffer(renderer_->screen_framebuffer_);
}
}
void DrawBegin(bool must_clear_color, float clear_r, float clear_g,
float clear_b, float clear_a) override {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
Bind();
#if BA_CARDBOARD_BUILD
int x, y;
// Viewport offsets only apply to the screen render-target.
if (type_ == Type::kScreen) {
x = renderer_->VRGetViewportX();
y = renderer_->VRGetViewportY();
} else {
x = 0;
y = 0;
}
renderer_->SetViewport_(x, y, physical_width_, physical_height_);
#else
renderer_->SetViewport_(0, 0, static_cast<GLsizei>(physical_width_),
static_cast<GLsizei>(physical_height_));
#endif
{
// Clear depth, color, etc.
GLuint clear_mask = 0;
// If they *requested* a clear for color, do so. Otherwise invalidate.
if (must_clear_color) {
clear_mask |= GL_COLOR_BUFFER_BIT;
} else {
renderer_->InvalidateFramebuffer(true, false, false);
}
if (depth_) {
// FIXME make sure depth writing is turned on at this point.
// this needs to be on for glClear to work on depth.
if (!renderer_->depth_writing_enabled_) {
BA_LOG_ONCE(
LogLevel::kWarning,
"RendererGL: depth-writing not enabled when clearing depth");
}
clear_mask |= GL_DEPTH_BUFFER_BIT;
}
if (clear_mask != 0) {
if (clear_mask & GL_COLOR_BUFFER_BIT) {
glClearColor(clear_r, clear_g, clear_b, clear_a);
BA_DEBUG_CHECK_GL_ERROR;
}
glClear(clear_mask);
BA_DEBUG_CHECK_GL_ERROR;
}
}
}
auto GetFramebufferID() -> GLuint {
if (type_ == Type::kFramebuffer) {
assert(framebuffer_.Exists());
return framebuffer_->id();
} else {
return 0; // screen
}
}
auto framebuffer() -> FramebufferObjectGL* {
assert(type_ == Type::kFramebuffer);
return framebuffer_.Get();
}
// Screen constructor.
explicit RenderTargetGL(RendererGL* renderer)
: RenderTarget(Type::kScreen), renderer_(renderer) {
assert(g_base->InGraphicsThread());
depth_ = true;
// This will update our width/height values.
OnScreenSizeChange();
}
// Framebuffer constructor.
RenderTargetGL(RendererGL* renderer, int width, int height,
bool linear_interp, bool depth, bool texture,
bool depth_texture, bool high_quality, bool msaa, bool alpha)
: RenderTarget(Type::kFramebuffer), renderer_(renderer) {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
framebuffer_ = Object::New<FramebufferObjectGL>(
renderer, width, height, linear_interp, depth, texture, depth_texture,
high_quality, msaa, alpha);
physical_width_ = static_cast<float>(width);
physical_height_ = static_cast<float>(height);
depth_ = depth;
BA_DEBUG_CHECK_GL_ERROR;
}
private:
Object::Ref<FramebufferObjectGL> framebuffer_;
RendererGL* renderer_{};
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_RENDER_TARGET_GL_H_

File diff suppressed because it is too large Load Diff

View File

@ -15,10 +15,27 @@
#include "ballistica/base/graphics/renderer/renderer.h"
#include "ballistica/shared/foundation/object.h"
// Can be handy to check GL errors on opt builds.
#define BA_FORCE_CHECK_GL_ERRORS 0
#define BA_CHECK_GL_ERROR CheckGLError(__FILE__, __LINE__)
#if BA_DEBUG_BUILD || BA_FORCE_CHECK_GL_ERRORS
#define BA_DEBUG_CHECK_GL_ERROR CheckGLError(__FILE__, __LINE__)
#else
#define BA_DEBUG_CHECK_GL_ERROR ((void)0)
#endif
namespace ballistica::base {
// for now lets not go above 8 since that's what the iPhone 3gs has..
// ...haha perhaps should revisit this
// extern int g_msaa_max_samples_rgb565;
// extern int g_msaa_max_samples_rgb8;
// extern bool g_vao_support;
// extern bool g_running_es3;
// extern bool g_anisotropic_support;
// extern float g_max_anisotropy;
// For now lets not go above 8 since that's what the iPhone 3gs has. ...haha
// perhaps can reconsider that since the 3gs was 15 years ago.
constexpr int kMaxGLTexUnitsUsed = 5;
class RendererGL : public Renderer {
@ -38,22 +55,72 @@ class RendererGL : public Renderer {
class FragmentShaderGL;
class VertexShaderGL;
class ProgramGL;
class SimpleProgramGL;
class ObjectProgramGL;
class SmokeProgramGL;
class BlurProgramGL;
class ShieldProgramGL;
class PostProcessProgramGL;
class SpriteProgramGL;
class ProgramSimpleGL;
class ProgramObjectGL;
class ProgramSmokeGL;
class ProgramBlurGL;
class ProgramShieldGL;
class ProgramPostProcessGL;
class ProgramSpriteGL;
public:
static void CheckGLError(const char* file, int line);
static auto GLErrorToString(GLenum err) -> std::string;
static auto GetGLTextureFormat(TextureFormat f) -> GLenum;
RendererGL();
~RendererGL() override;
void Unload() override;
void Load() override;
void PostLoad() override;
// our vertex attrs
// Flags used internally by shaders.
enum ShaderPrivateFlags {
PFLAG_USES_POSITION_ATTR = 1,
PFLAG_USES_UV_ATTR = 1 << 1,
PFLAG_USES_NORMAL_ATTR = 1 << 2,
PFLAG_USES_MODEL_WORLD_MATRIX = 1 << 3,
PFLAG_USES_CAM_POS = 1 << 4,
PFLAG_USES_SHADOW_PROJECTION_MATRIX = 1 << 5,
PFLAG_WORLD_SPACE_PTS = 1 << 6,
PFLAG_USES_ERODE_ATTR = 1 << 7,
PFLAG_USES_COLOR_ATTR = 1 << 8,
PFLAG_USES_SIZE_ATTR = 1 << 9,
PFLAG_USES_DIFFUSE_ATTR = 1 << 10,
PFLAG_USES_CAM_ORIENT_MATRIX = 1 << 11,
PFLAG_USES_MODEL_VIEW_MATRIX = 1 << 12,
PFLAG_USES_UV2_ATTR = 1 << 13
};
// Flags affecting shader creation.
enum ShaderFlag {
SHD_REFLECTION = 1,
SHD_TEXTURE = 1 << 1,
SHD_MODULATE = 1 << 2,
SHD_COLORIZE = 1 << 3,
SHD_LIGHT_SHADOW = 1 << 4,
SHD_WORLD_SPACE_PTS = 1 << 5,
SHD_DEBUG_PRINT = 1 << 6,
SHD_ADD = 1 << 7,
SHD_OBJ_TRANSPARENT = 1 << 8,
SHD_COLOR = 1 << 9,
SHD_EXP2 = 1 << 10,
SHD_CAMERA_ALIGNED = 1 << 11,
SHD_DISTORT = 1 << 12,
SHD_PREMULTIPLY = 1 << 13,
SHD_OVERLAY = 1 << 14,
SHD_EYES = 1 << 15,
SHD_COLORIZE2 = 1 << 16,
SHD_HIGHER_QUALITY = 1 << 17,
SHD_SHADOW = 1 << 18,
SHD_GLOW = 1 << 19,
SHD_MASKED = 1 << 20,
SHD_MASK_UV2 = 1 << 21,
SHD_CONDITIONAL = 1 << 22,
SHD_FLATNESS = 1 << 23,
SHD_DEPTH_BUG_TEST = 1 << 24
};
enum VertexAttr {
kVertexAttrPosition,
kVertexAttrUV,
@ -66,12 +133,12 @@ class RendererGL : public Renderer {
kVertexAttrCount
};
void CheckCapabilities() override;
auto GetAutoGraphicsQuality() -> GraphicsQuality override;
auto GetAutoTextureQuality() -> TextureQuality override;
#if BA_OSTYPE_ANDROID
std::string GetAutoAndroidRes() override;
#endif // BA_OSTYPE_ANDROID
#endif
protected:
void DrawDebug() override;
@ -119,36 +186,66 @@ class RendererGL : public Renderer {
#if BA_VR_BUILD
void VRSyncRenderStates() override;
#endif // BA_VR_BUILD
#endif
// TEMP
auto current_vertex_array() const -> GLuint { return current_vertex_array_; }
auto anisotropic_support() const { return anisotropic_support_; }
auto max_anisotropy() const {
assert(anisotropic_support_);
return max_anisotropy_;
}
auto invalidate_framebuffer_support() const {
return invalidate_framebuffer_support_;
}
auto msaa_max_samples_rgb565() const {
assert(msaa_max_samples_rgb565_ != -1);
return msaa_max_samples_rgb565_;
}
auto msaa_max_samples_rgb8() const {
assert(msaa_max_samples_rgb8_ != -1);
return msaa_max_samples_rgb8_;
}
auto gl_is_es() const -> bool {
#if BA_OPENGL_IS_ES
return true;
#else
return false;
#endif
}
auto DebugGLGetInt(GLenum name) -> int;
private:
void CheckFunkyDepthIssue();
auto GetMSAASamplesForFramebuffer(int width, int height) -> int;
void UpdateMSAAEnabled() override;
void CheckGLExtensions();
void UpdateVignetteTex(bool force) override;
void StandardPostProcessSetup(PostProcessProgramGL* p,
const RenderPass& pass);
void SyncGLState();
void RetainShader(ProgramGL* p);
void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height);
void UseProgram(ProgramGL* p);
auto GetActiveProgram() const -> ProgramGL* {
static auto GetFunkyDepthIssue_() -> bool;
// static auto GetDrawsShieldsFunny_()->bool;
void CheckFunkyDepthIssue_();
auto GetMSAASamplesForFramebuffer_(int width, int height) -> int;
void UpdateMSAAEnabled_() override;
void CheckGLCapabilities_();
void UpdateVignetteTex_(bool force) override;
void StandardPostProcessSetup_(ProgramPostProcessGL* p,
const RenderPass& pass);
void SyncGLState_();
void RetainShader_(ProgramGL* p);
void SetViewport_(GLint x, GLint y, GLsizei width, GLsizei height);
void UseProgram_(ProgramGL* p);
auto GetActiveProgram_() const -> ProgramGL* {
assert(current_program_);
return current_program_;
}
void SetDoubleSided(bool d);
void ScissorPush(const Rect& rIn, RenderTarget* render_target);
void ScissorPop(RenderTarget* render_target);
void BindVertexArray(GLuint v);
void SetDoubleSided_(bool d);
void ScissorPush_(const Rect& rIn, RenderTarget* render_target);
void ScissorPop_(RenderTarget* render_target);
void BindVertexArray_(GLuint v);
// Note: This is only for use when VAOs aren't supported.
void SetVertexAttribArrayEnabled(GLuint i, bool enabled);
void BindTexture(GLuint type, const TextureAsset* t, GLuint tex_unit = 0);
void BindTexture(GLuint type, GLuint tex, GLuint tex_unit = 0);
// void SetVertexAttributeArrayEnabled_(GLuint i, bool enabled);
void BindTexture_(GLuint type, const TextureAsset* t, GLuint tex_unit = 0);
void BindTexture_(GLuint type, GLuint tex, GLuint tex_unit = 0);
void BindTextureUnit(uint32_t tex_unit);
void BindFramebuffer(GLuint fb);
void BindArrayBuffer(GLuint b);
@ -156,7 +253,7 @@ class RendererGL : public Renderer {
void SetBlendPremult(bool b);
millisecs_t dof_update_time_{};
std::vector<Object::Ref<FramebufferObjectGL> > blur_buffers_;
bool supports_depth_textures_{};
// bool supports_depth_textures_{};
bool first_extension_check_{true};
bool is_tegra_4_{};
bool is_tegra_k1_{};
@ -179,56 +276,54 @@ class RendererGL : public Renderer {
bool depth_testing_enabled_{};
bool data_loaded_{};
bool draw_front_{};
GLuint screen_framebuffer_{};
bool got_screen_framebuffer_{};
GLuint screen_framebuffer_{};
GLuint random_tex_{};
GLuint vignette_tex_{};
GraphicsQuality vignette_quality_{};
std::vector<std::unique_ptr<ProgramGL> > shaders_;
GLint viewport_x_{};
GLint viewport_y_{};
GLint viewport_width_{};
GLint viewport_height_{};
SimpleProgramGL* simple_color_prog_{};
SimpleProgramGL* simple_tex_prog_{};
SimpleProgramGL* simple_tex_dtest_prog_{};
SimpleProgramGL* simple_tex_mod_prog_{};
SimpleProgramGL* simple_tex_mod_flatness_prog_{};
SimpleProgramGL* simple_tex_mod_shadow_prog_{};
SimpleProgramGL* simple_tex_mod_shadow_flatness_prog_{};
SimpleProgramGL* simple_tex_mod_glow_prog_{};
SimpleProgramGL* simple_tex_mod_glow_maskuv2_prog_{};
SimpleProgramGL* simple_tex_mod_colorized_prog_{};
SimpleProgramGL* simple_tex_mod_colorized2_prog_{};
SimpleProgramGL* simple_tex_mod_colorized2_masked_prog_{};
ObjectProgramGL* obj_prog_{};
ObjectProgramGL* obj_transparent_prog_{};
ObjectProgramGL* obj_lightshad_transparent_prog_{};
ObjectProgramGL* obj_refl_prog_{};
ObjectProgramGL* obj_refl_worldspace_prog_{};
ObjectProgramGL* obj_refl_transparent_prog_{};
ObjectProgramGL* obj_refl_add_transparent_prog_{};
ObjectProgramGL* obj_lightshad_prog_{};
ObjectProgramGL* obj_lightshad_worldspace_prog_{};
ObjectProgramGL* obj_refl_lightshad_prog_{};
ObjectProgramGL* obj_refl_lightshad_worldspace_prog_{};
ObjectProgramGL* obj_refl_lightshad_colorize_prog_{};
ObjectProgramGL* obj_refl_lightshad_colorize2_prog_{};
ObjectProgramGL* obj_refl_lightshad_add_prog_{};
ObjectProgramGL* obj_refl_lightshad_add_colorize_prog_{};
ObjectProgramGL* obj_refl_lightshad_add_colorize2_prog_{};
SmokeProgramGL* smoke_prog_{};
SmokeProgramGL* smoke_overlay_prog_{};
SpriteProgramGL* sprite_prog_{};
SpriteProgramGL* sprite_camalign_prog_{};
SpriteProgramGL* sprite_camalign_overlay_prog_{};
BlurProgramGL* blur_prog_{};
ShieldProgramGL* shield_prog_{};
PostProcessProgramGL* postprocess_prog_{};
PostProcessProgramGL* postprocess_eyes_prog_{};
PostProcessProgramGL* postprocess_distort_prog_{};
static auto GetFunkyDepthIssue() -> bool;
static auto GetDrawsShieldsFunny() -> bool;
std::vector<std::unique_ptr<ProgramGL> > shaders_;
ProgramSimpleGL* simple_color_prog_{};
ProgramSimpleGL* simple_tex_prog_{};
ProgramSimpleGL* simple_tex_dtest_prog_{};
ProgramSimpleGL* simple_tex_mod_prog_{};
ProgramSimpleGL* simple_tex_mod_flatness_prog_{};
ProgramSimpleGL* simple_tex_mod_shadow_prog_{};
ProgramSimpleGL* simple_tex_mod_shadow_flatness_prog_{};
ProgramSimpleGL* simple_tex_mod_glow_prog_{};
ProgramSimpleGL* simple_tex_mod_glow_maskuv2_prog_{};
ProgramSimpleGL* simple_tex_mod_colorized_prog_{};
ProgramSimpleGL* simple_tex_mod_colorized2_prog_{};
ProgramSimpleGL* simple_tex_mod_colorized2_masked_prog_{};
ProgramObjectGL* obj_prog_{};
ProgramObjectGL* obj_transparent_prog_{};
ProgramObjectGL* obj_lightshad_transparent_prog_{};
ProgramObjectGL* obj_refl_prog_{};
ProgramObjectGL* obj_refl_worldspace_prog_{};
ProgramObjectGL* obj_refl_transparent_prog_{};
ProgramObjectGL* obj_refl_add_transparent_prog_{};
ProgramObjectGL* obj_lightshad_prog_{};
ProgramObjectGL* obj_lightshad_worldspace_prog_{};
ProgramObjectGL* obj_refl_lightshad_prog_{};
ProgramObjectGL* obj_refl_lightshad_worldspace_prog_{};
ProgramObjectGL* obj_refl_lightshad_colorize_prog_{};
ProgramObjectGL* obj_refl_lightshad_colorize2_prog_{};
ProgramObjectGL* obj_refl_lightshad_add_prog_{};
ProgramObjectGL* obj_refl_lightshad_add_colorize_prog_{};
ProgramObjectGL* obj_refl_lightshad_add_colorize2_prog_{};
ProgramSmokeGL* smoke_prog_{};
ProgramSmokeGL* smoke_overlay_prog_{};
ProgramSpriteGL* sprite_prog_{};
ProgramSpriteGL* sprite_camalign_prog_{};
ProgramSpriteGL* sprite_camalign_overlay_prog_{};
ProgramBlurGL* blur_prog_{};
ProgramShieldGL* shield_prog_{};
ProgramPostProcessGL* postprocess_prog_{};
ProgramPostProcessGL* postprocess_eyes_prog_{};
ProgramPostProcessGL* postprocess_distort_prog_{};
static bool funky_depth_issue_set_;
static bool funky_depth_issue_;
static bool draws_shields_funny_set_;
@ -236,7 +331,7 @@ class RendererGL : public Renderer {
#if BA_OSTYPE_ANDROID
static bool is_speedy_android_device_;
static bool is_extra_speedy_android_device_;
#endif // BA_OSTYPE_ANDROID
#endif
ProgramGL* current_program_{};
bool double_sided_{};
std::vector<Rect> scissor_rects_;
@ -257,6 +352,12 @@ class RendererGL : public Renderer {
std::vector<MeshDataSmokeFullGL*> recycle_mesh_datas_smoke_full_;
std::vector<MeshDataSpriteGL*> recycle_mesh_datas_sprite_;
int error_check_counter_{};
GLint combined_texture_image_unit_count_{};
GLint anisotropic_support_{};
GLfloat max_anisotropy_{};
bool invalidate_framebuffer_support_{};
int msaa_max_samples_rgb565_{-1};
int msaa_max_samples_rgb8_{-1};
};
} // namespace ballistica::base

View File

@ -0,0 +1,329 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_GRAPHICS_GL_TEXTURE_DATA_GL_H_
#define BALLISTICA_BASE_GRAPHICS_GL_TEXTURE_DATA_GL_H_
#if BA_ENABLE_OPENGL
#include "ballistica/base/assets/texture_asset_preload_data.h"
#include "ballistica/base/assets/texture_asset_renderer_data.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
namespace ballistica::base {
class RendererGL::TextureDataGL : public TextureAssetRendererData {
public:
TextureDataGL(const TextureAsset& texture_in, RendererGL* renderer_in)
: tex_media_(&texture_in), texture_(0), renderer_(renderer_in) {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
glGenTextures(1, &texture_);
BA_DEBUG_CHECK_GL_ERROR;
}
~TextureDataGL() override {
if (!g_base->InGraphicsThread()) {
Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread.");
} else {
// If we're currently bound as anything, clear that out (otherwise a
// new texture with that same ID won't be bindable).
for (int i = 0; i < kMaxGLTexUnitsUsed; i++) {
if ((renderer_->bound_textures_2d_[i]) == texture_) {
renderer_->bound_textures_2d_[i] = -1;
}
if ((renderer_->bound_textures_cube_map_[i]) == texture_) {
renderer_->bound_textures_cube_map_[i] = -1;
}
}
if (!g_base->graphics_server->renderer_context_lost()) {
glDeleteTextures(1, &texture_);
BA_DEBUG_CHECK_GL_ERROR;
}
}
}
auto GetTexture() const -> GLuint { return texture_; }
void Load() override {
assert(g_base->InGraphicsThread());
BA_DEBUG_CHECK_GL_ERROR;
if (tex_media_->texture_type() == TextureType::k2D) {
renderer_->BindTexture_(GL_TEXTURE_2D, texture_);
const TextureAssetPreloadData* preload_data =
&tex_media_->preload_datas()[0];
int base_src_level = preload_data->base_level;
assert(preload_data->buffers[base_src_level]);
GraphicsQuality q = g_base->graphics_server->quality();
// Determine whether to use anisotropic sampling on this texture:
// basically all the UI stuff that is only ever seen from straight on
// doesn't need it.
bool allow_ani = true;
// FIXME: filtering based on filename. Once we get this stuff on a
// server we should include this as metadata instead.
const char* n = tex_media_->file_name().c_str();
// The following exceptions should *never* need aniso-sampling.
{
if (!strcmp(n, "fontBig")) {
allow_ani = false;
// Lets splurge on this for higher but not high (names over
// characters might benefit, though most text doesnt).
} else if (strstr(n, "Icon")) {
allow_ani = false;
} else if (strstr(n, "characterIconMask")) {
allow_ani = false;
} else if (!strcmp(n, "bg")) {
allow_ani = false;
} else if (strstr(n, "light")) {
allow_ani = false;
} else if (strstr(n, "shadow")) {
allow_ani = false;
} else if (!strcmp(n, "sparks")) {
allow_ani = false;
} else if (!strcmp(n, "smoke")) {
allow_ani = false;
} else if (!strcmp(n, "scorch")) {
allow_ani = false;
} else if (!strcmp(n, "scorchBig")) {
allow_ani = false;
} else if (!strcmp(n, "white")) {
allow_ani = false;
} else if (!strcmp(n, "buttonBomb")) {
allow_ani = false;
} else if (!strcmp(n, "buttonJump")) {
allow_ani = false;
} else if (!strcmp(n, "buttonPickUp")) {
allow_ani = false;
} else if (!strcmp(n, "buttonPunch")) {
allow_ani = false;
} else if (strstr(n, "touchArrows")) {
allow_ani = false;
} else if (!strcmp(n, "actionButtons")) {
allow_ani = false;
}
}
// The following are considered 'nice to have' - we turn aniso. off for
// them in anything less than 'higher' mode.
if (allow_ani && (q < GraphicsQuality::kHigher)) {
if (strstr(n, "ColorMask")) {
allow_ani = false; // character color-masks
} else if (strstr(n, "softRect")) {
allow_ani = false;
} else if (strstr(n, "BG")) {
allow_ani = false; // level backgrounds
} else if (!strcmp(n, "explosion")) {
allow_ani = false;
} else if (!strcmp(n, "bar")) {
allow_ani = false;
}
}
// In higher quality we do anisotropic trilinear mipmap.
if (q >= GraphicsQuality::kHigher) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
if (renderer_->anisotropic_support() && allow_ani) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min(16.0f, renderer_->max_anisotropy()));
}
} else if (q >= GraphicsQuality::kHigh) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
if (renderer_->anisotropic_support() && allow_ani)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min(16.0f, renderer_->max_anisotropy()));
} else if (q >= GraphicsQuality::kMedium) {
// In medium quality we don't do anisotropy but do trilinear.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
} else {
// In low quality we do bilinear.
assert(q == GraphicsQuality::kLow);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST);
}
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
int src_level = base_src_level;
int level = 0;
bool all_levels_handled = false;
while (preload_data->buffers[src_level] != nullptr
&& !all_levels_handled) {
switch (preload_data->formats[src_level]) {
case TextureFormat::kRGBA_8888: {
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA,
preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGBA,
GL_UNSIGNED_BYTE, preload_data->buffers[src_level]);
// At the moment we always just let GL generate mipmaps for
// uncompressed textures; is there any reason not to?
glGenerateMipmap(GL_TEXTURE_2D);
all_levels_handled = true;
break;
}
case TextureFormat::kRGBA_4444: {
glTexImage2D(
GL_TEXTURE_2D, level, GL_RGBA, preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGBA,
GL_UNSIGNED_SHORT_4_4_4_4, preload_data->buffers[src_level]);
// At the moment we always just let GL generate mipmaps for
// uncompressed textures; is there any reason not to?
glGenerateMipmap(GL_TEXTURE_2D);
all_levels_handled = true;
break;
}
case TextureFormat::kRGB_565: {
glTexImage2D(
GL_TEXTURE_2D, level, GL_RGB, preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, preload_data->buffers[src_level]);
// At the moment we always just let GL generate mipmaps for
// uncompressed textures; is there any reason not to?
glGenerateMipmap(GL_TEXTURE_2D);
all_levels_handled = true;
break;
}
case TextureFormat::kRGB_888: {
glTexImage2D(GL_TEXTURE_2D, level, GL_RGB,
preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGB,
GL_UNSIGNED_BYTE, preload_data->buffers[src_level]);
// At the moment we always just let GL generate mipmaps for
// uncompressed textures; is there any reason not to?
glGenerateMipmap(GL_TEXTURE_2D);
all_levels_handled = true;
break;
}
default: {
glCompressedTexImage2D(
GL_TEXTURE_2D, level,
GetGLTextureFormat(preload_data->formats[src_level]),
preload_data->widths[src_level],
preload_data->heights[src_level], 0,
static_cast_check_fit<GLsizei>(preload_data->sizes[src_level]),
preload_data->buffers[src_level]);
break;
}
}
src_level++;
level++;
BA_DEBUG_CHECK_GL_ERROR;
}
BA_GL_LABEL_OBJECT(GL_TEXTURE, texture_, tex_media_->GetName().c_str());
} else if (tex_media_->texture_type() == TextureType::kCubeMap) {
// Cube map.
renderer_->BindTexture_(GL_TEXTURE_CUBE_MAP, texture_);
bool do_generate_mips = false;
for (uint32_t i = 0; i < 6; i++) {
const TextureAssetPreloadData* preload_data =
&tex_media_->preload_datas()[i];
int base_src_level = preload_data->base_level;
assert(preload_data->buffers[base_src_level]);
GraphicsQuality q = g_base->graphics_server->quality();
// Do trilinear in higher quality; otherwise bilinear is good enough.
if (q >= GraphicsQuality::kHigher) {
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
} else {
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST);
}
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
int src_level = base_src_level;
int level = 0;
bool generating_remaining_mips = false;
while (preload_data->buffers[src_level] != nullptr
&& !generating_remaining_mips) {
switch (preload_data->formats[src_level]) {
case TextureFormat::kRGBA_8888:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, GL_RGBA,
preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGBA,
GL_UNSIGNED_BYTE, preload_data->buffers[src_level]);
generating_remaining_mips = do_generate_mips = true;
break;
case TextureFormat::kRGBA_4444:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, GL_RGBA,
preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGBA,
GL_UNSIGNED_SHORT_4_4_4_4,
preload_data->buffers[src_level]);
generating_remaining_mips = do_generate_mips = true;
break;
case TextureFormat::kRGB_565:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, GL_RGB,
preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5,
preload_data->buffers[src_level]);
generating_remaining_mips = do_generate_mips = true;
break;
case TextureFormat::kRGB_888:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, GL_RGB,
preload_data->widths[src_level],
preload_data->heights[src_level], 0, GL_RGB,
GL_UNSIGNED_BYTE, preload_data->buffers[src_level]);
generating_remaining_mips = do_generate_mips = true;
break;
default:
glCompressedTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level,
GetGLTextureFormat(preload_data->formats[src_level]),
preload_data->widths[src_level],
preload_data->heights[src_level], 0,
static_cast_check_fit<GLsizei>(
preload_data->sizes[src_level]),
preload_data->buffers[src_level]);
break;
}
src_level++;
level++;
BA_DEBUG_CHECK_GL_ERROR;
}
}
// If we're generating remaining mips on the gpu, do so.
if (do_generate_mips) {
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
}
BA_GL_LABEL_OBJECT(GL_TEXTURE, texture_, tex_media_->GetName().c_str());
} else {
throw Exception();
}
BA_DEBUG_CHECK_GL_ERROR;
}
private:
const TextureAsset* tex_media_;
RendererGL* renderer_;
GLuint texture_;
};
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_GL_TEXTURE_DATA_GL_H_

View File

@ -11,13 +11,14 @@
#include "ballistica/base/graphics/component/simple_component.h"
#include "ballistica/base/graphics/component/special_component.h"
#include "ballistica/base/graphics/component/sprite_component.h"
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/graphics/graphics_vr.h"
#include "ballistica/base/graphics/support/camera.h"
#include "ballistica/base/graphics/support/net_graph.h"
#include "ballistica/base/graphics/text/text_graphics.h"
#include "ballistica/base/input/input.h"
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/platform/base_platform.h"
#include "ballistica/base/python/support/python_context_call.h"
#include "ballistica/base/support/app_config.h"
#include "ballistica/base/ui/dev_console.h"
@ -35,6 +36,14 @@ const float kProgressBarZDepth{0.0f};
const int kProgressBarFadeTime{500};
const float kDebugImgZDepth{-0.04f};
auto Graphics::Create() -> Graphics* {
#if BA_VR_BUILD
return new GraphicsVR();
#else
return new Graphics();
#endif
}
auto Graphics::IsShaderTransparent(ShadingType c) -> bool {
switch (c) {
case ShadingType::kSimpleColorTransparent:
@ -106,94 +115,10 @@ void Graphics::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
void Graphics::DoApplyAppConfig() {
assert(g_base->InLogicThread());
// Not relevant for fullscreen anymore since we use fullscreen windows.
// everywhere.
int width = 800;
int height = 600;
// Texture quality.
TextureQualityRequest texture_quality_requested;
std::string texqualstr =
g_base->app_config->Resolve(AppConfig::StringID::kTextureQuality);
if (texqualstr == "Auto") {
texture_quality_requested = TextureQualityRequest::kAuto;
} else if (texqualstr == "High") {
texture_quality_requested = TextureQualityRequest::kHigh;
} else if (texqualstr == "Medium") {
texture_quality_requested = TextureQualityRequest::kMedium;
} else if (texqualstr == "Low") {
texture_quality_requested = TextureQualityRequest::kLow;
} else {
Log(LogLevel::kError,
"Invalid texture quality: '" + texqualstr + "'; defaulting to low.");
texture_quality_requested = TextureQualityRequest::kLow;
}
// Graphics quality.
std::string gqualstr =
g_base->app_config->Resolve(AppConfig::StringID::kGraphicsQuality);
GraphicsQualityRequest graphics_quality_requested;
if (gqualstr == "Auto") {
graphics_quality_requested = GraphicsQualityRequest::kAuto;
} else if (gqualstr == "Higher") {
graphics_quality_requested = GraphicsQualityRequest::kHigher;
} else if (gqualstr == "High") {
graphics_quality_requested = GraphicsQualityRequest::kHigh;
} else if (gqualstr == "Medium") {
graphics_quality_requested = GraphicsQualityRequest::kMedium;
} else if (gqualstr == "Low") {
graphics_quality_requested = GraphicsQualityRequest::kLow;
} else {
Log(LogLevel::kError,
"Invalid graphics quality: '" + gqualstr + "'; defaulting to auto.");
graphics_quality_requested = GraphicsQualityRequest::kAuto;
}
// Android res string.
std::string android_res =
g_base->app_config->Resolve(AppConfig::StringID::kResolutionAndroid);
bool fullscreen = g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen);
// Note: when the graphics-thread applies the first set-screen event it will
// trigger the remainder of startup such as media-loading; make sure nothing
// below this point will affect that.
g_base->graphics_server->PushSetScreenCall(
fullscreen, width, height, texture_quality_requested,
graphics_quality_requested, android_res);
show_fps_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowFPS);
show_ping_ = g_base->app_config->Resolve(AppConfig::BoolID::kShowPing);
tv_border_ = g_base->app_config->Resolve(AppConfig::BoolID::kEnableTVBorder);
g_base->graphics_server->PushSetScreenGammaCall(
g_base->app_config->Resolve(AppConfig::FloatID::kScreenGamma));
g_base->graphics_server->PushSetScreenPixelScaleCall(
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale));
// V-sync setting.
std::string v_sync =
g_base->app_config->Resolve(AppConfig::StringID::kVerticalSync);
bool do_v_sync{};
bool auto_v_sync{};
if (v_sync == "Auto") {
do_v_sync = true;
auto_v_sync = true;
} else if (v_sync == "Always") {
do_v_sync = true;
auto_v_sync = false;
} else if (v_sync == "Never") {
do_v_sync = false;
auto_v_sync = false;
} else {
do_v_sync = false;
auto_v_sync = false;
Log(LogLevel::kError, "Invalid 'Vertical Sync' value: '" + v_sync + "'");
}
g_base->graphics_server->PushSetVSyncCall(do_v_sync, auto_v_sync);
bool disable_camera_shake =
g_base->app_config->Resolve(AppConfig::BoolID::kDisableCameraShake);
set_camera_shake_disabled(disable_camera_shake);
@ -218,6 +143,66 @@ void Graphics::RunCleanFrameCommands() {
clean_frame_commands_.clear();
}
auto Graphics::TextureQualityFromAppConfig() -> TextureQualityRequest {
// Texture quality.
TextureQualityRequest texture_quality_requested;
std::string texqualstr =
g_base->app_config->Resolve(AppConfig::StringID::kTextureQuality);
if (texqualstr == "Auto") {
texture_quality_requested = TextureQualityRequest::kAuto;
} else if (texqualstr == "High") {
texture_quality_requested = TextureQualityRequest::kHigh;
} else if (texqualstr == "Medium") {
texture_quality_requested = TextureQualityRequest::kMedium;
} else if (texqualstr == "Low") {
texture_quality_requested = TextureQualityRequest::kLow;
} else {
Log(LogLevel::kError,
"Invalid texture quality: '" + texqualstr + "'; defaulting to low.");
texture_quality_requested = TextureQualityRequest::kLow;
}
return texture_quality_requested;
}
auto Graphics::VSyncFromAppConfig() -> VSyncRequest {
std::string v_sync =
g_base->app_config->Resolve(AppConfig::StringID::kVerticalSync);
if (v_sync == "Auto") {
return VSyncRequest::kAuto;
} else if (v_sync == "Always") {
return VSyncRequest::kAuto;
} else if (v_sync == "Never") {
return VSyncRequest::kNever;
}
Log(LogLevel::kError, "Invalid 'Vertical Sync' value: '" + v_sync + "'");
return VSyncRequest::kNever;
}
auto Graphics::GraphicsQualityFromAppConfig() -> GraphicsQualityRequest {
// Graphics quality.
std::string gqualstr =
g_base->app_config->Resolve(AppConfig::StringID::kGraphicsQuality);
GraphicsQualityRequest graphics_quality_requested;
if (gqualstr == "Auto") {
graphics_quality_requested = GraphicsQualityRequest::kAuto;
} else if (gqualstr == "Higher") {
graphics_quality_requested = GraphicsQualityRequest::kHigher;
} else if (gqualstr == "High") {
graphics_quality_requested = GraphicsQualityRequest::kHigh;
} else if (gqualstr == "Medium") {
graphics_quality_requested = GraphicsQualityRequest::kMedium;
} else if (gqualstr == "Low") {
graphics_quality_requested = GraphicsQualityRequest::kLow;
} else {
Log(LogLevel::kError,
"Invalid graphics quality: '" + gqualstr + "'; defaulting to auto.");
graphics_quality_requested = GraphicsQualityRequest::kAuto;
}
return graphics_quality_requested;
}
void Graphics::SetGyroEnabled(bool enable) {
// If we're turning back on, suppress gyro updates for a bit.
if (enable && !gyro_enabled_) {
@ -1452,9 +1437,9 @@ void Graphics::DrawCursor(FrameDef* frame_def) {
|| real_time - last_cursor_visibility_event_time_ > 2000) {
hardware_cursor_visible_ = new_cursor_visibility;
last_cursor_visibility_event_time_ = real_time;
g_core->main_event_loop()->PushCall([this] {
g_base->app_adapter->PushMainThreadCall([this] {
assert(g_core && g_core->InMainThread());
g_core->platform->SetHardwareCursorVisible(hardware_cursor_visible_);
g_base->platform->SetHardwareCursorVisible(hardware_cursor_visible_);
});
}
} else {
@ -1899,6 +1884,10 @@ void Graphics::SetScreenSize(float virtual_width, float virtual_height,
// Need to rebuild internal components (some are sized to the screen).
internal_components_inited_ = false;
// This will inform all applicable logic thread subsystems.
g_base->logic->OnScreenSizeChange(virtual_width, virtual_height, pixel_width,
pixel_height);
}
void Graphics::ScreenMessageEntry::UpdateTranslation() {

View File

@ -50,8 +50,8 @@ const float kCursorZDepth{0.9f};
// Client class for graphics operations (used from the logic thread).
class Graphics {
public:
Graphics();
virtual ~Graphics();
/// Instantiate the Graphics subclass for the current build.
static auto Create() -> Graphics*;
void OnAppStart();
void OnAppPause();
@ -61,10 +61,16 @@ class Graphics {
void OnScreenSizeChange();
void DoApplyAppConfig();
/// Called by the graphics server to keep us up to date in the logic
/// thread. Dispatches the news to all logic subsystems that care.
void SetScreenSize(float virtual_width, float virtual_height,
float physical_width, float physical_height);
void StepDisplayTime();
auto TextureQualityFromAppConfig() -> TextureQualityRequest;
auto GraphicsQualityFromAppConfig() -> GraphicsQualityRequest;
auto VSyncFromAppConfig() -> VSyncRequest;
static auto IsShaderTransparent(ShadingType c) -> bool;
static auto CubeMapFromReflectionType(ReflectionType reflection_type)
-> SysCubeMapTextureID;
@ -265,11 +271,13 @@ class Graphics {
}
return y * (res_y_virtual_ / res_y_);
}
// FIXME: This should probably move to Renderer or AppAdapter once we
// support switching renderers.
auto supports_high_quality_graphics() const -> bool {
assert(has_supports_high_quality_graphics_value_);
return supports_high_quality_graphics_;
}
void SetSupportsHighQualityGraphics(bool s);
auto has_supports_high_quality_graphics_value() const -> bool {
return has_supports_high_quality_graphics_value_;
@ -319,6 +327,8 @@ class Graphics {
}
protected:
Graphics();
virtual ~Graphics();
virtual void DoDrawFade(FrameDef* frame_def, float amt);
private:

View File

@ -2,50 +2,35 @@
#include "ballistica/base/graphics/graphics_server.h"
// Kill this.
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/logic/logic.h"
#include "ballistica/shared/foundation/event_loop.h"
// FIXME: clear out this conditional stuff.
#if BA_SDL_BUILD
#include "ballistica/base/app_adapter/app_adapter_sdl.h"
#else
#include "ballistica/base/app_adapter/app_adapter.h"
#endif
#include "ballistica/base/assets/assets.h"
#include "ballistica/base/graphics/mesh/mesh_data.h"
#include "ballistica/base/graphics/renderer/renderer.h"
#include "ballistica/base/graphics/support/frame_def.h"
#include "ballistica/base/logic/logic.h"
#include "ballistica/core/platform/core_platform.h"
#endif
#include "ballistica/shared/foundation/event_loop.h"
namespace ballistica::base {
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_XCODE_NEW_PROJECT
void GraphicsServer::FullscreenCheck() {
if (!fullscreen_enabled()) {
#if BA_ENABLE_OPENGL
SDL_WM_ToggleFullScreen(gl_context_->sdl_screen_surface());
#endif
}
}
#endif
GraphicsServer::GraphicsServer() : event_loop_(g_core->main_event_loop()) {}
GraphicsServer::~GraphicsServer() { assert(g_base->graphics); }
GraphicsServer::GraphicsServer() {}
GraphicsServer::~GraphicsServer() = default;
void GraphicsServer::SetRenderHold() {
assert(g_base->InGraphicsThread());
render_hold_++;
}
void GraphicsServer::OnMainThreadStartApp() {
// For janky old non-event-push mode, just fall back on a timer for rendering.
if (!g_core->platform->IsEventPushMode()) {
render_timer_ = this->event_loop()->NewTimer(
1000 / 60, true, NewLambdaRunnable([this] { TryRender(); }));
}
}
void GraphicsServer::OnMainThreadStartApp() {}
void GraphicsServer::EnqueueFrameDef(FrameDef* framedef) {
// Note: we're just setting the framedef directly here even though this
@ -54,13 +39,42 @@ void GraphicsServer::EnqueueFrameDef(FrameDef* framedef) {
// for new frames to appear which would prevent that from working; we
// would need to change that code.
{
std::scoped_lock llock(frame_def_mutex_);
std::scoped_lock frame_def_lock(frame_def_mutex_);
assert(frame_def_ == nullptr);
frame_def_ = framedef;
}
}
auto GraphicsServer::GetRenderFrameDef() -> FrameDef* {
auto GraphicsServer::TryRender() -> bool {
assert(g_base->InGraphicsThread());
bool success{};
if (FrameDef* frame_def = WaitForRenderFrameDef_()) {
// Apply settings such as tv-mode that were passed along via the
// frame-def.
ApplyFrameDefSettings(frame_def);
// Note: we run mesh-updates on each frame-def that comes through even
// if we don't actually render the frame.
RunFrameDefMeshUpdates(frame_def);
// Only actually render if we have a screen and aren't in a hold.
auto target = renderer()->screen_render_target();
if (target != nullptr && render_hold_ == 0) {
PreprocessRenderFrameDef(frame_def);
DrawRenderFrameDef(frame_def);
FinishRenderFrameDef(frame_def);
success = true;
}
// Send this frame_def back to the logic thread for deletion or recycling.
g_base->graphics->ReturnCompletedFrameDef(frame_def);
}
return success;
}
auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
assert(g_base->InGraphicsThread());
millisecs_t app_time = g_core->GetAppTimeMillisecs();
@ -68,8 +82,7 @@ auto GraphicsServer::GetRenderFrameDef() -> FrameDef* {
return nullptr;
}
// If the app says it's minimized, don't do anything (on iOS we'll get
// shut down if we make GL calls in this state, etc).
// If the app is paused, never render.
if (g_base->app_adapter->app_paused()) {
return nullptr;
}
@ -95,11 +108,10 @@ auto GraphicsServer::GetRenderFrameDef() -> FrameDef* {
return frame_def;
}
// If there's no frame_def for us, sleep for a bit and wait for it.
// But if we've been waiting for too long, give up.
// On some platforms such as Android, this frame will still get flipped
// whether we draw in it or not, so we *really* want to not skip drawing
// if we can help it.
// If there's no frame_def for us, sleep for a bit and wait for it. But
// if we've been waiting for too long, give up. On some platforms such
// as Android, this frame will still get flipped whether we draw in it
// or not, so we *really* want to not skip drawing if we can help it.
millisecs_t t = g_core->GetAppTimeMillisecs() - app_time;
if (t >= 1000) {
if (g_buildconfig.debug_build()) {
@ -129,6 +141,7 @@ void GraphicsServer::RunFrameDefMeshUpdates(FrameDef* frame_def) {
i->iterator_ = mesh_datas_.insert(mesh_datas_.end(), i);
i->Load(renderer_);
}
for (auto&& i : frame_def->mesh_data_destroys()) {
assert(i != nullptr);
i->Unload(renderer_);
@ -161,40 +174,11 @@ void GraphicsServer::FinishRenderFrameDef(FrameDef* frame_def) {
assert(renderer_);
if (renderer_) {
renderer_->FinishFrameDef(frame_def);
// Let the app know a frame render is complete (it may need to do a
// swap/etc).
g_base->app_adapter->DidFinishRenderingFrame(frame_def);
}
}
void GraphicsServer::TryRender() {
assert(g_base->InGraphicsThread());
if (FrameDef* frame_def = GetRenderFrameDef()) {
// Apply settings such as tv-mode that were passed along on the
// frame-def.
ApplyFrameDefSettings(frame_def);
// Note: we run mesh-updates on each frame-def that comes through even
// if we don't actually render the frame.
RunFrameDefMeshUpdates(frame_def);
// Only actually render if we have a screen and aren't in a hold.
auto target = renderer()->screen_render_target();
if (target != nullptr && render_hold_ == 0) {
PreprocessRenderFrameDef(frame_def);
DrawRenderFrameDef(frame_def);
FinishRenderFrameDef(frame_def);
}
// Send this frame_def back to the logic thread for deletion or recycling.
g_base->graphics->ReturnCompletedFrameDef(frame_def);
}
}
// Reload all media (for debugging/benchmarking purposes).
void GraphicsServer::ReloadMedia() {
void GraphicsServer::ReloadMedia_() {
assert(g_base->InGraphicsThread());
// Immediately unload all renderer data here in this thread.
@ -210,8 +194,8 @@ void GraphicsServer::ReloadMedia() {
SetRenderHold();
// Now tell the logic thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs.
// progress bar drawing, and then tell the graphics thread to stop
// ignoring frame-defs.
g_base->logic->event_loop()->PushCall([this] {
g_base->assets->MarkAllAssetsForLoad();
g_base->graphics->EnableProgressBar(false);
@ -219,12 +203,12 @@ void GraphicsServer::ReloadMedia() {
});
}
// Call when renderer context has been lost.
void GraphicsServer::RebuildLostContext() {
// Call when a renderer context has been lost.
void GraphicsServer::ReloadLostRenderer() {
assert(g_base->InGraphicsThread());
if (!renderer_) {
Log(LogLevel::kError, "No renderer on GraphicsServer::_rebuildContext.");
Log(LogLevel::kError, "No renderer on GraphicsServer::ReloadLostRenderer.");
return;
}
@ -232,7 +216,7 @@ void GraphicsServer::RebuildLostContext() {
// down itself.
set_renderer_context_lost(true);
// Unload all texture and mesh data here in the render thread.
// Unload all texture and mesh data here in the graphics thread.
g_base->assets->UnloadRendererBits(true, true);
// Also unload dynamic meshes.
@ -253,17 +237,17 @@ void GraphicsServer::RebuildLostContext() {
i->Load(renderer_);
}
renderer_->ScreenSizeChanged();
renderer_->OnScreenSizeChange();
// Set a render-hold so we ignore all frame_defs up until the point at which
// we receive the corresponding remove-hold.
// (At which point subsequent frame-defs will be be progress-bar frame_defs so
// we won't hitch if we actually render them.)
// Set a render-hold so we ignore all frame_defs up until the point at
// which we receive the corresponding remove-hold. (At which point
// subsequent frame-defs will be be progress-bar frame_defs so we won't
// hitch if we actually render them.)
SetRenderHold();
// Now tell the logic thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs.
// progress bar drawing, and then tell the graphics thread to stop
// ignoring frame-defs.
g_base->logic->event_loop()->PushCall([this] {
g_base->assets->MarkAllAssetsForLoad();
g_base->graphics->EnableProgressBar(false);
@ -271,176 +255,43 @@ void GraphicsServer::RebuildLostContext() {
});
}
void GraphicsServer::SetScreen(
bool fullscreen, int width, int height,
TextureQualityRequest texture_quality_requested,
GraphicsQualityRequest graphics_quality_requested,
const std::string& android_res) {
assert(g_base->InGraphicsThread());
void GraphicsServer::SetNullGraphics() {
// We don't actually make or update a renderer in headless, but we
// still need to set our list of supported textures types/etc. to avoid
// complaints.
std::list<TextureCompressionType> c_types;
SetTextureCompressionTypes(c_types);
graphics_quality_requested_ = GraphicsQualityRequest::kLow;
graphics_quality_ = GraphicsQuality::kLow;
graphics_quality_set_ = true;
texture_quality_requested_ = TextureQualityRequest::kLow;
texture_quality_ = TextureQuality::kLow;
texture_quality_set_ = true;
// If we know what we support, filter request types to those we support.
// (will keep us from rebuilding contexts if request type is flipping between
// different types 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;
}
}
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && BA_SDL_BUILD
bool create_fullscreen_check_timer = false;
#endif
bool do_toggle_fs = false;
bool do_set_existing_fs = false;
if (g_core->HeadlessMode()) {
// We don't actually make or update a renderer in headless, but we
// still need to set our list of supported textures types/etc. to avoid
// complaints.
std::list<TextureCompressionType> c_types;
SetTextureCompressionTypes(c_types);
graphics_quality_requested_ = GraphicsQualityRequest::kLow;
graphics_quality_ = GraphicsQuality::kLow;
graphics_quality_set_ = true;
texture_quality_requested_ = TextureQualityRequest::kLow;
texture_quality_ = TextureQuality::kLow;
texture_quality_set_ = true;
} else {
// OK - starting in SDL2 we never pass in specific resolution requests.
// we request fullscreen-windows for full-screen situations and that's it.
// (otherwise we may wind up with huge windows due to passing in desktop
// resolutions and retina wonkiness)
width = static_cast<int>(kBaseVirtualResX * 0.8f);
height = static_cast<int>(kBaseVirtualResY * 0.8f);
// We should never have to recreate the context after the initial time on
// our modern builds.
bool need_full_context_rebuild = (!renderer_);
bool need_renderer_reload;
// We need a full renderer reload if quality values have changed.
need_renderer_reload =
((texture_quality_requested_ != texture_quality_requested)
|| (graphics_quality_requested_ != graphics_quality_requested)
|| !texture_quality_set() || !graphics_quality_set());
// This stuff requires a full context rebuild.
if (need_full_context_rebuild || need_renderer_reload) {
HandleFullContextScreenRebuild(need_full_context_rebuild, fullscreen,
width, height, graphics_quality_requested,
texture_quality_requested);
// On mac, we let window save/restore handle our fullscreen restoring for
// us. However if document restore is turned off we'll start windowed on
// every launch. So if we're trying to make a fullscreen setup, lets
// check after a short delay to make sure we have it, and run a
// full-screen-toggle ourself if not.
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && BA_SDL_BUILD
if (fullscreen) {
create_fullscreen_check_timer = true;
}
#endif // BA_OSTYPE_MACOS
} else {
// on SDL2 builds we can just set fullscreen on the existing window; no
// need for a context rebuild
#if BA_SDL2_BUILD
do_set_existing_fs = true;
#else
// On our old custom SDL1.2 mac build, fullscreen toggling winds up here.
// this doesn't require a context rebuild either.
if (fullscreen != fullscreen_enabled()) {
do_toggle_fs = true;
}
#endif // BA_SDL2_BUILD
}
HandlePushAndroidRes(android_res);
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && BA_SDL_BUILD
if (create_fullscreen_check_timer) {
event_loop()->NewTimer(1000, false,
NewLambdaRunnable([this] { FullscreenCheck(); }));
}
#endif // BA_OSTYPE_MACOS
HandleFullscreenToggling(do_set_existing_fs, do_toggle_fs, fullscreen);
}
// The first time we complete setting up our screen, we send a message
// back to the logic thread to complete the init process. (they can't start
// loading graphics and things until we have our context set up so we know
// what types of textures to load, etc)
if (!initial_screen_created_) {
initial_screen_created_ = true;
g_base->logic->event_loop()->PushCall(
[] { g_base->logic->OnInitialScreenCreated(); });
}
// Let the logic thread know screen creation is done (or lack thereof).
g_base->logic->event_loop()->PushCall(
[] { g_base->logic->OnGraphicsReady(); });
}
#pragma clang diagnostic push
#pragma ide diagnostic ignored "ConstantConditionsOC"
#pragma ide diagnostic ignored "ConstantParameter"
void GraphicsServer::HandleFullContextScreenRebuild(
bool need_full_context_rebuild, bool fullscreen, int width, int height,
GraphicsQualityRequest graphics_quality_requested,
TextureQualityRequest texture_quality_requested) {
// Unload renderer-specific data (display-lists, internal textures, etc)
if (renderer_) {
// Unload all textures and meshes. These will be reloaded as-needed
// automatically for the new context.
g_base->assets->UnloadRendererBits(true, true);
// Also unload all dynamic meshes.
for (auto&& i : mesh_datas_) {
i->Unload(renderer_);
}
// And all internal renderer stuff.
renderer_->Unload();
}
// Handle screen/context recreation.
if (need_full_context_rebuild) {
// On mac we store the values we *want* separate from those we get.. (so
// we know when our request has changed; not our result).
#if !(BA_OSTYPE_MACOS && BA_XCODE_BUILD)
fullscreen_enabled_ = fullscreen;
#endif
target_res_x_ = static_cast<float>(width);
target_res_y_ = static_cast<float>(height);
#if BA_ENABLE_OPENGL
gl_context_ = std::make_unique<GLContext>(width, height, fullscreen);
res_x_ = static_cast<float>(gl_context_->res_x());
res_y_ = static_cast<float>(gl_context_->res_y());
#endif
UpdateVirtualScreenRes();
// Inform graphics client and logic thread subsystems of the change.
g_base->logic->event_loop()->PushCall(
[vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] {
g_base->graphics->SetScreenSize(vx, vy, x, y);
g_base->logic->OnScreenSizeChange(vx, vy, x, y);
});
}
void GraphicsServer::set_renderer(Renderer* renderer) {
assert(g_base->InGraphicsThread());
assert(!renderer_loaded_);
assert(!renderer_);
renderer_ = renderer;
}
void GraphicsServer::LoadRenderer() {
assert(g_base->InGraphicsThread());
if (!renderer_) {
#if BA_ENABLE_OPENGL
renderer_ = new RendererGL();
#endif
Log(LogLevel::kError, "LoadRenderer() called with no renderer present.");
return;
}
if (renderer_loaded_) {
Log(LogLevel::kError,
"LoadRenderer() called with an already-loaded renderer present.");
return;
}
// Make sure we've done this first so we can properly set auto values and
// whatnot.
renderer_->CheckCapabilities();
// Update graphics quality based on request.
graphics_quality_requested_ = graphics_quality_requested;
switch (graphics_quality_requested_) {
case GraphicsQualityRequest::kLow:
graphics_quality_ = GraphicsQuality::kLow;
@ -474,7 +325,6 @@ void GraphicsServer::HandleFullContextScreenRebuild(
graphics_quality_set_ = true;
// Update texture quality based on request.
texture_quality_requested_ = texture_quality_requested;
switch (texture_quality_requested_) {
case TextureQualityRequest::kLow:
texture_quality_ = TextureQuality::kLow;
@ -503,18 +353,20 @@ void GraphicsServer::HandleFullContextScreenRebuild(
for (auto&& i : mesh_datas_) {
i->Load(renderer_);
}
renderer_->ScreenSizeChanged();
renderer_->OnScreenSizeChange();
renderer_->PostLoad();
// Set a render-hold so we ignore all frame_defs up until the point at which
// we receive the corresponding remove-hold.
// (At which point subsequent frame-defs will be be progress-bar frame_defs
// so we won't hitch if we actually render them.)
renderer_loaded_ = true;
// Set a render-hold so we ignore all frame_defs up until the point at
// which we receive the corresponding remove-hold. (At which point
// subsequent frame-defs will be be progress-bar frame_defs so we won't
// hitch if we actually render them.)
SetRenderHold();
// Now tell the logic thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs.
// progress bar drawing, and then tell the graphics thread to stop
// ignoring frame-defs.
g_base->logic->event_loop()->PushCall([this] {
g_base->assets->MarkAllAssetsForLoad();
g_base->graphics->set_internal_components_inited(false);
@ -523,24 +375,50 @@ void GraphicsServer::HandleFullContextScreenRebuild(
});
}
#pragma clang diagnostic pop
void GraphicsServer::UnloadRenderer() {
assert(g_base->InGraphicsThread());
if (!renderer_) {
Log(LogLevel::kError, "UnloadRenderer() called with no renderer present.");
return;
}
if (!renderer_loaded_) {
Log(LogLevel::kError,
"UnloadRenderer() called with an already unloaded renderer present.");
return;
}
// Unload all textures and meshes. These will be reloaded on-demand for
// the new context.
g_base->assets->UnloadRendererBits(true, true);
// Also unload all dynamic meshes.
for (auto&& i : mesh_datas_) {
i->Unload(renderer_);
}
// And all internal renderer stuff.
renderer_->Unload();
renderer_loaded_ = false;
}
// Given physical res, calculate virtual res.
void GraphicsServer::CalcVirtualRes(float* x, float* y) {
float x_in = (*x);
float y_in = (*y);
if ((*x) / (*y) > static_cast<float>(kBaseVirtualResX)
/ static_cast<float>(kBaseVirtualResY)) {
(*y) = kBaseVirtualResY;
(*x) = (*y) * (x_in / y_in);
void GraphicsServer::CalcVirtualRes_(float* x, float* y) {
float x_in = *x;
float y_in = *y;
if (*x / *y > static_cast<float>(kBaseVirtualResX)
/ static_cast<float>(kBaseVirtualResY)) {
*y = kBaseVirtualResY;
*x = *y * (x_in / y_in);
} else {
*x = kBaseVirtualResX;
*y = (*x) * (y_in / x_in);
*y = *x * (y_in / x_in);
}
}
void GraphicsServer::UpdateVirtualScreenRes() {
void GraphicsServer::UpdateVirtualScreenRes_() {
assert(g_base->InGraphicsThread());
// In vr mode our virtual res is independent of our screen size.
// (since it gets drawn to an overlay)
if (g_core->IsVRMode()) {
@ -549,33 +427,30 @@ void GraphicsServer::UpdateVirtualScreenRes() {
} else {
res_x_virtual_ = res_x_;
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) {
assert(g_base->InGraphicsThread());
if (target_res_x_ == h && target_res_y_ == v) {
// Ignore redundant sets.
if (res_x_ == h && res_y_ == v) {
return;
}
target_res_x_ = h;
target_res_y_ = v;
res_x_ = h;
res_y_ = v;
UpdateVirtualScreenRes();
UpdateVirtualScreenRes_();
// Inform renderer of the change.
if (renderer_) {
renderer_->ScreenSizeChanged();
renderer_->OnScreenSizeChange();
}
// Inform graphics client and logic thread subsystems of the change.
// Inform all logic thread bits of this change.
g_base->logic->event_loop()->PushCall(
[vx = res_x_virtual_, vy = res_y_virtual_, x = res_x_, y = res_y_] {
g_base->graphics->SetScreenSize(vx, vy, x, y);
g_base->logic->OnScreenSizeChange(vx, vy, x, y);
});
}
@ -587,9 +462,10 @@ void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) {
return;
}
// We push android res to the java layer here. We don't actually worry
// about screen-size-changed callbacks and whatnot, since those will happen
// automatically once things actually change. We just want to be sure that
// we have a renderer so we can calc what our auto res should be.
// about screen-size-changed callbacks and whatnot, since those will
// happen automatically once things actually change. We just want to be
// sure that we have a renderer so we can calc what our auto res should
// be.
assert(renderer_);
std::string fin_res;
if (android_res == "Auto") {
@ -601,52 +477,51 @@ void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) {
}
}
#pragma clang diagnostic push
#pragma ide diagnostic ignored "ConstantConditionsOC"
#pragma ide diagnostic ignored "ConstantParameter"
// void GraphicsServer::HandleFullscreenToggling(bool do_set_existing_fs,
// bool do_toggle_fs,
// bool fullscreen) {
// if (do_set_existing_fs) {
// #if BA_SDL2_BUILD
// bool rift_vr_mode = false;
// #if BA_RIFT_BUILD
// if (g_core->IsVRMode()) {
// rift_vr_mode = true;
// }
// #endif // BA_RIFT_BUILD
// if (explicit_bool(!rift_vr_mode)) {
// #if BA_OSTYPE_IOS_TVOS
// set_fullscreen_enabled(true);
void GraphicsServer::HandleFullscreenToggling(bool do_set_existing_fs,
bool do_toggle_fs,
bool fullscreen) {
if (do_set_existing_fs) {
#if BA_SDL2_BUILD
bool rift_vr_mode = false;
#if BA_RIFT_BUILD
if (g_core->IsVRMode()) {
rift_vr_mode = true;
}
#endif // BA_RIFT_BUILD
if (explicit_bool(!rift_vr_mode)) {
#if BA_OSTYPE_IOS_TVOS
set_fullscreen_enabled(true);
// #else // BA_OSTYPE_IOS_TVOS
// auto* app_adapter_sdl = AppAdapterSDL::Get();
// uint32_t fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
// SDL_SetWindowFullscreen(app_adapter_sdl->sdl_window_,
// fullscreen ? fullscreen_flag : 0);
#else // BA_OSTYPE_IOS_TVOS
uint32_t fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
SDL_SetWindowFullscreen(gl_context_->sdl_window(),
fullscreen ? fullscreen_flag : 0);
// Ideally this should be driven by OS events and not just explicitly by
// us (so, for instance, if someone presses fullscreen on mac we'd know
// we've gone into fullscreen). But this works for now.
set_fullscreen_enabled(fullscreen);
#endif // BA_OSTYPE_IOS_TVOS
}
#endif // BA_SDL2_BUILD
} else if (do_toggle_fs) {
// If we're doing a fullscreen-toggle, we need to do it after coming out of
// sync mode (because the toggle triggers sync-mode itself).
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_XCODE_NEW_PROJECT
#if BA_ENABLE_OPENGL
SDL_WM_ToggleFullScreen(gl_context_->sdl_screen_surface());
#endif
#endif // macos && xcode_build
}
}
#pragma clang diagnostic pop
// // Ideally this should be driven by OS events and not just explicitly
// by
// // us (so, for instance, if someone presses fullscreen on mac we'd know
// // we've gone into fullscreen). But this works for now.
// set_fullscreen_enabled(fullscreen);
// #endif // BA_OSTYPE_IOS_TVOS
// }
// #endif // BA_SDL2_BUILD
// } else if (do_toggle_fs) {
// // If we're doing a fullscreen-toggle, we need to do it after coming out
// of
// // sync mode (because the toggle triggers sync-mode itself).
// #if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_XCODE_NEW_PROJECT
// #if BA_ENABLE_OPENGL
// SDL_WM_ToggleFullScreen(gl_context_->sdl_screen_surface());
// #endif
// #endif // macos && xcode_build
// }
// }
void GraphicsServer::SetTextureCompressionTypes(
const std::list<TextureCompressionType>& types) {
texture_compression_types_ = 0; // Reset.
assert(g_base->InGraphicsThread());
texture_compression_types_ = 0;
for (auto&& i : types) {
texture_compression_types_ |= (0x01u << (static_cast<uint32_t>(i)));
}
@ -656,6 +531,7 @@ void GraphicsServer::SetTextureCompressionTypes(
void GraphicsServer::SetOrthoProjection(float left, float right, float bottom,
float top, float nearval,
float farval) {
assert(g_base->InGraphicsThread());
float tx = -((right + left) / (right - left));
float ty = -((top + bottom) / (top - bottom));
float tz = -((farval + nearval) / (farval - nearval));
@ -695,26 +571,25 @@ void GraphicsServer::SetCamera(const Vector3f& eye, const Vector3f& target,
auto side = Vector3f::Cross(forward, up_vector).Normalized();
Vector3f up = Vector3f::Cross(side, forward);
//------------------
model_view_matrix_.m[0] = side.x;
model_view_matrix_.m[4] = side.y;
model_view_matrix_.m[8] = side.z;
model_view_matrix_.m[12] = 0.0f;
//------------------
model_view_matrix_.m[1] = up.x;
model_view_matrix_.m[5] = up.y;
model_view_matrix_.m[9] = up.z;
model_view_matrix_.m[13] = 0.0f;
//------------------
model_view_matrix_.m[2] = -forward.x;
model_view_matrix_.m[6] = -forward.y;
model_view_matrix_.m[10] = -forward.z;
model_view_matrix_.m[14] = 0.0f;
//------------------
model_view_matrix_.m[3] = model_view_matrix_.m[7] = model_view_matrix_.m[11] =
0.0f;
model_view_matrix_.m[15] = 1.0f;
//------------------
model_view_matrix_ =
Matrix44fTranslate(-eye.x, -eye.y, -eye.z) * model_view_matrix_;
view_world_matrix_ = model_view_matrix_.Inverse();
@ -728,10 +603,7 @@ void GraphicsServer::SetCamera(const Vector3f& eye, const Vector3f& target,
cam_orient_matrix_dirty_ = true;
}
#pragma clang diagnostic push
#pragma ide diagnostic ignored "ConstantConditionsOC"
void GraphicsServer::UpdateCamOrientMatrix() {
void GraphicsServer::UpdateCamOrientMatrix_() {
assert(g_base->InGraphicsThread());
if (cam_orient_matrix_dirty_) {
cam_orient_matrix_ = kMatrix44fIdentity;
@ -758,37 +630,12 @@ void GraphicsServer::UpdateCamOrientMatrix() {
}
}
#pragma clang diagnostic pop
#pragma mark PushCalls
void GraphicsServer::PushSetScreenCall(
bool fullscreen, int width, int height,
TextureQualityRequest texture_quality_request,
GraphicsQualityRequest graphics_quality_request,
const std::string& android_res) {
event_loop()->PushCall([=] {
SetScreen(fullscreen, width, height, texture_quality_request,
graphics_quality_request, android_res);
});
}
void GraphicsServer::PushReloadMediaCall() {
event_loop()->PushCall([this] { ReloadMedia(); });
}
void GraphicsServer::PushSetScreenGammaCall(float gamma) {
event_loop()->PushCall([this, gamma] {
assert(g_base->InGraphicsThread());
if (!renderer_) {
return;
}
renderer_->set_screen_gamma(gamma);
});
g_base->app_adapter->PushMainThreadCall([this] { ReloadMedia_(); });
}
void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
event_loop()->PushCall([this, pixel_scale] {
g_base->app_adapter->PushMainThreadCall([this, pixel_scale] {
assert(g_base->InGraphicsThread());
if (!renderer_) {
return;
@ -797,38 +644,10 @@ void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
});
}
void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) {
event_loop()->PushCall([this, sync, auto_sync] {
assert(g_base->InGraphicsThread());
#if BA_SDL_BUILD
// Currently only supported for AppAdapterSDL.
// May want to revisit this later.
if (g_buildconfig.sdl_build()) {
// Even if we were built with SDL, we may not be running in sdl-app-mode
// (for instance, Rift in VR mode). Only do this if we're an sdl app.
if (auto app = dynamic_cast<AppAdapterSDL*>(g_base->app_adapter)) {
v_sync_ = sync;
auto_vsync_ = auto_sync;
if (gl_context_) {
app->SetAutoVSync(auto_vsync_);
// Set it directly if not auto...
if (!auto_vsync_) {
gl_context_->SetVSync(v_sync_);
}
} else {
Log(LogLevel::kError, "Got SetVSyncCall with no gl context_ref.");
}
}
}
#endif // BA_HEADLESS_BUILD
});
}
void GraphicsServer::PushComponentUnloadCall(
const std::vector<Object::Ref<Asset>*>& components) {
event_loop()->PushCall([components] {
g_base->app_adapter->PushMainThreadCall([components] {
assert(g_base->InGraphicsThread());
// Unload the components.
for (auto&& i : components) {
(**i).Unload();
@ -843,7 +662,8 @@ void GraphicsServer::PushComponentUnloadCall(
}
void GraphicsServer::PushRemoveRenderHoldCall() {
event_loop()->PushCall([this] {
g_base->app_adapter->PushMainThreadCall([this] {
assert(g_base->InGraphicsThread());
assert(render_hold_);
render_hold_--;
if (render_hold_ < 0) {

View File

@ -15,26 +15,58 @@
namespace ballistica::base {
/// A server that runs in the graphics thread and renders frame_defs shipped
/// to it by the logic thread.
/// A mechanism used by the AppAdapter to render frame-defs shipped from the
/// logic thread. This may happen in the main thread or in other dedicated
/// thread(s) depending on the AppAdapter and environment.
class GraphicsServer {
public:
GraphicsServer();
~GraphicsServer();
void OnMainThreadStartApp();
/// Should be called to inform ballistica of screen size changes; this
/// will be applied to the server and then sent to the logic thread to
/// apply to various app systems (ui, etc.).
/// The current renderer.
auto renderer() const { return renderer_; }
/// Assign a renderer.
void set_renderer(Renderer* renderer);
/// Load the current renderer. This will lock in various things such as
/// quality settings and will allow renderer-specific forms of assets and
/// other components to be created.
void LoadRenderer();
/// Unload the current renderer. Destroys all renderer-specific forms of
/// assets and other components.
void UnloadRenderer();
/// Call this if a renderer's context has been lost. This is basically
/// an UnloadRenderer() followed by a LoadRenderer() except that the
/// renderer is not asked to delete components during the unload.
void ReloadLostRenderer();
/// Return whether the current renderer is loaded.
auto renderer_loaded() const {
assert(renderer_);
return renderer_loaded_;
}
/// The AppAdapter should call this to inform the engine of screen size
/// changes. Changes will be applied to the server and then sent to the
/// logic thread to apply to various app systems (ui, etc.).
void SetScreenResolution(float h, float v);
void PushSetScreenGammaCall(float gamma);
/// Used by headless builds to init the graphics-server into a
/// non-functional state.
void SetNullGraphics();
// void PushSetScreenGammaCall(float gamma);
void PushSetScreenPixelScaleCall(float pixel_scale);
void PushSetVSyncCall(bool sync, bool auto_sync);
void PushSetScreenCall(bool fullscreen, int width, int height,
TextureQualityRequest texture_quality_request,
GraphicsQualityRequest graphics_quality_request,
const std::string& android_res);
// void PushSetVSyncCall(bool sync, bool auto_sync);
// void PushSetScreenCall(bool fullscreen, int width, int height,
// TextureQualityRequest texture_quality_request,
// GraphicsQualityRequest graphics_quality_request,
// const std::string& android_res);
void PushReloadMediaCall();
void PushRemoveRenderHoldCall();
void PushComponentUnloadCall(
@ -45,17 +77,11 @@ class GraphicsServer {
/// rendering.
void EnqueueFrameDef(FrameDef* framedef);
// Returns the next frame_def to be rendered, waiting for it to arrive if
// necessary. this can return nullptr if no frame_defs come in within a
// reasonable amount of time. a frame_def here *must* be rendered and
// disposed of using the RenderFrameDef* calls.
auto GetRenderFrameDef() -> FrameDef*;
void ApplyFrameDefSettings(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.
void PreprocessRenderFrameDef(FrameDef* frame_def);
// Does the default drawing to the screen, either from the left or right
@ -65,11 +91,11 @@ class GraphicsServer {
// Clean up the frame_def once done drawing it.
void FinishRenderFrameDef(FrameDef* frame_def);
// Equivalent to calling GetRenderFrameDef() and then preprocess, draw (in
// mono), and finish.
void TryRender();
// Attempts to wait for a frame-def to come in and render it.
// Returns true if a frame was rendered.
auto TryRender() -> bool;
// init the modelview matrix to look here
// Init the modelview matrix to look here.
void SetCamera(const Vector3f& eye, const Vector3f& target,
const Vector3f& up);
@ -109,42 +135,45 @@ class GraphicsServer {
return light_shadow_projection_matrix_;
}
// Returns the modelview * projection matrix.
// Return the modelview * projection matrix.
auto GetModelViewProjectionMatrix() -> const Matrix44f& {
UpdateModelViewProjectionMatrix();
UpdateModelViewProjectionMatrix_();
return model_view_projection_matrix_;
}
auto GetModelViewProjectionMatrixState() -> uint32_t {
UpdateModelViewProjectionMatrix();
UpdateModelViewProjectionMatrix_();
return model_view_projection_matrix_state_;
}
auto GetModelWorldMatrix() -> const Matrix44f& {
UpdateModelWorldMatrix();
UpdateModelWorldMatrix_();
return model_world_matrix_;
}
auto GetModelWorldMatrixState() -> uint32_t {
UpdateModelWorldMatrix();
UpdateModelWorldMatrix_();
return model_world_matrix_state_;
}
auto cam_pos() -> const Vector3f& { return cam_pos_; }
auto cam_pos_state() -> uint32_t { return cam_pos_state_; }
auto GetCamOrientMatrix() -> const Matrix44f& {
UpdateCamOrientMatrix();
UpdateCamOrientMatrix_();
return cam_orient_matrix_;
}
auto GetCamOrientMatrixState() -> uint32_t {
UpdateCamOrientMatrix();
UpdateCamOrientMatrix_();
return cam_orient_matrix_state_;
}
auto model_view_matrix() const -> const Matrix44f& {
return model_view_matrix_;
}
void SetModelViewMatrix(const Matrix44f& m) {
model_view_matrix_ = m;
model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true;
@ -153,6 +182,7 @@ class GraphicsServer {
auto projection_matrix() const -> const Matrix44f& {
return projection_matrix_;
}
void PushTransform() {
model_view_stack_.push_back(model_view_matrix_);
assert(model_view_stack_.size() < 20);
@ -185,10 +215,6 @@ class GraphicsServer {
model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true;
}
void RebuildLostContext();
~GraphicsServer();
auto renderer() { return renderer_; }
auto quality() const -> GraphicsQuality {
assert(graphics_quality_set_);
return graphics_quality_;
@ -203,6 +229,7 @@ class GraphicsServer {
assert(g_base->InGraphicsThread());
return res_x_;
}
auto screen_pixel_height() const -> float {
assert(g_base->InGraphicsThread());
return res_y_;
@ -212,16 +239,19 @@ class GraphicsServer {
assert(g_base->InGraphicsThread());
return res_x_virtual_;
}
auto screen_virtual_height() const -> float {
assert(g_base->InGraphicsThread());
return res_y_virtual_;
}
auto tv_border() const {
assert(g_base->InGraphicsThread());
return tv_border_;
}
auto graphics_quality_set() const { return graphics_quality_set_; }
auto texture_quality_set() const { return texture_quality_set_; }
auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool {
@ -235,87 +265,90 @@ class GraphicsServer {
auto texture_compression_types_are_set() const {
return texture_compression_types_set_;
}
auto 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 fullscreen_enabled() const { return fullscreen_enabled_; }
// auto fullscreen_enabled() const { return fullscreen_enabled_; }
// This doesn't actually toggle fullscreen. It is used to inform the game
// when fullscreen changes under it.
auto set_fullscreen_enabled(bool fs) { fullscreen_enabled_ = fs; }
// auto set_fullscreen_enabled(bool fs) { fullscreen_enabled_ = fs; }
#if BA_ENABLE_OPENGL
auto gl_context() const -> GLContext* { return gl_context_.get(); }
#endif
// #if BA_ENABLE_OPENGL
// auto gl_context() const -> GLContext* { return gl_context_.get(); }
// #endif
auto graphics_quality_requested() const {
return graphics_quality_requested_;
}
void set_graphics_quality_requested(GraphicsQualityRequest val) {
graphics_quality_requested_ = val;
}
void set_texture_quality_requested(TextureQualityRequest val) {
texture_quality_requested_ = val;
}
auto graphics_quality() const { return graphics_quality_; }
auto texture_quality_requested() const { return texture_quality_requested_; }
auto renderer() const { return renderer_; }
auto initial_screen_created() const { return initial_screen_created_; }
auto event_loop() const -> EventLoop* { return event_loop_; }
// auto initial_screen_created() const { return initial_screen_created_; }
void HandlePushAndroidRes(const std::string& android_res);
// void HandleFullContextScreenRebuild(
// bool need_full_context_rebuild, bool fullscreen,
// GraphicsQualityRequest graphics_quality_requested,
// TextureQualityRequest texture_quality_requested);
// void HandleFullscreenToggling(bool do_set_existing_fs, bool do_toggle_fs,
// bool fullscreen);
private:
void HandleFullscreenToggling(bool do_set_existing_fs, bool do_toggle_fs,
bool fullscreen);
void HandlePushAndroidRes(const std::string& android_res);
void HandleFullContextScreenRebuild(
bool need_full_context_rebuild, bool fullscreen, int width, int height,
GraphicsQualityRequest graphics_quality_requested,
TextureQualityRequest texture_quality_requested);
// Return the next frame_def to be rendered, waiting for it to arrive if
// necessary. this can return nullptr if no frame_defs come in within a
// reasonable amount of time. a frame_def here *must* be rendered and
// disposed of using the RenderFrameDef* calls.
auto WaitForRenderFrameDef_() -> FrameDef*;
// Update virtual screen dimensions based on the current physical ones.
static void CalcVirtualRes(float* x, float* y);
void UpdateVirtualScreenRes();
void UpdateCamOrientMatrix();
void ReloadMedia();
void UpdateModelViewProjectionMatrix() {
static void CalcVirtualRes_(float* x, float* y);
void UpdateVirtualScreenRes_();
void UpdateCamOrientMatrix_();
void ReloadMedia_();
void UpdateModelViewProjectionMatrix_() {
if (model_view_projection_matrix_dirty_) {
model_view_projection_matrix_ = model_view_matrix_ * projection_matrix_;
model_view_projection_matrix_state_++;
model_view_projection_matrix_dirty_ = false;
}
}
void UpdateModelWorldMatrix() {
void UpdateModelWorldMatrix_() {
if (model_world_matrix_dirty_) {
model_world_matrix_ = model_view_matrix_ * view_world_matrix_;
model_world_matrix_state_++;
model_world_matrix_dirty_ = false;
}
}
void SetScreen(bool fullscreen, int width, int height,
TextureQualityRequest texture_quality_request,
GraphicsQualityRequest graphics_quality_request,
const std::string& android_res);
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD
void FullscreenCheck();
#endif
#if BA_ENABLE_OPENGL
std::unique_ptr<GLContext> gl_context_;
#endif
EventLoop* event_loop_{};
float res_x_{};
float res_y_{};
float res_x_virtual_{};
float res_y_virtual_{};
bool tv_border_{};
bool renderer_context_lost_{};
uint32_t texture_compression_types_{};
bool texture_compression_types_set_{};
TextureQualityRequest texture_quality_requested_{
TextureQualityRequest::kUnset};
TextureQuality texture_quality_{TextureQuality::kLow};
GraphicsQualityRequest graphics_quality_requested_{
GraphicsQualityRequest::kUnset};
GraphicsQuality graphics_quality_{GraphicsQuality::kUnset};
bool graphics_quality_set_{};
bool texture_quality_set_{};
bool fullscreen_enabled_{};
float target_res_x_{800.0f};
float target_res_y_{600.0f};
// bool fullscreen_enabled_{};
// float target_res_x_{800.0f};
// float target_res_y_{600.0f};
Matrix44f model_view_matrix_{kMatrix44fIdentity};
Matrix44f view_world_matrix_{kMatrix44fIdentity};
Matrix44f projection_matrix_{kMatrix44fIdentity};
@ -325,8 +358,6 @@ class GraphicsServer {
uint32_t projection_matrix_state_{1};
uint32_t model_view_projection_matrix_state_{1};
uint32_t model_world_matrix_state_{1};
bool model_view_projection_matrix_dirty_{true};
bool model_world_matrix_dirty_{true};
Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity};
uint32_t light_shadow_projection_matrix_state_{1};
Vector3f cam_pos_{0.0f, 0.0f, 0.0f};
@ -334,14 +365,22 @@ class GraphicsServer {
uint32_t cam_pos_state_{1};
Matrix44f cam_orient_matrix_ = kMatrix44fIdentity;
uint32_t cam_orient_matrix_state_{1};
bool cam_orient_matrix_dirty_{true};
std::list<MeshData*> mesh_datas_;
bool v_sync_{};
bool auto_vsync_{};
Timer* render_timer_{};
Renderer* renderer_{};
FrameDef* frame_def_{};
bool initial_screen_created_{};
// bool initial_screen_created_{};
bool renderer_loaded_{};
bool v_sync_{};
bool auto_vsync_{};
bool model_view_projection_matrix_dirty_{true};
bool model_world_matrix_dirty_{true};
bool graphics_quality_set_{};
bool texture_quality_set_{};
bool tv_border_{};
bool renderer_context_lost_{};
bool texture_compression_types_set_{};
bool cam_orient_matrix_dirty_{true};
int render_hold_{};
std::mutex frame_def_mutex_{};
};

View File

@ -63,20 +63,21 @@ void GraphicsVR::DoDrawFade(FrameDef* frame_def, float amt) {
Vector3f side = Vector3f::Cross(diff, Vector3f(0.0f, 1.0f, 0.0f));
Vector3f up = Vector3f::Cross(diff, side);
c.SetColor(0, 0, 0);
c.PushTransform();
// We start in vr-overlay screen space; get back to world.
c.Translate(cam_pt.x, cam_pt.y, cam_pt.z);
c.MultMatrix(Matrix44fOrient(diff, up).m);
// At the very end we stay turned around so we get 100% black.
if (amt < 0.98f) {
c.Translate(0, 0, 40.0f * amt);
c.Rotate(180, 1, 0, 0);
{
auto xf = c.ScopedTransform();
// We start in vr-overlay screen space; get back to world.
c.Translate(cam_pt.x, cam_pt.y, cam_pt.z);
c.MultMatrix(Matrix44fOrient(diff, up).m);
// At the very end we stay turned around so we get 100% black.
if (amt < 0.98f) {
c.Translate(0, 0, 40.0f * amt);
c.Rotate(180, 1, 0, 0);
}
float inv_a = 1.0f - amt;
float s = 100.0f * inv_a + 5.0f * amt;
c.Scale(s, s, s);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kVRFade));
}
float inv_a = 1.0f - amt;
float s = 100.0f * inv_a + 5.0f * amt;
c.Scale(s, s, s);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kVRFade));
c.PopTransform();
c.Submit();
}
@ -281,13 +282,14 @@ void GraphicsVR::DrawVROverlay(FrameDef* frame_def) {
// Draw our overlay-flat stuff into our overlay pass.
SpecialComponent c(frame_def->overlay_pass(),
SpecialComponent::Source::kVROverlayBuffer);
c.PushTransform();
c.Translate(0.5f * kBaseVirtualResX, 0.5f * kBaseVirtualResY, 0.0f);
c.Scale(kBaseVirtualResX * (1.0f + kVRBorder),
kBaseVirtualResY * (1.0f + kVRBorder),
kBaseVirtualResX * (1.0f + kVRBorder));
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kVROverlay));
c.PopTransform();
{
auto xf = c.ScopedTransform();
c.Translate(0.5f * kBaseVirtualResX, 0.5f * kBaseVirtualResY, 0.0f);
c.Scale(kBaseVirtualResX * (1.0f + kVRBorder),
kBaseVirtualResY * (1.0f + kVRBorder),
kBaseVirtualResX * (1.0f + kVRBorder));
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kVROverlay));
}
c.Submit();
}
}
@ -297,15 +299,16 @@ void GraphicsVR::DrawOverlayBounds(RenderPass* pass) {
if (draw_overlay_bounds_) {
SimpleComponent c(pass);
c.SetColor(1, 0, 0);
c.PushTransform();
float width = screen_virtual_width();
float height = screen_virtual_height();
{
auto xf = c.ScopedTransform();
float width = screen_virtual_width();
float height = screen_virtual_height();
// Slight offset in z to reduce z fighting.
c.Translate(0.5f * width, 0.5f * height, 1.0f);
c.Scale(width, height, 100.0f);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kOverlayGuide));
c.PopTransform();
// Slight offset in z to reduce z fighting.
c.Translate(0.5f * width, 0.5f * height, 1.0f);
c.Scale(width, height, 100.0f);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kOverlayGuide));
}
c.Submit();
}
}
@ -326,12 +329,13 @@ void GraphicsVR::DrawVRControllers(FrameDef* frame_def) {
c.SetTexture(g_base->assets->SysTexture(SysTextureID::kBoxingGlove));
c.SetReflection(ReflectionType::kSoft);
c.SetReflectionScale(0.4f, 0.4f, 0.4f);
c.PushTransform();
c.VRTransformToHead();
c.Translate(0, 0, 5);
c.Scale(2, 2, 2);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBoxingGlove));
c.PopTransform();
{
auto xf = c.ScopedTransform();
c.VRTransformToHead();
c.Translate(0, 0, 5);
c.Scale(2, 2, 2);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBoxingGlove));
}
c.Submit();
}
@ -346,11 +350,12 @@ void GraphicsVR::DrawVRControllers(FrameDef* frame_def) {
c.SetTexture(g_base->assets->SysTexture(SysTextureID::kBoxingGlove));
c.SetReflection(ReflectionType::kSoft);
c.SetReflectionScale(0.4f, 0.4f, 0.4f);
c.PushTransform();
c.VRTransformToRightHand();
c.Scale(10, 10, 10);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBoxingGlove));
c.PopTransform();
{
auto xf = c.ScopedTransform();
c.VRTransformToRightHand();
c.Scale(10, 10, 10);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBoxingGlove));
}
c.Submit();
break;
}
@ -365,11 +370,12 @@ void GraphicsVR::DrawVRControllers(FrameDef* frame_def) {
c.SetTexture(g_base->assets->SysTexture(SysTextureID::kBoxingGlove));
c.SetReflection(ReflectionType::kSoft);
c.SetReflectionScale(0.4f, 0.4f, 0.4f);
c.PushTransform();
c.VRTransformToLeftHand();
c.Scale(10, 10, 10);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBoxingGlove));
c.PopTransform();
{
auto xf = c.ScopedTransform();
c.VRTransformToLeftHand();
c.Scale(10, 10, 10);
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kBoxingGlove));
}
c.Submit();
break;
}

View File

@ -3,12 +3,13 @@
#ifndef BALLISTICA_BASE_GRAPHICS_MESH_MESH_BUFFER_VERTEX_SIMPLE_FULL_H_
#define BALLISTICA_BASE_GRAPHICS_MESH_MESH_BUFFER_VERTEX_SIMPLE_FULL_H_
#include "ballistica/base/base.h"
#include "ballistica/base/graphics/mesh/mesh_buffer.h"
namespace ballistica::base {
// just make this a vanilla child class of our template
// (simply so we could predeclare this)
// Just make this a vanilla child class of our template (simply so we could
// predeclare this).
class MeshBufferVertexSimpleFull : public MeshBuffer<VertexSimpleFull> {
using MeshBuffer::MeshBuffer;
};

View File

@ -3,12 +3,13 @@
#ifndef BALLISTICA_BASE_GRAPHICS_MESH_MESH_BUFFER_VERTEX_SMOKE_FULL_H_
#define BALLISTICA_BASE_GRAPHICS_MESH_MESH_BUFFER_VERTEX_SMOKE_FULL_H_
#include "ballistica/base/base.h"
#include "ballistica/base/graphics/mesh/mesh_buffer.h"
namespace ballistica::base {
// just make this a vanilla child class of our template
// (simply so we could predeclare this)
// Just make this a vanilla child class of our template (simply so we could
// predeclare this).
class MeshBufferVertexSmokeFull : public MeshBuffer<VertexSmokeFull> {
using MeshBuffer::MeshBuffer;
};

View File

@ -3,12 +3,13 @@
#ifndef BALLISTICA_BASE_GRAPHICS_MESH_MESH_BUFFER_VERTEX_SPRITE_H_
#define BALLISTICA_BASE_GRAPHICS_MESH_MESH_BUFFER_VERTEX_SPRITE_H_
#include "ballistica/base/base.h"
#include "ballistica/base/graphics/mesh/mesh_buffer.h"
namespace ballistica::base {
// just make this a vanilla child class of our template
// (simply so we could predeclare this)
// Just make this a vanilla child class of our template (simply so we could
// predeclare this).
class MeshBufferVertexSprite : public MeshBuffer<VertexSprite> {
using MeshBuffer::MeshBuffer;
};

View File

@ -7,7 +7,7 @@
namespace ballistica::base {
// standard buffer for indices
// Standard buffer for indices.
class MeshIndexBuffer16 : public MeshBuffer<uint16_t> {
using MeshBuffer::MeshBuffer;
};

View File

@ -7,7 +7,7 @@
namespace ballistica::base {
// standard buffer for indices
// Standard buffer for indices.
class MeshIndexBuffer32 : public MeshBuffer<uint32_t> {
using MeshBuffer::MeshBuffer;
};

View File

@ -7,7 +7,7 @@
namespace ballistica::base {
// a simple mesh with all data provided together (either static or dynamic)
// A simple mesh with all data provided together (either static or dynamic).
class MeshIndexedSimpleFull
: public MeshIndexed<VertexSimpleFull, MeshDataType::kIndexedSimpleFull> {
using MeshIndexed::MeshIndexed; // wheee c++11 magic

View File

@ -7,7 +7,7 @@
namespace ballistica::base {
// a mesh with static indices and UVs and dynamic positions
// A mesh with static indices and UVs and dynamic positions.
class MeshIndexedSimpleSplit
: public MeshIndexedStaticDynamic<VertexSimpleSplitStatic,
VertexSimpleSplitDynamic,

View File

@ -7,7 +7,7 @@
namespace ballistica::base {
// a mesh with all data provided together (either static or dynamic)
// A mesh with all data provided together (either static or dynamic).
class MeshIndexedSmokeFull
: public MeshIndexed<VertexSmokeFull, MeshDataType::kIndexedSmokeFull> {
using MeshIndexed::MeshIndexed; // wheee c++11 magic

View File

@ -4,6 +4,7 @@
#define BALLISTICA_BASE_GRAPHICS_MESH_MESH_NON_INDEXED_H_
#include "ballistica/base/graphics/mesh/mesh.h"
#include "ballistica/base/graphics/mesh/mesh_buffer.h"
namespace ballistica::base {

View File

@ -7,7 +7,7 @@
namespace ballistica::base {
// an indexed sprite-mesh
// An indexed sprite-mesh.
class SpriteMesh : public MeshIndexed<VertexSprite, MeshDataType::kSprite> {
using MeshIndexed::MeshIndexed; // wheeee c++11 magic
};

View File

@ -9,10 +9,9 @@
namespace ballistica::base {
// a mesh set up to draw text
// in general you should not use this directly; use TextGroup below, which will
// automatically handle switching meshes/textures in order to support the full
// unicode range
// A mesh set up to draw text. In general you should not use this directly;
// use TextGroup, which will automatically handle switching meshes/textures
// in order to support the full unicode range.
class TextMesh : public MeshIndexedDualTextureFull {
public:
enum class HAlign { kLeft, kCenter, kRight };

View File

@ -432,8 +432,8 @@ void RenderPass::Reset() {
throw Exception();
}
// By default, logical width matches physical width, but for overlay passes
// it can be independent.
// By default, logical width matches physical width, but for overlay
// passes it can be independent.
switch (type()) {
case Type::kOverlayPass:
case Type::kOverlayFrontPass:

View File

@ -12,7 +12,7 @@ RenderTarget::RenderTarget(Type type) : type_(type) {
RenderTarget::~RenderTarget() = default;
void RenderTarget::ScreenSizeChanged() {
void RenderTarget::OnScreenSizeChange() {
assert(type_ == Type::kScreen);
physical_width_ = g_base->graphics_server->screen_pixel_width();
physical_height_ = g_base->graphics_server->screen_pixel_height();

View File

@ -27,7 +27,7 @@ class RenderTarget : public Object {
clear_color.w);
}
void ScreenSizeChanged();
void OnScreenSizeChange();
auto physical_width() const -> float { return physical_width_; }
auto physical_height() const -> float { return physical_height_; }
auto GetScissorScaleX() const -> float;

View File

@ -6,9 +6,9 @@
#include "ballistica/core/core.h"
// FIXME: Clear out conditional stuff.
#if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
#include "ballistica/core/platform/support/min_sdl.h"
#endif
// #if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
// #include "ballistica/core/platform/support/min_sdl.h"
// #endif
#if BA_VR_BUILD
#include "ballistica/base/graphics/graphics_vr.h"
@ -58,9 +58,9 @@ void Renderer::PreprocessFrameDef(FrameDef* frame_def) {
UpdateSizesQualitiesAndColors(frame_def);
// Handle a weird gamma reset issue on our legacy mac build (SDL 1.2).
#if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
HandleFunkyMacGammaIssue(frame_def);
#endif
// #if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
// HandleFunkyMacGammaIssue(frame_def);
// #endif
// In some cases we draw to a lower-res backing buffer instead of native
// screen res.
@ -425,7 +425,7 @@ void Renderer::UpdateSizesQualitiesAndColors(FrameDef* frame_def) {
// If screen-size has changed, handle that.
if (screen_size_dirty_) {
msaa_enabled_dirty_ = true;
screen_render_target()->ScreenSizeChanged();
screen_render_target()->OnScreenSizeChange();
// These render targets are dependent on screen size so they need to be
// remade.
@ -459,7 +459,7 @@ void Renderer::UpdateSizesQualitiesAndColors(FrameDef* frame_def) {
} else {
set_vignette_outer(frame_def->vignette_outer());
}
UpdateVignetteTex(false);
UpdateVignetteTex_(false);
}
void Renderer::UpdateLightAndShadowBuffers(FrameDef* frame_def) {
@ -581,7 +581,7 @@ void Renderer::UpdateCameraRenderTargets(FrameDef* frame_def) {
// If screen size just changed or whatnot,
// update whether we should do msaa.
if (msaa_enabled_dirty_) {
UpdateMSAAEnabled();
UpdateMSAAEnabled_();
msaa_enabled_dirty_ = false;
}
@ -612,7 +612,7 @@ void Renderer::UpdatePixelScaleAndBackingBuffer(FrameDef* frame_def) {
// If our pixel-scale is changing its essentially the same as a resolution
// change, so we wanna rebuild our light/shadow buffers and all that.
if (pixel_scale_requested_ != pixel_scale_) {
ScreenSizeChanged();
OnScreenSizeChange();
}
// Create or destroy our backing render-target as necessary.
@ -658,21 +658,22 @@ void Renderer::LoadMedia(FrameDef* frame_def) {
}
}
#if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
void Renderer::HandleFunkyMacGammaIssue(FrameDef* frame_def) {
// FIXME - for some reason, on mac, gamma is getting switched back to
// default about 1 second after a res change, etc...
// so if we're using a non-1.0 gamma, lets keep setting it periodically
// to force the issue
millisecs_t t = g_core->GetAppTimeMillisecs();
if (screen_gamma_requested_ != screen_gamma_
|| (t - last_screen_gamma_update_time_ > 300 && screen_gamma_ != 1.0f)) {
screen_gamma_ = screen_gamma_requested_;
SDL_SetGamma(screen_gamma_, screen_gamma_, screen_gamma_);
last_screen_gamma_update_time_ = t;
}
}
#endif
// #if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
// void Renderer::HandleFunkyMacGammaIssue(FrameDef* frame_def) {
// // FIXME - for some reason, on mac, gamma is getting switched back to
// // default about 1 second after a res change, etc...
// // so if we're using a non-1.0 gamma, lets keep setting it periodically
// // to force the issue
// millisecs_t t = g_core->GetAppTimeMillisecs();
// if (screen_gamma_requested_ != screen_gamma_
// || (t - last_screen_gamma_update_time_ > 300 && screen_gamma_ != 1.0f))
// {
// screen_gamma_ = screen_gamma_requested_;
// SDL_SetGamma(screen_gamma_, screen_gamma_, screen_gamma_);
// last_screen_gamma_update_time_ = t;
// }
// }
// #endif
void Renderer::DrawWorldToCameraBuffer(FrameDef* frame_def) {
#if BA_CARDBOARD_BUILD
@ -754,7 +755,7 @@ void Renderer::UpdateDOFParams(FrameDef* frame_def) {
}
}
void Renderer::ScreenSizeChanged() {
void Renderer::OnScreenSizeChange() {
assert(g_base->InGraphicsThread());
// We can actually get these events at times when we don't have a valid
@ -763,8 +764,6 @@ void Renderer::ScreenSizeChanged() {
screen_size_dirty_ = true;
}
void Renderer::CheckCapabilities() {}
void Renderer::Unload() {
light_render_target_.Clear();
light_shadow_render_target_.Clear();
@ -777,13 +776,13 @@ void Renderer::Load() {
screen_render_target_ = Object::CompleteDeferred(NewScreenRenderTarget());
// Restore current gamma value.
if (screen_gamma_ != 1.0f) {
#if BA_SDL2_BUILD
// Not supporting gamma in SDL2 currently.
#elif BA_SDL_BUILD
SDL_SetGamma(screen_gamma_, screen_gamma_, screen_gamma_);
#endif
}
// if (screen_gamma_ != 1.0f) {
// #if BA_SDL2_BUILD
// // Not supporting gamma in SDL2 currently.
// #elif BA_SDL_BUILD
// SDL_SetGamma(screen_gamma_, screen_gamma_, screen_gamma_);
// #endif
// }
}
void Renderer::PostLoad() {

View File

@ -82,7 +82,7 @@ class Renderer {
auto light_pitch() const -> float { return light_pitch_; }
auto light_heading() const -> float { return light_heading_; }
void set_pixel_scale(float s) { pixel_scale_requested_ = s; }
void set_screen_gamma(float val) { screen_gamma_requested_ = val; }
// void set_screen_gamma(float val) { screen_gamma_requested_ = val; }
void set_debug_draw_mode(bool debugModeIn) { debug_draw_mode_ = debugModeIn; }
auto debug_draw_mode() -> bool { return debug_draw_mode_; }
@ -90,13 +90,12 @@ class Renderer {
virtual void Unload();
virtual void Load();
virtual void PostLoad();
virtual void CheckCapabilities();
virtual auto GetAutoGraphicsQuality() -> GraphicsQuality = 0;
virtual auto GetAutoTextureQuality() -> TextureQuality = 0;
virtual auto GetAutoAndroidRes() -> std::string;
void ScreenSizeChanged();
void OnScreenSizeChange();
auto has_camera_render_target() const -> bool {
return camera_render_target_.Exists();
}
@ -148,7 +147,7 @@ class Renderer {
float eyeZ, int viewport_x, int viewport_y);
int VRGetViewportX() const { return vr_viewport_x_; }
int VRGetViewportY() const { return vr_viewport_y_; }
#endif // BA_VR_BUILD
#endif
virtual auto NewMeshAssetData(const MeshAsset& mesh)
-> Object::Ref<MeshAssetRendererData> = 0;
@ -166,7 +165,7 @@ class Renderer {
protected:
virtual void DrawDebug() = 0;
virtual void CheckForErrors() = 0;
virtual void UpdateVignetteTex(bool force) = 0;
virtual void UpdateVignetteTex_(bool force) = 0;
virtual void GenerateCameraBufferBlurPasses() = 0;
virtual void UpdateMeshes(
const std::vector<Object::Ref<MeshDataClientHandle>>& meshes,
@ -190,7 +189,7 @@ class Renderer {
bool linear_interpolation, bool force_shader_blit,
bool invalidate_source) = 0;
virtual auto IsMSAAEnabled() const -> bool = 0;
virtual void UpdateMSAAEnabled() = 0;
virtual void UpdateMSAAEnabled_() = 0;
virtual void VREyeRenderBegin() = 0;
virtual void RenderFrameDefEnd() = 0;
virtual void CardboardDisableScissor() = 0;
@ -209,46 +208,47 @@ class Renderer {
void DrawWorldToCameraBuffer(FrameDef* frame_def);
void UpdatePixelScaleAndBackingBuffer(FrameDef* frame_def);
void UpdateCameraRenderTargets(FrameDef* frame_def);
#if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
void HandleFunkyMacGammaIssue(FrameDef* frame_def);
#endif
// #if BA_OSTYPE_MACOS && BA_SDL_BUILD && !BA_SDL2_BUILD
// void HandleFunkyMacGammaIssue(FrameDef* frame_def);
// #endif
void LoadMedia(FrameDef* frame_def);
void UpdateDOFParams(FrameDef* frame_def);
#if BA_VR_BUILD
void VRPreprocess(FrameDef* frame_def);
void VRUpdateForEyeRender(FrameDef* frame_def);
void VRDrawOverlayFlatPass(FrameDef* frame_def);
// raw values from vr system
VRHandsState vr_raw_hands_state_;
float vr_raw_head_tx_ = 0.0f;
float vr_raw_head_ty_ = 0.0f;
float vr_raw_head_tz_ = 0.0f;
float vr_raw_head_yaw_ = 0.0f;
float vr_raw_head_pitch_ = 0.0f;
float vr_raw_head_roll_ = 0.0f;
// final game-space transforms
Matrix44f vr_base_transform_ = kMatrix44fIdentity;
Matrix44f vr_transform_right_hand_ = kMatrix44fIdentity;
Matrix44f vr_transform_left_hand_ = kMatrix44fIdentity;
Matrix44f vr_transform_head_ = kMatrix44fIdentity;
// values for current eye render
bool vr_use_fov_tangents_ = false;
float vr_fov_l_tan_ = 1.0f;
float vr_fov_r_tan_ = 1.0f;
float vr_fov_b_tan_ = 1.0f;
float vr_fov_t_tan_ = 1.0f;
float vr_fov_degrees_x_ = 30.0f;
float vr_fov_degrees_y_ = 30.0f;
float vr_eye_x_ = 0.0f;
float vr_eye_y_ = 0.0f;
float vr_eye_z_ = 0.0f;
int vr_eye_ = 0;
float vr_eye_yaw_ = 0.0f;
float vr_eye_pitch_ = 0.0f;
float vr_eye_roll_ = 0.0f;
int vr_viewport_x_ = 0;
int vr_viewport_y_ = 0;
// Raw values from vr system.
VRHandsState vr_raw_hands_state_{};
float vr_raw_head_tx_{};
float vr_raw_head_ty_{};
float vr_raw_head_tz_{};
float vr_raw_head_yaw_{};
float vr_raw_head_pitch_{};
float vr_raw_head_roll_{};
// Final game-space transforms.
Matrix44f vr_base_transform_{kMatrix44fIdentity};
Matrix44f vr_transform_right_hand_{kMatrix44fIdentity};
Matrix44f vr_transform_left_hand_{kMatrix44fIdentity};
Matrix44f vr_transform_head_{kMatrix44fIdentity};
// Values for current eye render.
bool vr_use_fov_tangents_{};
float vr_fov_l_tan_{1.0f};
float vr_fov_r_tan_{1.0f};
float vr_fov_b_tan_{1.0f};
float vr_fov_t_tan_{1.0f};
float vr_fov_degrees_x_{30.0f};
float vr_fov_degrees_y_{30.0f};
float vr_eye_x_{};
float vr_eye_y_{};
float vr_eye_z_{};
int vr_eye_{};
float vr_eye_yaw_{};
float vr_eye_pitch_{};
float vr_eye_roll_{};
int vr_viewport_x_{};
int vr_viewport_y_{};
#endif // BA_VR_BUILD
bool screen_size_dirty_{};
bool msaa_enabled_dirty_{};
millisecs_t dof_update_time_{};
@ -269,7 +269,7 @@ class Renderer {
Vector3f vignette_outer_{0.0f, 0.0f, 0.0f};
Vector3f vignette_inner_{1.0f, 1.0f, 1.0f};
int shadow_res_{-1};
float screen_gamma_requested_{1.0f};
// float screen_gamma_requested_{1.0f};
float screen_gamma_{1.0f};
float pixel_scale_requested_{1.0f};
float pixel_scale_{1.0f};

View File

@ -163,8 +163,12 @@ class FrameDef {
auto shake_original() const -> const Vector3f& { return shake_original_; }
#if BA_DEBUG_BUILD
auto defining_component() const -> bool { return defining_component_; }
void set_defining_component(bool val) { defining_component_ = val; }
// For debugging; there should ever only be a single RenderComponent writing
// commands to us at once.
auto active_render_component() const { return active_render_component_; }
void set_active_render_component(RenderComponent* c) {
active_render_component_ = c;
}
#endif
private:
@ -191,6 +195,7 @@ class FrameDef {
// Sanity checking: make sure components are completely submitted
// before new ones are started (so we dont get scrambled command buffers).
bool defining_component_{};
RenderComponent* active_render_component_{};
#endif
std::unique_ptr<RenderPass> light_pass_;

View File

@ -158,17 +158,18 @@ void NetGraph::Draw(RenderPass* pass, double time, double x, double y, double w,
SimpleComponent c2(pass);
c2.SetTransparent(true);
c2.SetColor(1, 0, 0, 1);
c2.PushTransform();
c2.Translate(static_cast<float>(x), static_cast<float>(y + h));
float scale = static_cast<float>(h) * 0.006f;
c2.Scale(scale, scale);
int text_elem_count = impl_->max_vel_text.GetElementCount();
for (int e = 0; e < text_elem_count; e++) {
c2.SetTexture(impl_->max_vel_text.GetElementTexture(e));
c2.SetFlatness(1.0f);
c2.DrawMesh(impl_->max_vel_text.GetElementMesh(e));
{
auto xf = c2.ScopedTransform();
c2.Translate(static_cast<float>(x), static_cast<float>(y + h));
float scale = static_cast<float>(h) * 0.006f;
c2.Scale(scale, scale);
int text_elem_count = impl_->max_vel_text.GetElementCount();
for (int e = 0; e < text_elem_count; e++) {
c2.SetTexture(impl_->max_vel_text.GetElementTexture(e));
c2.SetFlatness(1.0f);
c2.DrawMesh(impl_->max_vel_text.GetElementMesh(e));
}
}
c2.PopTransform();
c2.Submit();
}

View File

@ -19,16 +19,19 @@ namespace ballistica::base {
// this class provides to you, drawing each in the same manner.
class TextGroup : public Object {
public:
// the number of meshes needing to be drawn for this text
// The number of meshes needing to be drawn for this text.
auto GetElementCount() -> int { return static_cast<int>(entries_.size()); }
auto GetElementMesh(int index) const -> TextMesh* {
assert(index < static_cast<int>(entries_.size()));
return &(entries_[index]->mesh);
}
auto GetElementTexture(int index) const -> TextureAsset* {
assert(index < static_cast<int>(entries_.size()));
return entries_[index]->tex.Get();
}
// if you are doing any shader effects in UV-space (such as drop-shadows),
// scale them by this ..this will account for different character sheets
// with different sized characters
@ -36,18 +39,22 @@ class TextGroup : public Object {
assert(index < static_cast<int>(entries_.size()));
return entries_[index]->u_scale;
}
auto GetElementVScale(int index) -> float {
assert(index < static_cast<int>(entries_.size()));
return entries_[index]->v_scale;
}
auto GetElementMaxFlatness(int index) const -> float {
assert(index < static_cast<int>(entries_.size()));
return entries_[index]->max_flatness;
}
auto GetElementCanColor(int index) const -> bool {
assert(index < static_cast<int>(entries_.size()));
return entries_[index]->can_color;
}
auto GetElementMaskUV2Texture(int index) const -> TextureAsset* {
assert(index < static_cast<int>(entries_.size()));
return g_base->assets->SysTexture(entries_[index]->type
@ -55,11 +62,14 @@ class TextGroup : public Object {
? SysTextureID::kSoftRect2
: SysTextureID::kSoftRect);
}
void SetText(const std::string& text,
TextMesh::HAlign alignment_h = TextMesh::HAlign::kLeft,
TextMesh::VAlign alignment_v = TextMesh::VAlign::kNone,
bool big = false, float resolution_scale = 1.0f);
auto text() const -> const std::string& { return text_; }
void GetCaratPts(const std::string& text_in, TextMesh::HAlign alignment_h,
TextMesh::VAlign alignment_v, int carat_pos, float* carat_x,
float* carat_y);

View File

@ -5,8 +5,6 @@
#include "ballistica/core/core.h"
#include "ballistica/core/platform/core_platform.h"
#if BA_ENABLE_OPENGL
/* DDS loader written by Jon Watte 2002 */
/* Permission granted to use freely, as long as Jon Watte */
/* is held harmless for all possible damages resulting from */
@ -16,10 +14,6 @@
namespace ballistica::base {
// Should tidy this up to use unsigned vals but don't want to touch for now.
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
struct DdsLoadInfo {
bool compressed;
bool swap;
@ -139,8 +133,4 @@ void LoadDDS(const std::string& file_name, unsigned char** buffers, int* widths,
fclose(f);
}
#pragma clang diagnostic pop
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL

View File

@ -3,9 +3,6 @@
#ifndef BALLISTICA_BASE_GRAPHICS_TEXTURE_DDS_H_
#define BALLISTICA_BASE_GRAPHICS_TEXTURE_DDS_H_
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedMacroInspection"
/* DDS loader written by Jon Watte 2002 */
/* Permission granted to use freely, as long as Jon Watte */
/* is held harmless for all possible damages resulting from */
@ -17,8 +14,6 @@
#include "ballistica/base/base.h"
#if BA_ENABLE_OPENGL
// little-endian, of course
#define DDS_MAGIC 0x20534444
@ -98,8 +93,6 @@
#define PF_IS_INDEX8(pf) \
(((pf).dwFlags & DDPF_INDEXED) && ((pf).dwRGBBitCount == 8))
#pragma clang diagnostic pop
namespace ballistica::base {
union DDS_header {
@ -152,6 +145,4 @@ void LoadDDS(const std::string& file_name, unsigned char** buffers, int* widths,
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_TEXTURE_DDS_H_

View File

@ -5,23 +5,8 @@
#include "ballistica/core/core.h"
#include "ballistica/core/platform/core_platform.h"
#if !BA_HEADLESS_BUILD
namespace ballistica::base {
// Inspection is not terribly happy about this file but it works so not
// gonna touch it for now.
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
#pragma ide diagnostic ignored "bugprone-narrowing-conversions"
#pragma ide diagnostic ignored "bugprone-macro-parentheses"
#pragma ide diagnostic ignored "UnusedValue"
#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
#pragma ide diagnostic ignored "OCUnusedMacroInspection"
#pragma ide diagnostic ignored "clang-analyzer-deadcode.DeadStores"
#pragma clang diagnostic ignored "-Wunused-variable"
// We don't want this to be dependent on GL so
// lets just define the few bits we need here
#ifndef GL_COMPRESSED_RGB8_ETC2
@ -2291,8 +2276,4 @@ void KTXUnpackETC(const GLubyte* srcETC, const GLenum srcFormat,
}
}
#pragma clang diagnostic pop
} // namespace ballistica::base
#endif // !BA_HEADLESS_BUILD

View File

@ -7,9 +7,6 @@
#include "ballistica/base/base.h"
// currently need gl for this stuff.. probably not necessary.
#if BA_ENABLE_OPENGL
namespace ballistica::base {
void LoadKTX(const std::string& file_name, unsigned char** buffers, int* widths,
@ -24,6 +21,4 @@ void KTXUnpackETC(const uint8_t* src_etc, unsigned int src_format,
} // namespace ballistica::base
#endif // BA_ENABLE_OPENGL
#endif // BALLISTICA_BASE_GRAPHICS_TEXTURE_KTX_H_

View File

@ -66,7 +66,7 @@ JoystickInput::JoystickInput(int sdl_joystick_id,
// In SDL2 we're passed a device-id but that's only used to open the
// joystick; events and most everything else use an instance ID, so we store
// that instead.
#if BA_SDL2_BUILD
// #if BA_SDL2_BUILD
sdl_joystick_id_ = SDL_JoystickInstanceID(sdl_joystick_);
raw_sdl_joystick_name_ = SDL_JoystickName(sdl_joystick_);
@ -78,14 +78,15 @@ JoystickInput::JoystickInput(int sdl_joystick_id,
&& raw_sdl_joystick_name_.size() <= 22) {
raw_sdl_joystick_name_ = "XInput Controller";
}
#else
raw_sdl_joystick_name_ = SDL_JoystickName(sdl_joystick_id_);
#endif // BA_SDL2_BUILD
// #else
// raw_sdl_joystick_name_ = SDL_JoystickName(sdl_joystick_id_);
// #endif // BA_SDL2_BUILD
// If its an SDL joystick and we're using our custom sdl 1.2 build, ask it.
#if BA_XCODE_BUILD && BA_OSTYPE_MACOS && !BA_SDL2_BUILD
raw_sdl_joystick_identifier_ = SDL_JoystickIdentifier(sdl_joystick_id_);
#endif
// #if BA_XCODE_BUILD && BA_OSTYPE_MACOS && !BA_SDL2_BUILD
// raw_sdl_joystick_identifier_ =
// SDL_JoystickIdentifier(sdl_joystick_id_);
// #endif
// Some special-cases on mac.
if (strstr(raw_sdl_joystick_name_.c_str(), "PLAYSTATION") != nullptr) {
@ -307,7 +308,7 @@ JoystickInput::~JoystickInput() {
#if BA_ENABLE_SDL_JOYSTICKS
assert(g_base->app_adapter);
auto joystick = sdl_joystick_;
g_core->main_event_loop()->PushCall(
g_base->app_adapter->PushMainThreadCall(
[joystick] { SDL_JoystickClose(joystick); });
sdl_joystick_ = nullptr;
#else

Some files were not shown because too many files have changed in this diff Show More